From 70c096b6c7edb8724a6754766672bba10bced3eb Mon Sep 17 00:00:00 2001 From: Andy Killorin <37423245+Speedy6451@users.noreply.github.com> Date: Sat, 30 Dec 2023 09:48:44 -0600 Subject: [PATCH] vendored the SwarmBot schematic parsing code (of dubious licensure) --- server/Cargo.toml | 3 + server/src/main.rs | 1 + server/src/vendored/mod.rs | 2 + server/src/vendored/schematic.rs | 139 +++++++++++++++++++++++++++++++ 4 files changed, 145 insertions(+) create mode 100644 server/src/vendored/mod.rs create mode 100644 server/src/vendored/schematic.rs diff --git a/server/Cargo.toml b/server/Cargo.toml index 2c2ef26..e8389e4 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -45,3 +45,6 @@ tracing-appender = "0.2.3" ron = "0.8.1" crossbeam = "0.8.3" reqwest = "0.11.23" +swarmbot-interfaces = { git = "https://github.com/SwarmBotMC/SwarmBot" } +hematite-nbt = "0.5.2" +more-asserts = "0.3.1" diff --git a/server/src/main.rs b/server/src/main.rs index eacf765..a32e419 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -41,6 +41,7 @@ mod turtle_api; mod tasks; mod depot; mod googleforms; +mod vendored; static PORT: OnceCell = OnceCell::const_new(); static SAVE: OnceCell = OnceCell::const_new(); diff --git a/server/src/vendored/mod.rs b/server/src/vendored/mod.rs new file mode 100644 index 0000000..723554f --- /dev/null +++ b/server/src/vendored/mod.rs @@ -0,0 +1,2 @@ +/// MIT (Andrew Gazelka) from [SwarmBot](https://github.com/SwarmBotMC/SwarmBot/tree/b25367843f30ae72797db694d95ceeb0d49da82a) +pub mod schematic; diff --git a/server/src/vendored/schematic.rs b/server/src/vendored/schematic.rs new file mode 100644 index 0000000..a429d08 --- /dev/null +++ b/server/src/vendored/schematic.rs @@ -0,0 +1,139 @@ +use std::io::Read; + +use anyhow::Context; +use swarmbot_interfaces::types::{BlockLocation, BlockState}; +use serde::{Deserialize, Serialize}; + +/// The `WorldEdit` schematic format +/// +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "PascalCase")] +pub struct Schematic { + pub width: i16, + pub height: i16, + pub length: i16, + materials: String, + blocks: Vec, + add_blocks: Option>, + data: Vec, + w_e_origin_x: Option, + w_e_origin_y: Option, + w_e_origin_z: Option, + w_e_offset_x: Option, + w_e_offset_y: Option, + w_e_offset_z: Option, +} + +impl Schematic { + #[allow(unused)] + pub const fn volume(&self) -> u64 { + let v = (self.width as i64) * (self.height as i64) * (self.length as i64); + v.abs_diff(0) + } + + #[allow(unused)] + pub fn load(reader: &mut impl Read) -> anyhow::Result { + let res: Result = + nbt::from_gzip_reader(reader).context("could not load schematic"); + + res + } + + #[allow(unused)] + pub fn is_valid(&self) -> bool { + self.volume() == self.blocks.len() as u64 + } + + #[allow(unused)] + pub fn origin(&self) -> Option { + match (self.w_e_origin_x, self.w_e_origin_y, self.w_e_origin_z) { + (Some(x), Some(y), Some(z)) => Some(BlockLocation::new(x, y as i16, z)), + _ => None, + } + } + + #[allow(unused)] + pub fn offset(&self) -> Option { + match (self.w_e_offset_x, self.w_e_offset_y, self.w_e_offset_z) { + (Some(x), Some(y), Some(z)) => Some(BlockLocation::new(x, y as i16, z)), + _ => None, + } + } + + #[allow(unused, clippy::unwrap_used)] + pub fn width(&self) -> u64 { + u64::try_from(self.width).unwrap() + } + + #[allow(unused, clippy::unwrap_used)] + pub fn height(&self) -> u64 { + u64::try_from(self.width).unwrap() + } + + #[allow(unused, clippy::unwrap_used)] + pub fn length(&self) -> u64 { + u64::try_from(self.length).unwrap() + } + + #[allow(unused, clippy::unwrap_used, clippy::indexing_slicing)] + pub fn blocks(&self) -> impl Iterator + '_ { + let origin = self.origin().unwrap_or_default(); + + (0..self.volume()).map(move |idx| { + let x = idx % self.width(); + + let leftover = idx / self.width(); + let z = leftover % self.length(); + + let y = leftover / self.length(); + + let location = BlockLocation::new(x as i32, y as i16, z as i32) + origin; + + let id = self.blocks[idx as usize].abs_diff(0); + let data = self.data[idx as usize].abs_diff(0); + let state = BlockState::from(u32::from(id), u16::from(data)); + + (location, state) + }) + } +} + +#[cfg(test)] +mod tests { + use std::{collections::HashMap, fs::OpenOptions}; + + use swarmbot_interfaces::types::BlockLocation; + use more_asserts::*; + + use super::Schematic; + + #[test] + fn test_load() { + let mut reader = OpenOptions::new() + .read(true) + .open("test-data/parkour.schematic") + .unwrap(); + + let schematic = Schematic::load(&mut reader).unwrap(); + + assert!(schematic.is_valid()); + + let origin = schematic.origin().unwrap_or_default(); + + let mut map = HashMap::new(); + for (loc, state) in schematic.blocks() { + assert_ge!(loc.x, origin.x); + assert_lt!(loc.x, origin.x + schematic.width as i32); + + assert_ge!(loc.y, origin.y); + assert_lt!(loc.y, origin.y + schematic.height); + + assert_ge!(loc.z, origin.z); + assert_lt!(loc.z, origin.z + schematic.length as i32); + map.insert(loc, state); + } + + let stained_glass = map[&BlockLocation::new(-162, 81, -357)]; + assert_eq!(stained_glass.id(), 95); + } +}