mindustry logic execution, map- and schematic- parsing and rendering
unit deser, take two (#9)
and final take
bendn 2023-08-26
parent e834ff8 · commit f20f6f7
-rw-r--r--Cargo.toml2
-rw-r--r--src/block/mod.rs4
-rw-r--r--src/block/payload.rs9
-rw-r--r--src/block/units.rs2
-rw-r--r--src/data/entity_mapping.rs13
-rw-r--r--src/data/map.rs11
-rw-r--r--src/data/mod.rs22
-rw-r--r--src/lib.rs7
-rw-r--r--src/unit/mod.rs118
9 files changed, 131 insertions, 57 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 9509c1f..7166acf 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "mindus"
-version = "4.0.9"
+version = "4.0.10"
edition = "2021"
description = "A library for working with mindustry data formats (eg schematics and maps) (fork of plandustry)"
authors = [
diff --git a/src/block/mod.rs b/src/block/mod.rs
index f48a62d..dda19a5 100644
--- a/src/block/mod.rs
+++ b/src/block/mod.rs
@@ -328,7 +328,7 @@ impl Block {
/// should you send context to [`image`]?
#[must_use]
#[inline]
- pub fn wants_context(&self) -> bool {
+ pub const fn wants_context(&self) -> bool {
use BlockLogicEnum::*;
matches!(
self.logic,
@@ -936,7 +936,7 @@ make_register! {
"water-extractor" -> BasicBlock::new(2, true, cost!(Copper: 30, Lead: 30, Metaglass: 30, Graphite: 30));
"oil-extractor" -> BasicBlock::new(3, true, cost!(Copper: 150, Lead: 115, Graphite: 175, Thorium: 115, Silicon: 75));
"vent-condenser" -> ProductionBlock::new(3, true, cost!(Graphite: 20, Beryllium: 60));
- "cliff-crusher" -> WallDrillBlock::new(2, false, cost!(Beryllium: 100, Graphite: 40));
+ "cliff-crusher" => WallDrillBlock::new(2, false, cost!(Beryllium: 100, Graphite: 40));
"plasma-bore" => DrillBlock::new(2, false, cost!(Beryllium: 40));
"large-plasma-bore" => DrillBlock::new(3, false, cost!(Silicon: 100, Oxide: 25, Beryllium: 100, Tungsten: 70));
"impact-drill" -> DrillBlock::new(4, true, cost!(Silicon: 70, Beryllium: 90, Graphite: 60));
diff --git a/src/block/payload.rs b/src/block/payload.rs
index 300e366..48b8e67 100644
--- a/src/block/payload.rs
+++ b/src/block/payload.rs
@@ -64,7 +64,8 @@ make_simple!(
base.overlay(& load!(concat "over" => n which is ["payload-router" | "reinforced-payload-router"], s));
}
base
- } // read_payload_router
+ },
+ read_payload_router
);
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
@@ -203,7 +204,7 @@ pub(crate) fn read_payload_block(buff: &mut DataRead) -> Result<(), DataReadErro
/// - if type == 2 (paylood unit):
/// - id: [`u8`]
/// - call [`UnitClass::read`](crate::data::entity_mapping::UnitClass::read)
-fn read_payload(buff: &mut DataRead) -> Result<(), DataReadError> {
+pub fn read_payload(buff: &mut DataRead) -> Result<(), DataReadError> {
if !buff.read_bool()? {
return Ok(());
}
@@ -213,9 +214,11 @@ fn read_payload(buff: &mut DataRead) -> Result<(), DataReadError> {
match t {
BLOCK => {
let b = buff.read_u16()?;
+ buff.skip(1)?;
let b = BlockEnum::try_from(b).unwrap_or(BlockEnum::Router);
let block = BLOCK_REGISTRY.get(b.get_name()).unwrap();
- block.logic.read(&mut Build::new(block), buff)?;
+ let mut b = Build::new(block);
+ let _ = b.read(buff);
}
UNIT => {
let u = buff.read_u8()? as usize;
diff --git a/src/block/units.rs b/src/block/units.rs
index 4af1a64..1fe0ade 100644
--- a/src/block/units.rs
+++ b/src/block/units.rs
@@ -3,10 +3,10 @@ use thiserror::Error;
use super::payload::{read_payload_block, read_payload_seq};
use crate::block::simple::*;
+use crate::block::*;
use crate::data::command::UnitCommand;
use crate::data::dynamic::DynType;
use crate::unit;
-use crate::{block::*, Serializable};
// fn is_pay(b: &str) -> bool {
// matches!(
diff --git a/src/data/entity_mapping.rs b/src/data/entity_mapping.rs
index eda8479..beed2b4 100644
--- a/src/data/entity_mapping.rs
+++ b/src/data/entity_mapping.rs
@@ -1,15 +1,18 @@
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum UnitClass {
Block,
+ // same read impl as block
Legs,
Elevated,
Crawl,
+ Boat,
+ Tank,
+ Air,
+ // different read impl from block
Mech,
Tethered,
Payload,
Bomb,
- Boat,
- Tank,
}
pub static ID: [Option<UnitClass>; 47] = amap::amap! {
@@ -17,10 +20,12 @@ pub static ID: [Option<UnitClass>; 47] = amap::amap! {
24 => UnitClass::Legs,
45 => UnitClass::Elevated,
46 => UnitClass::Crawl,
- 4 => UnitClass::Mech,
36 => UnitClass::Tethered,
- 5 => UnitClass::Payload,
+ 5 | 23 | 26 => UnitClass::Payload,
39 => UnitClass::Bomb,
20 => UnitClass::Boat,
43 => UnitClass::Tank,
+ 4 | 17 | 19 | 32 => UnitClass::Mech,
+ 21 | 29 | 33 => UnitClass::Legs,
+ 3 | 16 | 18 | 0 | 30 | 31 => UnitClass::Air,
};
diff --git a/src/data/map.rs b/src/data/map.rs
index edb3cb2..c4ad43d 100644
--- a/src/data/map.rs
+++ b/src/data/map.rs
@@ -84,7 +84,7 @@ use crate::team::{self, Team};
#[cfg(doc)]
use crate::{block::content, data::*, fluid, item, modifier, unit};
-use super::Serializable;
+use super::{entity_mapping, Serializable};
use crate::content::Content;
use crate::utils::image::ImageUtils;
@@ -655,7 +655,14 @@ impl<'l> Serializable for Map<'l> {
// read world entities (#412). eg units
for _ in 0..buff.read_u32()? {
let len = buff.read_u16()? as usize;
- buff.skip(len)?;
+ let id = buff.read_u8()? as usize;
+ let Some(&Some(u)) = entity_mapping::ID.get(id) else {
+ buff.skip(len - 1)?;
+ continue;
+ // return Ok(());
+ };
+ buff.skip(4)?;
+ let _ = u.read(buff)?;
}
Ok::<(), ReadError>(())
})?;
diff --git a/src/data/mod.rs b/src/data/mod.rs
index 85cbbd6..0cd1d66 100644
--- a/src/data/mod.rs
+++ b/src/data/mod.rs
@@ -139,28 +139,26 @@ impl<'d> DataRead<'d> {
} else {
self.read_u16()? as usize
};
- self.read = 0;
+ let rb4 = self.read;
let r = f(self);
+ let read = self.read - rb4;
match r {
Err(e) => {
// skip this chunk
- assert!(
- len >= self.read,
- "overread; supposed to read {len}; read {}",
- self.read
- );
- let n = len - self.read;
+ assert!(len >= read, "overread; supposed to read {len}; read {read}");
+ let n = len - read;
if n != 0 {
#[cfg(debug_assertions)]
- println!(
- "supposed to read {len}; read {} - skipping excess",
- self.read
- );
+ println!("supposed to read {len}; read {read} - skipping excess");
self.skip(n)?;
};
Err(e)
}
- Ok(_) => Ok(()),
+ Ok(_) => {
+ debug_assert!(len >= read, "overread; supposed to read {len}; read {read}");
+ debug_assert!((len - read) == 0, "supposed to read {len}; read {read}");
+ Ok(())
+ }
}
}
diff --git a/src/lib.rs b/src/lib.rs
index e13c4b0..20c3ab7 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -8,7 +8,12 @@
let_chains,
effects
)]
-#![allow(clippy::missing_safety_doc, clippy::missing_const_for_fn, clippy::perf)]
+#![warn(
+ clippy::missing_safety_doc,
+ clippy::missing_const_for_fn,
+ clippy::dbg_macro,
+ clippy::perf
+)]
pub mod block;
mod content;
pub mod data;
diff --git a/src/unit/mod.rs b/src/unit/mod.rs
index b6db959..c7d66bf 100644
--- a/src/unit/mod.rs
+++ b/src/unit/mod.rs
@@ -1,6 +1,7 @@
//! units
//!
//! [source](https://github.com/Anuken/Mindustry/blob/master/core/src/mindustry/content/UnitTypes.java)
+use crate::block::payload::read_payload;
use crate::content::content_enum;
use crate::data::command::UnitCommand;
use crate::data::dynamic::DynData;
@@ -177,36 +178,89 @@ impl UnitClass {
let mut state = UnitState::default();
read_abilities(buff)?;
state.ammo = buff.read_f32()?;
- state.controller = Controller::read(buff)?;
- state.elevation = buff.read_f32()?;
- state.flag = buff.read_i64()?;
- state.health = buff.read_f32()?;
- state.is_shooting = buff.read_bool()?;
- // TODO apparently i fucked up my impl
- // dbg!(&state);
- // read_tile(buff)?;
- // read_mounts(buff)?;
- // read_plans(buff)?;
- // state.rotation = buff.read_f32()?;
- // state.shield = buff.read_f32()?;
- // buff.skip(1)?; // spawned_by_core
- // state.stack = read_stack(buff)?;
- // state.status = read_status(buff)?;
- // state.team = Team::of(buff.read_u8()?);
- // let ty = Type::try_from(buff.read_u16()?).unwrap();
- // buff.skip(1)?; // update_building
- // state.velocity = (buff.read_f32()?, buff.read_f32()?);
- // state.position = (buff.read_f32()?, buff.read_f32()?);
-
- Ok(Unit {
- state,
- ty: Type::Alpha,
- })
+ match self {
+ Self::Block
+ | Self::Legs
+ | Self::Elevated
+ | Self::Crawl
+ | Self::Boat
+ | Self::Tank
+ | Self::Air => {
+ state.controller = Controller::read(buff)?;
+ state.elevation = buff.read_f32()?;
+ state.flag = buff.read_i64()?;
+ state.health = buff.read_f32()?;
+ state.is_shooting = buff.read_bool()?;
+ read_tile(buff)?;
+ read_mounts(buff)?;
+ }
+ Self::Mech => {
+ buff.skip(4)?; // base rotation
+ state.controller = Controller::read(buff)?;
+ state.elevation = buff.read_f32()?;
+ state.flag = buff.read_i64()?;
+ state.health = buff.read_f32()?;
+ state.is_shooting = buff.read_bool()?;
+ read_tile(buff)?;
+ read_mounts(buff)?;
+ }
+ Self::Payload => {
+ state.controller = Controller::read(buff)?;
+ state.elevation = buff.read_f32()?;
+ state.flag = buff.read_i64()?;
+ state.health = buff.read_f32()?;
+ state.is_shooting = buff.read_bool()?;
+ read_tile(buff)?;
+ read_mounts(buff)?;
+ for _ in 0..buff.read_i32()? {
+ // recursion more!
+ // this is unreliable, as read_payload may not read the full block.
+ // if read_plans reports a error, with a payload unit, this is why
+ let _ = read_payload(buff);
+ }
+ }
+ Self::Bomb => {
+ state.controller = Controller::read(buff)?;
+ state.elevation = buff.read_f32()?;
+ state.flag = buff.read_i64()?;
+ state.health = buff.read_f32()?;
+ state.is_shooting = buff.read_bool()?;
+ buff.skip(4)?; // lifetime
+ read_tile(buff)?;
+ read_mounts(buff)?;
+ }
+ Self::Tethered => {
+ buff.skip(4)?;
+ state.controller = Controller::read(buff)?;
+ state.elevation = buff.read_f32()?;
+ state.flag = buff.read_i64()?;
+ state.health = buff.read_f32()?;
+ state.is_shooting = buff.read_bool()?;
+ read_tile(buff)?;
+ read_mounts(buff)?;
+ for _ in 0..buff.read_i32()? {
+ // recursion more!
+ read_payload(buff)?;
+ }
+ }
+ }
+ read_plans(buff)?;
+ state.rotation = buff.read_f32()?;
+ state.shield = buff.read_f32()?;
+ buff.skip(1)?; // spawned_by_core
+ state.stack = read_stack(buff)?;
+ state.status = read_status(buff)?;
+ state.team = Team::of(buff.read_u8()?);
+ let ty = Type::try_from(buff.read_u16()?).unwrap();
+ buff.skip(1)?; // update_building
+ state.velocity = (buff.read_f32()?, buff.read_f32()?);
+ state.position = (buff.read_f32()?, buff.read_f32()?);
+ Ok(Unit { state, ty })
}
}
/// format:
-/// - iterate [u8]
+/// - iterate [`u8`]
/// - ability: [`f32`]
fn read_abilities(buff: &mut DataRead) -> Result<(), ReadError> {
let n = buff.read_u8()? as usize;
@@ -225,7 +279,7 @@ fn read_tile(buff: &mut DataRead) -> Result<(), ReadError> {
/// - x aim: [`f32`]
/// - y aim: [`f32`]
fn read_mounts(buff: &mut DataRead) -> Result<(), ReadError> {
- let n = dbg!(buff.read_u8()? as usize);
+ let n = buff.read_u8()? as usize;
buff.skip(n * 9)
}
@@ -235,7 +289,7 @@ fn read_mounts(buff: &mut DataRead) -> Result<(), ReadError> {
/// - iterate `plan_count`
/// - call [`read_plan`]
fn read_plans(buff: &mut DataRead) -> Result<(), ReadError> {
- let used = dbg!(buff.read_i32()?);
+ let used = buff.read_i32()?;
if used == -1 {
return Ok(());
}
@@ -280,12 +334,14 @@ fn read_stack(buff: &mut DataRead) -> Result<(Option<Item>, u32), ReadError> {
/// format:
/// - iterate [`i32`]
/// - status: [`u16`] attempt into [`Status`]
+/// - duration: [`f32`]
fn read_status(buff: &mut DataRead) -> Result<[Status; 3], ReadError> {
let mut status = [Status::None, Status::None, Status::None];
for i in 0..buff.read_i32()? {
- let this = Status::try_from(buff.read_u16()?).unwrap_or_default();
- if i < 3 {
- status[i as usize] = this;
+ let this = Status::try_from(buff.read_u16()?);
+ buff.skip(4)?;
+ if let Ok(s) = this && i < 3 {
+ status[i as usize] = s;
}
}
Ok(status)