chore: swap permissions_manger to dashmap

i need this also PatchBukkit
This commit is contained in:
Alexander Medvedev 2026-08-19 19:50:20 +02:00
parent 489403c58b
commit 61bd8f77b6
43 changed files with 245 additions and 220 deletions

2
Cargo.lock generated
View File

@ -3595,6 +3595,7 @@ dependencies = [
"colored",
"criterion",
"crypto-bigint 0.7.5",
"dashmap",
"ecdsa 0.17.0",
"md5",
"num-derive",
@ -3610,7 +3611,6 @@ dependencies = [
"sha2 0.11.0",
"syn 3.0.3",
"thiserror 2.0.20",
"tokio",
"ureq",
"uuid",
]

View File

@ -22,7 +22,7 @@ syn = { workspace = true, optional = true }
proc-macro2 = { workspace = true, optional = true }
uuid.workspace = true
tokio = { workspace = true, features = ["sync"] }
dashmap.workspace = true
base64.workspace = true
p384 = { workspace = true, features = ["ecdsa"] }
thiserror.workspace = true

View File

@ -1,7 +1,6 @@
use dashmap::DashMap;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::RwLock;
/// Describes the default behaviour for permissions.
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
@ -65,7 +64,7 @@ impl Permission {
#[derive(Default)]
pub struct PermissionRegistry {
/// All registered permissions.
permissions: HashMap<String, Permission>,
permissions: DashMap<String, Permission>,
}
impl PermissionRegistry {
@ -76,7 +75,7 @@ impl PermissionRegistry {
#[must_use]
pub fn new() -> Self {
Self {
permissions: HashMap::new(),
permissions: DashMap::new(),
}
}
@ -88,7 +87,7 @@ impl PermissionRegistry {
/// # Returns
/// - `Ok(())` if the permission was successfully registered.
/// - `Err(String)` if a permission with the same node already exists.
pub fn register_permission(&mut self, permission: Permission) -> Result<(), String> {
pub fn register_permission(&self, permission: Permission) -> Result<(), String> {
if self.permissions.contains_key(&permission.node) {
return Err(format!(
"Permission {} is already registered",
@ -108,7 +107,7 @@ impl PermissionRegistry {
/// # Parameters
/// - `permission`: The `Permission` instance to add.
#[allow(clippy::expect_used)]
pub fn register_permission_or_panic(&mut self, permission: Permission) {
pub fn register_permission_or_panic(&self, permission: Permission) {
self.register_permission(permission)
.expect("Permission should have been registered successfully");
}
@ -119,9 +118,12 @@ impl PermissionRegistry {
/// - `node`: The full permission node string to look up.
///
/// # Returns
/// `Some(&Permission)` if the node exists, or `None` otherwise.
/// `Some(Ref<'_, String, Permission>)` if the node exists, or `None` otherwise.
#[must_use]
pub fn get_permission(&self, node: &str) -> Option<&Permission> {
pub fn get_permission(
&self,
node: &str,
) -> Option<dashmap::mapref::one::Ref<'_, String, Permission>> {
self.permissions.get(node)
}
@ -137,8 +139,9 @@ impl PermissionRegistry {
/// # Returns
/// - `true` if the node existed and its default was updated.
/// - `false` if no permission with that node is registered.
pub fn set_default(&mut self, node: &str, default: PermissionDefault) -> bool {
if let Some(permission) = self.permissions.get_mut(node) {
#[must_use]
pub fn set_default(&self, node: &str, default: PermissionDefault) -> bool {
if let Some(mut permission) = self.permissions.get_mut(node) {
permission.default = default;
true
} else {
@ -163,7 +166,7 @@ impl PermissionRegistry {
#[derive(Default, Clone, Debug, Serialize, Deserialize)]
pub struct PermissionAttachment {
/// Directly assigned permissions.
permissions: HashMap<String, bool>,
pub permissions: HashMap<String, bool>,
}
impl PermissionAttachment {
@ -217,53 +220,103 @@ impl PermissionAttachment {
}
}
/// Manager for player permissions.
/// Manager for player and server permissions.
#[derive(Default)]
pub struct PermissionManager {
/// Global registry of permissions.
pub registry: Arc<RwLock<PermissionRegistry>>,
/// Player permission attachments.
pub attachments: HashMap<uuid::Uuid, Arc<RwLock<PermissionAttachment>>>,
pub registry: PermissionRegistry,
/// Player permission attachments keyed by player UUID.
pub attachments: DashMap<uuid::Uuid, DashMap<String, bool>>,
}
impl PermissionManager {
/// Creates a new `PermissionManager`.
///
/// # Parameters
/// - `registry`: An `Arc<RwLock<PermissionRegistry>>` containing the global permissions registry.
///
/// # Returns
/// A `PermissionManager` with an empty attachments map.
pub fn new(registry: Arc<RwLock<PermissionRegistry>>) -> Self {
/// Creates a new empty `PermissionManager`.
#[must_use]
pub fn new() -> Self {
Self {
registry,
attachments: HashMap::new(),
registry: PermissionRegistry::new(),
attachments: DashMap::new(),
}
}
/// Retrieves the `PermissionAttachment` for a given player, creating one if it doesn't exist.
///
/// # Parameters
/// - `player_id`: The UUID of the player.
///
/// # Returns
/// An `Arc<RwLock<PermissionAttachment>>` representing the player's attachment.
pub fn get_attachment(&mut self, player_id: uuid::Uuid) -> Arc<RwLock<PermissionAttachment>> {
Arc::clone(
self.attachments
.entry(player_id)
.or_insert_with(|| Arc::new(RwLock::new(PermissionAttachment::new()))),
)
/// Creates a new `PermissionManager` with an existing `PermissionRegistry`.
#[must_use]
pub fn with_registry(registry: PermissionRegistry) -> Self {
Self {
registry,
attachments: DashMap::new(),
}
}
/// Removes the `PermissionAttachment` for a given player.
///
/// # Parameters
/// - `player_id`: The UUID of the player.
pub fn remove_attachment(&mut self, player_id: &uuid::Uuid) {
/// Registers a new permission node in the global registry.
pub fn register_permission(&self, permission: Permission) -> Result<(), String> {
self.registry.register_permission(permission)
}
/// Registers a new permission node in the global registry or panics if already registered.
pub fn register_permission_or_panic(&self, permission: Permission) {
self.registry.register_permission_or_panic(permission);
}
/// Retrieves a permission node by its name from the registry.
#[must_use]
pub fn get_permission(
&self,
node: &str,
) -> Option<dashmap::mapref::one::Ref<'_, String, Permission>> {
self.registry.get_permission(node)
}
/// Overrides the default behaviour of an already-registered permission in the registry.
#[must_use]
pub fn set_default(&self, node: &str, default: PermissionDefault) -> bool {
self.registry.set_default(node, default)
}
/// Checks whether a permission node exists in the registry.
#[must_use]
pub fn has_registered_permission(&self, node: &str) -> bool {
self.registry.has_permission(node)
}
/// Sets a player's explicit permission.
pub fn set_permission(&self, player_id: uuid::Uuid, node: impl Into<String>, value: bool) {
self.attachments
.entry(player_id)
.or_default()
.insert(node.into(), value);
}
/// Unsets a player's explicit permission.
pub fn unset_permission(&self, player_id: &uuid::Uuid, node: &str) {
if let Some(player_perms) = self.attachments.get(player_id) {
player_perms.remove(node);
}
}
/// Checks if a permission is explicitly set for a player.
#[must_use]
pub fn has_permission_set(&self, player_id: &uuid::Uuid, node: &str) -> Option<bool> {
self.attachments
.get(player_id)
.and_then(|p| p.get(node).as_deref().copied())
}
/// Removes all permission attachments for a player.
pub fn remove_attachment(&self, player_id: &uuid::Uuid) {
self.attachments.remove(player_id);
}
/// Retrieves all explicitly assigned permissions for a player.
#[must_use]
pub fn get_player_permissions(&self, player_id: &uuid::Uuid) -> Option<HashMap<String, bool>> {
self.attachments.get(player_id).map(|p| {
p.iter()
.map(|item| (item.key().clone(), *item.value()))
.collect()
})
}
/// Checks if a player has a specific permission.
///
/// # Parameters
@ -273,21 +326,18 @@ impl PermissionManager {
///
/// # Returns
/// `true` if the player has the permission, `false` otherwise.
pub async fn has_permission(
#[must_use]
pub fn has_permission(
&self,
player_id: &uuid::Uuid,
permission_node: &str,
player_op_level: PermissionLvl,
) -> bool {
let reg = self.registry.read().await;
// Check explicitly set permissions
if let Some(attachment) = self.attachments.get(player_id) {
let attachment = attachment.read().await;
// Check for the exact permission match
if let Some(value) = attachment.has_permission_set(permission_node) {
return value;
if let Some(value) = attachment.get(permission_node) {
return *value;
}
// Check parent nodes (for wildcard permissions)
@ -298,23 +348,21 @@ impl PermissionManager {
// Check wildcard permissions at each level
let mut current_node = namespace.to_string();
if let Some(value) = attachment.has_permission_set(&format!("{current_node}:*")) {
return value;
if let Some(value) = attachment.get(&format!("{current_node}:*")) {
return *value;
}
current_node.push(':');
for (i, part) in key_parts.iter().enumerate() {
current_node.push_str(part);
if let Some(value) = attachment.has_permission_set(&current_node) {
return value;
if let Some(value) = attachment.get(&current_node) {
return *value;
}
if i < key_parts.len() - 1 {
if let Some(value) =
attachment.has_permission_set(&format!("{current_node}.*"))
{
return value;
if let Some(value) = attachment.get(&format!("{current_node}.*")) {
return *value;
}
current_node.push('.');
}
@ -322,17 +370,20 @@ impl PermissionManager {
}
// Check for inherited permissions from parent nodes
for (node, value) in attachment.get_permissions() {
if let Some(permission) = reg.get_permission(node)
for item in attachment.iter() {
let node = item.key();
let value = *item.value();
if let Some(permission) = self.registry.get_permission(node)
&& let Some(child_val) = permission.children.get(permission_node)
{
return *value && *child_val;
return value && *child_val;
}
}
}
// Fall back to the default permission value
reg.get_permission(permission_node)
self.registry
.get_permission(permission_node)
.is_some_and(|permission| match permission.default {
PermissionDefault::Allow => true,
PermissionDefault::Deny => false,

View File

@ -562,7 +562,7 @@ impl CommandExecutor for EveryAdvancementExecutor {
}
/// register the advancement command
pub fn register(dispatcher: &mut CommandDispatcher, registry: &mut PermissionRegistry) {
pub fn register(dispatcher: &mut CommandDispatcher, registry: &PermissionRegistry) {
registry.register_permission_or_panic(Permission::new(
PERMISSION,
DESCRIPTION,

View File

@ -450,7 +450,7 @@ impl CommandExecutor for ModifierGetExecutor {
}
}
pub fn register(dispatcher: &mut CommandDispatcher, registry: &mut PermissionRegistry) {
pub fn register(dispatcher: &mut CommandDispatcher, registry: &PermissionRegistry) {
registry.register_permission_or_panic(Permission::new(
PERMISSION,
DESCRIPTION,

View File

@ -106,7 +106,7 @@ impl CommandExecutor for BanListCommandExecutor {
}
}
pub fn register(dispatcher: &mut CommandDispatcher, registry: &mut PermissionRegistry) {
pub fn register(dispatcher: &mut CommandDispatcher, registry: &PermissionRegistry) {
registry.register_permission_or_panic(Permission::new(
PERMISSION,
DESCRIPTION,

View File

@ -264,7 +264,7 @@ impl CommandExecutor for CloneExecutor {
}
}
pub fn register(dispatcher: &mut CommandDispatcher, registry: &mut PermissionRegistry) {
pub fn register(dispatcher: &mut CommandDispatcher, registry: &PermissionRegistry) {
registry.register_permission_or_panic(Permission::new(
PERMISSION,
DESCRIPTION,

View File

@ -86,7 +86,7 @@ impl CommandExecutor for DialogShowExecutor {
}
}
pub fn register(dispatcher: &mut CommandDispatcher, registry: &mut PermissionRegistry) {
pub fn register(dispatcher: &mut CommandDispatcher, registry: &PermissionRegistry) {
registry.register_permission_or_panic(Permission::new(
PERMISSION,
DESCRIPTION,

View File

@ -82,7 +82,7 @@ impl CommandExecutor for DifficultySetExecutor {
}
}
pub fn register(dispatcher: &mut CommandDispatcher, registry: &mut PermissionRegistry) {
pub fn register(dispatcher: &mut CommandDispatcher, registry: &PermissionRegistry) {
registry.register_permission_or_panic(Permission::new(
PERMISSION,
DESCRIPTION,

View File

@ -393,7 +393,7 @@ fn execute_summon_modifier<'a>(
}
#[allow(clippy::too_many_lines)]
pub fn register(dispatcher: &mut CommandDispatcher, registry: &mut PermissionRegistry) {
pub fn register(dispatcher: &mut CommandDispatcher, registry: &PermissionRegistry) {
registry.register_permission_or_panic(Permission::new(
PERMISSION,
DESCRIPTION,

View File

@ -364,7 +364,7 @@ impl CommandExecutor for PrintForEntityExecutor {
}
}
pub fn register(dispatcher: &mut CommandDispatcher, registry: &mut PermissionRegistry) {
pub fn register(dispatcher: &mut CommandDispatcher, registry: &PermissionRegistry) {
registry.register_permission_or_panic(Permission::new(
PERMISSION,
DESCRIPTION,

View File

@ -158,7 +158,7 @@ impl CommandExecutor for FillBiomeExecutor {
}
}
pub fn register(dispatcher: &mut CommandDispatcher, registry: &mut PermissionRegistry) {
pub fn register(dispatcher: &mut CommandDispatcher, registry: &PermissionRegistry) {
registry.register_permission_or_panic(Permission::new(
PERMISSION,
DESCRIPTION,

View File

@ -347,7 +347,7 @@ impl CommandExecutor for ForceloadQueryExecutor {
}
}
pub fn register(dispatcher: &mut CommandDispatcher, registry: &mut PermissionRegistry) {
pub fn register(dispatcher: &mut CommandDispatcher, registry: &PermissionRegistry) {
registry.register_permission_or_panic(Permission::new(
PERMISSION,
DESCRIPTION,

View File

@ -373,7 +373,7 @@ impl CommandExecutor for HelpCommandExecutor {
}
}
pub fn register(dispatcher: &mut CommandDispatcher, registry: &mut PermissionRegistry) {
pub fn register(dispatcher: &mut CommandDispatcher, registry: &PermissionRegistry) {
registry.register_permission_or_panic(Permission::new(
PERMISSION,
DESCRIPTION,

View File

@ -72,7 +72,7 @@ impl CommandExecutor for SelfExecutor {
}
}
pub fn register(dispatcher: &mut CommandDispatcher, registry: &mut PermissionRegistry) {
pub fn register(dispatcher: &mut CommandDispatcher, registry: &PermissionRegistry) {
registry.register_permission_or_panic(Permission::new(
PERMISSION,
DESCRIPTION,

View File

@ -99,7 +99,7 @@ fn get_player_names_and_ids(players: &[Arc<Player>]) -> TextComponent {
TextComponent::join_with_comma(names_and_ids)
}
pub fn register(dispatcher: &mut CommandDispatcher, registry: &mut PermissionRegistry) {
pub fn register(dispatcher: &mut CommandDispatcher, registry: &PermissionRegistry) {
registry.register_permission_or_panic(Permission::new(
PERMISSION,
DESCRIPTION,

View File

@ -220,7 +220,7 @@ impl CommandExecutor for LootExecutor {
}
#[expect(clippy::too_many_lines)]
pub fn register(dispatcher: &mut CommandDispatcher, registry: &mut PermissionRegistry) {
pub fn register(dispatcher: &mut CommandDispatcher, registry: &PermissionRegistry) {
registry.register_permission_or_panic(Permission::new(
PERMISSION,
DESCRIPTION,

View File

@ -3,9 +3,8 @@ use crate::command::tree::Command;
use pumpkin_config::{BasicConfiguration, CommandsConfig};
use pumpkin_util::{
PermissionLvl,
permission::{Permission, PermissionDefault, PermissionRegistry},
permission::{Permission, PermissionDefault, PermissionManager, PermissionRegistry},
};
use tokio::sync::RwLock;
use tracing::{info, warn};
mod advancement;
@ -87,15 +86,13 @@ mod worldborder;
#[allow(clippy::too_many_lines)]
#[must_use]
pub async fn default_dispatcher(
registry: &RwLock<PermissionRegistry>,
pub fn default_dispatcher(
permission_manager: &PermissionManager,
_basic_config: &BasicConfiguration,
commands_config: &CommandsConfig,
) -> CommandDispatcher {
let mut dispatcher = crate::command::dispatcher::CommandDispatcher::default();
let mut registry_lock = registry.write().await;
let registry = &mut *registry_lock;
let registry = &permission_manager.registry;
register_permissions(registry);
@ -227,7 +224,7 @@ pub async fn default_dispatcher(
/// this affects both the legacy and the node-based dispatchers uniformly.
fn apply_command_overrides(
dispatcher: &mut CommandDispatcher,
registry: &mut PermissionRegistry,
registry: &PermissionRegistry,
commands_config: &CommandsConfig,
) {
for (raw_name, settings) in &commands_config.overrides {
@ -278,11 +275,16 @@ fn apply_command_overrides(
};
if let Some(node) = resolve_permission_node(dispatcher, registry, &name) {
registry.set_default(&node, default);
info!(
"The /{name} command now needs permission level {} to use",
level as u8
);
if registry.set_default(&node, default) {
info!(
"The /{name} command now needs permission level {} to use",
level as u8
);
} else {
warn!(
"Command override for /{name} sets a permission level, but matching permission node could not be updated; leaving it unchanged"
);
}
} else {
warn!(
"Command override for /{name} sets a permission level, but no matching permission node could be found; leaving it unchanged"
@ -316,7 +318,7 @@ fn resolve_permission_node(
None
}
fn register_permissions(registry: &mut PermissionRegistry) {
fn register_permissions(registry: &PermissionRegistry) {
// Register level 0 permissions (allowed by default)
register_level_0_permissions(registry);
@ -336,7 +338,7 @@ fn register_permissions(registry: &mut PermissionRegistry) {
.unwrap_or_else(|e| tracing::warn!("{e}"));
}
fn register_level_0_permissions(registry: &mut PermissionRegistry) {
fn register_level_0_permissions(registry: &PermissionRegistry) {
// Register permissions for builtin commands that are allowed for everyone
registry
.register_permission(Permission::new(
@ -362,7 +364,7 @@ fn register_level_0_permissions(registry: &mut PermissionRegistry) {
}
#[expect(clippy::too_many_lines)]
fn register_level_2_permissions(registry: &mut PermissionRegistry) {
fn register_level_2_permissions(registry: &PermissionRegistry) {
// Register permissions for commands with PermissionLvl::Two
registry
.register_permission(Permission::new(
@ -576,7 +578,7 @@ fn register_level_2_permissions(registry: &mut PermissionRegistry) {
.unwrap_or_else(|e| tracing::warn!("{e}"));
}
fn register_level_3_permissions(registry: &mut PermissionRegistry) {
fn register_level_3_permissions(registry: &PermissionRegistry) {
// Register permissions for commands with PermissionLvl::Three
registry
.register_permission(Permission::new(
@ -661,8 +663,7 @@ fn register_level_3_permissions(registry: &mut PermissionRegistry) {
mod override_tests {
use pumpkin_config::{BasicConfiguration, CommandOverride, CommandsConfig};
use pumpkin_util::PermissionLvl;
use pumpkin_util::permission::{PermissionDefault, PermissionRegistry};
use tokio::sync::RwLock;
use pumpkin_util::permission::{PermissionDefault, PermissionManager};
use super::default_dispatcher;
@ -686,16 +687,16 @@ mod override_tests {
);
}
#[tokio::test]
async fn disabling_a_command_removes_and_hides_it() {
#[test]
fn disabling_a_command_removes_and_hides_it() {
let basic = BasicConfiguration::default();
let mut commands = CommandsConfig::default();
// `gamemode` lives on the legacy dispatcher; disabling it should remove
// it there and flag it on the wrapper.
disabled(&mut commands, "gamemode");
let registry = RwLock::new(PermissionRegistry::new());
let dispatcher = default_dispatcher(&registry, &basic, &commands).await;
let manager = PermissionManager::new();
let dispatcher = default_dispatcher(&manager, &basic, &commands);
assert!(dispatcher.is_disabled("gamemode"));
assert!(
@ -708,16 +709,16 @@ mod override_tests {
);
}
#[tokio::test]
async fn disabling_an_alias_turns_off_the_whole_command() {
#[test]
fn disabling_an_alias_turns_off_the_whole_command() {
let basic = BasicConfiguration::default();
let mut commands = CommandsConfig::default();
// `tp` is an alias of `teleport`; disabling it should take the whole
// command down, including the primary name.
disabled(&mut commands, "tp");
let registry = RwLock::new(PermissionRegistry::new());
let dispatcher = default_dispatcher(&registry, &basic, &commands).await;
let manager = PermissionManager::new();
let dispatcher = default_dispatcher(&manager, &basic, &commands);
assert!(dispatcher.is_disabled("tp"));
assert!(dispatcher.is_disabled("teleport"));
@ -725,60 +726,59 @@ mod override_tests {
assert!(dispatcher.fallback_dispatcher.get_tree("teleport").is_err());
}
#[tokio::test]
async fn disabling_a_node_command_also_disables_its_aliases() {
#[test]
fn disabling_a_node_command_also_disables_its_aliases() {
let basic = BasicConfiguration::default();
let mut commands = CommandsConfig::default();
// `help` is a node-based command with the aliases `h` and `?`.
disabled(&mut commands, "help");
let registry = RwLock::new(PermissionRegistry::new());
let dispatcher = default_dispatcher(&registry, &basic, &commands).await;
let manager = PermissionManager::new();
let dispatcher = default_dispatcher(&manager, &basic, &commands);
assert!(dispatcher.is_disabled("help"));
assert!(dispatcher.is_disabled("h"));
assert!(dispatcher.is_disabled("?"));
}
#[tokio::test]
async fn override_for_unknown_command_is_ignored() {
#[test]
fn override_for_unknown_command_is_ignored() {
let basic = BasicConfiguration::default();
let mut commands = CommandsConfig::default();
// A command name that does not exist (usually a typo in the config)
// should be ignored, not silently swallow a real command or panic.
disabled(&mut commands, "notacommand");
let registry = RwLock::new(PermissionRegistry::new());
let dispatcher = default_dispatcher(&registry, &basic, &commands).await;
let manager = PermissionManager::new();
let dispatcher = default_dispatcher(&manager, &basic, &commands);
assert!(!dispatcher.is_disabled("notacommand"));
assert!(dispatcher.fallback_dispatcher.get_tree("gamemode").is_ok());
}
#[tokio::test]
async fn override_is_case_insensitive() {
#[test]
fn override_is_case_insensitive() {
let basic = BasicConfiguration::default();
let mut commands = CommandsConfig::default();
disabled(&mut commands, "GameMode");
let registry = RwLock::new(PermissionRegistry::new());
let dispatcher = default_dispatcher(&registry, &basic, &commands).await;
let manager = PermissionManager::new();
let dispatcher = default_dispatcher(&manager, &basic, &commands);
assert!(dispatcher.is_disabled("gamemode"));
}
#[tokio::test]
async fn permission_level_override_rewrites_the_registry_default() {
#[test]
fn permission_level_override_rewrites_the_registry_default() {
let basic = BasicConfiguration::default();
let mut commands = CommandsConfig::default();
// `gamemode` is normally level 2; bump it to owner-only.
permission(&mut commands, "gamemode", PermissionLvl::Four);
let registry = RwLock::new(PermissionRegistry::new());
let _dispatcher = default_dispatcher(&registry, &basic, &commands).await;
let manager = PermissionManager::new();
let _dispatcher = default_dispatcher(&manager, &basic, &commands);
let registry = registry.read().await;
let permission = registry
let permission = manager
.get_permission("minecraft:command.gamemode")
.expect("gamemode permission should be registered");
assert_eq!(
@ -787,8 +787,8 @@ mod override_tests {
);
}
#[tokio::test]
async fn permission_override_resolves_node_command_by_convention() {
#[test]
fn permission_override_resolves_node_command_by_convention() {
let basic = BasicConfiguration::default();
let mut commands = CommandsConfig::default();
// `kill` is a node-based command whose permission node is not recorded in
@ -796,11 +796,10 @@ mod override_tests {
// `minecraft:command.kill` naming convention.
permission(&mut commands, "kill", PermissionLvl::Four);
let registry = RwLock::new(PermissionRegistry::new());
let _dispatcher = default_dispatcher(&registry, &basic, &commands).await;
let manager = PermissionManager::new();
let _dispatcher = default_dispatcher(&manager, &basic, &commands);
let registry = registry.read().await;
let permission = registry
let permission = manager
.get_permission("minecraft:command.kill")
.expect("kill permission should be registered");
assert_eq!(
@ -809,17 +808,16 @@ mod override_tests {
);
}
#[tokio::test]
async fn permission_level_zero_allows_everyone() {
#[test]
fn permission_level_zero_allows_everyone() {
let basic = BasicConfiguration::default();
let mut commands = CommandsConfig::default();
permission(&mut commands, "gamemode", PermissionLvl::Zero);
let registry = RwLock::new(PermissionRegistry::new());
let _dispatcher = default_dispatcher(&registry, &basic, &commands).await;
let manager = PermissionManager::new();
let _dispatcher = default_dispatcher(&manager, &basic, &commands);
let registry = registry.read().await;
let permission = registry
let permission = manager
.get_permission("minecraft:command.gamemode")
.expect("gamemode permission should be registered");
assert_eq!(permission.default, PermissionDefault::Allow);

View File

@ -105,7 +105,7 @@ impl SuggestionProvider for OpSuggestionProvider {
}
}
pub fn register(dispatcher: &mut CommandDispatcher, registry: &mut PermissionRegistry) {
pub fn register(dispatcher: &mut CommandDispatcher, registry: &PermissionRegistry) {
registry.register_permission_or_panic(Permission::new(
PERMISSION,
DESCRIPTION,

View File

@ -701,7 +701,7 @@ fn apply_delta(
}
}
pub fn register(dispatcher: &mut CommandDispatcher, registry: &mut PermissionRegistry) {
pub fn register(dispatcher: &mut CommandDispatcher, registry: &PermissionRegistry) {
registry.register_permission_or_panic(Permission::new(
PERMISSION,
DESCRIPTION,

View File

@ -88,7 +88,7 @@ impl CommandExecutor for RandomExecutor {
}
}
pub fn register(dispatcher: &mut CommandDispatcher, registry: &mut PermissionRegistry) {
pub fn register(dispatcher: &mut CommandDispatcher, registry: &PermissionRegistry) {
registry.register_permission_or_panic(Permission::new(
PERMISSION,
DESCRIPTION,

View File

@ -223,7 +223,7 @@ impl CommandExecutor for RecipeTakeExecutor {
}
}
pub fn register(dispatcher: &mut CommandDispatcher, registry: &mut PermissionRegistry) {
pub fn register(dispatcher: &mut CommandDispatcher, registry: &PermissionRegistry) {
registry.register_permission_or_panic(Permission::new(
PERMISSION,
DESCRIPTION,

View File

@ -178,7 +178,7 @@ impl CommandExecutor for RideDismountExecutor {
}
}
pub fn register(dispatcher: &mut CommandDispatcher, registry: &mut PermissionRegistry) {
pub fn register(dispatcher: &mut CommandDispatcher, registry: &PermissionRegistry) {
registry.register_permission_or_panic(Permission::new(
PERMISSION,
DESCRIPTION,

View File

@ -60,7 +60,7 @@ impl CommandExecutor for SaveAllExecutor {
}
}
pub fn register(dispatcher: &mut CommandDispatcher, registry: &mut PermissionRegistry) {
pub fn register(dispatcher: &mut CommandDispatcher, registry: &PermissionRegistry) {
registry.register_permission_or_panic(Permission::new(
PERMISSION,
DESCRIPTION,

View File

@ -53,7 +53,7 @@ impl CommandExecutor for SaveOffExecutor {
}
}
pub fn register(dispatcher: &mut CommandDispatcher, registry: &mut PermissionRegistry) {
pub fn register(dispatcher: &mut CommandDispatcher, registry: &PermissionRegistry) {
registry.register_permission_or_panic(Permission::new(
PERMISSION,
DESCRIPTION,

View File

@ -53,7 +53,7 @@ impl CommandExecutor for SaveOnExecutor {
}
}
pub fn register(dispatcher: &mut CommandDispatcher, registry: &mut PermissionRegistry) {
pub fn register(dispatcher: &mut CommandDispatcher, registry: &PermissionRegistry) {
registry.register_permission_or_panic(Permission::new(
PERMISSION,
DESCRIPTION,

View File

@ -205,7 +205,7 @@ impl CommandExecutor for ObjectivesRemoveExecutor {
}
}
pub fn register(dispatcher: &mut CommandDispatcher, registry: &mut PermissionRegistry) {
pub fn register(dispatcher: &mut CommandDispatcher, registry: &PermissionRegistry) {
registry.register_permission_or_panic(Permission::new(
PERMISSION,
DESCRIPTION,

View File

@ -50,7 +50,7 @@ impl CommandExecutor for SeedCommandExecutor {
}
}
pub fn register(dispatcher: &mut CommandDispatcher, registry: &mut PermissionRegistry) {
pub fn register(dispatcher: &mut CommandDispatcher, registry: &PermissionRegistry) {
registry.register_permission_or_panic(Permission::new(
PERMISSION,
DESCRIPTION,

View File

@ -58,7 +58,7 @@ impl CommandExecutor for SetIdleTimeoutExecutor {
}
}
pub fn register(dispatcher: &mut CommandDispatcher, registry: &mut PermissionRegistry) {
pub fn register(dispatcher: &mut CommandDispatcher, registry: &PermissionRegistry) {
registry.register_permission_or_panic(Permission::new(
PERMISSION,
DESCRIPTION,

View File

@ -320,7 +320,7 @@ impl CommandExecutor for SpreadPlayersExecutor {
}
}
pub fn register(dispatcher: &mut CommandDispatcher, registry: &mut PermissionRegistry) {
pub fn register(dispatcher: &mut CommandDispatcher, registry: &PermissionRegistry) {
registry.register_permission_or_panic(Permission::new(
PERMISSION,
DESCRIPTION,

View File

@ -37,7 +37,7 @@ impl CommandExecutor for StopCommandExecutor {
}
}
pub fn register(dispatcher: &mut CommandDispatcher, registry: &mut PermissionRegistry) {
pub fn register(dispatcher: &mut CommandDispatcher, registry: &PermissionRegistry) {
registry.register_permission_or_panic(Permission::new(
PERMISSION,
DESCRIPTION,

View File

@ -177,7 +177,7 @@ impl CommandExecutor for ListExecutor {
}
}
pub fn register(dispatcher: &mut CommandDispatcher, registry: &mut PermissionRegistry) {
pub fn register(dispatcher: &mut CommandDispatcher, registry: &PermissionRegistry) {
registry.register_permission_or_panic(Permission::new(
PERMISSION,
DESCRIPTION,

View File

@ -1114,7 +1114,7 @@ fn modify_branch() -> LiteralArgumentBuilder {
)
}
pub fn register(dispatcher: &mut CommandDispatcher, registry: &mut PermissionRegistry) {
pub fn register(dispatcher: &mut CommandDispatcher, registry: &PermissionRegistry) {
registry.register_permission_or_panic(Permission::new(
PERMISSION,
DESCRIPTION,

View File

@ -81,7 +81,7 @@ impl CommandExecutor for TeamMsgCommandExecutor {
}
}
pub fn register(dispatcher: &mut CommandDispatcher, registry: &mut PermissionRegistry) {
pub fn register(dispatcher: &mut CommandDispatcher, registry: &PermissionRegistry) {
registry.register_permission_or_panic(Permission::new(
PERMISSION,
DESCRIPTION,

View File

@ -366,7 +366,7 @@ const fn time_argument() -> TimeArgumentType {
TimeArgumentType::new(1)
}
pub fn register(dispatcher: &mut CommandDispatcher, registry: &mut PermissionRegistry) {
pub fn register(dispatcher: &mut CommandDispatcher, registry: &PermissionRegistry) {
registry.register_permission_or_panic(Permission::new(
PERMISSION,
DESCRIPTION,

View File

@ -234,7 +234,7 @@ impl CommandExecutor for SetTriggerExecutor {
}
}
pub fn register(dispatcher: &mut CommandDispatcher, registry: &mut PermissionRegistry) {
pub fn register(dispatcher: &mut CommandDispatcher, registry: &PermissionRegistry) {
registry.register_permission_or_panic(Permission::new(
PERMISSION,
DESCRIPTION,

View File

@ -768,11 +768,10 @@ impl CommandDispatcher {
mod test {
use pumpkin_config::BasicConfiguration;
use pumpkin_data::translation;
use pumpkin_util::permission::PermissionRegistry;
use pumpkin_util::permission::PermissionManager;
use pumpkin_util::text::TextContent;
use pumpkin_util::text::click::ClickEvent;
use pumpkin_util::text::color::{Color, NamedColor};
use tokio::sync::RwLock;
use super::{
PathParsingFailure, render_syntax_error_messages, select_parse_error,
@ -789,26 +788,24 @@ mod test {
}
}
#[tokio::test]
async fn dynamic_command() {
#[test]
fn dynamic_command() {
let config = BasicConfiguration::default();
let commands_config = pumpkin_config::CommandsConfig::default();
let registry = RwLock::new(PermissionRegistry::new());
let mut dispatcher = default_dispatcher(&registry, &config, &commands_config)
.await
.fallback_dispatcher;
let manager = PermissionManager::new();
let mut dispatcher =
default_dispatcher(&manager, &config, &commands_config).fallback_dispatcher;
let tree = CommandTree::new(["test"], "test_desc");
dispatcher.register(tree, "minecraft:test");
}
#[tokio::test]
async fn pumpkin_command_aliases() {
#[test]
fn pumpkin_command_aliases() {
let config = BasicConfiguration::default();
let commands_config = pumpkin_config::CommandsConfig::default();
let registry = RwLock::new(PermissionRegistry::new());
let dispatcher = default_dispatcher(&registry, &config, &commands_config)
.await
.fallback_dispatcher;
let manager = PermissionManager::new();
let dispatcher =
default_dispatcher(&manager, &config, &commands_config).fallback_dispatcher;
let pumpkin_tree = dispatcher.get_tree("pumpkin").unwrap();
let version_tree = dispatcher.get_tree("version").unwrap();

View File

@ -174,8 +174,7 @@ impl CommandSender {
Self::Console | Self::Rcon(_) => true, // Console and RCON always have all permissions
Self::Player(p) => p.has_permission(server, node).await,
Self::CommandBlock(..) | Self::Dummy => {
let perm_reg = server.permission_registry.read().await;
let Some(p) = perm_reg.get_permission(node) else {
let Some(p) = server.permission_manager.get_permission(node) else {
return false;
};
match p.default {

View File

@ -5294,11 +5294,11 @@ impl Player {
}
pub async fn has_permission(self: &Arc<Self>, server: &Server, node: &str) -> bool {
let perm_manager = server.permission_manager.read().await;
let result = perm_manager
.has_permission(&self.gameprofile.id, node, self.permission_lvl.load())
.await;
drop(perm_manager);
let result = server.permission_manager.has_permission(
&self.gameprofile.id,
node,
self.permission_lvl.load(),
);
let mut event = PlayerPermissionCheckEvent::new(self.clone(), node.to_string(), result);
let server_arc = self.world().server.upgrade();

View File

@ -37,7 +37,7 @@ pub struct Context {
pub server: Arc<Server>,
pub handlers: Arc<RwLock<HandlerMap>>,
pub plugin_manager: Arc<PluginManager>,
pub permission_manager: Arc<RwLock<PermissionManager>>,
pub permission_manager: Arc<PermissionManager>,
pub logger: Arc<OnceLock<LoggerOption>>,
}
impl Context {
@ -229,7 +229,7 @@ impl Context {
}
/// Register a permission for this plugin
pub async fn register_permission(&self, permission: Permission) -> Result<(), String> {
pub fn register_permission(&self, permission: Permission) -> Result<(), String> {
// Ensure the permission has the correct namespace
if !permission
.node
@ -241,23 +241,20 @@ impl Context {
));
}
let registry = &self.permission_manager.read().await.registry;
registry.write().await.register_permission(permission)
self.permission_manager.register_permission(permission)
}
/// Check if a player has a permission
pub async fn player_has_permission(&self, player_uuid: &uuid::Uuid, permission: &str) -> bool {
let permission_manager = self.permission_manager.read().await;
#[must_use]
pub fn player_has_permission(&self, player_uuid: &uuid::Uuid, permission: &str) -> bool {
// If the player isn't online, we need to find their op level
let player_op_level = self
.server
.get_player_by_uuid(*player_uuid)
.map_or(PermissionLvl::Zero, |player| player.permission_lvl.load());
permission_manager
self.permission_manager
.has_permission(player_uuid, permission, player_op_level)
.await
}
/// Asynchronously registers an event handler for a specific event type.

View File

@ -1963,10 +1963,7 @@ impl pumpkin::plugin::context::HostContext for PluginHostState {
let context_res = self
.resource_table
.get_mut::<ContextResource>(&Resource::new_own(context.rep()))?;
Ok(context_res
.provider
.register_permission(util_permission)
.await)
Ok(context_res.provider.register_permission(util_permission))
}
async fn get_data_folder(&mut self, _context: Resource<Context>) -> wasmtime::Result<String> {

View File

@ -1365,11 +1365,9 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState {
let player = player_from_resource(self, &player)?;
let server = self.server.as_ref().expect("server not available");
let mut perm_manager = server.permission_manager.write().await;
let attachment = perm_manager.get_attachment(player.gameprofile.id);
drop(perm_manager);
attachment.write().await.set_permission(&node, value);
server
.permission_manager
.set_permission(player.gameprofile.id, node, value);
Ok(())
}
@ -1382,11 +1380,9 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState {
let player = player_from_resource(self, &player)?;
let server = self.server.as_ref().expect("server not available");
let mut perm_manager = server.permission_manager.write().await;
let attachment = perm_manager.get_attachment(player.gameprofile.id);
drop(perm_manager);
attachment.write().await.unset_permission(&node);
server
.permission_manager
.unset_permission(&player.gameprofile.id, &node);
Ok(())
}
@ -1399,11 +1395,9 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState {
let player = player_from_resource(self, &player)?;
let server = self.server.as_ref().expect("server not available");
let mut perm_manager = server.permission_manager.write().await;
let attachment = perm_manager.get_attachment(player.gameprofile.id);
drop(perm_manager);
Ok(attachment.read().await.has_permission_set(&node))
Ok(server
.permission_manager
.has_permission_set(&player.gameprofile.id, &node))
}
async fn has_permission(

View File

@ -23,7 +23,7 @@ use key_store::KeyStore;
use pumpkin_config::{AdvancedConfiguration, BasicConfiguration};
use pumpkin_data::dimension::Dimension;
use pumpkin_data::entity::EntityType;
use pumpkin_util::permission::{PermissionManager, PermissionRegistry};
use pumpkin_util::permission::PermissionManager;
use pumpkin_util::text::color::NamedColor;
use pumpkin_world::dimension::into_level;
use pumpkin_world::world::WorldPortalExt;
@ -47,7 +47,7 @@ use std::net::IpAddr;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, AtomicI32, AtomicI64, AtomicU32};
use std::{future::Future, sync::atomic::Ordering, time::Duration};
use tokio::sync::{Mutex, OnceCell, RwLock};
use tokio::sync::{Mutex, OnceCell};
use tokio::task::{JoinHandle, JoinSet};
use tokio_util::task::TaskTracker;
@ -79,9 +79,7 @@ pub struct Server {
pub plugin_manager: Arc<PluginManager>,
/// Permission manager for the server.
pub permission_manager: Arc<RwLock<PermissionManager>>,
/// Permission registry for the server.
pub permission_registry: Arc<RwLock<PermissionRegistry>>,
pub permission_manager: Arc<PermissionManager>,
/// Handles cryptographic keys for secure communication.
key_store: OnceCell<Arc<KeyStore>>,
@ -154,16 +152,13 @@ impl Server {
advanced_config: AdvancedConfiguration,
vanilla_data: VanillaData,
) -> Arc<Self> {
let permission_registry = Arc::new(RwLock::new(PermissionRegistry::new()));
let permission_manager = Arc::new(PermissionManager::new());
// First register the default commands. After that, plugins can put in their own.
let command_dispatcher = ArcSwap::from_pointee(
default_dispatcher(
&permission_registry,
&basic_config,
&advanced_config.commands,
)
.await,
);
let command_dispatcher = ArcSwap::from_pointee(default_dispatcher(
&permission_manager,
&basic_config,
&advanced_config.commands,
));
crate::command::set_broadcast_console_to_ops(
advanced_config.commands.broadcast_console_to_ops,
@ -264,10 +259,7 @@ impl Server {
advanced_config,
data: vanilla_data,
plugin_manager: Arc::new(PluginManager::new()),
permission_manager: Arc::new(RwLock::new(PermissionManager::new(
permission_registry.clone(),
))),
permission_registry,
permission_manager,
container_id: 0.into(),
recipe_manager: Arc::new(recipe::RecipeManager::new()),
enchantment_manager: Arc::new(enchantment::EnchantmentManager::new()),