mirror of
https://github.com/Pumpkin-MC/Pumpkin.git
synced 2026-08-30 20:14:23 +00:00
Added native operator permission management (#348)
* 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. * 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. * Remove temp warn message * 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. * cargo fmt + cargo clippy * Sync permission change with client * 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 * refactor into `data` folder * create data dir when needed * add to readme * fix: conflicts --------- Co-authored-by: Alexander Medvedev <lilalexmed@proton.me>
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -2,6 +2,8 @@ services:
|
||||
pumpkin:
|
||||
build: .
|
||||
ports:
|
||||
- 25565:25565
|
||||
- "25565:25565"
|
||||
volumes:
|
||||
- ./data:/pumpkin
|
||||
stdin_open: true
|
||||
tty: true
|
||||
|
||||
@@ -7,5 +7,6 @@ edition.workspace = true
|
||||
pumpkin-core = { path = "../pumpkin-core" }
|
||||
serde.workspace = true
|
||||
log.workspace = true
|
||||
uuid.workspace = true
|
||||
|
||||
toml = "0.8"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use log::warn;
|
||||
use logging::LoggingConfig;
|
||||
use pumpkin_core::{Difficulty, GameMode};
|
||||
use pumpkin_core::{Difficulty, GameMode, PermissionLvl};
|
||||
use query::QueryConfig;
|
||||
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||
|
||||
@@ -29,6 +29,7 @@ pub use server_links::ServerLinksConfig;
|
||||
mod commands;
|
||||
pub mod compression;
|
||||
mod lan_broadcast;
|
||||
pub mod op;
|
||||
mod pvp;
|
||||
mod rcon;
|
||||
mod server_links;
|
||||
@@ -79,6 +80,8 @@ pub struct BasicConfiguration {
|
||||
pub simulation_distance: NonZeroU8,
|
||||
/// The default game difficulty.
|
||||
pub default_difficulty: Difficulty,
|
||||
/// The op level assign by the /op command
|
||||
pub op_permission_level: PermissionLvl,
|
||||
/// Whether the Nether dimension is enabled.
|
||||
pub allow_nether: bool,
|
||||
/// Whether the server is in hardcore mode.
|
||||
@@ -109,6 +112,7 @@ impl Default for BasicConfiguration {
|
||||
view_distance: NonZeroU8::new(10).unwrap(),
|
||||
simulation_distance: NonZeroU8::new(10).unwrap(),
|
||||
default_difficulty: Difficulty::Normal,
|
||||
op_permission_level: PermissionLvl::Four,
|
||||
allow_nether: true,
|
||||
hardcore: false,
|
||||
online_mode: true,
|
||||
|
||||
27
pumpkin-config/src/op.rs
Normal file
27
pumpkin-config/src/op.rs
Normal file
@@ -0,0 +1,27 @@
|
||||
use pumpkin_core::permission::PermissionLvl;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Default)]
|
||||
pub struct Op {
|
||||
pub uuid: Uuid,
|
||||
pub name: String,
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,11 @@
|
||||
pub mod gamemode;
|
||||
pub mod math;
|
||||
pub mod permission;
|
||||
pub mod random;
|
||||
pub mod text;
|
||||
|
||||
pub use gamemode::GameMode;
|
||||
pub use permission::PermissionLvl;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
|
||||
56
pumpkin-core/src/permission.rs
Normal file
56
pumpkin-core/src/permission.rs
Normal file
@@ -0,0 +1,56 @@
|
||||
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`: `normal`: Player can use basic commands.
|
||||
/// - `One`: `moderator`: Player can bypass spawn protection.
|
||||
/// - `Two`: `gamemaster`: Player or executor can use more commands and player can use command blocks.
|
||||
/// - `Three`: `admin`: Player or executor can use commands related to multiplayer management.
|
||||
/// - `Four`: `owner`: Player or executor can use all of the commands, including commands related to server management.
|
||||
#[derive(FromPrimitive, ToPrimitive, Clone, Copy, Default, PartialEq, Eq)]
|
||||
#[repr(i8)]
|
||||
pub enum PermissionLvl {
|
||||
#[default]
|
||||
Zero = 0,
|
||||
One = 1,
|
||||
Two = 2,
|
||||
Three = 3,
|
||||
Four = 4,
|
||||
}
|
||||
|
||||
impl PartialOrd for PermissionLvl {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
(*self as u8).partial_cmp(&(*other as u8))
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for PermissionLvl {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
serializer.serialize_u8(*self as u8)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for PermissionLvl {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
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
|
||||
))),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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"];
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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"];
|
||||
|
||||
|
||||
@@ -49,6 +49,7 @@ impl CommandExecutor for KickExecutor {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Permission
|
||||
pub fn init_command_tree() -> CommandTree {
|
||||
CommandTree::new(NAMES, DESCRIPTION)
|
||||
.with_child(argument(ARG_TARGET, PlayersArgumentConsumer).execute(KickExecutor))
|
||||
|
||||
80
pumpkin/src/command/commands/cmd_op.rs
Normal file
80
pumpkin/src/command/commands/cmd_op.rs
Normal file
@@ -0,0 +1,80 @@
|
||||
use crate::{
|
||||
command::{
|
||||
args::{arg_players::PlayersArgumentConsumer, Arg, ConsumedArgs},
|
||||
tree::CommandTree,
|
||||
tree_builder::{argument, require},
|
||||
CommandError, CommandExecutor, CommandSender,
|
||||
},
|
||||
data::{op_data::OPERATOR_CONFIG, SaveJSONConfiguration},
|
||||
};
|
||||
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 = "Grants operator status to a player.";
|
||||
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 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 = new_level;
|
||||
} else {
|
||||
config.ops.push(op_entry);
|
||||
}
|
||||
config.save();
|
||||
|
||||
player
|
||||
.set_permission_lvl(new_level, &server.command_dispatcher)
|
||||
.await;
|
||||
|
||||
let player_name = player.gameprofile.name.clone();
|
||||
let message = format!("Made {player_name} a server operator.");
|
||||
let msg = TextComponent::text(&message);
|
||||
sender.send_message(msg).await;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
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)),
|
||||
)
|
||||
}
|
||||
@@ -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"];
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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"];
|
||||
|
||||
|
||||
@@ -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"];
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"];
|
||||
|
||||
|
||||
@@ -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"];
|
||||
|
||||
|
||||
@@ -7,6 +7,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;
|
||||
|
||||
@@ -4,17 +4,19 @@ 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;
|
||||
use async_trait::async_trait;
|
||||
use commands::cmd_op;
|
||||
use commands::{
|
||||
cmd_clear, 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, cmd_worldborder,
|
||||
};
|
||||
use dispatcher::CommandError;
|
||||
use pumpkin_core::math::vector3::Vector3;
|
||||
use pumpkin_core::permission::PermissionLvl;
|
||||
use pumpkin_core::text::TextComponent;
|
||||
|
||||
pub mod args;
|
||||
@@ -76,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(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,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),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,6 +130,7 @@ pub fn default_dispatcher() -> CommandDispatcher {
|
||||
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());
|
||||
|
||||
dispatcher
|
||||
}
|
||||
|
||||
88
pumpkin/src/data/mod.rs
Normal file
88
pumpkin/src/data/mod.rs
Normal file
@@ -0,0 +1,88 @@
|
||||
use std::{env, fs, path::Path};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
const DATA_FOLDER: &str = "data/";
|
||||
|
||||
pub mod op_data;
|
||||
|
||||
pub trait LoadJSONConfiguration {
|
||||
#[must_use]
|
||||
fn load() -> Self
|
||||
where
|
||||
Self: Sized + Default + Serialize + for<'de> Deserialize<'de>,
|
||||
{
|
||||
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)
|
||||
.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 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()) {
|
||||
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.",
|
||||
);
|
||||
}
|
||||
|
||||
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 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 data config to {:?}. Reason: {}",
|
||||
path,
|
||||
err
|
||||
);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
if let Err(err) = std::fs::write(&path, content) {
|
||||
log::warn!(
|
||||
"Couldn't write operator config to {:?}. Reason: {}",
|
||||
path,
|
||||
err
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
26
pumpkin/src/data/op_data.rs
Normal file
26
pumpkin/src/data/op_data.rs
Normal file
@@ -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<tokio::sync::RwLock<OperatorConfig>> =
|
||||
LazyLock::new(|| tokio::sync::RwLock::new(OperatorConfig::load()));
|
||||
|
||||
#[derive(Deserialize, Serialize, Default)]
|
||||
#[serde(transparent)]
|
||||
pub struct OperatorConfig {
|
||||
pub ops: Vec<op::Op>,
|
||||
}
|
||||
|
||||
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 {}
|
||||
@@ -8,7 +8,7 @@ use std::{
|
||||
};
|
||||
|
||||
use crossbeam::atomic::AtomicCell;
|
||||
use num_derive::{FromPrimitive, ToPrimitive};
|
||||
use num_derive::FromPrimitive;
|
||||
use num_traits::Pow;
|
||||
use pumpkin_config::{ADVANCED_CONFIG, BASIC_CONFIG};
|
||||
use pumpkin_core::{
|
||||
@@ -18,6 +18,7 @@ use pumpkin_core::{
|
||||
vector2::Vector2,
|
||||
vector3::Vector3,
|
||||
},
|
||||
permission::PermissionLvl,
|
||||
text::TextComponent,
|
||||
GameMode,
|
||||
};
|
||||
@@ -54,11 +55,12 @@ 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,
|
||||
@@ -66,6 +68,7 @@ use crate::{
|
||||
server::Server,
|
||||
world::World,
|
||||
};
|
||||
use crate::{error::PumpkinError, net::GameProfile};
|
||||
|
||||
use super::living::LivingEntity;
|
||||
|
||||
@@ -116,12 +119,10 @@ pub struct Player {
|
||||
pub last_keep_alive_time: AtomicCell<Instant>,
|
||||
/// Amount of ticks since last attack
|
||||
pub last_attacked_ticks: AtomicU32,
|
||||
|
||||
/// The players op permission level
|
||||
pub permission_lvl: AtomicCell<PermissionLvl>,
|
||||
/// Tell tasks to stop if we are closing
|
||||
cancel_tasks: Notify,
|
||||
|
||||
/// the players op permission level
|
||||
permission_lvl: PermissionLvl,
|
||||
}
|
||||
|
||||
impl Player {
|
||||
@@ -143,6 +144,8 @@ impl Player {
|
||||
},
|
||||
|profile| profile,
|
||||
);
|
||||
|
||||
let gameprofile_clone = gameprofile.clone();
|
||||
let config = client.config.lock().await.clone().unwrap_or_default();
|
||||
let bounding_box_size = BoundingBoxSize {
|
||||
width: 0.6,
|
||||
@@ -186,8 +189,17 @@ impl Player {
|
||||
last_keep_alive_time: AtomicCell::new(std::time::Instant::now()),
|
||||
last_attacked_ticks: AtomicU32::new(0),
|
||||
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(AtomicCell::new(PermissionLvl::Zero), |op| {
|
||||
AtomicCell::new(op.level)
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -431,20 +443,20 @@ 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;
|
||||
}
|
||||
|
||||
/// 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: &Arc<Self>,
|
||||
lvl: PermissionLvl,
|
||||
command_dispatcher: &RwLock<CommandDispatcher>,
|
||||
) {
|
||||
self.permission_lvl.store(lvl);
|
||||
self.send_permission_lvl_update().await;
|
||||
}
|
||||
|
||||
/// get the players permission level
|
||||
pub fn permission_lvl(&self) -> PermissionLvl {
|
||||
self.permission_lvl
|
||||
client_cmd_suggestions::send_c_commands_packet(self, command_dispatcher).await;
|
||||
}
|
||||
|
||||
/// Sends the world time to just the player.
|
||||
@@ -821,19 +833,3 @@ pub enum ChatMode {
|
||||
/// All messages should be hidden
|
||||
Hidden,
|
||||
}
|
||||
|
||||
/// the player's permission level
|
||||
#[derive(Debug, FromPrimitive, ToPrimitive, Clone, Copy)]
|
||||
#[repr(i8)]
|
||||
pub enum PermissionLvl {
|
||||
/// `normal`: Player can use basic commands.
|
||||
Zero = 0,
|
||||
/// `moderator`: Player can bypass spawn protection.
|
||||
One = 1,
|
||||
/// `gamemaster`: Player or executor can use more commands and player can use command blocks.
|
||||
Two = 2,
|
||||
/// `admin`: Player or executor can use commands related to multiplayer management.
|
||||
Three = 3,
|
||||
/// `owner`: Player or executor can use all of the commands, including commands related to server management.
|
||||
Four = 4,
|
||||
}
|
||||
|
||||
@@ -55,6 +55,7 @@ use std::time::Instant;
|
||||
|
||||
pub mod block;
|
||||
pub mod command;
|
||||
pub mod data;
|
||||
pub mod entity;
|
||||
pub mod error;
|
||||
pub mod net;
|
||||
|
||||
Reference in New Issue
Block a user