mirror of
https://github.com/zyllian/classics.git
synced 2025-05-12 06:20:55 -07:00
implement level rules feature, resolves #8
This commit is contained in:
parent
ff09a06b16
commit
728fc156c8
5 changed files with 512 additions and 2 deletions
|
@ -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("(<username> or <x> <y> <z>"),
|
||||
"&fTeleports to the given username or coordinates.".to_string(),
|
||||
],
|
||||
CMD_LEVELRULE => vec![
|
||||
c("<rule> [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}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
81
src/level.rs
81
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<u8>,
|
||||
/// 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<usize>,
|
||||
|
@ -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<u8> 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<BTreeMap<String, String>> {
|
||||
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<String> {
|
||||
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::<bool>();
|
||||
let f64_type_id = TypeId::of::<f64>();
|
||||
let string_type_id = TypeId::of::<String>();
|
||||
|
||||
fn parse_and_apply<T>(value: &str, field_mut: &mut dyn PartialReflect) -> Result<(), String>
|
||||
where
|
||||
T: std::str::FromStr + PartialReflect,
|
||||
{
|
||||
let value = value
|
||||
.parse::<T>()
|
||||
.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::<bool>(value, field_mut)?;
|
||||
} else if id == f64_type_id {
|
||||
parse_and_apply::<f64>(value, field_mut)?;
|
||||
} else if id == string_type_id {
|
||||
parse_and_apply::<String>(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 }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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!(
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue