From 728fc156c84401f7c75ee04f791da914ac941f72 Mon Sep 17 00:00:00 2001 From: zyl Date: Sat, 18 Jan 2025 00:56:50 -0800 Subject: [PATCH] implement level rules feature, resolves #8 --- Cargo.lock | 375 ++++++++++++++++++++++++++++++++++++++++++++++++- Cargo.toml | 1 + src/command.rs | 51 ++++++- src/level.rs | 81 +++++++++++ src/server.rs | 6 + 5 files changed, 512 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ec1f2d6..c9243c8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,12 +17,37 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "const-random", + "getrandom", + "once_cell", + "version_check", + "zerocopy", +] + [[package]] name = "allocator-api2" version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" +[[package]] +name = "assert_type_match" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f548ad2c4031f2902e3edc1f29c29e835829437de49562d8eb5dc5584d3a1043" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "autocfg" version = "1.4.0" @@ -44,6 +69,81 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "bevy_macro_utils" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bdb3a681c24abace65bf18ed467ad8befbedb42468b32e459811bfdb01e506c" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "toml_edit", +] + +[[package]] +name = "bevy_ptr" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa65df6a190b7dfc84d79f09cf02d47ae046fa86a613e202c31559e06d8d3710" + +[[package]] +name = "bevy_reflect" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bab3264acc3b6f48bc23fbd09fdfea6e5d9b7bfec142e4f3333f532acf195bca" +dependencies = [ + "assert_type_match", + "bevy_ptr", + "bevy_reflect_derive", + "bevy_utils", + "derive_more", + "disqualified", + "downcast-rs", + "erased-serde", + "serde", + "smallvec", +] + +[[package]] +name = "bevy_reflect_derive" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f83876a322130ab38a47d5dcf75258944bf76b3387d1acdb3750920fda63e2" +dependencies = [ + "bevy_macro_utils", + "proc-macro2", + "quote", + "syn", + "uuid", +] + +[[package]] +name = "bevy_utils" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f01088c048960ea50ee847c3f668942ecf49ed26be12a1585a5e59b6a941d9a" +dependencies = [ + "ahash", + "bevy_utils_proc_macros", + "getrandom", + "hashbrown 0.14.5", + "thread_local", + "tracing", + "web-time", +] + +[[package]] +name = "bevy_utils_proc_macros" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a0c3244d543cc964545b7aa074f6fb18a915a7121cf3de5d7ed37a4aae8662d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "bincode" version = "2.0.0-rc.3" @@ -79,6 +179,12 @@ dependencies = [ "syn", ] +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + [[package]] name = "byteorder" version = "1.5.0" @@ -101,6 +207,7 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" name = "classics" version = "0.1.0" dependencies = [ + "bevy_reflect", "bincode", "bitmask-enum", "bytes", @@ -119,6 +226,26 @@ dependencies = [ "tokio", ] +[[package]] +name = "const-random" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" +dependencies = [ + "const-random-macro", +] + +[[package]] +name = "const-random-macro" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" +dependencies = [ + "getrandom", + "once_cell", + "tiny-keccak", +] + [[package]] name = "crc32fast" version = "1.4.2" @@ -134,12 +261,55 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" +[[package]] +name = "derive_more" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "unicode-xid", +] + +[[package]] +name = "disqualified" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9c272297e804878a2a4b707cfcfc6d2328b5bb936944613b4fdf2b9269afdfd" + +[[package]] +name = "downcast-rs" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" + [[package]] name = "equivalent" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +[[package]] +name = "erased-serde" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24e2389d65ab4fab27dc2a5de7b191e1f6617d1f1c8855c0dc569c94a4cbb18d" +dependencies = [ + "serde", + "typeid", +] + [[package]] name = "flate2" version = "1.0.35" @@ -163,8 +333,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi", + "wasm-bindgen", ] [[package]] @@ -184,6 +356,17 @@ dependencies = [ "serde", ] +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", + "allocator-api2", + "serde", +] + [[package]] name = "hashbrown" version = "0.15.2" @@ -201,13 +384,23 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "indexmap" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" +dependencies = [ + "equivalent", + "hashbrown 0.15.2", +] + [[package]] name = "internment" version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "636d4b0f6a39fd684effe2a73f5310df16a3fa7954c26d36833e98f44d1977a2" dependencies = [ - "hashbrown", + "hashbrown 0.15.2", "serde", ] @@ -217,6 +410,16 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" +[[package]] +name = "js-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + [[package]] name = "libc" version = "0.2.169" @@ -233,6 +436,12 @@ dependencies = [ "scopeguard", ] +[[package]] +name = "log" +version = "0.4.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" + [[package]] name = "memchr" version = "2.7.4" @@ -277,6 +486,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "once_cell" +version = "1.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" + [[package]] name = "optional_struct" version = "0.4.1" @@ -574,6 +789,25 @@ dependencies = [ "syn", ] +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + [[package]] name = "tokio" version = "1.43.0" @@ -603,12 +837,75 @@ dependencies = [ "syn", ] +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" + +[[package]] +name = "toml_edit" +version = "0.22.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tracing" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +dependencies = [ + "once_cell", +] + +[[package]] +name = "typeid" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e13db2e0ccd5e14a544e8a246ba2312cd25223f616442d7f2cb0e3db614236e" + [[package]] name = "unicode-ident" version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "uuid" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "744018581f9a3454a9e15beb8a33b017183f1e7c0cd170232a2d1453b23a51c4" +dependencies = [ + "getrandom", +] + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + [[package]] name = "virtue" version = "0.0.13" @@ -621,6 +918,73 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasm-bindgen" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +dependencies = [ + "cfg-if", + "once_cell", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "windows-sys" version = "0.52.0" @@ -694,6 +1058,15 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "winnow" +version = "0.6.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8d71a593cc5c42ad7876e2c1fda56f314f3754c084128833e64f1345ff8a03a" +dependencies = [ + "memchr", +] + [[package]] name = "zerocopy" version = "0.7.35" diff --git a/Cargo.toml b/Cargo.toml index af6ec3d..80001aa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,7 @@ name = "classics" version = "0.1.0" [dependencies] +bevy_reflect = "0.15" bincode = "2.0.0-rc.3" bitmask-enum = "2" bytes = "1" diff --git a/src/command.rs b/src/command.rs index ef7971d..75b2a9b 100644 --- a/src/command.rs +++ b/src/command.rs @@ -26,6 +26,7 @@ const CMD_SETLEVELSPAWN: &str = "setlevelspawn"; const CMD_WEATHER: &str = "weather"; const CMD_SAVE: &str = "save"; const CMD_TELEPORT: &str = "tp"; +const CMD_LEVELRULE: &str = "levelrule"; const USERNAME_SELF: &str = "@s"; @@ -44,6 +45,7 @@ pub const COMMANDS_LIST: &[&str] = &[ CMD_WEATHER, CMD_SAVE, CMD_TELEPORT, + CMD_LEVELRULE, ]; /// enum for possible commands @@ -92,6 +94,11 @@ pub enum Command<'m> { username: &'m str, mode: TeleportMode<'m>, }, + /// gets or sets a level rule for the current level + LevelRule { + rule: &'m str, + value: Option<&'m str>, + }, } #[derive(Debug, Clone)] @@ -167,6 +174,12 @@ impl<'m> Command<'m> { Self::Teleport { username, mode } } + CMD_LEVELRULE => { + let rule = Self::next_string(&mut arguments)?; + let value = Self::next_string(&mut arguments).ok(); + + Self::LevelRule { rule, value } + } _ => return Err(format!("Unknown command: {command_name}")), }) } @@ -187,6 +200,7 @@ impl<'m> Command<'m> { Self::Weather { .. } => CMD_WEATHER, Self::Save => CMD_SAVE, Self::Teleport { .. } => CMD_TELEPORT, + Self::LevelRule { .. } => CMD_LEVELRULE, } } @@ -257,6 +271,11 @@ impl<'m> Command<'m> { c("( or "), "&fTeleports to the given username or coordinates.".to_string(), ], + CMD_LEVELRULE => vec![ + c(" [value]"), + "&fGets or sets the given level rule. The special rule \"all\" will get all rules." + .to_string(), + ], _ => vec!["&eUnknown command!".to_string()], } } @@ -645,7 +664,37 @@ impl<'m> Command<'m> { player.packets_to_send.push(packet); } } else { - messages.push(format!("&fUnknown username: {username}!")); + messages.push(format!("Unknown username: {username}!")); + } + } + + Command::LevelRule { rule, value } => { + if rule == "all" { + // get all rules + if let Some(rules) = data.level.level_rules.get_all_rules_info() { + for (name, rule) in rules { + messages.push(format!("&f{name}: {rule}")); + } + } else { + messages.push("Unable to fetch level rules!".to_string()); + } + } else if let Some(value) = value { + // set a rule + match data.level.level_rules.set_rule(rule, value) { + Ok(()) => { + messages.push(format!("&fUpdated rule {rule}")); + } + Err(err) => { + messages.push(err.to_string()); + } + } + } else { + // get a rule + if let Some(info) = data.level.level_rules.get_rule(rule) { + messages.push(format!("&f{info}")); + } else { + messages.push(format!("Unknown rule: {rule}")); + } } } } diff --git a/src/level.rs b/src/level.rs index 8200244..3be9c3a 100644 --- a/src/level.rs +++ b/src/level.rs @@ -1,9 +1,11 @@ use std::{ + any::TypeId, collections::{BTreeMap, BTreeSet}, io::{Read, Write}, path::Path, }; +use bevy_reflect::{PartialReflect, Struct}; use serde::{Deserialize, Serialize}; use crate::{ @@ -33,6 +35,9 @@ pub struct Level { pub blocks: Vec, /// the level's weather pub weather: WeatherType, + /// the level's level rules + #[serde(default)] + pub level_rules: LevelRules, /// index of blocks which need to be updated in the next tick pub awaiting_update: BTreeSet, @@ -55,6 +60,7 @@ impl Level { z_size, blocks: vec![0; x_size * y_size * z_size], weather: WeatherType::Sunny, + level_rules: Default::default(), awaiting_update: Default::default(), updates: Default::default(), save_now: false, @@ -202,3 +208,78 @@ impl From for WeatherType { } } } + +/// Struct for rules in the level. +#[derive(Debug, Clone, Serialize, Deserialize, bevy_reflect::Reflect)] +pub struct LevelRules { + /// whether fluids should spread in the level + pub fluid_spread: bool, +} + +impl LevelRules { + /// Gets information about all level rules. + pub fn get_all_rules_info(&self) -> Option> { + let info = self.get_represented_struct_info()?; + let mut rules = BTreeMap::new(); + for name in info.field_names() { + rules.insert(name.to_string(), self.get_rule(name)?); + } + Some(rules) + } + + /// Gets information about a single level rule. + pub fn get_rule(&self, name: &str) -> Option { + let info = self.get_represented_struct_info()?; + Some(format!( + "{:?} ({})", + self.field(name)?, + info.field(name)?.type_path_table().ident()? + )) + } + + /// Sets a rule to the given value if possible. + pub fn set_rule(&mut self, name: &str, value: &str) -> Result<(), String> { + let bool_type_id = TypeId::of::(); + let f64_type_id = TypeId::of::(); + let string_type_id = TypeId::of::(); + + fn parse_and_apply(value: &str, field_mut: &mut dyn PartialReflect) -> Result<(), String> + where + T: std::str::FromStr + PartialReflect, + { + let value = value + .parse::() + .map_err(|_| "Failed to parse value".to_string())?; + field_mut.apply(value.as_partial_reflect()); + Ok(()) + } + + let info = self + .get_represented_struct_info() + .ok_or_else(|| "Failed to get field info".to_string())?; + let field = info + .field(name) + .ok_or_else(|| format!("Unknown field: {name}"))?; + let field_mut = self + .field_mut(name) + .ok_or_else(|| format!("Unknown field: {name}"))?; + let id = field.type_id(); + if id == bool_type_id { + parse_and_apply::(value, field_mut)?; + } else if id == f64_type_id { + parse_and_apply::(value, field_mut)?; + } else if id == string_type_id { + parse_and_apply::(value, field_mut)?; + } else { + return Err(format!("Field has unknown type: {}", field.type_path())); + }; + + Ok(()) + } +} + +impl Default for LevelRules { + fn default() -> Self { + Self { fluid_spread: true } + } +} diff --git a/src/server.rs b/src/server.rs index d0b1975..8837944 100644 --- a/src/server.rs +++ b/src/server.rs @@ -219,6 +219,9 @@ fn tick(data: &mut ServerData, tick: usize) { stationary, ticks_to_spread, } => { + if !level.level_rules.fluid_spread { + continue; + } if tick % ticks_to_spread == 0 { let update = BlockUpdate { index, @@ -266,6 +269,9 @@ fn tick(data: &mut ServerData, tick: usize) { } } BlockType::FluidStationary { moving } => { + if !level.level_rules.fluid_spread { + continue; + } let mut needs_update = false; for (nx, ny, nz) in neighbors_minus_up(level, x, y, z) { if matches!(