From 8a6f0d168a166969f8e133def9ce2d64be02809b Mon Sep 17 00:00:00 2001 From: kyled Date: Tue, 26 Nov 2024 10:41:05 -0500 Subject: [PATCH 01/11] Starting work on Operator permission system This commit adds the ops.json configuration found in vanilla servers, and updates the basic configuration to handle the default permission level. Server now uses ops file and defaults players to op level 0 The /op command has been added but needs players to rejoin for now. --- pumpkin-config/Cargo.toml | 4 +- pumpkin-config/src/lib.rs | 112 ++++++++++++++++++++++++- pumpkin-config/src/op.rs | 20 +++++ pumpkin/src/command/commands/cmd_op.rs | 75 +++++++++++++++++ pumpkin/src/command/commands/mod.rs | 1 + pumpkin/src/command/mod.rs | 2 + pumpkin/src/entity/player.rs | 40 +++++++-- 7 files changed, 242 insertions(+), 12 deletions(-) create mode 100644 pumpkin-config/src/op.rs create mode 100644 pumpkin/src/command/commands/cmd_op.rs diff --git a/pumpkin-config/Cargo.toml b/pumpkin-config/Cargo.toml index d3ce22e43..e5a5eeae0 100644 --- a/pumpkin-config/Cargo.toml +++ b/pumpkin-config/Cargo.toml @@ -7,5 +7,7 @@ edition.workspace = true pumpkin-core = { path = "../pumpkin-core" } serde.workspace = true log.workspace = true - +uuid.workspace = true +tokio.workspace = true +serde_json.workspace = true toml = "0.8" diff --git a/pumpkin-config/src/lib.rs b/pumpkin-config/src/lib.rs index a69c53496..91a7a6e4e 100644 --- a/pumpkin-config/src/lib.rs +++ b/pumpkin-config/src/lib.rs @@ -1,5 +1,6 @@ use log::warn; use logging::LoggingConfig; +use op::Op; use pumpkin_core::{Difficulty, GameMode}; use query::QueryConfig; use serde::{de::DeserializeOwned, Deserialize, Serialize}; @@ -13,6 +14,7 @@ use std::{ pub mod auth; pub mod logging; +pub mod op; pub mod proxy; pub mod query; pub mod resource_pack; @@ -35,11 +37,20 @@ mod server_links; use proxy::ProxyConfig; use resource_pack::ResourcePackConfig; +pub static OPERATOR_CONFIG: LazyLock> = + LazyLock::new(|| tokio::sync::RwLock::new(OperatorConfig::load())); + pub static ADVANCED_CONFIG: LazyLock = LazyLock::new(AdvancedConfiguration::load); pub static BASIC_CONFIG: LazyLock = LazyLock::new(BasicConfiguration::load); +#[derive(Deserialize, Serialize, Default)] +#[serde(transparent)] +pub struct OperatorConfig { + pub ops: Vec, +} + /// The idea is that Pumpkin should very customizable. /// You can Enable or Disable Features depending on your needs. /// @@ -76,6 +87,8 @@ pub struct BasicConfiguration { pub simulation_distance: u8, /// The default game difficulty. pub default_difficulty: Difficulty, + /// The operator level set by the /op command + pub op_permission_level: op::OpLevel, /// Whether the Nether dimension is enabled. pub allow_nether: bool, /// Whether the server is in hardcore mode. @@ -106,6 +119,7 @@ impl Default for BasicConfiguration { view_distance: 10, simulation_distance: 10, default_difficulty: Difficulty::Normal, + op_permission_level: op::OpLevel::Owner, allow_nether: true, hardcore: false, online_mode: true, @@ -120,7 +134,7 @@ impl Default for BasicConfiguration { } } -trait LoadConfiguration { +trait LoadTomlConfiguration { fn load() -> Self where Self: Sized + Default + Serialize + DeserializeOwned, @@ -160,7 +174,84 @@ trait LoadConfiguration { fn validate(&self); } -impl LoadConfiguration for AdvancedConfiguration { +pub trait LoadJSONConfiguration { + fn load() -> Self + where + Self: Sized + Default + Serialize + for<'de> Deserialize<'de>, + { + let path = Self::get_path(); + + let config = if path.exists() { + let file_content = fs::read_to_string(&path) + .unwrap_or_else(|_| panic!("Couldn't read configuration file at {:?}", path)); + + serde_json::from_str(&file_content).unwrap_or_else(|err| { + panic!( + "Couldn't parse config at {:?}. Reason: {}. This is probably caused by a config update. Just delete the old config and restart.", + path, err + ) + }) + } else { + let content = Self::default(); + + if let Err(err) = fs::write(&path, serde_json::to_string_pretty(&content).unwrap()) { + eprintln!( + "Couldn't write default config to {:?}. Reason: {}. This is probably caused by a config update. Just delete the old config and restart.", + path, err + ); + } + + content + }; + + config.validate(); + config + } + + fn get_path() -> &'static Path; + + fn validate(&self); +} + +pub trait SaveJSONConfiguration: LoadJSONConfiguration { + async fn save(&self) + where + Self: Sized + Default + Serialize + for<'de> Deserialize<'de>, + { + let path = ::get_path(); + + let content = match serde_json::to_string_pretty(self) { + Ok(content) => content, + Err(err) => { + warn!( + "Couldn't serialize operator config to {:?}. Reason: {}", + path, err + ); + return; + } + }; + + if let Err(err) = tokio::fs::write(path, content).await { + warn!( + "Couldn't write operator config to {:?}. Reason: {}", + path, err + ); + } + } +} + +impl LoadJSONConfiguration for OperatorConfig { + fn get_path() -> &'static Path { + Path::new("ops.json") + } + fn validate(&self) { + // TODO: Validate the operator configuration + } +} + +impl SaveJSONConfiguration for OperatorConfig {} + +impl LoadTomlConfiguration for AdvancedConfiguration { fn get_path() -> &'static Path { Path::new("features.toml") } @@ -170,7 +261,22 @@ impl LoadConfiguration for AdvancedConfiguration { } } -impl LoadConfiguration for BasicConfiguration { +// impl OperatorConfig { + +// pub async fn reload() where Self: LoadJSONConfiguration { +// let mut config = OPERATOR_CONFIG.write().await; +// *config = OperatorConfig::load(); +// } + +// pub async fn save(&self) where Self: SaveJSONConfiguration { +// Box::pin(async move { +// self.save().await; +// }).await +// } + +// } + +impl LoadTomlConfiguration for BasicConfiguration { fn get_path() -> &'static Path { Path::new("configuration.toml") } diff --git a/pumpkin-config/src/op.rs b/pumpkin-config/src/op.rs new file mode 100644 index 000000000..6e5b982e9 --- /dev/null +++ b/pumpkin-config/src/op.rs @@ -0,0 +1,20 @@ +use serde::{Deserialize, Serialize}; +use uuid::Uuid; + +#[derive(Serialize, Deserialize,Clone, Copy)] +#[repr(u8)] +pub enum OpLevel { + None = 0, + Basic = 1, + Moderator = 2, + Admin = 3, + Owner = 4, +} + +#[derive(Serialize, Deserialize, Clone)] +pub struct Op { + pub uuid: Uuid, + pub name: String, + pub level: OpLevel, + pub bypasses_player_limit: bool, +} \ No newline at end of file diff --git a/pumpkin/src/command/commands/cmd_op.rs b/pumpkin/src/command/commands/cmd_op.rs new file mode 100644 index 000000000..b964417f8 --- /dev/null +++ b/pumpkin/src/command/commands/cmd_op.rs @@ -0,0 +1,75 @@ +use crate::{ + command::{ + args::{arg_players::PlayersArgumentConsumer, Arg, ConsumedArgs}, + tree::CommandTree, + tree_builder::{argument, require}, + CommandError, CommandExecutor, CommandSender, + }, + entity::player::PermissionLvl, +}; +use async_trait::async_trait; +use pumpkin_config::{op::Op, SaveJSONConfiguration, BASIC_CONFIG, OPERATOR_CONFIG}; +use pumpkin_core::text::TextComponent; +use CommandError::InvalidConsumption; + +const NAMES: [&str; 1] = ["op"]; +const DESCRIPTION: &str = "Specifies one or more game profiles (player profiles). Must be a player name (should be a real one if the server is in online mode) or a player-type target selector"; +const ARG_TARGET: &str = "player"; + +struct OpExecutor; + +#[async_trait] +impl CommandExecutor for OpExecutor { + async fn execute<'a>( + &self, + sender: &mut CommandSender<'a>, + _server: &crate::server::Server, + args: &ConsumedArgs<'a>, + ) -> Result<(), CommandError> { + let mut config = OPERATOR_CONFIG.write().await; + + let Some(Arg::Players(targets)) = args.get(&ARG_TARGET) else { + return Err(InvalidConsumption(Some(ARG_TARGET.into()))); + }; + + // log each player to the console. + for player in targets { + let op_entry = Op { + uuid: player.gameprofile.id, + name: player.gameprofile.name.clone(), + level: BASIC_CONFIG.op_permission_level, + bypasses_player_limit: false, + }; + if let Some(op) = config + .ops + .iter_mut() + .find(|o| o.uuid == player.gameprofile.id) + { + op.level = BASIC_CONFIG.op_permission_level; + } else { + config.ops.push(op_entry); + } + config.save().await; + + // TODO: can't fully implement until require can accept async closures + // player + // .set_permission_lvl(BASIC_CONFIG.op_permission_level.into()) + // .await; + + let player_name = player.gameprofile.name.clone(); + let message = format!("Made {} a server operator.", player_name); + let msg = TextComponent::text(&message); + sender.send_message(msg).await; + log::warn!("{} has been made a server operator. Please level and rejoin to see the changes...", player_name); + } + + Ok(()) + } +} + +pub fn init_command_tree<'a>() -> CommandTree<'a> { + CommandTree::new(NAMES, DESCRIPTION).with_child( + require(&|sender| sender.has_permission_lvl(PermissionLvl::Four)) + .with_child(argument(ARG_TARGET, &PlayersArgumentConsumer).execute(&OpExecutor)), + ) +} diff --git a/pumpkin/src/command/commands/mod.rs b/pumpkin/src/command/commands/mod.rs index cbd1cb2cd..407efe9a3 100644 --- a/pumpkin/src/command/commands/mod.rs +++ b/pumpkin/src/command/commands/mod.rs @@ -18,3 +18,4 @@ pub mod cmd_teleport; pub mod cmd_time; pub mod cmd_transfer; pub mod cmd_worldborder; +pub mod cmd_op; diff --git a/pumpkin/src/command/mod.rs b/pumpkin/src/command/mod.rs index 09c85c81c..c0aa849f9 100644 --- a/pumpkin/src/command/mod.rs +++ b/pumpkin/src/command/mod.rs @@ -9,6 +9,7 @@ use crate::server::Server; use crate::world::World; use args::ConsumedArgs; use async_trait::async_trait; +use commands::cmd_op; use commands::{ cmd_clear, cmd_craft, cmd_echest, cmd_fill, cmd_gamemode, cmd_give, cmd_help, cmd_kick, cmd_kill, cmd_list, cmd_pumpkin, cmd_say, cmd_setblock, cmd_stop, cmd_teleport, cmd_time, @@ -131,6 +132,7 @@ pub fn default_dispatcher<'a>() -> Arc> { dispatcher.register(cmd_seed::init_command_tree()); dispatcher.register(cmd_transfer::init_command_tree()); dispatcher.register(cmd_fill::init_command_tree()); + dispatcher.register(cmd_op::init_command_tree()); Arc::new(dispatcher) } diff --git a/pumpkin/src/entity/player.rs b/pumpkin/src/entity/player.rs index 79e9f0325..10c1f337d 100644 --- a/pumpkin/src/entity/player.rs +++ b/pumpkin/src/entity/player.rs @@ -10,7 +10,8 @@ use std::{ use crossbeam::atomic::AtomicCell; use itertools::Itertools; use num_derive::{FromPrimitive, ToPrimitive}; -use pumpkin_config::ADVANCED_CONFIG; +use parking_lot::RwLock; +use pumpkin_config::{op::OpLevel, ADVANCED_CONFIG, OPERATOR_CONFIG}; use pumpkin_core::{ math::{ boundingbox::{BoundingBox, BoundingBoxSize}, @@ -158,7 +159,7 @@ pub struct Player { cancel_tasks: Notify, /// the players op permission level - permission_lvl: PermissionLvl, + permission_lvl: parking_lot::RwLock, } impl Player { @@ -180,6 +181,8 @@ impl Player { }, |profile| profile, ); + + let gameprofile_clone = gameprofile.clone(); let config = client.config.lock().await.clone().unwrap_or_default(); let view_distance = config.view_distance; let bounding_box_size = BoundingBoxSize { @@ -218,8 +221,15 @@ impl Player { pending_chunks: Arc::new(parking_lot::Mutex::new(HashMap::new())), pending_chunk_batch: parking_lot::Mutex::new(HashMap::new()), cancel_tasks: Notify::new(), - // TODO: change this - permission_lvl: PermissionLvl::Four, + // Minecraft has no why to change the default permission level of new players. + // Minecrafts default permission level is 0 + permission_lvl: OPERATOR_CONFIG + .read() + .await + .ops + .iter() + .find(|op| op.uuid == gameprofile_clone.id) + .map_or(parking_lot::RwLock::new(PermissionLvl::Zero), |op| parking_lot::RwLock::new(op.level.into())), } } @@ -506,20 +516,22 @@ impl Player { self.client .send_packet(&CEntityStatus::new( self.entity_id(), - 24 + self.permission_lvl as i8, + 24 + self.permission_lvl() as i8, )) .await; } /// sets the players permission level and syncs it with the client - pub async fn set_permission_lvl(&mut self, lvl: PermissionLvl) { - self.permission_lvl = lvl; + pub async fn set_permission_lvl(&self, lvl: PermissionLvl) { + let mut level = self.permission_lvl.write(); + *level = lvl; self.send_permission_lvl_update().await; } + /// get the players permission level pub fn permission_lvl(&self) -> PermissionLvl { - self.permission_lvl + self.permission_lvl.read().clone() } /// yaw and pitch in degrees @@ -863,3 +875,15 @@ pub enum PermissionLvl { Three = 3, Four = 4, } + +impl From for PermissionLvl { + fn from(op: OpLevel) -> Self { + match op { + OpLevel::None => PermissionLvl::Zero, + OpLevel::Basic => PermissionLvl::One, + OpLevel::Moderator => PermissionLvl::Two, + OpLevel::Admin => PermissionLvl::Three, + OpLevel::Owner => PermissionLvl::Four, + } + } +} \ No newline at end of file From 151b0d606e474e86755540dcdc1ba7005b2a4aff Mon Sep 17 00:00:00 2001 From: kyled Date: Thu, 28 Nov 2024 17:17:16 -0500 Subject: [PATCH 02/11] Clippy Fix + need to Rejoin after /op removed I found the source of the DeadLock. Updated set permission function. Fix cargo formatting issues and clippy issues. --- pumpkin-config/src/lib.rs | 11 ++++++----- pumpkin-config/src/op.rs | 8 ++++---- pumpkin/src/command/commands/cmd_op.rs | 15 +++++++++------ pumpkin/src/command/commands/mod.rs | 2 +- pumpkin/src/entity/player.rs | 19 +++++++++++-------- 5 files changed, 31 insertions(+), 24 deletions(-) diff --git a/pumpkin-config/src/lib.rs b/pumpkin-config/src/lib.rs index 91a7a6e4e..2335b614f 100644 --- a/pumpkin-config/src/lib.rs +++ b/pumpkin-config/src/lib.rs @@ -1,6 +1,5 @@ use log::warn; use logging::LoggingConfig; -use op::Op; use pumpkin_core::{Difficulty, GameMode}; use query::QueryConfig; use serde::{de::DeserializeOwned, Deserialize, Serialize}; @@ -182,7 +181,7 @@ pub trait LoadJSONConfiguration { let path = Self::get_path(); let config = if path.exists() { - let file_content = fs::read_to_string(&path) + let file_content = fs::read_to_string(path) .unwrap_or_else(|_| panic!("Couldn't read configuration file at {:?}", path)); serde_json::from_str(&file_content).unwrap_or_else(|err| { @@ -194,7 +193,7 @@ pub trait LoadJSONConfiguration { } else { let content = Self::default(); - if let Err(err) = fs::write(&path, serde_json::to_string_pretty(&content).unwrap()) { + if let Err(err) = fs::write(path, serde_json::to_string_pretty(&content).unwrap()) { eprintln!( "Couldn't write default config to {:?}. Reason: {}. This is probably caused by a config update. Just delete the old config and restart.", path, err @@ -214,7 +213,9 @@ pub trait LoadJSONConfiguration { } pub trait SaveJSONConfiguration: LoadJSONConfiguration { - async fn save(&self) + // suppress clippy warning + + fn save(&self) where Self: Sized + Default + Serialize + for<'de> Deserialize<'de>, { @@ -231,7 +232,7 @@ pub trait SaveJSONConfiguration: LoadJSONConfiguration { } }; - if let Err(err) = tokio::fs::write(path, content).await { + if let Err(err) = fs::write(path, content) { warn!( "Couldn't write operator config to {:?}. Reason: {}", path, err diff --git a/pumpkin-config/src/op.rs b/pumpkin-config/src/op.rs index 6e5b982e9..053066b52 100644 --- a/pumpkin-config/src/op.rs +++ b/pumpkin-config/src/op.rs @@ -1,12 +1,12 @@ use serde::{Deserialize, Serialize}; use uuid::Uuid; -#[derive(Serialize, Deserialize,Clone, Copy)] +#[derive(Serialize, Deserialize, Clone, Copy)] #[repr(u8)] pub enum OpLevel { None = 0, - Basic = 1, - Moderator = 2, + Basic = 1, + Moderator = 2, Admin = 3, Owner = 4, } @@ -17,4 +17,4 @@ pub struct Op { pub name: String, pub level: OpLevel, pub bypasses_player_limit: bool, -} \ No newline at end of file +} diff --git a/pumpkin/src/command/commands/cmd_op.rs b/pumpkin/src/command/commands/cmd_op.rs index b964417f8..66c9697dd 100644 --- a/pumpkin/src/command/commands/cmd_op.rs +++ b/pumpkin/src/command/commands/cmd_op.rs @@ -49,18 +49,21 @@ impl CommandExecutor for OpExecutor { } else { config.ops.push(op_entry); } - config.save().await; + config.save(); // TODO: can't fully implement until require can accept async closures - // player - // .set_permission_lvl(BASIC_CONFIG.op_permission_level.into()) - // .await; + player + .set_permission_lvl(BASIC_CONFIG.op_permission_level.into()) + .await; let player_name = player.gameprofile.name.clone(); - let message = format!("Made {} a server operator.", player_name); + let message = format!("Made {player_name} a server operator."); let msg = TextComponent::text(&message); sender.send_message(msg).await; - log::warn!("{} has been made a server operator. Please level and rejoin to see the changes...", player_name); + log::warn!( + "{} has been made a server operator. Please level and rejoin to see the changes...", + player_name + ); } Ok(()) diff --git a/pumpkin/src/command/commands/mod.rs b/pumpkin/src/command/commands/mod.rs index 407efe9a3..6ef767550 100644 --- a/pumpkin/src/command/commands/mod.rs +++ b/pumpkin/src/command/commands/mod.rs @@ -9,6 +9,7 @@ pub mod cmd_help; pub mod cmd_kick; pub mod cmd_kill; pub mod cmd_list; +pub mod cmd_op; pub mod cmd_pumpkin; pub mod cmd_say; pub mod cmd_seed; @@ -18,4 +19,3 @@ pub mod cmd_teleport; pub mod cmd_time; pub mod cmd_transfer; pub mod cmd_worldborder; -pub mod cmd_op; diff --git a/pumpkin/src/entity/player.rs b/pumpkin/src/entity/player.rs index 10c1f337d..8f96e7c28 100644 --- a/pumpkin/src/entity/player.rs +++ b/pumpkin/src/entity/player.rs @@ -10,7 +10,6 @@ use std::{ use crossbeam::atomic::AtomicCell; use itertools::Itertools; use num_derive::{FromPrimitive, ToPrimitive}; -use parking_lot::RwLock; use pumpkin_config::{op::OpLevel, ADVANCED_CONFIG, OPERATOR_CONFIG}; use pumpkin_core::{ math::{ @@ -159,7 +158,7 @@ pub struct Player { cancel_tasks: Notify, /// the players op permission level - permission_lvl: parking_lot::RwLock, + permission_lvl: parking_lot::Mutex, } impl Player { @@ -229,7 +228,9 @@ impl Player { .ops .iter() .find(|op| op.uuid == gameprofile_clone.id) - .map_or(parking_lot::RwLock::new(PermissionLvl::Zero), |op| parking_lot::RwLock::new(op.level.into())), + .map_or(parking_lot::Mutex::new(PermissionLvl::Zero), |op| { + parking_lot::Mutex::new(op.level.into()) + }), } } @@ -523,15 +524,17 @@ impl Player { /// sets the players permission level and syncs it with the client pub async fn set_permission_lvl(&self, lvl: PermissionLvl) { - let mut level = self.permission_lvl.write(); - *level = lvl; + { + let mut level = self.permission_lvl.lock(); + *level = lvl; + } + self.send_permission_lvl_update().await; } - /// get the players permission level pub fn permission_lvl(&self) -> PermissionLvl { - self.permission_lvl.read().clone() + *self.permission_lvl.lock() } /// yaw and pitch in degrees @@ -886,4 +889,4 @@ impl From for PermissionLvl { OpLevel::Owner => PermissionLvl::Four, } } -} \ No newline at end of file +} From e8400f6eb2386eebd7d7066e90a0e7e16745cbfc Mon Sep 17 00:00:00 2001 From: kyled Date: Thu, 28 Nov 2024 17:22:05 -0500 Subject: [PATCH 03/11] Remove temp warn message --- pumpkin/src/command/commands/cmd_op.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/pumpkin/src/command/commands/cmd_op.rs b/pumpkin/src/command/commands/cmd_op.rs index 66c9697dd..c42f430d4 100644 --- a/pumpkin/src/command/commands/cmd_op.rs +++ b/pumpkin/src/command/commands/cmd_op.rs @@ -60,10 +60,6 @@ impl CommandExecutor for OpExecutor { let message = format!("Made {player_name} a server operator."); let msg = TextComponent::text(&message); sender.send_message(msg).await; - log::warn!( - "{} has been made a server operator. Please level and rejoin to see the changes...", - player_name - ); } Ok(()) From 9c323a1d865ebdd1546301f1b9dddc63f39874ac Mon Sep 17 00:00:00 2001 From: kyled Date: Mon, 2 Dec 2024 12:53:58 -0500 Subject: [PATCH 04/11] Move OperatorConfig to server As Snowiii pointed out, OperatorConfig is runtime data. Revert most changes in pumpkin-config. op_permission_level must say in the basic configuration for parity with Minecraft. --- pumpkin-config/Cargo.toml | 3 +- pumpkin-config/src/lib.rs | 119 ++----------------------- pumpkin/src/command/commands/cmd_op.rs | 4 +- pumpkin/src/entity/player.rs | 4 +- pumpkin/src/server/json_config.rs | 95 ++++++++++++++++++++ pumpkin/src/server/mod.rs | 2 + 6 files changed, 111 insertions(+), 116 deletions(-) create mode 100644 pumpkin/src/server/json_config.rs diff --git a/pumpkin-config/Cargo.toml b/pumpkin-config/Cargo.toml index e5a5eeae0..c9bf9ba85 100644 --- a/pumpkin-config/Cargo.toml +++ b/pumpkin-config/Cargo.toml @@ -8,6 +8,5 @@ pumpkin-core = { path = "../pumpkin-core" } serde.workspace = true log.workspace = true uuid.workspace = true -tokio.workspace = true -serde_json.workspace = true + toml = "0.8" diff --git a/pumpkin-config/src/lib.rs b/pumpkin-config/src/lib.rs index 2335b614f..f5a827af3 100644 --- a/pumpkin-config/src/lib.rs +++ b/pumpkin-config/src/lib.rs @@ -1,5 +1,6 @@ use log::warn; use logging::LoggingConfig; +use op::OpLevel; use pumpkin_core::{Difficulty, GameMode}; use query::QueryConfig; use serde::{de::DeserializeOwned, Deserialize, Serialize}; @@ -13,7 +14,6 @@ use std::{ pub mod auth; pub mod logging; -pub mod op; pub mod proxy; pub mod query; pub mod resource_pack; @@ -32,24 +32,17 @@ mod lan_broadcast; mod pvp; mod rcon; mod server_links; +pub mod op; + use proxy::ProxyConfig; use resource_pack::ResourcePackConfig; -pub static OPERATOR_CONFIG: LazyLock> = - LazyLock::new(|| tokio::sync::RwLock::new(OperatorConfig::load())); - pub static ADVANCED_CONFIG: LazyLock = LazyLock::new(AdvancedConfiguration::load); pub static BASIC_CONFIG: LazyLock = LazyLock::new(BasicConfiguration::load); -#[derive(Deserialize, Serialize, Default)] -#[serde(transparent)] -pub struct OperatorConfig { - pub ops: Vec, -} - /// The idea is that Pumpkin should very customizable. /// You can Enable or Disable Features depending on your needs. /// @@ -86,8 +79,8 @@ pub struct BasicConfiguration { pub simulation_distance: u8, /// The default game difficulty. pub default_difficulty: Difficulty, - /// The operator level set by the /op command - pub op_permission_level: op::OpLevel, + /// The op level assign by the /op command + pub op_permission_level: OpLevel, /// Whether the Nether dimension is enabled. pub allow_nether: bool, /// Whether the server is in hardcore mode. @@ -118,7 +111,7 @@ impl Default for BasicConfiguration { view_distance: 10, simulation_distance: 10, default_difficulty: Difficulty::Normal, - op_permission_level: op::OpLevel::Owner, + op_permission_level: OpLevel::Owner, allow_nether: true, hardcore: false, online_mode: true, @@ -133,7 +126,7 @@ impl Default for BasicConfiguration { } } -trait LoadTomlConfiguration { +trait LoadConfiguration { fn load() -> Self where Self: Sized + Default + Serialize + DeserializeOwned, @@ -173,86 +166,7 @@ trait LoadTomlConfiguration { fn validate(&self); } -pub trait LoadJSONConfiguration { - fn load() -> Self - where - Self: Sized + Default + Serialize + for<'de> Deserialize<'de>, - { - let path = Self::get_path(); - - let config = if path.exists() { - let file_content = fs::read_to_string(path) - .unwrap_or_else(|_| panic!("Couldn't read configuration file at {:?}", path)); - - serde_json::from_str(&file_content).unwrap_or_else(|err| { - panic!( - "Couldn't parse config at {:?}. Reason: {}. This is probably caused by a config update. Just delete the old config and restart.", - path, err - ) - }) - } else { - let content = Self::default(); - - if let Err(err) = fs::write(path, serde_json::to_string_pretty(&content).unwrap()) { - eprintln!( - "Couldn't write default config to {:?}. Reason: {}. This is probably caused by a config update. Just delete the old config and restart.", - path, err - ); - } - - content - }; - - config.validate(); - config - } - - fn get_path() -> &'static Path; - - fn validate(&self); -} - -pub trait SaveJSONConfiguration: LoadJSONConfiguration { - // suppress clippy warning - - fn save(&self) - where - Self: Sized + Default + Serialize + for<'de> Deserialize<'de>, - { - let path = ::get_path(); - - let content = match serde_json::to_string_pretty(self) { - Ok(content) => content, - Err(err) => { - warn!( - "Couldn't serialize operator config to {:?}. Reason: {}", - path, err - ); - return; - } - }; - - if let Err(err) = fs::write(path, content) { - warn!( - "Couldn't write operator config to {:?}. Reason: {}", - path, err - ); - } - } -} - -impl LoadJSONConfiguration for OperatorConfig { - fn get_path() -> &'static Path { - Path::new("ops.json") - } - fn validate(&self) { - // TODO: Validate the operator configuration - } -} - -impl SaveJSONConfiguration for OperatorConfig {} - -impl LoadTomlConfiguration for AdvancedConfiguration { +impl LoadConfiguration for AdvancedConfiguration { fn get_path() -> &'static Path { Path::new("features.toml") } @@ -262,22 +176,7 @@ impl LoadTomlConfiguration for AdvancedConfiguration { } } -// impl OperatorConfig { - -// pub async fn reload() where Self: LoadJSONConfiguration { -// let mut config = OPERATOR_CONFIG.write().await; -// *config = OperatorConfig::load(); -// } - -// pub async fn save(&self) where Self: SaveJSONConfiguration { -// Box::pin(async move { -// self.save().await; -// }).await -// } - -// } - -impl LoadTomlConfiguration for BasicConfiguration { +impl LoadConfiguration for BasicConfiguration { fn get_path() -> &'static Path { Path::new("configuration.toml") } diff --git a/pumpkin/src/command/commands/cmd_op.rs b/pumpkin/src/command/commands/cmd_op.rs index c42f430d4..081cf46f5 100644 --- a/pumpkin/src/command/commands/cmd_op.rs +++ b/pumpkin/src/command/commands/cmd_op.rs @@ -5,10 +5,10 @@ use crate::{ tree_builder::{argument, require}, CommandError, CommandExecutor, CommandSender, }, - entity::player::PermissionLvl, + entity::player::PermissionLvl, server::json_config::{SaveJSONConfiguration, OPERATOR_CONFIG}, }; use async_trait::async_trait; -use pumpkin_config::{op::Op, SaveJSONConfiguration, BASIC_CONFIG, OPERATOR_CONFIG}; +use pumpkin_config::{op::Op,BASIC_CONFIG}; use pumpkin_core::text::TextComponent; use CommandError::InvalidConsumption; diff --git a/pumpkin/src/entity/player.rs b/pumpkin/src/entity/player.rs index 8f96e7c28..532593fd0 100644 --- a/pumpkin/src/entity/player.rs +++ b/pumpkin/src/entity/player.rs @@ -10,7 +10,7 @@ use std::{ use crossbeam::atomic::AtomicCell; use itertools::Itertools; use num_derive::{FromPrimitive, ToPrimitive}; -use pumpkin_config::{op::OpLevel, ADVANCED_CONFIG, OPERATOR_CONFIG}; +use pumpkin_config::{op::OpLevel, ADVANCED_CONFIG}; use pumpkin_core::{ math::{ boundingbox::{BoundingBox, BoundingBoxSize}, @@ -46,7 +46,7 @@ use tokio::sync::{Mutex, Notify}; use tokio::task::JoinHandle; use super::Entity; -use crate::error::PumpkinError; +use crate::{error::PumpkinError, server::json_config::OPERATOR_CONFIG}; use crate::{ client::{ authentication::GameProfile, diff --git a/pumpkin/src/server/json_config.rs b/pumpkin/src/server/json_config.rs new file mode 100644 index 000000000..5c1a8a7ff --- /dev/null +++ b/pumpkin/src/server/json_config.rs @@ -0,0 +1,95 @@ +use std::{fs, path::Path, sync::LazyLock}; + +use log::warn; +use pumpkin_config::op; +use serde::{Deserialize, Serialize}; + +pub static OPERATOR_CONFIG: LazyLock> = + LazyLock::new(|| tokio::sync::RwLock::new(OperatorConfig::load())); + + +#[derive(Deserialize, Serialize, Default)] +#[serde(transparent)] +pub struct OperatorConfig { + pub ops: Vec, +} + +pub trait LoadJSONConfiguration { + fn load() -> Self + where + Self: Sized + Default + Serialize + for<'de> Deserialize<'de>, + { + let path = Self::get_path(); + + let config = if path.exists() { + let file_content = fs::read_to_string(path) + .unwrap_or_else(|_| panic!("Couldn't read configuration file at {:?}", path)); + + serde_json::from_str(&file_content).unwrap_or_else(|err| { + panic!( + "Couldn't parse config at {:?}. Reason: {}. This is probably caused by a config update. Just delete the old config and restart.", + path, err + ) + }) + } else { + let content = Self::default(); + + if let Err(err) = fs::write(path, serde_json::to_string_pretty(&content).unwrap()) { + eprintln!( + "Couldn't write default config to {:?}. Reason: {}. This is probably caused by a config update. Just delete the old config and restart.", + path, err + ); + } + + content + }; + + config.validate(); + config + } + + fn get_path() -> &'static Path; + + fn validate(&self); +} + +pub trait SaveJSONConfiguration: LoadJSONConfiguration { + // suppress clippy warning + + fn save(&self) + where + Self: Sized + Default + Serialize + for<'de> Deserialize<'de>, + { + let path = ::get_path(); + + let content = match serde_json::to_string_pretty(self) { + Ok(content) => content, + Err(err) => { + warn!( + "Couldn't serialize operator config to {:?}. Reason: {}", + path, err + ); + return; + } + }; + + if let Err(err) = fs::write(path, content) { + warn!( + "Couldn't write operator config to {:?}. Reason: {}", + path, err + ); + } + } +} + +impl LoadJSONConfiguration for OperatorConfig { + fn get_path() -> &'static Path { + Path::new("ops.json") + } + fn validate(&self) { + // TODO: Validate the operator configuration + } +} + + +impl SaveJSONConfiguration for OperatorConfig {} \ No newline at end of file diff --git a/pumpkin/src/server/mod.rs b/pumpkin/src/server/mod.rs index db295cf41..64a753c55 100644 --- a/pumpkin/src/server/mod.rs +++ b/pumpkin/src/server/mod.rs @@ -29,12 +29,14 @@ use crate::{ world::World, }; +pub mod json_config; mod connection_cache; mod key_store; pub mod ticker; pub const CURRENT_MC_VERSION: &str = "1.21.3"; + /// Represents a Minecraft server instance. pub struct Server { /// Handles cryptographic keys for secure communication. From b71159646b32750f84e14ee429d497e0b19c7680 Mon Sep 17 00:00:00 2001 From: kyled Date: Mon, 2 Dec 2024 13:02:20 -0500 Subject: [PATCH 05/11] cargo fmt + cargo clippy --- pumpkin-config/src/lib.rs | 3 +-- pumpkin/src/command/commands/cmd_op.rs | 5 +++-- pumpkin/src/entity/player.rs | 2 +- pumpkin/src/server/json_config.rs | 13 +++++-------- pumpkin/src/server/mod.rs | 3 +-- 5 files changed, 11 insertions(+), 15 deletions(-) diff --git a/pumpkin-config/src/lib.rs b/pumpkin-config/src/lib.rs index f5a827af3..6370af32f 100644 --- a/pumpkin-config/src/lib.rs +++ b/pumpkin-config/src/lib.rs @@ -29,11 +29,10 @@ pub use server_links::ServerLinksConfig; mod commands; pub mod compression; mod lan_broadcast; +pub mod op; mod pvp; mod rcon; mod server_links; -pub mod op; - use proxy::ProxyConfig; use resource_pack::ResourcePackConfig; diff --git a/pumpkin/src/command/commands/cmd_op.rs b/pumpkin/src/command/commands/cmd_op.rs index 081cf46f5..b2dee18f6 100644 --- a/pumpkin/src/command/commands/cmd_op.rs +++ b/pumpkin/src/command/commands/cmd_op.rs @@ -5,10 +5,11 @@ use crate::{ tree_builder::{argument, require}, CommandError, CommandExecutor, CommandSender, }, - entity::player::PermissionLvl, server::json_config::{SaveJSONConfiguration, OPERATOR_CONFIG}, + entity::player::PermissionLvl, + server::json_config::{SaveJSONConfiguration, OPERATOR_CONFIG}, }; use async_trait::async_trait; -use pumpkin_config::{op::Op,BASIC_CONFIG}; +use pumpkin_config::{op::Op, BASIC_CONFIG}; use pumpkin_core::text::TextComponent; use CommandError::InvalidConsumption; diff --git a/pumpkin/src/entity/player.rs b/pumpkin/src/entity/player.rs index 532593fd0..341ca3fb2 100644 --- a/pumpkin/src/entity/player.rs +++ b/pumpkin/src/entity/player.rs @@ -46,7 +46,6 @@ use tokio::sync::{Mutex, Notify}; use tokio::task::JoinHandle; use super::Entity; -use crate::{error::PumpkinError, server::json_config::OPERATOR_CONFIG}; use crate::{ client::{ authentication::GameProfile, @@ -56,6 +55,7 @@ use crate::{ server::Server, world::World, }; +use crate::{error::PumpkinError, server::json_config::OPERATOR_CONFIG}; use super::living::LivingEntity; diff --git a/pumpkin/src/server/json_config.rs b/pumpkin/src/server/json_config.rs index 5c1a8a7ff..c756a1d65 100644 --- a/pumpkin/src/server/json_config.rs +++ b/pumpkin/src/server/json_config.rs @@ -7,7 +7,6 @@ use serde::{Deserialize, Serialize}; pub static OPERATOR_CONFIG: LazyLock> = LazyLock::new(|| tokio::sync::RwLock::new(OperatorConfig::load())); - #[derive(Deserialize, Serialize, Default)] #[serde(transparent)] pub struct OperatorConfig { @@ -15,6 +14,7 @@ pub struct OperatorConfig { } pub trait LoadJSONConfiguration { + #[must_use] fn load() -> Self where Self: Sized + Default + Serialize + for<'de> Deserialize<'de>, @@ -23,12 +23,11 @@ pub trait LoadJSONConfiguration { let config = if path.exists() { let file_content = fs::read_to_string(path) - .unwrap_or_else(|_| panic!("Couldn't read configuration file at {:?}", path)); + .unwrap_or_else(|_| panic!("Couldn't read configuration file at {path:?}")); serde_json::from_str(&file_content).unwrap_or_else(|err| { panic!( - "Couldn't parse config at {:?}. Reason: {}. This is probably caused by a config update. Just delete the old config and restart.", - path, err + "Couldn't parse config at {path:?}. Reason: {err}. This is probably caused by a config update. Just delete the old config and restart.", ) }) } else { @@ -36,8 +35,7 @@ pub trait LoadJSONConfiguration { if let Err(err) = fs::write(path, serde_json::to_string_pretty(&content).unwrap()) { eprintln!( - "Couldn't write default config to {:?}. Reason: {}. This is probably caused by a config update. Just delete the old config and restart.", - path, err + "Couldn't write default config to {path:?}. Reason: {err}. This is probably caused by a config update. Just delete the old config and restart.", ); } @@ -91,5 +89,4 @@ impl LoadJSONConfiguration for OperatorConfig { } } - -impl SaveJSONConfiguration for OperatorConfig {} \ No newline at end of file +impl SaveJSONConfiguration for OperatorConfig {} diff --git a/pumpkin/src/server/mod.rs b/pumpkin/src/server/mod.rs index 64a753c55..cae83acf4 100644 --- a/pumpkin/src/server/mod.rs +++ b/pumpkin/src/server/mod.rs @@ -29,14 +29,13 @@ use crate::{ world::World, }; -pub mod json_config; mod connection_cache; +pub mod json_config; mod key_store; pub mod ticker; pub const CURRENT_MC_VERSION: &str = "1.21.3"; - /// Represents a Minecraft server instance. pub struct Server { /// Handles cryptographic keys for secure communication. From 48ba281ffd97b529ca976c66c7e1eb7c603cae0d Mon Sep 17 00:00:00 2001 From: kyled Date: Mon, 2 Dec 2024 15:52:36 -0500 Subject: [PATCH 06/11] Sync permission change with client --- docker-compose.yml | 4 +++- pumpkin-config/src/op.rs | 33 ++++++++++++++++++++++++-- pumpkin/src/command/commands/cmd_op.rs | 8 ++++--- pumpkin/src/entity/player.rs | 8 ++++++- 4 files changed, 46 insertions(+), 7 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index a8c019a08..418bdac0c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,6 +2,8 @@ services: pumpkin: build: . ports: - - 25565:25565 + - "25565:25565" volumes: - ./data:/pumpkin + stdin_open: true + tty: true diff --git a/pumpkin-config/src/op.rs b/pumpkin-config/src/op.rs index 053066b52..077654531 100644 --- a/pumpkin-config/src/op.rs +++ b/pumpkin-config/src/op.rs @@ -1,7 +1,7 @@ -use serde::{Deserialize, Serialize}; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; use uuid::Uuid; -#[derive(Serialize, Deserialize, Clone, Copy)] +#[derive(Clone, Copy)] #[repr(u8)] pub enum OpLevel { None = 0, @@ -11,6 +11,35 @@ pub enum OpLevel { Owner = 4, } +impl Serialize for OpLevel { + fn serialize(&self, serializer: S) -> Result<::Ok, ::Error> + where + S: Serializer, + { + serializer.serialize_u8(*self as u8) + } +} + +impl<'de> Deserialize<'de> for OpLevel { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let value = u8::deserialize(deserializer)?; + match value { + 0 => Ok(OpLevel::None), + 1 => Ok(OpLevel::Basic), + 2 => Ok(OpLevel::Moderator), + 3 => Ok(OpLevel::Admin), + 4 => Ok(OpLevel::Owner), + _ => Err(serde::de::Error::custom(format!( + "Invalid value for OpLevel: {}", + value + ))), + } + } +} + #[derive(Serialize, Deserialize, Clone)] pub struct Op { pub uuid: Uuid, diff --git a/pumpkin/src/command/commands/cmd_op.rs b/pumpkin/src/command/commands/cmd_op.rs index b2dee18f6..d8d2fea71 100644 --- a/pumpkin/src/command/commands/cmd_op.rs +++ b/pumpkin/src/command/commands/cmd_op.rs @@ -24,7 +24,7 @@ impl CommandExecutor for OpExecutor { async fn execute<'a>( &self, sender: &mut CommandSender<'a>, - _server: &crate::server::Server, + server: &crate::server::Server, args: &ConsumedArgs<'a>, ) -> Result<(), CommandError> { let mut config = OPERATOR_CONFIG.write().await; @@ -52,9 +52,11 @@ impl CommandExecutor for OpExecutor { } config.save(); - // TODO: can't fully implement until require can accept async closures player - .set_permission_lvl(BASIC_CONFIG.op_permission_level.into()) + .set_permission_lvl( + BASIC_CONFIG.op_permission_level.into(), + &server.command_dispatcher, + ) .await; let player_name = player.gameprofile.name.clone(); diff --git a/pumpkin/src/entity/player.rs b/pumpkin/src/entity/player.rs index 341ca3fb2..952995b5f 100644 --- a/pumpkin/src/entity/player.rs +++ b/pumpkin/src/entity/player.rs @@ -52,6 +52,7 @@ use crate::{ combat::{self, player_attack_sound, AttackType}, Client, PlayerConfig, }, + command::{client_cmd_suggestions, dispatcher::CommandDispatcher}, server::Server, world::World, }; @@ -523,13 +524,18 @@ impl Player { } /// sets the players permission level and syncs it with the client - pub async fn set_permission_lvl(&self, lvl: PermissionLvl) { + pub async fn set_permission_lvl( + self: &Arc, + lvl: PermissionLvl, + command_dispatcher: &Arc>, + ) { { let mut level = self.permission_lvl.lock(); *level = lvl; } self.send_permission_lvl_update().await; + client_cmd_suggestions::send_c_commands_packet(self, command_dispatcher).await; } /// get the players permission level From fa594799a8a4c43253ee693a18b6c0fa037f822e Mon Sep 17 00:00:00 2001 From: kyled Date: Fri, 6 Dec 2024 18:03:10 -0500 Subject: [PATCH 07/11] Fixs issues @Commandcracker found in review - Move PermissionLvl to core and removed OpLevel - Move ops.json to /data/ops.json - Fix permissions issue with /op command - Shorten /op command description --- pumpkin-config/src/lib.rs | 7 +-- pumpkin-config/src/op.rs | 62 +++++++------------- pumpkin-core/src/lib.rs | 1 + pumpkin-core/src/permission.rs | 55 +++++++++++++++++ pumpkin/src/command/commands/cmd_fill.rs | 3 +- pumpkin/src/command/commands/cmd_gamemode.rs | 2 +- pumpkin/src/command/commands/cmd_give.rs | 2 +- pumpkin/src/command/commands/cmd_op.rs | 29 +++++---- pumpkin/src/command/commands/cmd_say.rs | 14 ++--- pumpkin/src/command/commands/cmd_seed.rs | 2 +- pumpkin/src/command/commands/cmd_setblock.rs | 2 +- pumpkin/src/command/commands/cmd_stop.rs | 2 +- pumpkin/src/command/commands/cmd_teleport.rs | 2 +- pumpkin/src/command/commands/cmd_time.rs | 10 ++-- pumpkin/src/command/commands/cmd_transfer.rs | 2 +- pumpkin/src/command/mod.rs | 3 +- pumpkin/src/entity/player.rs | 30 ++-------- pumpkin/src/server/json_config.rs | 2 +- 18 files changed, 121 insertions(+), 109 deletions(-) create mode 100644 pumpkin-core/src/permission.rs diff --git a/pumpkin-config/src/lib.rs b/pumpkin-config/src/lib.rs index 6370af32f..18f0d7d37 100644 --- a/pumpkin-config/src/lib.rs +++ b/pumpkin-config/src/lib.rs @@ -1,7 +1,6 @@ use log::warn; use logging::LoggingConfig; -use op::OpLevel; -use pumpkin_core::{Difficulty, GameMode}; +use pumpkin_core::{permission::PermissionLvl, Difficulty, GameMode}; use query::QueryConfig; use serde::{de::DeserializeOwned, Deserialize, Serialize}; @@ -79,7 +78,7 @@ pub struct BasicConfiguration { /// The default game difficulty. pub default_difficulty: Difficulty, /// The op level assign by the /op command - pub op_permission_level: OpLevel, + pub op_permission_level: PermissionLvl, /// Whether the Nether dimension is enabled. pub allow_nether: bool, /// Whether the server is in hardcore mode. @@ -110,7 +109,7 @@ impl Default for BasicConfiguration { view_distance: 10, simulation_distance: 10, default_difficulty: Difficulty::Normal, - op_permission_level: OpLevel::Owner, + op_permission_level: PermissionLvl::Four, allow_nether: true, hardcore: false, online_mode: true, diff --git a/pumpkin-config/src/op.rs b/pumpkin-config/src/op.rs index 077654531..edbd71bca 100644 --- a/pumpkin-config/src/op.rs +++ b/pumpkin-config/src/op.rs @@ -1,49 +1,27 @@ -use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use pumpkin_core::permission::PermissionLvl; +use serde::{Deserialize, Serialize}; use uuid::Uuid; -#[derive(Clone, Copy)] -#[repr(u8)] -pub enum OpLevel { - None = 0, - Basic = 1, - Moderator = 2, - Admin = 3, - Owner = 4, -} - -impl Serialize for OpLevel { - fn serialize(&self, serializer: S) -> Result<::Ok, ::Error> - where - S: Serializer, - { - serializer.serialize_u8(*self as u8) - } -} - -impl<'de> Deserialize<'de> for OpLevel { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let value = u8::deserialize(deserializer)?; - match value { - 0 => Ok(OpLevel::None), - 1 => Ok(OpLevel::Basic), - 2 => Ok(OpLevel::Moderator), - 3 => Ok(OpLevel::Admin), - 4 => Ok(OpLevel::Owner), - _ => Err(serde::de::Error::custom(format!( - "Invalid value for OpLevel: {}", - value - ))), - } - } -} - -#[derive(Serialize, Deserialize, Clone)] +#[derive(Serialize, Deserialize, Clone, Default)] pub struct Op { pub uuid: Uuid, pub name: String, - pub level: OpLevel, + pub level: PermissionLvl, pub bypasses_player_limit: bool, } + +impl Op { + pub fn new( + uuid: Uuid, + name: String, + level: PermissionLvl, + bypasses_player_limit: bool, + ) -> Self { + Self { + uuid, + name, + level, + bypasses_player_limit, + } + } +} diff --git a/pumpkin-core/src/lib.rs b/pumpkin-core/src/lib.rs index f566404df..35feb51a7 100644 --- a/pumpkin-core/src/lib.rs +++ b/pumpkin-core/src/lib.rs @@ -1,5 +1,6 @@ pub mod gamemode; pub mod math; +pub mod permission; pub mod random; pub mod text; diff --git a/pumpkin-core/src/permission.rs b/pumpkin-core/src/permission.rs new file mode 100644 index 000000000..0a3d1abbd --- /dev/null +++ b/pumpkin-core/src/permission.rs @@ -0,0 +1,55 @@ +use num_derive::{FromPrimitive, ToPrimitive}; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; + +/// Represents the player's permission level +/// +/// Permission levels determine the player's access to commands and server operations. +/// Each numeric level corresponds to a specific role: +/// - `Zero`: None +/// - `One`: Basic +/// - `Two`: Moderator +/// - `Three`: Admin +/// - `Four`: Owner +#[derive(FromPrimitive, ToPrimitive, Clone, Copy, Default, PartialEq, Eq)] +#[repr(i8)] +pub enum PermissionLvl { + #[default] + Zero = 0, + Two = 2, + Three = 3, + Four = 4, +} + +impl PartialOrd for PermissionLvl { + fn partial_cmp(&self, other: &Self) -> Option { + (*self as u8).partial_cmp(&(*other as u8)) + } +} + +impl Serialize for PermissionLvl { + fn serialize(&self, serializer: S) -> Result<::Ok, ::Error> + where + S: Serializer, + { + serializer.serialize_u8(*self as u8) + } +} + +impl<'de> Deserialize<'de> for PermissionLvl { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let value = u8::deserialize(deserializer)?; + match value { + 0 => Ok(PermissionLvl::Zero), + 2 => Ok(PermissionLvl::Two), + 3 => Ok(PermissionLvl::Three), + 4 => Ok(PermissionLvl::Four), + _ => Err(serde::de::Error::custom(format!( + "Invalid value for OpLevel: {}", + value + ))), + } + } +} diff --git a/pumpkin/src/command/commands/cmd_fill.rs b/pumpkin/src/command/commands/cmd_fill.rs index 031ba1143..596ddbe0a 100644 --- a/pumpkin/src/command/commands/cmd_fill.rs +++ b/pumpkin/src/command/commands/cmd_fill.rs @@ -4,10 +4,11 @@ use crate::command::args::{ConsumedArgs, FindArg}; use crate::command::tree::CommandTree; use crate::command::tree_builder::{argument, literal, require}; use crate::command::{CommandError, CommandExecutor, CommandSender}; -use crate::entity::player::PermissionLvl; + use async_trait::async_trait; use pumpkin_core::math::position::WorldPosition; use pumpkin_core::math::vector3::Vector3; +use pumpkin_core::permission::PermissionLvl; use pumpkin_core::text::TextComponent; const NAMES: [&str; 1] = ["fill"]; diff --git a/pumpkin/src/command/commands/cmd_gamemode.rs b/pumpkin/src/command/commands/cmd_gamemode.rs index faab95375..9112b170c 100644 --- a/pumpkin/src/command/commands/cmd_gamemode.rs +++ b/pumpkin/src/command/commands/cmd_gamemode.rs @@ -3,8 +3,8 @@ use async_trait::async_trait; use crate::command::args::arg_gamemode::GamemodeArgumentConsumer; use crate::command::args::GetCloned; -use crate::entity::player::PermissionLvl; use crate::TextComponent; +use pumpkin_core::permission::PermissionLvl; use crate::command::args::arg_players::PlayersArgumentConsumer; diff --git a/pumpkin/src/command/commands/cmd_give.rs b/pumpkin/src/command/commands/cmd_give.rs index 36edb640c..f306e114e 100644 --- a/pumpkin/src/command/commands/cmd_give.rs +++ b/pumpkin/src/command/commands/cmd_give.rs @@ -9,7 +9,7 @@ use crate::command::args::{ConsumedArgs, FindArg, FindArgDefaultName}; use crate::command::tree::CommandTree; use crate::command::tree_builder::{argument, argument_default_name, require}; use crate::command::{CommandError, CommandExecutor, CommandSender}; -use crate::entity::player::PermissionLvl; +use pumpkin_core::permission::PermissionLvl; const NAMES: [&str; 1] = ["give"]; diff --git a/pumpkin/src/command/commands/cmd_op.rs b/pumpkin/src/command/commands/cmd_op.rs index d8d2fea71..ed830fa51 100644 --- a/pumpkin/src/command/commands/cmd_op.rs +++ b/pumpkin/src/command/commands/cmd_op.rs @@ -5,16 +5,16 @@ use crate::{ tree_builder::{argument, require}, CommandError, CommandExecutor, CommandSender, }, - entity::player::PermissionLvl, server::json_config::{SaveJSONConfiguration, OPERATOR_CONFIG}, }; use async_trait::async_trait; use pumpkin_config::{op::Op, BASIC_CONFIG}; +use pumpkin_core::permission::PermissionLvl; use pumpkin_core::text::TextComponent; use CommandError::InvalidConsumption; const NAMES: [&str; 1] = ["op"]; -const DESCRIPTION: &str = "Specifies one or more game profiles (player profiles). Must be a player name (should be a real one if the server is in online mode) or a player-type target selector"; +const DESCRIPTION: &str = "Grants operator status to a player."; const ARG_TARGET: &str = "player"; struct OpExecutor; @@ -35,28 +35,31 @@ impl CommandExecutor for OpExecutor { // log each player to the console. for player in targets { - let op_entry = Op { - uuid: player.gameprofile.id, - name: player.gameprofile.name.clone(), - level: BASIC_CONFIG.op_permission_level, - bypasses_player_limit: false, + let new_level = if BASIC_CONFIG.op_permission_level > sender.permission_lvl() { + sender.permission_lvl() + } else { + BASIC_CONFIG.op_permission_level }; + + let op_entry = Op::new( + player.gameprofile.id, + player.gameprofile.name.clone(), + new_level, + false, + ); if let Some(op) = config .ops .iter_mut() .find(|o| o.uuid == player.gameprofile.id) { - op.level = BASIC_CONFIG.op_permission_level; + op.level = new_level; } else { config.ops.push(op_entry); } config.save(); player - .set_permission_lvl( - BASIC_CONFIG.op_permission_level.into(), - &server.command_dispatcher, - ) + .set_permission_lvl(new_level, &server.command_dispatcher) .await; let player_name = player.gameprofile.name.clone(); @@ -71,7 +74,7 @@ impl CommandExecutor for OpExecutor { pub fn init_command_tree<'a>() -> CommandTree<'a> { CommandTree::new(NAMES, DESCRIPTION).with_child( - require(&|sender| sender.has_permission_lvl(PermissionLvl::Four)) + require(&|sender| sender.has_permission_lvl(PermissionLvl::Three)) .with_child(argument(ARG_TARGET, &PlayersArgumentConsumer).execute(&OpExecutor)), ) } diff --git a/pumpkin/src/command/commands/cmd_say.rs b/pumpkin/src/command/commands/cmd_say.rs index 66b743385..7ced92b6d 100644 --- a/pumpkin/src/command/commands/cmd_say.rs +++ b/pumpkin/src/command/commands/cmd_say.rs @@ -2,15 +2,13 @@ use async_trait::async_trait; use pumpkin_core::text::TextComponent; use pumpkin_protocol::client::play::CSystemChatMessage; -use crate::{ - command::{ - args::{arg_message::MsgArgConsumer, Arg, ConsumedArgs}, - tree::CommandTree, - tree_builder::{argument, require}, - CommandError, CommandExecutor, CommandSender, - }, - entity::player::PermissionLvl, +use crate::command::{ + args::{arg_message::MsgArgConsumer, Arg, ConsumedArgs}, + tree::CommandTree, + tree_builder::{argument, require}, + CommandError, CommandExecutor, CommandSender, }; +use pumpkin_core::permission::PermissionLvl; use CommandError::InvalidConsumption; const NAMES: [&str; 1] = ["say"]; diff --git a/pumpkin/src/command/commands/cmd_seed.rs b/pumpkin/src/command/commands/cmd_seed.rs index 653697aa7..d3ed719d5 100644 --- a/pumpkin/src/command/commands/cmd_seed.rs +++ b/pumpkin/src/command/commands/cmd_seed.rs @@ -2,8 +2,8 @@ use crate::command::tree_builder::require; use crate::command::{ args::ConsumedArgs, tree::CommandTree, CommandError, CommandExecutor, CommandSender, }; -use crate::entity::player::PermissionLvl; use async_trait::async_trait; +use pumpkin_core::permission::PermissionLvl; use pumpkin_core::text::click::ClickEvent; use pumpkin_core::text::hover::HoverEvent; use pumpkin_core::text::{color::NamedColor, TextComponent}; diff --git a/pumpkin/src/command/commands/cmd_setblock.rs b/pumpkin/src/command/commands/cmd_setblock.rs index 4bd3b2644..b17c961c9 100644 --- a/pumpkin/src/command/commands/cmd_setblock.rs +++ b/pumpkin/src/command/commands/cmd_setblock.rs @@ -8,7 +8,7 @@ use crate::command::args::{ConsumedArgs, FindArg}; use crate::command::tree::CommandTree; use crate::command::tree_builder::{argument, literal, require}; use crate::command::{CommandError, CommandExecutor, CommandSender}; -use crate::entity::player::PermissionLvl; +use pumpkin_core::permission::PermissionLvl; const NAMES: [&str; 1] = ["setblock"]; diff --git a/pumpkin/src/command/commands/cmd_stop.rs b/pumpkin/src/command/commands/cmd_stop.rs index 0f1bc7afa..a15b6184b 100644 --- a/pumpkin/src/command/commands/cmd_stop.rs +++ b/pumpkin/src/command/commands/cmd_stop.rs @@ -6,7 +6,7 @@ use crate::command::args::ConsumedArgs; use crate::command::tree::CommandTree; use crate::command::tree_builder::require; use crate::command::{CommandError, CommandExecutor, CommandSender}; -use crate::entity::player::PermissionLvl; +use pumpkin_core::permission::PermissionLvl; const NAMES: [&str; 1] = ["stop"]; diff --git a/pumpkin/src/command/commands/cmd_teleport.rs b/pumpkin/src/command/commands/cmd_teleport.rs index fc368db16..ff6819ea9 100644 --- a/pumpkin/src/command/commands/cmd_teleport.rs +++ b/pumpkin/src/command/commands/cmd_teleport.rs @@ -12,7 +12,7 @@ use crate::command::tree::CommandTree; use crate::command::tree_builder::{argument, literal, require}; use crate::command::CommandError; use crate::command::{CommandExecutor, CommandSender}; -use crate::entity::player::PermissionLvl; +use pumpkin_core::permission::PermissionLvl; const NAMES: [&str; 2] = ["teleport", "tp"]; const DESCRIPTION: &str = "Teleports entities, including players."; // todo diff --git a/pumpkin/src/command/commands/cmd_time.rs b/pumpkin/src/command/commands/cmd_time.rs index ad2894018..eefc582e5 100644 --- a/pumpkin/src/command/commands/cmd_time.rs +++ b/pumpkin/src/command/commands/cmd_time.rs @@ -5,13 +5,11 @@ use pumpkin_core::text::TextComponent; use crate::command::args::arg_bounded_num::BoundedNumArgumentConsumer; use crate::command::args::FindArgDefaultName; use crate::command::tree_builder::{argument_default_name, literal}; -use crate::{ - command::{ - tree::CommandTree, tree_builder::require, CommandError, CommandExecutor, CommandSender, - ConsumedArgs, - }, - entity::player::PermissionLvl, +use crate::command::{ + tree::CommandTree, tree_builder::require, CommandError, CommandExecutor, CommandSender, + ConsumedArgs, }; +use pumpkin_core::permission::PermissionLvl; const NAMES: [&str; 1] = ["time"]; diff --git a/pumpkin/src/command/commands/cmd_transfer.rs b/pumpkin/src/command/commands/cmd_transfer.rs index b73f2418b..0d97f1eba 100644 --- a/pumpkin/src/command/commands/cmd_transfer.rs +++ b/pumpkin/src/command/commands/cmd_transfer.rs @@ -13,7 +13,7 @@ use crate::command::tree_builder::{argument, argument_default_name, require}; use crate::command::{ args::ConsumedArgs, tree::CommandTree, CommandError, CommandExecutor, CommandSender, }; -use crate::entity::player::PermissionLvl; +use pumpkin_core::permission::PermissionLvl; const NAMES: [&str; 1] = ["transfer"]; diff --git a/pumpkin/src/command/mod.rs b/pumpkin/src/command/mod.rs index c0aa849f9..b7e81aea0 100644 --- a/pumpkin/src/command/mod.rs +++ b/pumpkin/src/command/mod.rs @@ -4,7 +4,7 @@ use std::sync::Arc; use crate::command::commands::cmd_seed; use crate::command::commands::{cmd_bossbar, cmd_transfer}; use crate::command::dispatcher::CommandDispatcher; -use crate::entity::player::{PermissionLvl, Player}; +use crate::entity::player::Player; use crate::server::Server; use crate::world::World; use args::ConsumedArgs; @@ -17,6 +17,7 @@ use commands::{ }; use dispatcher::CommandError; use pumpkin_core::math::vector3::Vector3; +use pumpkin_core::permission::PermissionLvl; use pumpkin_core::text::TextComponent; pub mod args; diff --git a/pumpkin/src/entity/player.rs b/pumpkin/src/entity/player.rs index 952995b5f..e4ccb13f0 100644 --- a/pumpkin/src/entity/player.rs +++ b/pumpkin/src/entity/player.rs @@ -9,8 +9,8 @@ use std::{ use crossbeam::atomic::AtomicCell; use itertools::Itertools; -use num_derive::{FromPrimitive, ToPrimitive}; -use pumpkin_config::{op::OpLevel, ADVANCED_CONFIG}; +use num_derive::FromPrimitive; +use pumpkin_config::ADVANCED_CONFIG; use pumpkin_core::{ math::{ boundingbox::{BoundingBox, BoundingBoxSize}, @@ -18,6 +18,7 @@ use pumpkin_core::{ vector2::Vector2, vector3::Vector3, }, + permission::PermissionLvl, text::TextComponent, GameMode, }; @@ -230,7 +231,7 @@ impl Player { .iter() .find(|op| op.uuid == gameprofile_clone.id) .map_or(parking_lot::Mutex::new(PermissionLvl::Zero), |op| { - parking_lot::Mutex::new(op.level.into()) + parking_lot::Mutex::new(op.level) }), } } @@ -873,26 +874,3 @@ pub enum ChatMode { /// All messages should be hidden Hidden, } - -/// the player's permission level -#[derive(FromPrimitive, ToPrimitive, Clone, Copy)] -#[repr(i8)] -pub enum PermissionLvl { - Zero = 0, - One = 1, - Two = 2, - Three = 3, - Four = 4, -} - -impl From for PermissionLvl { - fn from(op: OpLevel) -> Self { - match op { - OpLevel::None => PermissionLvl::Zero, - OpLevel::Basic => PermissionLvl::One, - OpLevel::Moderator => PermissionLvl::Two, - OpLevel::Admin => PermissionLvl::Three, - OpLevel::Owner => PermissionLvl::Four, - } - } -} diff --git a/pumpkin/src/server/json_config.rs b/pumpkin/src/server/json_config.rs index c756a1d65..ecd65607e 100644 --- a/pumpkin/src/server/json_config.rs +++ b/pumpkin/src/server/json_config.rs @@ -82,7 +82,7 @@ pub trait SaveJSONConfiguration: LoadJSONConfiguration { impl LoadJSONConfiguration for OperatorConfig { fn get_path() -> &'static Path { - Path::new("ops.json") + Path::new("data/ops.json") } fn validate(&self) { // TODO: Validate the operator configuration From e4446b0f71e687eb8cdcde1e95e1bc70f7ae5f45 Mon Sep 17 00:00:00 2001 From: Alexander Medvedev Date: Fri, 27 Dec 2024 16:48:53 +0100 Subject: [PATCH 08/11] refactor into `data` folder --- pumpkin/src/command/commands/cmd_op.rs | 2 +- .../{server/json_config.rs => data/mod.rs} | 36 +++++-------------- pumpkin/src/data/op_data.rs | 26 ++++++++++++++ pumpkin/src/entity/player.rs | 3 +- pumpkin/src/main.rs | 1 + pumpkin/src/server/mod.rs | 1 - 6 files changed, 39 insertions(+), 30 deletions(-) rename pumpkin/src/{server/json_config.rs => data/mod.rs} (71%) create mode 100644 pumpkin/src/data/op_data.rs diff --git a/pumpkin/src/command/commands/cmd_op.rs b/pumpkin/src/command/commands/cmd_op.rs index ed830fa51..a98ce5b37 100644 --- a/pumpkin/src/command/commands/cmd_op.rs +++ b/pumpkin/src/command/commands/cmd_op.rs @@ -5,7 +5,7 @@ use crate::{ tree_builder::{argument, require}, CommandError, CommandExecutor, CommandSender, }, - server::json_config::{SaveJSONConfiguration, OPERATOR_CONFIG}, + data::{op_data::OPERATOR_CONFIG, SaveJSONConfiguration}, }; use async_trait::async_trait; use pumpkin_config::{op::Op, BASIC_CONFIG}; diff --git a/pumpkin/src/server/json_config.rs b/pumpkin/src/data/mod.rs similarity index 71% rename from pumpkin/src/server/json_config.rs rename to pumpkin/src/data/mod.rs index ecd65607e..c96aa7e1b 100644 --- a/pumpkin/src/server/json_config.rs +++ b/pumpkin/src/data/mod.rs @@ -1,17 +1,8 @@ -use std::{fs, path::Path, sync::LazyLock}; +use std::{fs, path::Path}; -use log::warn; -use pumpkin_config::op; use serde::{Deserialize, Serialize}; -pub static OPERATOR_CONFIG: LazyLock> = - LazyLock::new(|| tokio::sync::RwLock::new(OperatorConfig::load())); - -#[derive(Deserialize, Serialize, Default)] -#[serde(transparent)] -pub struct OperatorConfig { - pub ops: Vec, -} +pub mod op_data; pub trait LoadJSONConfiguration { #[must_use] @@ -63,30 +54,21 @@ pub trait SaveJSONConfiguration: LoadJSONConfiguration { let content = match serde_json::to_string_pretty(self) { Ok(content) => content, Err(err) => { - warn!( + log::warn!( "Couldn't serialize operator config to {:?}. Reason: {}", - path, err + path, + err ); return; } }; - if let Err(err) = fs::write(path, content) { - warn!( + if let Err(err) = std::fs::write(path, content) { + log::warn!( "Couldn't write operator config to {:?}. Reason: {}", - path, err + path, + err ); } } } - -impl LoadJSONConfiguration for OperatorConfig { - fn get_path() -> &'static Path { - Path::new("data/ops.json") - } - fn validate(&self) { - // TODO: Validate the operator configuration - } -} - -impl SaveJSONConfiguration for OperatorConfig {} diff --git a/pumpkin/src/data/op_data.rs b/pumpkin/src/data/op_data.rs new file mode 100644 index 000000000..c1dada051 --- /dev/null +++ b/pumpkin/src/data/op_data.rs @@ -0,0 +1,26 @@ +use std::{path::Path, sync::LazyLock}; + +use pumpkin_config::op; +use serde::{Deserialize, Serialize}; + +use super::{LoadJSONConfiguration, SaveJSONConfiguration}; + +pub static OPERATOR_CONFIG: LazyLock> = + LazyLock::new(|| tokio::sync::RwLock::new(OperatorConfig::load())); + +#[derive(Deserialize, Serialize, Default)] +#[serde(transparent)] +pub struct OperatorConfig { + pub ops: Vec, +} + +impl LoadJSONConfiguration for OperatorConfig { + fn get_path() -> &'static Path { + Path::new("ops.json") + } + fn validate(&self) { + // TODO: Validate the operator configuration + } +} + +impl SaveJSONConfiguration for OperatorConfig {} diff --git a/pumpkin/src/entity/player.rs b/pumpkin/src/entity/player.rs index f68b07a96..b9695ed45 100644 --- a/pumpkin/src/entity/player.rs +++ b/pumpkin/src/entity/player.rs @@ -51,6 +51,7 @@ use pumpkin_world::{ use tokio::sync::{Mutex, Notify}; use super::Entity; +use crate::error::PumpkinError; use crate::{ client::{ authentication::GameProfile, @@ -58,10 +59,10 @@ use crate::{ Client, PlayerConfig, }, command::{client_cmd_suggestions, dispatcher::CommandDispatcher}, + data::op_data::OPERATOR_CONFIG, server::Server, world::World, }; -use crate::{error::PumpkinError, server::json_config::OPERATOR_CONFIG}; use super::living::LivingEntity; diff --git a/pumpkin/src/main.rs b/pumpkin/src/main.rs index 755658f24..7ba557f64 100644 --- a/pumpkin/src/main.rs +++ b/pumpkin/src/main.rs @@ -41,6 +41,7 @@ use std::time::Instant; pub mod block; pub mod client; pub mod command; +pub mod data; pub mod entity; pub mod error; pub mod lan_broadcast; diff --git a/pumpkin/src/server/mod.rs b/pumpkin/src/server/mod.rs index 5c428ce61..b01d7d0c8 100644 --- a/pumpkin/src/server/mod.rs +++ b/pumpkin/src/server/mod.rs @@ -33,7 +33,6 @@ use crate::{ }; mod connection_cache; -pub mod json_config; mod key_store; pub mod ticker; From 1f5e6fa8598481b30b5a38caa4b8c0fd8ad6ad03 Mon Sep 17 00:00:00 2001 From: Alexander Medvedev Date: Fri, 27 Dec 2024 16:55:00 +0100 Subject: [PATCH 09/11] create data dir when needed --- pumpkin/src/data/mod.rs | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/pumpkin/src/data/mod.rs b/pumpkin/src/data/mod.rs index c96aa7e1b..1bc17afe6 100644 --- a/pumpkin/src/data/mod.rs +++ b/pumpkin/src/data/mod.rs @@ -1,7 +1,9 @@ -use std::{fs, path::Path}; +use std::{env, fs, path::Path}; use serde::{Deserialize, Serialize}; +const DATA_FOLDER: &str = "data/"; + pub mod op_data; pub trait LoadJSONConfiguration { @@ -10,23 +12,29 @@ pub trait LoadJSONConfiguration { where Self: Sized + Default + Serialize + for<'de> Deserialize<'de>, { - let path = Self::get_path(); + let exe_dir = env::current_dir().unwrap(); + let data_dir = exe_dir.join(DATA_FOLDER); + if !data_dir.exists() { + log::debug!("creating new data root folder"); + fs::create_dir(&data_dir).expect("Failed to create data root folder"); + } + let path = data_dir.join(Self::get_path()); let config = if path.exists() { - let file_content = fs::read_to_string(path) + let file_content = fs::read_to_string(&path) .unwrap_or_else(|_| panic!("Couldn't read configuration file at {path:?}")); serde_json::from_str(&file_content).unwrap_or_else(|err| { panic!( - "Couldn't parse config at {path:?}. Reason: {err}. This is probably caused by a config update. Just delete the old config and restart.", + "Couldn't parse data config at {path:?}. Reason: {err}. This is probably caused by a config update. Just delete the old data config and restart.", ) }) } else { let content = Self::default(); - if let Err(err) = fs::write(path, serde_json::to_string_pretty(&content).unwrap()) { + if let Err(err) = fs::write(&path, serde_json::to_string_pretty(&content).unwrap()) { eprintln!( - "Couldn't write default config to {path:?}. Reason: {err}. This is probably caused by a config update. Just delete the old config and restart.", + "Couldn't write default data config to {path:?}. Reason: {err}. This is probably caused by a config update. Just delete the old data config and restart.", ); } @@ -49,13 +57,19 @@ pub trait SaveJSONConfiguration: LoadJSONConfiguration { where Self: Sized + Default + Serialize + for<'de> Deserialize<'de>, { - let path = ::get_path(); + let exe_dir = env::current_dir().unwrap(); + let data_dir = exe_dir.join(DATA_FOLDER); + if !data_dir.exists() { + log::debug!("creating new data root folder"); + fs::create_dir(&data_dir).expect("Failed to create data root folder"); + } + let path = data_dir.join(Self::get_path()); let content = match serde_json::to_string_pretty(self) { Ok(content) => content, Err(err) => { log::warn!( - "Couldn't serialize operator config to {:?}. Reason: {}", + "Couldn't serialize operator data config to {:?}. Reason: {}", path, err ); @@ -63,7 +77,7 @@ pub trait SaveJSONConfiguration: LoadJSONConfiguration { } }; - if let Err(err) = std::fs::write(path, content) { + if let Err(err) = std::fs::write(&path, content) { log::warn!( "Couldn't write operator config to {:?}. Reason: {}", path, From 4b0abbb270435bc7bce07aed4dfe2f8c5e4025c7 Mon Sep 17 00:00:00 2001 From: Alexander Medvedev Date: Fri, 27 Dec 2024 16:59:11 +0100 Subject: [PATCH 10/11] add to readme --- README.md | 1 + pumpkin/src/command/commands/cmd_kick.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/README.md b/README.md index 7e4df0ef3..d2673e824 100644 --- a/README.md +++ b/README.md @@ -79,6 +79,7 @@ and customizable experience. It prioritizes performance and player enjoyment whi - [x] Particles - [x] Chat - [x] Commands + - [x] OP Permission - Proxy - [x] Bungeecord - [x] Velocity diff --git a/pumpkin/src/command/commands/cmd_kick.rs b/pumpkin/src/command/commands/cmd_kick.rs index d0a8eec90..feded74ad 100644 --- a/pumpkin/src/command/commands/cmd_kick.rs +++ b/pumpkin/src/command/commands/cmd_kick.rs @@ -49,6 +49,7 @@ impl CommandExecutor for KickExecutor { } } +// TODO: Permission pub fn init_command_tree<'a>() -> CommandTree<'a> { CommandTree::new(NAMES, DESCRIPTION) .with_child(argument(ARG_TARGET, &PlayersArgumentConsumer).execute(&KickExecutor)) From a03f554212b3c8329820b75cf73d9d51494758d0 Mon Sep 17 00:00:00 2001 From: Alexander Medvedev Date: Fri, 27 Dec 2024 17:16:53 +0100 Subject: [PATCH 11/11] fix: conflicts --- pumpkin/src/command/commands/cmd_kick.rs | 4 +-- pumpkin/src/command/commands/cmd_op.rs | 6 ++--- pumpkin/src/command/mod.rs | 4 +-- pumpkin/src/data/mod.rs | 2 +- pumpkin/src/entity/player.rs | 33 ++++++++---------------- 5 files changed, 19 insertions(+), 30 deletions(-) diff --git a/pumpkin/src/command/commands/cmd_kick.rs b/pumpkin/src/command/commands/cmd_kick.rs index feded74ad..51832258b 100644 --- a/pumpkin/src/command/commands/cmd_kick.rs +++ b/pumpkin/src/command/commands/cmd_kick.rs @@ -50,7 +50,7 @@ impl CommandExecutor for KickExecutor { } // TODO: Permission -pub fn init_command_tree<'a>() -> CommandTree<'a> { +pub fn init_command_tree() -> CommandTree { CommandTree::new(NAMES, DESCRIPTION) - .with_child(argument(ARG_TARGET, &PlayersArgumentConsumer).execute(&KickExecutor)) + .with_child(argument(ARG_TARGET, PlayersArgumentConsumer).execute(KickExecutor)) } diff --git a/pumpkin/src/command/commands/cmd_op.rs b/pumpkin/src/command/commands/cmd_op.rs index a98ce5b37..614ffcc7d 100644 --- a/pumpkin/src/command/commands/cmd_op.rs +++ b/pumpkin/src/command/commands/cmd_op.rs @@ -72,9 +72,9 @@ impl CommandExecutor for OpExecutor { } } -pub fn init_command_tree<'a>() -> CommandTree<'a> { +pub fn init_command_tree() -> CommandTree { CommandTree::new(NAMES, DESCRIPTION).with_child( - require(&|sender| sender.has_permission_lvl(PermissionLvl::Three)) - .with_child(argument(ARG_TARGET, &PlayersArgumentConsumer).execute(&OpExecutor)), + require(|sender| sender.has_permission_lvl(PermissionLvl::Three)) + .with_child(argument(ARG_TARGET, PlayersArgumentConsumer).execute(OpExecutor)), ) } diff --git a/pumpkin/src/command/mod.rs b/pumpkin/src/command/mod.rs index 2633d4fb7..c05017ed5 100644 --- a/pumpkin/src/command/mod.rs +++ b/pumpkin/src/command/mod.rs @@ -78,7 +78,7 @@ impl<'a> CommandSender<'a> { pub fn permission_lvl(&self) -> PermissionLvl { match self { CommandSender::Console | CommandSender::Rcon(_) => PermissionLvl::Four, - CommandSender::Player(p) => p.permission_lvl(), + CommandSender::Player(p) => p.permission_lvl.load(), } } @@ -86,7 +86,7 @@ impl<'a> CommandSender<'a> { pub fn has_permission_lvl(&self, lvl: PermissionLvl) -> bool { match self { CommandSender::Console | CommandSender::Rcon(_) => true, - CommandSender::Player(p) => (p.permission_lvl() as i8) >= (lvl as i8), + CommandSender::Player(p) => p.permission_lvl.load().ge(&lvl), } } diff --git a/pumpkin/src/data/mod.rs b/pumpkin/src/data/mod.rs index 1bc17afe6..7faf2d16f 100644 --- a/pumpkin/src/data/mod.rs +++ b/pumpkin/src/data/mod.rs @@ -33,7 +33,7 @@ pub trait LoadJSONConfiguration { let content = Self::default(); if let Err(err) = fs::write(&path, serde_json::to_string_pretty(&content).unwrap()) { - eprintln!( + log::error!( "Couldn't write default data config to {path:?}. Reason: {err}. This is probably caused by a config update. Just delete the old data config and restart.", ); } diff --git a/pumpkin/src/entity/player.rs b/pumpkin/src/entity/player.rs index a87fe7912..eae83b3ed 100644 --- a/pumpkin/src/entity/player.rs +++ b/pumpkin/src/entity/player.rs @@ -55,20 +55,20 @@ use pumpkin_world::{ ItemStack, }, }; -use tokio::sync::{Mutex, Notify}; +use tokio::sync::{Mutex, Notify, RwLock}; use super::Entity; -use crate::{error::PumpkinError, net::GameProfile}; use crate::{ + command::{client_cmd_suggestions, dispatcher::CommandDispatcher}, + data::op_data::OPERATOR_CONFIG, net::{ combat::{self, player_attack_sound, AttackType}, Client, PlayerConfig, }, - command::{client_cmd_suggestions, dispatcher::CommandDispatcher}, - data::op_data::OPERATOR_CONFIG, server::Server, world::World, }; +use crate::{error::PumpkinError, net::GameProfile}; use super::living::LivingEntity; @@ -119,12 +119,10 @@ pub struct Player { pub last_keep_alive_time: AtomicCell, /// Amount of ticks since last attack pub last_attacked_ticks: AtomicU32, - + /// The players op permission level + pub permission_lvl: AtomicCell, /// Tell tasks to stop if we are closing cancel_tasks: Notify, - - /// the players op permission level - permission_lvl: parking_lot::Mutex, } impl Player { @@ -199,8 +197,8 @@ impl Player { .ops .iter() .find(|op| op.uuid == gameprofile_clone.id) - .map_or(parking_lot::Mutex::new(PermissionLvl::Zero), |op| { - parking_lot::Mutex::new(op.level) + .map_or(AtomicCell::new(PermissionLvl::Zero), |op| { + AtomicCell::new(op.level) }), } } @@ -445,7 +443,7 @@ impl Player { self.client .send_packet(&CEntityStatus::new( self.entity_id(), - 24 + self.permission_lvl() as i8, + 24 + self.permission_lvl.load() as i8, )) .await; } @@ -454,22 +452,13 @@ impl Player { pub async fn set_permission_lvl( self: &Arc, lvl: PermissionLvl, - command_dispatcher: &Arc>, + command_dispatcher: &RwLock, ) { - { - let mut level = self.permission_lvl.lock(); - *level = lvl; - } - + self.permission_lvl.store(lvl); self.send_permission_lvl_update().await; client_cmd_suggestions::send_c_commands_packet(self, command_dispatcher).await; } - /// get the players permission level - pub fn permission_lvl(&self) -> PermissionLvl { - *self.permission_lvl.lock() - } - /// Sends the world time to just the player. pub async fn send_time(&self, world: &World) { let l_world = world.level_time.lock().await;