mindustry logic execution, map- and schematic- parsing and rendering
-rw-r--r--mindus/Cargo.toml2
-rw-r--r--mindus/src/block/content.rs20
-rw-r--r--mindus/src/content.rs11
-rw-r--r--mindus/src/data/map.rs81
-rw-r--r--mindus/src/data/renderer.rs8
-rw-r--r--mindus/src/exe/map.rs6
-rw-r--r--mindus/src/lib.rs1
-rw-r--r--mindus/src/unit.rs23
8 files changed, 113 insertions, 39 deletions
diff --git a/mindus/Cargo.toml b/mindus/Cargo.toml
index 6610afc..7871249 100644
--- a/mindus/Cargo.toml
+++ b/mindus/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "mindus"
-version = "5.0.32"
+version = "5.0.33"
edition = "2021"
description = "A library for working with mindustry data formats (eg schematics and maps) (fork of plandustry)"
authors = [
diff --git a/mindus/src/block/content.rs b/mindus/src/block/content.rs
index d25c83f..5fdb007 100644
--- a/mindus/src/block/content.rs
+++ b/mindus/src/block/content.rs
@@ -6,6 +6,8 @@ content_enum! {
{
"air",
"spawn",
+ "remove-wall",
+ "remove-ore",
"cliff",
"build1",
"build2",
@@ -69,6 +71,8 @@ content_enum! {
"yellow-stone-vent",
"red-stone-vent",
"crystalline-vent",
+ "stone-vent",
+ "basalt-vent",
"redmat",
"bluemat",
"grass",
@@ -184,6 +188,7 @@ content_enum! {
"slag-heater",
"phase-heater",
"heat-redirector",
+ "small-heat-redirector",
"heat-router",
"slag-incinerator",
"carbide-crucible",
@@ -309,6 +314,7 @@ content_enum! {
"oil-extractor",
"vent-condenser",
"cliff-crusher",
+ "large-cliff-crusher",
"plasma-bore",
"large-plasma-bore",
"impact-drill",
@@ -365,8 +371,8 @@ content_enum! {
"ship-fabricator",
"mech-fabricator",
"tank-refabricator",
- "mech-refabricator",
"ship-refabricator",
+ "mech-refabricator",
"prime-refabricator",
"tank-assembler",
"ship-assembler",
@@ -401,6 +407,8 @@ content_enum! {
"legacy-unit-factory-ground",
"command-center",
"launch-pad",
+ "advanced-launch-pad",
+ "landing-pad",
"interplanetary-accelerator",
"message",
"switch",
@@ -411,20 +419,12 @@ content_enum! {
"memory-bank",
"logic-display",
"large-logic-display",
+ "tile-logic-display",
"canvas",
"reinforced-message",
"world-processor",
"world-cell",
"world-message",
-
-
-
- // shouldn't really exist
- "large-cliff-crusher",
- "small-heat-redirector",
- "advanced-launch-pad",
- "landing-pad",
"world-switch",
- "tile-logic-display",
}
}
diff --git a/mindus/src/content.rs b/mindus/src/content.rs
index 60e1ca3..10d612b 100644
--- a/mindus/src/content.rs
+++ b/mindus/src/content.rs
@@ -66,6 +66,17 @@ macro_rules! content_enum {
impl $tname {
pub const ALL: [Self; $crate::content::count_exprs!($($val)+)] = [$(Self::[<$val:camel>]),+];
+
+ pub (crate) fn by_name(name:&str)-> Option<Self> {
+ static MAPPER: std::sync::LazyLock<std::collections::HashMap<&str, $tname>> = std::sync::LazyLock::new(
+ || std::collections::HashMap::from_iter(
+ [
+ $(($val, $tname::[<$val:camel>]),)+
+ ]
+ )
+ );
+ MAPPER.get(name).copied()
+ }
}
impl $crate::content::Content for $tname {
diff --git a/mindus/src/data/map.rs b/mindus/src/data/map.rs
index f848f95..bb49804 100644
--- a/mindus/src/data/map.rs
+++ b/mindus/src/data/map.rs
@@ -8,14 +8,14 @@
//!
//! ZLIB compressed stream contains:
//! - header: 4b = `MSCH` [`MapReader::header`]
-//! - version: `u32` (should be 7) [`MapReader::version`]
+//! - version: `u32` (should be 7+) [`MapReader::version`]
//! - tag section `<u32>` [`MapReader::tags`]
//! - 1 byte of idk (skip)
//! - string map (`u16` for map len, iterate each, read `utf`)
-//! - content header section `<u32>`: [`MapReader::skip`]
-//! - iterate `i8` (should = `8`)'
+//! - content header section `<u32>`: [`MapReader::content`] (note: if map v8, will use [`BlockEnum`] and skip reading this)
+//! - iterate `i8` (should = `10`)'
//! - the type: `i8` (0: item, block: 1, liquid: 4, status: 5, unit: 6, weather: 7, sector: 9, planet: 13
-//! - item count: `u16` (item: 22, block: 412, liquid: 11, status: 21, unit: 66, weather: 6, sector: 35, planet: 7)
+//! - item count: `u16` (item: 22, block: 422, liquid: 11, status: 21, unit: 66, weather: 6, sector: 35, planet: 7)
//! - these types all have their own modules: [`item`], [`content`], [`fluid`], [`modifier`], [`mod@unit`], [`weather`], [`sector`], [`planet`]
//! - iterate `u16`
//! - name: `utf`
@@ -70,6 +70,7 @@
//! - continue
//! - id: `u32`
//! - entity read
+//! - markers section (v8)
use std::collections::HashMap;
use std::ops::CoroutineState::*;
use std::ops::{Coroutine, Index, IndexMut};
@@ -520,7 +521,7 @@ pub enum ReadError {
#[error("unsupported version ({0})")]
Version(u8),
#[error("unknown block {0:?}")]
- NoSuchBlock(String),
+ NoSuchBlock(u16),
#[error("failed to read block data")]
ReadState(#[from] super::dynamic::ReadError),
}
@@ -569,13 +570,13 @@ pub enum EntityData {
}
macro_rules! tiles {
- ($count:ident, $me:ident, $w: ident) => {
+ ($count:ident, $me:ident, $w: ident,$r:ident) => {
let mut i = 0;
while i < $count {
let floor_id = $me.buff.read_u16()?;
let overlay_id = $me.buff.read_u16()?;
- let floor = BlockEnum::try_from(floor_id).unwrap_or(BlockEnum::Stone);
- let ore = BlockEnum::try_from(overlay_id).unwrap_or(BlockEnum::Air);
+ let &floor = $r.get(floor_id as usize).unwrap_or(&BlockEnum::Stone);
+ let &ore = $r.get(overlay_id as usize).unwrap_or(&BlockEnum::Air);
yield $w::Tile { floor, ore };
let consecutives = $me.buff.read_u8()? as usize;
for _ in 0..consecutives {
@@ -587,6 +588,7 @@ macro_rules! tiles {
};
}
+pub type Registrar = [BlockEnum; BlockEnum::ALL.len()];
impl MapReader {
pub fn new(buff: &mut DataRead<'_>) -> Result<Self, ReadError> {
let backing = buff.deflate()?;
@@ -604,9 +606,8 @@ impl MapReader {
}
pub fn version(&mut self) -> Result<u32, ReadError> {
- // NOTE: will change to 8 soon
let x = self.buff.read_u32()?;
- (x == 7)
+ (x == 7 || x == 8)
.then_some(x)
.ok_or(ReadError::Version(x.try_into().unwrap_or(0)))
}
@@ -622,6 +623,31 @@ impl MapReader {
Ok(tags)
}
+ pub fn content(&mut self, version: u32) -> Result<Registrar, ReadError> {
+ let mut registrar = BlockEnum::ALL;
+ let n = self.buff.read_u32()?;
+ if version < 8 {
+ for _ in 0..self.buff.read_i8()? {
+ let ty = self.buff.read_u8()?;
+ for index in 0..self.buff.read_u16()? as usize {
+ if ty == 1 {
+ let name = self.buff.read_utf()?;
+ registrar
+ .get_mut(index)
+ .map(|x| *x = BlockEnum::by_name(name).unwrap_or(BlockEnum::Air));
+ } else {
+ let n = self.buff.read_u16()?;
+ self.buff.skip(n as usize)?;
+ }
+ }
+ }
+ dbg!(registrar);
+ } else {
+ self.buff.skip(n as _)?;
+ }
+ Ok(registrar)
+ }
+
pub fn tags_alloc(&mut self) -> Result<HashMap<String, String>, ReadError> {
let mut tags = HashMap::new();
self.buff
@@ -645,6 +671,7 @@ impl MapReader {
pub fn thin_map(
&mut self,
+ r: Registrar,
) -> Result<
impl Coroutine<(), Return = Result<(), ReadError>, Yield = ThinMapData> + '_,
ReadError,
@@ -662,7 +689,7 @@ impl MapReader {
let w = w as usize;
let h = h as usize;
let count = w * h;
- tiles!(count, self, ThinMapData);
+ tiles!(count, self, ThinMapData, r);
let mut i = 0;
while i < count {
@@ -675,8 +702,9 @@ impl MapReader {
} else {
false
};
- let block = BlockEnum::try_from(block_id)
- .map_err(|_| ReadError::NoSuchBlock(block_id.to_string()))?;
+ let block = *r
+ .get(block_id as usize)
+ .ok_or(ReadError::NoSuchBlock(block_id))?;
let block = block.to_block();
let Some(block) = block else {
let consecutives = self.buff.read_u8()?;
@@ -724,8 +752,12 @@ impl MapReader {
Ok(map)
}
- pub fn collect_map(&mut self, tags: HashMap<String, String>) -> Result<Map, ReadError> {
- let mut co = self.map()?;
+ pub fn collect_map(
+ &mut self,
+ tags: HashMap<String, String>,
+ r: Registrar,
+ ) -> Result<Map, ReadError> {
+ let mut co = self.map(r)?;
let (w, h) = match Pin::new(&mut co).resume(()) {
Yielded(MapData::Init { width, height }) => (width as usize, height as usize),
Complete(Err(x)) => return Err(x),
@@ -771,6 +803,7 @@ impl MapReader {
pub fn map(
&mut self,
+ r: Registrar,
) -> Result<impl Coroutine<(), Return = Result<(), ReadError>, Yield = MapData> + '_, ReadError>
{
let len = self.buff.read_u32()? as usize;
@@ -787,7 +820,7 @@ impl MapReader {
let w = w as usize;
let h = h as usize;
let count = w * h;
- tiles!(count, self, MapData);
+ tiles!(count, self, MapData, r);
let mut i = 0;
while i < count {
@@ -800,8 +833,9 @@ impl MapReader {
} else {
false
};
- let block = BlockEnum::try_from(block_id)
- .map_err(|_| ReadError::NoSuchBlock(block_id.to_string()))?;
+ let block = r
+ .get(block_id as usize)
+ .ok_or(ReadError::NoSuchBlock(block_id))?;
let block = block.to_block();
let Some(block) = block else {
let consecutives = self.buff.read_u8()?;
@@ -930,11 +964,16 @@ impl Serializable for Map {
fn deserialize(buff: &mut DataRead<'_>) -> Result<Map, Self::ReadError> {
let mut buff = MapReader::new(buff)?;
buff.header()?;
- buff.version()?;
+ let v = buff.version()?;
let tags = buff.tags_alloc()?;
- buff.skip()?;
- let mut m = buff.collect_map(tags)?;
+ let r = buff.content(v)?;
+
+ let mut m = buff.collect_map(tags, r)?;
m.entities = buff.collect_entities()?;
+ if v == 8 {
+ // skip marker region
+ buff.skip()?;
+ }
// skip custom chunks
buff.skip()?;
diff --git a/mindus/src/data/renderer.rs b/mindus/src/data/renderer.rs
index ee5e66b..b5f3006 100644
--- a/mindus/src/data/renderer.rs
+++ b/mindus/src/data/renderer.rs
@@ -7,6 +7,7 @@ use super::schematic::Schematic;
use super::GridPos;
use crate::block::content::Type;
use crate::color_mapping::BLOCK2COLOR;
+use crate::data::map::Registrar;
use crate::team::Team;
pub(crate) use crate::utils::*;
use crate::Map;
@@ -365,9 +366,10 @@ pub fn draw_units(
/// Will walk through the map section. use [`draw_units`] after, if you like.
pub fn draw_map_single(
map: &mut crate::data::map::MapReader,
+ r: Registrar,
) -> Result<(Image<Box<[u8]>, 3>, (u16, u16)), super::map::ReadError> {
use std::ops::CoroutineState::*;
- let mut co = map.thin_map()?;
+ let mut co = map.thin_map(r)?;
let (w, h) = match Pin::new(&mut co).resume(()) {
Yielded(ThinMapData::Init { width, height }) => (width, height),
Complete(Err(x)) => return Err(x),
@@ -485,11 +487,13 @@ pub fn draw_map_single(
}
/// Draws a map in a single pass. Uses the silly team color thing that mindustry has.
+/// probably broken- if you want to use this ask me to update the block2color
pub fn draw_map_simple(
map: &mut crate::data::map::MapReader,
+ r: Registrar,
) -> Result<(Image<Box<[u8]>, 3>, (u16, u16)), super::map::ReadError> {
use std::ops::CoroutineState::*;
- let mut co = map.thin_map()?;
+ let mut co = map.thin_map(r)?;
let (w, h) = match Pin::new(&mut co).resume(()) {
Yielded(ThinMapData::Init { width, height }) => (width, height),
Complete(Err(x)) => return Err(x),
diff --git a/mindus/src/exe/map.rs b/mindus/src/exe/map.rs
index 96b14f0..09719bd 100644
--- a/mindus/src/exe/map.rs
+++ b/mindus/src/exe/map.rs
@@ -12,12 +12,12 @@ pub fn main(args: Args) {
match (|| {
let mut m = MapReader::new(&mut DataRead::new(&s))?;
m.header()?;
- m.version()?;
+ let v = m.version()?;
let t = m.tags()?;
dbg!(&t);
println!("rendering {}", t.get("name").unwrap_or(&"<unknown>"));
- m.skip()?;
- let (mut img, sz) = mindus::data::renderer::draw_map_single(&mut m)?;
+ let r = m.content(v)?;
+ let (mut img, sz) = mindus::data::renderer::draw_map_single(&mut m, r)?;
mindus::data::renderer::draw_units(&mut m, img.as_mut(), sz)?;
Ok::<_, mindus::data::map::ReadError>(img)
})() {
diff --git a/mindus/src/lib.rs b/mindus/src/lib.rs
index caf657b..ad3a532 100644
--- a/mindus/src/lib.rs
+++ b/mindus/src/lib.rs
@@ -1,5 +1,6 @@
//! crate for dealing with mindustry
#![feature(
+ inherent_associated_types,
maybe_uninit_write_slice,
stmt_expr_attributes,
generic_const_exprs,
diff --git a/mindus/src/unit.rs b/mindus/src/unit.rs
index b35e3a8..6a27b1d 100644
--- a/mindus/src/unit.rs
+++ b/mindus/src/unit.rs
@@ -153,7 +153,7 @@ impl Controller {
Ok(match buff.read_u8()? {
0 => Controller::Player(buff.read_i32()?),
3 => Controller::Logic(buff.read_i32()?),
- 6 => {
+ t @ (4 | 6 | 7 | 8) => {
let has_attack = buff.read_bool()?;
let pos = if buff.read_bool()? {
Some((buff.read_f32()?, buff.read_f32()?))
@@ -174,12 +174,31 @@ impl Controller {
} else {
None
};
+ if let 7 | 8 = t {
+ for _ in 0..buff.read_u8()? {
+ match buff.read_u8()? {
+ 0 | 1 => {
+ buff.read_u32()?;
+ }
+ 2 => {
+ buff.read_f32()?;
+ buff.read_f32()?;
+ }
+ _ => {}
+ }
+ }
+ }
+ if t == 8 {
+ // stance
+ buff.read_u8()?;
+ }
Controller::Command {
target,
pos,
command,
}
}
+ 5 => Controller::Assembler,
_ => Controller::Assembler,
})
}
@@ -193,7 +212,7 @@ pub struct Unit {
impl UnitClass {
pub fn read(self, buff: &mut DataRead) -> Result<Unit, ReadError> {
- buff.skip(2)?;
+ let _rev = buff.read_u16()?;
let mut state = UnitState::default();
read_abilities(buff)?;
state.ammo = buff.read_f32()?;