Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added native operator permission management #348

Merged
merged 15 commits into from
Dec 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 3 additions & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ services:
pumpkin:
build: .
ports:
- 25565:25565
- "25565:25565"
volumes:
- ./data:/pumpkin
stdin_open: true
tty: true
KairuDeibisu marked this conversation as resolved.
Show resolved Hide resolved
1 change: 1 addition & 0 deletions pumpkin-config/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ edition.workspace = true
pumpkin-core = { path = "../pumpkin-core" }
serde.workspace = true
log.workspace = true
uuid.workspace = true

toml = "0.8"
6 changes: 5 additions & 1 deletion pumpkin-config/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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};

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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,
Expand Down
27 changes: 27 additions & 0 deletions pumpkin-config/src/op.rs
Original file line number Diff line number Diff line change
@@ -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,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

prefer to use enums instead of bools, the function call is hard to read:

Op::new(..., false)

is hard to read

) -> Self {
Self {
uuid,
name,
level,
bypasses_player_limit,
}
}
}
2 changes: 2 additions & 0 deletions pumpkin-core/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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};

Expand Down
56 changes: 56 additions & 0 deletions pumpkin-core/src/permission.rs
Original file line number Diff line number Diff line change
@@ -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!(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can use serde::de::Error::invalid_value instead here

"Invalid value for OpLevel: {}",
value
))),
}
}
}
3 changes: 2 additions & 1 deletion pumpkin/src/command/commands/cmd_fill.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"];
Expand Down
2 changes: 1 addition & 1 deletion pumpkin/src/command/commands/cmd_gamemode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
2 changes: 1 addition & 1 deletion pumpkin/src/command/commands/cmd_give.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"];

Expand Down
1 change: 1 addition & 0 deletions pumpkin/src/command/commands/cmd_kick.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
80 changes: 80 additions & 0 deletions pumpkin/src/command/commands/cmd_op.rs
Original file line number Diff line number Diff line change
@@ -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)),
)
}
14 changes: 6 additions & 8 deletions pumpkin/src/command/commands/cmd_say.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"];
Expand Down
2 changes: 1 addition & 1 deletion pumpkin/src/command/commands/cmd_seed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down
2 changes: 1 addition & 1 deletion pumpkin/src/command/commands/cmd_setblock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"];

Expand Down
2 changes: 1 addition & 1 deletion pumpkin/src/command/commands/cmd_stop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"];

Expand Down
2 changes: 1 addition & 1 deletion pumpkin/src/command/commands/cmd_teleport.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
10 changes: 4 additions & 6 deletions pumpkin/src/command/commands/cmd_time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"];

Expand Down
2 changes: 1 addition & 1 deletion pumpkin/src/command/commands/cmd_transfer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"];

Expand Down
1 change: 1 addition & 0 deletions pumpkin/src/command/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Loading
Loading