mindustry logic execution, map- and schematic- parsing and rendering
cache ore and floor combinations
bendn 2023-08-08
parent 23582df · commit 4aa7e8a
-rw-r--r--Cargo.toml5
-rw-r--r--build.rs5
-rw-r--r--src/block/content.rs9
-rw-r--r--src/block/environment.rs10
-rw-r--r--src/content.rs2
-rw-r--r--src/data/map.rs107
-rw-r--r--src/data/renderer.rs33
-rw-r--r--src/lib.rs2
-rw-r--r--src/utils/image.rs14
-rw-r--r--src/utils/lazy.rs2
10 files changed, 127 insertions, 62 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 544ce8a..21e0352 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "mindus"
-version = "3.0.0"
+version = "3.0.1"
edition = "2021"
description = "A library for working with mindustry data formats (eg schematics and maps) (fork of plandustry)"
authors = [
@@ -22,6 +22,7 @@ thiserror = "1.0"
bobbin-bits = "0.1"
blurslice = { version = "0.1" }
enum_dispatch = "0.3"
+ahash = { version = "0.8.3", default-features = false, features = ["std", "no-rng"] }
[features]
bin = ["image/png"]
@@ -40,7 +41,7 @@ path = "src/exe/mod.rs"
[profile.release]
debug = 2
opt-level = 3
-lto = true
+lto = "thin"
incremental = true
[profile.dev.build-override]
diff --git a/build.rs b/build.rs
index 35e3d87..2b382f4 100644
--- a/build.rs
+++ b/build.rs
@@ -61,6 +61,7 @@ fn main() {
let mut warmup = File::create(o.join("warmup.rs")).unwrap();
wr!(warmup => "/// SAFETY: this function must only be called once.");
wr!(warmup => "pub unsafe fn warmup() {{");
+ wr!(warmup => "LazyLock::load(&EMPTY);");
for e in walkdir.into_iter().filter_map(|e| e.ok()) {
let path = e.path();
if path.is_file() && let Some(e) = path.extension() && e == "png" {
@@ -80,8 +81,8 @@ fn main() {
// boulders
let (mx, my) = if p.width() + p.height() == 48+48 {
(32, 32)
- // vents
- } else if path.contains("vent") {
+ // vents (dont match VENT_CONDENSER, do match (RHYOLITE_VENT)
+ } else if path.contains("_VENT") {
(32, 32)
} else {
(p.height(), p.width())
diff --git a/src/block/content.rs b/src/block/content.rs
index 55d5545..2275f6d 100644
--- a/src/block/content.rs
+++ b/src/block/content.rs
@@ -1,5 +1,5 @@
//! everything
-use crate::content::{content_enum, Content};
+use crate::content::{content_enum};
content_enum! {
pub enum Type / Block for u16 | TryFromU16Error
@@ -418,9 +418,4 @@ content_enum! {
"world-message",
}
}
-use crate::block::*;
-impl Type {
- pub fn to<'l>(&self, reg: &'l BlockRegistry) -> Option<&'l Block> {
- reg.get(self.get_name())
- }
-}
+
diff --git a/src/block/environment.rs b/src/block/environment.rs
index 267ebcd..3fff74f 100644
--- a/src/block/environment.rs
+++ b/src/block/environment.rs
@@ -67,14 +67,14 @@ register_env! {
"stone": 1;
"build1": 1;
"boulder": 1;
- "arkyic-vent": 3;
+ "arkyic-vent": 1;
"arkyic-wall-large": 2;
"arkyic-wall": 1;
"beryllic-stone-wall-large": 2;
"beryllic-stone-wall": 1;
"beryllic-stone": 1;
"bluemat": 1;
- "carbon-vent": 3;
+ "carbon-vent": 1;
"carbon-wall-large": 2;
"carbon-wall": 1;
"cliff": 1;
@@ -108,7 +108,7 @@ register_env! {
"red-ice-wall-large": 2;
"red-ice-wall": 1;
"red-ice": 1;
- "red-stone-vent": 3;
+ "red-stone-vent": 1;
"red-stone-wall-large": 2;
"red-stone-wall": 1;
"red-stone": 1;
@@ -117,7 +117,7 @@ register_env! {
"regolith-wall": 1;
"regolith": 1;
"rhyolite-crater": 1;
- "rhyolite-vent": 3;
+ "rhyolite-vent": 1;
"rhyolite-wall-large": 2;
"rhyolite-wall": 1;
"rhyolite": 1;
@@ -143,7 +143,7 @@ register_env! {
"tainted-water": 1;
"tar": 1;
"yellow-stone-plates": 1;
- "yellow-stone-vent": 3;
+ "yellow-stone-vent": 1;
"yellow-stone-wall-large": 2;
"yellow-stone-wall": 1;
// props
diff --git a/src/content.rs b/src/content.rs
index 5576458..44db19f 100644
--- a/src/content.rs
+++ b/src/content.rs
@@ -17,7 +17,7 @@ macro_rules! numeric_enum {
($vis:vis enum $tname:ident for $numeric:ty | $error:ident* {$($name:ident $(= $val:literal)?),* $(,)?}) =>
{
#[repr($numeric)]
- #[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
+ #[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Hash)]
$vis enum $tname { $($name $(= $val)?,)+ }
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
diff --git a/src/data/map.rs b/src/data/map.rs
index 5ee644d..acd0c88 100644
--- a/src/data/map.rs
+++ b/src/data/map.rs
@@ -71,10 +71,11 @@
//! - entity read
use std::collections::HashMap;
use std::ops::{Index, IndexMut};
+use std::sync::RwLock;
use thiserror::Error;
use crate::block::content::Type as BlockEnum;
-use crate::block::{environment, Block, BlockRegistry, Rotation, State};
+use crate::block::{Block, BlockRegistry, Rotation, State};
use crate::data::dynamic::DynSerializer;
use crate::data::renderer::*;
use crate::data::DataRead;
@@ -91,14 +92,14 @@ use crate::utils::image::ImageUtils;
/// a tile in a map
#[derive(Clone)]
pub struct Tile<'l> {
- pub floor: &'l Block,
- pub ore: Option<&'l Block>,
+ pub floor: BlockEnum,
+ pub ore: BlockEnum,
build: Option<Build<'l>>,
}
pub type EntityMapping = HashMap<u8, Box<dyn Content>>;
impl<'l> Tile<'l> {
- pub fn new(floor: &'l Block, ore: Option<&'l Block>) -> Self {
+ pub fn new(floor: BlockEnum, ore: BlockEnum) -> Self {
Self {
floor,
ore,
@@ -134,14 +135,85 @@ impl<'l> Tile<'l> {
1
}
- pub unsafe fn floor_image(&self, context: Option<&RenderingContext>, s: Scale) -> ImageHolder {
- let mut i = self.floor.image(None, context, Rotation::Up, s);
- if let Some(ore) = self.ore {
- i.overlay(ore.image(None, context, Rotation::Up, s).borrow());
+ pub unsafe fn floor_image(&self, s: Scale) -> ImageHolder {
+ macro_rules! lo {
+ ($v:expr => [$(|)? $($k:literal $(|)?)+], $scale: ident) => { paste::paste! {
+ match $v {
+ $(BlockEnum::[<$k:camel>] => load!($k, $scale),)+
+ n => unreachable!("{n:?}"),
+ } }
+ }
+ }
+ let floor = || {
+ lo!(self.floor => [
+ | "darksand"
+ | "sand-floor"
+ | "dacite"
+ | "dirt"
+ | "arkycite-floor"
+ | "basalt"
+ | "moss"
+ | "mud"
+ | "magmarock"
+ | "grass"
+ | "ice-snow"
+ | "hotrock"
+ | "char"
+ | "snow"
+ | "salt"
+ | "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"
+ | "molten-slag"
+ | "deep-water"
+ | "sand-water"
+ | "shallow-water"
+ | "space"
+ | "stone"
+ | "bluemat"
+ | "ferric-craters"
+ | "beryllic-stone"
+ | "rhyolite-crater"
+ | "core-zone"
+ | "crater-stone"
+ | "crystal-floor"
+ | "dense-red-stone"
+ | "redmat"
+ | "red-ice"
+ | "spore-moss"
+ | "arkyic-vent" | "red-stone-vent" | "rhyolite-vent" | "carbon-vent" | "crystalline-vent" | "yellow-stone-vent"
+ | "regolith"
+ | "rhyolite"
+ | "tainted-water"
+ | "tar"
+ | "empty"
+ ], s)
+ };
+ if self.ore != BlockEnum::Air {
+ type Floor = BlockEnum;
+ type Ore = BlockEnum;
+ // todo Rgb
+ static ORE_LAYS: RwLock<HashMap<(Floor, Ore), &'static RgbaImage, ahash::RandomState>> =
+ RwLock::new(HashMap::with_hasher(ahash::RandomState::with_seeds(
+ 401, 41209, 83123, 2110,
+ )));
+ if let Some(v) = ORE_LAYS.read().unwrap().get(&(self.floor, self.ore)) {
+ return ImageHolder::from(*v);
+ }
+ return ImageHolder::from(*ORE_LAYS.write().unwrap().entry((self.floor, self.ore)).or_insert_with(|| {
+ let mut base = floor();
+ base.overlay(lo!(self.ore => ["ore-copper" | "ore-beryllium" | "ore-lead" | "ore-scrap" | "ore-coal" | "ore-thorium" | "ore-titanium" | "ore-tungsten" | "pebbles" | "tendrils"], s).borrow());
+ Box::leak(Box::new(base))
+ }));
}
- i
+ floor()
}
+ /// # Safety
+ /// Must call [`warmup`](crate::warmup) first
pub unsafe fn build_image(&self, context: Option<&RenderingContext>, s: Scale) -> ImageHolder {
// building covers floore
let Some(b) = &self.build else {
@@ -156,9 +228,9 @@ impl std::fmt::Debug for Tile<'_> {
write!(
f,
"Tile@{}{}{}",
- self.floor.name(),
- if let Some(ore) = &self.ore {
- format!("+{}", ore.name())
+ self.floor.get_name(),
+ if self.ore != BlockEnum::Air {
+ format!("+{}", self.ore.get_name())
} else {
"".into()
},
@@ -437,7 +509,7 @@ impl<'l> Crossable for Map<'l> {
impl<'l> Map<'l> {
pub fn new(width: usize, height: usize, tags: HashMap<String, String>) -> Self {
Self {
- tiles: vec![Tile::new(&environment::STONE, None); width * height],
+ tiles: vec![Tile::new(BlockEnum::Stone, BlockEnum::Air); width * height],
height,
width,
tags,
@@ -526,13 +598,8 @@ impl<'l> Serializer<Map<'l>> for MapSerializer<'l> {
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)
- .to(self.0)
- .unwrap_or(&environment::STONE);
- let ore = BlockEnum::try_from(overlay_id)
- .unwrap_or(BlockEnum::Air)
- .to(self.0);
+ 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 {
diff --git a/src/data/renderer.rs b/src/data/renderer.rs
index 8423d98..70babd6 100644
--- a/src/data/renderer.rs
+++ b/src/data/renderer.rs
@@ -112,25 +112,32 @@ impl std::ops::Mul<u32> for Scale {
#[macro_export]
macro_rules! load {
+ ("empty", $scale:ident) => {
+ ImageHolder::from(unsafe { $crate::utils::Lock::get(match $scale {
+ $crate::data::renderer::Scale::Quarter => &$crate::data::renderer::quar::EMPTY,
+ $crate::data::renderer::Scale::Eigth => &$crate::data::renderer::eigh::EMPTY,
+ $crate::data::renderer::Scale::Full => &$crate::data::renderer::full::EMPTY,
+ })})
+ };
($name:literal, $scale:ident) => { paste::paste! {
- ImageHolder::from(unsafe { crate::utils::Lock::get(match $scale {
+ ImageHolder::from(unsafe { $crate::utils::Lock::get(match $scale {
$crate::data::renderer::Scale::Quarter => $crate::data::renderer::quar::[<$name:snake:upper>],
$crate::data::renderer::Scale::Eigth => $crate::data::renderer::eigh::[<$name:snake:upper>],
$crate::data::renderer::Scale::Full => $crate::data::renderer::full::[<$name:snake:upper>],
})})
} };
($name: literal) => { paste::paste! {
- [crate::data::renderer::full::[<$name:snake:upper>], crate::data::renderer::quar::[<$name:snake:upper>], crate::data::renderer::eigh::[<$name:snake:upper>]]
+ [$crate::data::renderer::full::[<$name:snake:upper>], $crate::data::renderer::quar::[<$name:snake:upper>], $crate::data::renderer::eigh::[<$name:snake:upper>]]
} };
(from $v:ident which is [$($k:literal $(|)?)+], $scale: ident) => {
- crate::data::renderer::load!($scale -> match $v {
+ $crate::data::renderer::load!($scale -> match $v {
$($k => $k,)+
})
};
// turn load!(s -> match x { "v" => "y" }) into match x { "v" => load!("y", s) }
($scale:ident -> match $v:ident { $($k:pat => $nam:literal $(,)?)+ }) => {
match $v {
- $($k => crate::data::renderer::load!($nam, $scale),)+
+ $($k => $crate::data::renderer::load!($nam, $scale),)+
#[allow(unreachable_patterns)]
n => unreachable!("{n:?}"),
}
@@ -138,10 +145,10 @@ macro_rules! load {
(concat $x:expr => $v:ident which is [$($k:literal $(|)?)+], $scale: ident) => { paste::paste! {
match $v {
$($k =>
- ImageHolder::from(unsafe { crate::utils::Lock::get(match $scale {
- crate::data::renderer::Scale::Quarter => crate::data::renderer::quar::[<$k:snake:upper _ $x:snake:upper>],
- crate::data::renderer::Scale::Eigth => crate::data::renderer::eigh::[<$k:snake:upper _ $x:snake:upper>],
- crate::data::renderer::Scale::Full => crate::data::renderer::full::[<$k:snake:upper _ $x:snake:upper>],
+ ImageHolder::from(unsafe { $crate::utils::Lock::get(match $scale {
+ $crate::data::renderer::Scale::Quarter => $crate::data::renderer::quar::[<$k:snake:upper _ $x:snake:upper>],
+ $crate::data::renderer::Scale::Eigth => $crate::data::renderer::eigh::[<$k:snake:upper _ $x:snake:upper>],
+ $crate::data::renderer::Scale::Full => $crate::data::renderer::full::[<$k:snake:upper _ $x:snake:upper>],
}) }),
)+
#[allow(unreachable_patterns)]
@@ -255,10 +262,10 @@ impl Renderable for Map<'_> {
)
}) {
// draw the floor first.
- let img: &RgbaImage = &tile.floor_image(None, scale);
+ let img: &RgbaImage = &tile.floor_image(scale);
// println!("draw {tile:?} ({x}, {y}) + {scale:?}");
- // assert_eq!(img.width(), scale.px() as u32);
- // assert_eq!(img.height(), scale.px() as u32);
+ debug_assert_eq!(img.width(), scale.px() as u32);
+ debug_assert_eq!(img.height(), scale.px() as u32);
floor.overlay_at(img, scale * x as u32, scale * y as u32);
if let Some(build) = tile.build() {
let s = build.block.get_size();
@@ -279,8 +286,8 @@ impl Renderable for Map<'_> {
None
};
let img: &RgbaImage = &tile.build_image(ctx.as_ref(), scale);
- // assert_eq!(img.width(), scale * build.block.get_size() as u32);
- // assert_eq!(img.height(), scale * build.block.get_size() as u32);
+ debug_assert_eq!(img.width(), scale * build.block.get_size() as u32);
+ debug_assert_eq!(img.height(), scale * build.block.get_size() as u32);
top.overlay_at(img, scale * x as u32, scale * y as u32);
}
}
diff --git a/src/lib.rs b/src/lib.rs
index 22a510d..a14bb24 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,5 +1,5 @@
//! crate for dealing with mindustry
-#![feature(array_chunks, const_trait_impl)]
+#![feature(array_chunks, const_trait_impl, const_collections_with_hasher)]
pub mod block;
mod content;
pub mod data;
diff --git a/src/utils/image.rs b/src/utils/image.rs
index 423a4c7..2aa6f21 100644
--- a/src/utils/image.rs
+++ b/src/utils/image.rs
@@ -33,16 +33,8 @@ impl Overlay<RgbImage> for RgbImage {
fn overlay_at(&mut self, with: &RgbImage, x: u32, y: u32) -> &mut Self {
for j in 0..with.height() {
for i in 0..with.width() {
- #[cfg(debug_assertions)]
- {
- let get = *with.get_pixel(i, j);
- self.put_pixel(i + x, j + y, get);
- }
- #[cfg(not(debug_assertions))]
- {
- let get = unsafe { with.unsafe_get_pixel(i, j) };
- unsafe { self.unsafe_put_pixel(i + x, j + y, get) };
- }
+ let get = unsafe { with.unsafe_get_pixel(i, j) };
+ unsafe { self.unsafe_put_pixel(i + x, j + y, get) };
}
}
self
@@ -117,6 +109,8 @@ impl ImageUtils for RgbaImage {
}
fn overlay(&mut self, with: &RgbaImage) -> &mut Self {
+ debug_assert_eq!(self.width(), with.width());
+ debug_assert_eq!(self.height(), with.height());
if self.len() % 4 != 0 || with.len() % 4 != 0 {
unsafe { std::hint::unreachable_unchecked() };
}
diff --git a/src/utils/lazy.rs b/src/utils/lazy.rs
index 268a81c..5a19029 100644
--- a/src/utils/lazy.rs
+++ b/src/utils/lazy.rs
@@ -36,7 +36,7 @@ impl<T, F> Lock<T, F> {
#[inline]
// SAFETY: CALL [load] FIRST!
pub unsafe fn get(&self) -> &T {
- &*(*self.data.get()).value
+ &(*self.data.get()).value
}
}