Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'src/data/map.rs')
| -rw-r--r-- | src/data/map.rs | 696 |
1 files changed, 0 insertions, 696 deletions
diff --git a/src/data/map.rs b/src/data/map.rs deleted file mode 100644 index 636e3f3..0000000 --- a/src/data/map.rs +++ /dev/null @@ -1,696 +0,0 @@ -//! the map module -//! ### format -//! note: utf = `len<u16>` + `utf8(read(len))` -//! -//! note: each section has a `u32` denoting its length -//! -//! key: `: T` and `x<T>` both mean read T, `iterate T` means iterate `read_T()` times -//! -//! ZLIB compressed stream contains: -//! - header: 4b = `MSCH` -//! - version: `u32` (should be 7) -//! - tag section `<u32>` -//! - 1 byte of idk (skip) -//! - string map (`u16` for map len, iterate each, read `utf`) -//! - content header section `<u32>`: -//! - iterate `i8` (should = `8`)' -//! - 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) -//! - these types all have their own modules: [`item`], [`content`], [`fluid`], [`modifier`], [`mod@unit`], [`weather`], [`sector`], [`planet`] -//! - iterate `u16` -//! - name: `utf` -//! - map section `<u32>` -//! - width: `u16`, height: `u16` -//! - floor and tiles: -//! - for `i` in `w * h` -//! - `x = i % w`, `y = i / w` -//! - floor id: `u16` -//! - overlay id: `u16` -//! - consecutives: `u8` -//! - iterate `(i + 1)..(i + 1 + consecutives)` -//! - `x = j % w`, `y = j / w` -//! - i += consecutives -//! - blocks -//! - for `i` in `w * h` -//! - block id: `u16` -//! - packed?: `i8` -//! - entity = `(packed & 1) not 0` -//! - data = `(packed & 2) not 0` -//! - if entity: central: `bool` -//! - if entity: -//! - if central: -//! - chunk len: `u16` -//! - if block == building: -//! - revision: `i8` -//! - [`read`] -//! - else skip `chunk len` -//! - or data -//! - data: `i8` -//! - else -//! - consecutives: `u8` -//! - iterate `(i + 1)..(i + 1 + consecutives)` -//! - same block -//! - i += consecutives -//! - entities section `<u32>` -//! - entity mapping -//! - iterate `u16` -//! - id: `i16`, name: `utf` -//! - team build plans -//! - for t in `teams<u32>` -//! - team = `team#<u32>` -//! - iterate `plans<u32>` -//! - x: `u16`, y: `u16`, rot: `u16`, id: `u16` -//! - o: `DynData` (refer to [`DynSerializer`]) -//! - world entities -//! - iterate `u32` -//! - len: `u16` -//! - type: `u8` -//! - if !mapping\[type\] -//! - skip(len - 1) -//! - continue -//! - id: `u32` -//! - entity read -use std::collections::HashMap; -use std::ops::{Index, IndexMut}; -use thiserror::Error; - -use crate::block::content::Type as BlockEnum; -use crate::block::{Block, BlockRegistry, Rotation, State}; -use crate::data::dynamic::DynSerializer; -use crate::data::renderer::*; -use crate::data::DataRead; -use crate::fluid::Type as Fluid; -use crate::item::{storage::Storage, Type as Item}; -use crate::team::{self, Team}; -#[cfg(doc)] -use crate::{block::content, data::*, fluid, item, modifier, unit}; - -use super::Serializer; -use crate::content::Content; -use crate::utils::image::ImageUtils; - -/// a tile in a map -#[derive(Clone)] -pub struct Tile<'l> { - pub floor: BlockEnum, - pub ore: BlockEnum, - build: Option<Build<'l>>, -} - -macro_rules! lo { - ($v:expr => [$(|)? $($k:literal $(|)?)+], $scale: ident) => { paste::paste! { - match $v { - $(BlockEnum::[<$k:camel>] => load!($k, $scale),)+ - n => unreachable!("{n:?}"), - } - } }; -} - -pub type EntityMapping = HashMap<u8, Box<dyn Content>>; -impl<'l> Tile<'l> { - #[must_use] - pub const fn new(floor: BlockEnum, ore: BlockEnum) -> Self { - Self { - floor, - ore, - build: None, - } - } - - fn set_block(&mut self, block: &'l Block) { - self.build = Some(Build { - block, - state: None, - items: Storage::new(), - liquids: Storage::new(), - rotation: Rotation::Up, - team: crate::team::SHARDED, - data: 0, - }); - } - - #[must_use] - pub const fn build(&self) -> Option<&Build<'l>> { - self.build.as_ref() - } - - /// size of this tile - /// - /// ._. - /// - /// dont think about it too much - #[must_use] - #[inline] - pub fn size(&self) -> u8 { - self.build.as_ref().map_or(1, |v| v.block.get_size()) - } - - #[inline] - pub(crate) fn floor(&self, s: Scale) -> ImageHolder<4> { - lo!(self.floor => [ - | "darksand" - | "sand-floor" - | "dacite" - | "dirt" - | "arkycite-floor" - | "basalt" - | "moss" - | "mud" - | "grass" - | "ice-snow" | "snow" | "salt" | "ice" - | "hotrock" | "char" | "magmarock" - | "shale" - | "metal-floor" | "metal-floor-2" | "metal-floor-3" | "metal-floor-4" | "metal-floor-5" | "metal-floor-damaged" - | "dark-panel-1" | "dark-panel-2" | "dark-panel-3" | "dark-panel-4" | "dark-panel-5" | "dark-panel-6" - | "darksand-tainted-water" | "darksand-water" | "deep-tainted-water" | "deep-water" | "sand-water" | "shallow-water" | "tainted-water" - | "tar" | "pooled-cryofluid" | "molten-slag" - | "space" - | "stone" - | "bluemat" - | "ferric-craters" - | "beryllic-stone" - | "rhyolite" | "rough-rhyolite" | "rhyolite-crater" | "rhyolite-vent" - | "core-zone" - | "crater-stone" - | "redmat" - | "red-ice" - | "spore-moss" - | "regolith" - | "ferric-stone" - | "arkyic-stone" | "arkyic-vent" - | "yellow-stone" | "yellow-stone-plates" | "yellow-stone-vent" - | "red-stone" | "red-stone-vent" | "dense-red-stone" - | "carbon-stone" | "carbon-vent" - | "crystal-floor" | "crystalline-stone" | "crystalline-vent" - | "empty"], s) - } - - #[must_use] - #[inline] - pub(crate) fn ore(&self, s: Scale) -> ImageHolder<4> { - lo!(self.ore => ["ore-copper" | "ore-beryllium" | "ore-lead" | "ore-scrap" | "ore-coal" | "ore-thorium" | "ore-titanium" | "ore-tungsten" | "pebbles" | "tendrils" | "ore-wall-tungsten" | "ore-wall-beryllium" | "ore-wall-thorium" | "spawn" | "ore-crystal-thorium"], s) - } - - #[must_use] - #[inline] - pub fn has_ore(&self) -> bool { - self.ore != BlockEnum::Air - } - - /// Draw the floor of this tile - #[must_use] - pub fn floor_image(&self, s: Scale) -> ImageHolder<4> { - let mut floor = self.floor(s); - if self.has_ore() { - unsafe { floor.overlay(&self.ore(s)) }; - } - floor - } - - /// Draw this tiles build. - #[must_use] - #[inline] - pub fn build_image(&self, context: Option<&RenderingContext>, s: Scale) -> ImageHolder<4> { - // building covers floore - let Some(b) = &self.build else { - unreachable!(); - }; - b.image(context, s) - } -} - -impl std::fmt::Debug for Tile<'_> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "Tile@{}{}{}", - self.floor.get_name(), - if self.ore != BlockEnum::Air { - format!("+{}", self.ore.get_name()) - } else { - String::new() - }, - if let Some(build) = &self.build { - format!(":{}", build.block.name()) - } else { - String::new() - } - ) - } -} - -impl<'l> BlockState<'l> for Tile<'l> { - fn get_block(&self) -> Option<&'l Block> { - Some(self.build()?.block) - } -} - -impl RotationState for Tile<'_> { - fn get_rotation(&self) -> Option<Rotation> { - Some(self.build()?.rotation) - } -} - -impl RotationState for Option<Tile<'_>> { - fn get_rotation(&self) -> Option<Rotation> { - self.as_ref().unwrap().get_rotation() - } -} - -impl<'l> BlockState<'l> for Option<Tile<'_>> { - fn get_block(&'l self) -> Option<&'l Block> { - self.as_ref().unwrap().get_block() - } -} - -/// a build on a tile in a map -#[derive(Clone)] -pub struct Build<'l> { - pub block: &'l Block, - pub items: Storage<Item>, - pub liquids: Storage<Fluid>, - pub state: Option<State>, - // pub health: f32, - pub rotation: Rotation, - pub team: Team, - pub data: i8, -} - -impl std::fmt::Debug for Build<'_> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "Build<{block}>", block = self.block.name(),) - } -} - -impl<'l> Build<'l> { - #[must_use] - pub fn new(block: &'l Block) -> Build<'l> { - Self { - block, - items: Storage::default(), - liquids: Storage::default(), - state: None, - rotation: Rotation::Up, - team: team::SHARDED, - data: 0, - } - } - - fn image(&self, context: Option<&RenderingContext>, s: Scale) -> ImageHolder<4> { - self.block - .image(self.state.as_ref(), context, self.rotation, s) - } - - #[must_use] - pub const fn name(&self) -> &str { - self.block.name() - } - - pub fn read( - &mut self, - buff: &mut DataRead<'_>, - reg: &BlockRegistry, - map: &EntityMapping, - ) -> Result<(), ReadError> { - // health - let _ = buff.read_f32()?; - let rot = buff.read_i8()? as i16; - // team - let _ = buff.read_i8()?; - self.rotation = Rotation::try_from((rot & 127) as u8).unwrap_or(Rotation::Up); - let mut mask = 0; - let mut version = 0; - if rot & 128 != 0 { - version = buff.read_u8()?; - if version < 3 { - return Err(ReadError::Version(version)); - } - buff.skip(1)?; - mask = buff.read_u8()?; - } - - if mask & 1 != 0 { - read_items(buff, &mut self.items)?; - } - if mask & 2 != 0 { - read_power(buff)?; - } - if mask & 4 != 0 { - read_liquids(buff, &mut self.liquids)?; - } - // "efficiency"? - buff.skip(2)?; - if version == 4 { - // visible flags for fog - buff.skip(4)?; - } - // "overridden by subclasses" - self.block.read(self, reg, map, buff)?; - // implementation not complete, simply error, causing the remaining bytes in the chunk to be skipped (TODO finish impl) - Err(ReadError::Version(0x0)) - // Ok(()) - } -} - -/// format: -/// - iterate [`u16`] -/// - item: [`u16`] as [`Item`] -/// - amount: [`u32`] -/// -fn read_items(from: &mut DataRead, to: &mut Storage<Item>) -> Result<(), ReadError> { - to.clear(); - for _ in 0..from.read_u16()? { - let item = from.read_u16()?; - let amount = from.read_u32()?; - if let Ok(item) = Item::try_from(item) { - to.set(item, amount); - } - } - Ok(()) -} - -/// format: -/// - iterate [`u16`] -/// - liquid: [`u16`] as [`Fluid`] -/// - amount: [`f32`] -fn read_liquids(from: &mut DataRead, to: &mut Storage<Fluid>) -> Result<(), ReadError> { - to.clear(); - for _ in 0..from.read_u16()? { - let fluid = from.read_u16()?; - let amount = from.read_f32()?; - if let Ok(fluid) = Fluid::try_from(fluid) { - to.set(fluid, (amount * 100.0) as u32); - } - } - Ok(()) -} - -/// format: -/// - iterate [`u16`] -/// - link: [`i32`] -/// - status: [`f32`] -fn read_power(from: &mut DataRead) -> Result<(), ReadError> { - let n = from.read_u16()? as usize; - from.skip((n + 1) * 4)?; - Ok(()) -} - -#[test] -fn test_read_items() { - let mut s = Storage::new(); - read_items( - &mut DataRead::new(&[ - 0, 6, 0, 0, 0, 0, 2, 187, 0, 1, 0, 0, 1, 154, 0, 2, 0, 0, 15, 160, 0, 3, 0, 0, 0, 235, - 0, 6, 0, 0, 1, 46, 0, 12, 0, 0, 1, 81, 255, 255, - ]), - &mut s, - ) - .unwrap(); - assert!(s.get_total() == 5983); -} - -#[test] -fn test_read_liquids() { - let mut s = Storage::new(); - read_liquids( - &mut DataRead::new(&[0, 1, 0, 0, 67, 111, 247, 126, 255, 255]), - &mut s, - ) - .unwrap(); - assert!(s.get(Fluid::Water) == 23996); -} - -/// a map. -/// ## Does not support serialization yet! -#[derive(Debug)] -pub struct Map<'l> { - pub width: usize, - pub height: usize, - pub tags: HashMap<String, String>, - /// row major 2d array - /// ```rs - /// (0, 0), (1, 0), (2, 0) - /// (0, 1), (1, 1), (2, 1) - /// (0, 2), (1, 2), (2, 2) - /// ``` - pub tiles: Vec<Tile<'l>>, -} - -macro_rules! cond { - ($cond: expr, $do: expr) => { - if $cond { - None - } else { - $do - } - }; -} - -impl<'l> Crossable for Map<'l> { - // N - // cond!(pos.position.1 >= (pos.height - 1) as u16, get(j + 1)), - // // E - // cond!( - // pos.position.0 >= (pos.height - 1) as u16, - // get(j + pos.height) - // ), - // // S - // cond!( - // pos.position.1 == 0 || pos.position.1 >= pos.height as u16, - // cond!(pos.position.1 >= (pos.height - 1), get(j + 1)), - // // E - // cond!(pos.position.0 >= (pos.height - 1), get(j + pos.height)), - // // S - // cond!( - // pos.position.1 == 0 || pos.position.1 >= pos.height, - // get(j - 1) - // ), - // // W - // cond!(j < pos.height, get(j - pos.height)), - fn cross(&self, j: usize, c: &PositionContext) -> Cross { - let get = |i| { - let b = &self[i]; - Some((b.get_block()?, b.get_rotation()?)) - }; - [ - cond![ - c.position.1 == 0 || c.position.1 >= c.height, - get(j + self.height) - ], - cond![c.position.0 >= (c.height - 1), get(j + 1)], - cond![c.position.1 >= (c.height - 1), get(j - self.width)], - cond![j < c.height, get(j - 1)], - ] - } -} - -impl<'l> Map<'l> { - #[must_use] - pub fn new(width: usize, height: usize, tags: HashMap<String, String>) -> Self { - Self { - tiles: vec![Tile::new(BlockEnum::Air, BlockEnum::Air); width * height], - height, - width, - tags, - } - } -} - -impl<'l> Index<usize> for Map<'l> { - type Output = Tile<'l>; - fn index(&self, index: usize) -> &Self::Output { - &self.tiles[index] - } -} - -impl<'l> IndexMut<usize> for Map<'l> { - fn index_mut(&mut self, index: usize) -> &mut Self::Output { - &mut self.tiles[index] - } -} - -const MAP_HEADER: [u8; 4] = [b'M', b'S', b'A', b'V']; - -/// error occurring when reading a map fails -#[derive(Debug, Error)] -pub enum ReadError { - #[error("failed to read from buffer")] - Read(#[from] super::ReadError), - #[error(transparent)] - Decompress(#[from] super::DecompressError), - #[error("incorrect header ({0:?})")] - Header([u8; 4]), - #[error("unsupported version ({0})")] - Version(u8), - #[error("unknown block {0:?}")] - NoSuchBlock(String), - #[error("failed to read block data")] - ReadState(#[from] super::dynamic::ReadError), -} - -/// serde map -pub struct MapSerializer<'l>(pub &'l BlockRegistry<'l>); -impl<'l> Serializer<Map<'l>> for MapSerializer<'l> { - type ReadError = ReadError; - type WriteError = (); - /// deserialize a map - /// - /// notes: - /// - does not deserialize data - /// - does not deserialize entities - fn deserialize(&mut self, buff: &mut DataRead<'_>) -> Result<Map<'l>, Self::ReadError> { - let buff = buff.deflate()?; - let mut buff = DataRead::new(&buff); - { - let mut b = [0; 4]; - buff.read_bytes(&mut b)?; - if b != MAP_HEADER { - return Err(ReadError::Header(b)); - } - } - let version = buff.read_u32()?; - if version != 7 { - return Err(ReadError::Version(version.try_into().unwrap_or(0))); - } - let mut tags = HashMap::new(); - buff.read_chunk(true, |buff| { - buff.skip(1)?; - for _ in 0..buff.read_u8()? { - let key = buff.read_utf()?; - let value = buff.read_utf()?; - tags.insert(key.to_owned(), value.to_owned()); - } - Ok::<(), super::ReadError>(()) - })?; - // we skip the content header (just keep the respective modules updated) - buff.skip_chunk()?; - // map section - let mut w = 0; - let mut h = 0; - let mut m = None; - buff.read_chunk(true, |buff| { - w = buff.read_u16()? as usize; - h = buff.read_u16()? as usize; - let mut map = Map::new(w, h, tags); - let count = w * h; - let mut i = 0; - while i < count { - let floor_id = buff.read_u16()?; - let overlay_id = 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); - map[i] = Tile::new(floor, ore); - let consecutives = buff.read_u8()? as usize; - if consecutives > 0 { - for i in (i + 1)..(i + 1 + consecutives) { - map[i] = Tile::new(floor, ore); - } - i += consecutives; - } - i += 1; - } - let mut i = 0usize; - while i < count { - let block_id = buff.read_u16()?; - let packed = buff.read_u8()?; - let entity = (packed & 1) != 0; - let data = (packed & 2) != 0; - let central = if entity { buff.read_bool()? } else { false }; - let block = BlockEnum::try_from(block_id) - .map_err(|_| ReadError::NoSuchBlock(block_id.to_string()))?; - let block = if block == BlockEnum::Air { - None - } else { - Some( - self.0 - .get(block.get_name()) - .ok_or_else(|| ReadError::NoSuchBlock(block.to_string()))?, - ) - }; - if central { - if let Some(block) = block { - map[i].set_block(block); - } - } - if entity { - if central { - let mut output = [0u8; 2]; - output.copy_from_slice(&buff.data[..2]); - let _ = buff.read_chunk(false, |buff| { - #[cfg(debug_assertions)] - println!("reading {:?}", map[i].build.as_ref().unwrap()); - let _ = buff.read_i8()?; - - map[i] - .build - .as_mut() - .unwrap() - // map not initialized yet - .read(buff, self.0, &HashMap::new())?; - Ok::<(), ReadError>(()) - }); - } - } else if data { - if let Some(block) = block { - map[i].set_block(block); - } - map[i].build.as_mut().unwrap().data = buff.read_i8()?; - } else { - let consecutives = buff.read_u8()? as usize; - for i in i..=i + consecutives { - if let Some(block) = block { - map.tiles[i].set_block(block); - } - } - i += consecutives; - } - i += 1; - } - m = Some(map); - Ok::<(), ReadError>(()) - })?; - let mut mapping = EntityMapping::new(); - buff.read_chunk(true, |buff| { - // read entity mapping (SaveVersion.java#436) - for _ in 0..buff.read_u16()? { - let id = buff.read_u16()? as u8; - let nam = buff.read_utf()?; - dbg!(nam); - mapping.insert(id, Box::new(Item::Copper)); - // mapping.push(content::Type::get_name(nam)); - } - // read team block plans (ghosts) (SaveVersion.java#389) - for _ in 0..buff.read_u32()? { - buff.skip(4)?; - for _ in 0..buff.read_u32()? { - buff.skip(8usize)?; - let _ = DynSerializer::deserialize(&mut DynSerializer, buff)?; - } - } - // read world entities (#412). eg units - for _ in 0..buff.read_u32()? { - let len = buff.read_u16()? as usize; - let ty = buff.read_u8()?; - if !mapping.contains_key(&ty) { - buff.skip(len - 1)?; - continue; - } - let _id = buff.read_u32()?; - // TODO - } - Ok::<(), ReadError>(()) - })?; - // skip custom chunks - buff.skip_chunk()?; - Ok(m.unwrap()) - } - - /// serialize a map (todo) - /// panics: always - fn serialize( - &mut self, - _: &mut super::DataWrite<'_>, - _: &Map<'_>, - ) -> Result<(), Self::WriteError> { - todo!() - } -} |