chore: swap permissions_manger to dashmap
i need this also PatchBukkit
This commit is contained in:
parent
489403c58b
commit
61bd8f77b6
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -3595,6 +3595,7 @@ dependencies = [
|
|||||||
"colored",
|
"colored",
|
||||||
"criterion",
|
"criterion",
|
||||||
"crypto-bigint 0.7.5",
|
"crypto-bigint 0.7.5",
|
||||||
|
"dashmap",
|
||||||
"ecdsa 0.17.0",
|
"ecdsa 0.17.0",
|
||||||
"md5",
|
"md5",
|
||||||
"num-derive",
|
"num-derive",
|
||||||
@ -3610,7 +3611,6 @@ dependencies = [
|
|||||||
"sha2 0.11.0",
|
"sha2 0.11.0",
|
||||||
"syn 3.0.3",
|
"syn 3.0.3",
|
||||||
"thiserror 2.0.20",
|
"thiserror 2.0.20",
|
||||||
"tokio",
|
|
||||||
"ureq",
|
"ureq",
|
||||||
"uuid",
|
"uuid",
|
||||||
]
|
]
|
||||||
|
|||||||
@ -22,7 +22,7 @@ syn = { workspace = true, optional = true }
|
|||||||
proc-macro2 = { workspace = true, optional = true }
|
proc-macro2 = { workspace = true, optional = true }
|
||||||
|
|
||||||
uuid.workspace = true
|
uuid.workspace = true
|
||||||
tokio = { workspace = true, features = ["sync"] }
|
dashmap.workspace = true
|
||||||
base64.workspace = true
|
base64.workspace = true
|
||||||
p384 = { workspace = true, features = ["ecdsa"] }
|
p384 = { workspace = true, features = ["ecdsa"] }
|
||||||
thiserror.workspace = true
|
thiserror.workspace = true
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
|
use dashmap::DashMap;
|
||||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
|
||||||
use tokio::sync::RwLock;
|
|
||||||
|
|
||||||
/// Describes the default behaviour for permissions.
|
/// Describes the default behaviour for permissions.
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
@ -65,7 +64,7 @@ impl Permission {
|
|||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct PermissionRegistry {
|
pub struct PermissionRegistry {
|
||||||
/// All registered permissions.
|
/// All registered permissions.
|
||||||
permissions: HashMap<String, Permission>,
|
permissions: DashMap<String, Permission>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PermissionRegistry {
|
impl PermissionRegistry {
|
||||||
@ -76,7 +75,7 @@ impl PermissionRegistry {
|
|||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
permissions: HashMap::new(),
|
permissions: DashMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,7 +87,7 @@ impl PermissionRegistry {
|
|||||||
/// # Returns
|
/// # Returns
|
||||||
/// - `Ok(())` if the permission was successfully registered.
|
/// - `Ok(())` if the permission was successfully registered.
|
||||||
/// - `Err(String)` if a permission with the same node already exists.
|
/// - `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) {
|
if self.permissions.contains_key(&permission.node) {
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
"Permission {} is already registered",
|
"Permission {} is already registered",
|
||||||
@ -108,7 +107,7 @@ impl PermissionRegistry {
|
|||||||
/// # Parameters
|
/// # Parameters
|
||||||
/// - `permission`: The `Permission` instance to add.
|
/// - `permission`: The `Permission` instance to add.
|
||||||
#[allow(clippy::expect_used)]
|
#[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)
|
self.register_permission(permission)
|
||||||
.expect("Permission should have been registered successfully");
|
.expect("Permission should have been registered successfully");
|
||||||
}
|
}
|
||||||
@ -119,9 +118,12 @@ impl PermissionRegistry {
|
|||||||
/// - `node`: The full permission node string to look up.
|
/// - `node`: The full permission node string to look up.
|
||||||
///
|
///
|
||||||
/// # Returns
|
/// # Returns
|
||||||
/// `Some(&Permission)` if the node exists, or `None` otherwise.
|
/// `Some(Ref<'_, String, Permission>)` if the node exists, or `None` otherwise.
|
||||||
#[must_use]
|
#[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)
|
self.permissions.get(node)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,8 +139,9 @@ impl PermissionRegistry {
|
|||||||
/// # Returns
|
/// # Returns
|
||||||
/// - `true` if the node existed and its default was updated.
|
/// - `true` if the node existed and its default was updated.
|
||||||
/// - `false` if no permission with that node is registered.
|
/// - `false` if no permission with that node is registered.
|
||||||
pub fn set_default(&mut self, node: &str, default: PermissionDefault) -> bool {
|
#[must_use]
|
||||||
if let Some(permission) = self.permissions.get_mut(node) {
|
pub fn set_default(&self, node: &str, default: PermissionDefault) -> bool {
|
||||||
|
if let Some(mut permission) = self.permissions.get_mut(node) {
|
||||||
permission.default = default;
|
permission.default = default;
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
@ -163,7 +166,7 @@ impl PermissionRegistry {
|
|||||||
#[derive(Default, Clone, Debug, Serialize, Deserialize)]
|
#[derive(Default, Clone, Debug, Serialize, Deserialize)]
|
||||||
pub struct PermissionAttachment {
|
pub struct PermissionAttachment {
|
||||||
/// Directly assigned permissions.
|
/// Directly assigned permissions.
|
||||||
permissions: HashMap<String, bool>,
|
pub permissions: HashMap<String, bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PermissionAttachment {
|
impl PermissionAttachment {
|
||||||
@ -217,53 +220,103 @@ impl PermissionAttachment {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Manager for player permissions.
|
/// Manager for player and server permissions.
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct PermissionManager {
|
pub struct PermissionManager {
|
||||||
/// Global registry of permissions.
|
/// Global registry of permissions.
|
||||||
pub registry: Arc<RwLock<PermissionRegistry>>,
|
pub registry: PermissionRegistry,
|
||||||
/// Player permission attachments.
|
/// Player permission attachments keyed by player UUID.
|
||||||
pub attachments: HashMap<uuid::Uuid, Arc<RwLock<PermissionAttachment>>>,
|
pub attachments: DashMap<uuid::Uuid, DashMap<String, bool>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PermissionManager {
|
impl PermissionManager {
|
||||||
/// Creates a new `PermissionManager`.
|
/// Creates a new empty `PermissionManager`.
|
||||||
///
|
#[must_use]
|
||||||
/// # Parameters
|
pub fn new() -> Self {
|
||||||
/// - `registry`: An `Arc<RwLock<PermissionRegistry>>` containing the global permissions registry.
|
Self {
|
||||||
///
|
registry: PermissionRegistry::new(),
|
||||||
/// # Returns
|
attachments: DashMap::new(),
|
||||||
/// A `PermissionManager` with an empty attachments map.
|
}
|
||||||
pub fn new(registry: Arc<RwLock<PermissionRegistry>>) -> Self {
|
}
|
||||||
|
|
||||||
|
/// Creates a new `PermissionManager` with an existing `PermissionRegistry`.
|
||||||
|
#[must_use]
|
||||||
|
pub fn with_registry(registry: PermissionRegistry) -> Self {
|
||||||
Self {
|
Self {
|
||||||
registry,
|
registry,
|
||||||
attachments: HashMap::new(),
|
attachments: DashMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieves the `PermissionAttachment` for a given player, creating one if it doesn't exist.
|
/// Registers a new permission node in the global registry.
|
||||||
///
|
pub fn register_permission(&self, permission: Permission) -> Result<(), String> {
|
||||||
/// # Parameters
|
self.registry.register_permission(permission)
|
||||||
/// - `player_id`: The UUID of the player.
|
}
|
||||||
///
|
|
||||||
/// # Returns
|
/// Registers a new permission node in the global registry or panics if already registered.
|
||||||
/// An `Arc<RwLock<PermissionAttachment>>` representing the player's attachment.
|
pub fn register_permission_or_panic(&self, permission: Permission) {
|
||||||
pub fn get_attachment(&mut self, player_id: uuid::Uuid) -> Arc<RwLock<PermissionAttachment>> {
|
self.registry.register_permission_or_panic(permission);
|
||||||
Arc::clone(
|
}
|
||||||
|
|
||||||
|
/// 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
|
self.attachments
|
||||||
.entry(player_id)
|
.entry(player_id)
|
||||||
.or_insert_with(|| Arc::new(RwLock::new(PermissionAttachment::new()))),
|
.or_default()
|
||||||
)
|
.insert(node.into(), value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Removes the `PermissionAttachment` for a given player.
|
/// Unsets a player's explicit permission.
|
||||||
///
|
pub fn unset_permission(&self, player_id: &uuid::Uuid, node: &str) {
|
||||||
/// # Parameters
|
if let Some(player_perms) = self.attachments.get(player_id) {
|
||||||
/// - `player_id`: The UUID of the player.
|
player_perms.remove(node);
|
||||||
pub fn remove_attachment(&mut self, player_id: &uuid::Uuid) {
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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);
|
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.
|
/// Checks if a player has a specific permission.
|
||||||
///
|
///
|
||||||
/// # Parameters
|
/// # Parameters
|
||||||
@ -273,21 +326,18 @@ impl PermissionManager {
|
|||||||
///
|
///
|
||||||
/// # Returns
|
/// # Returns
|
||||||
/// `true` if the player has the permission, `false` otherwise.
|
/// `true` if the player has the permission, `false` otherwise.
|
||||||
pub async fn has_permission(
|
#[must_use]
|
||||||
|
pub fn has_permission(
|
||||||
&self,
|
&self,
|
||||||
player_id: &uuid::Uuid,
|
player_id: &uuid::Uuid,
|
||||||
permission_node: &str,
|
permission_node: &str,
|
||||||
player_op_level: PermissionLvl,
|
player_op_level: PermissionLvl,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let reg = self.registry.read().await;
|
|
||||||
|
|
||||||
// Check explicitly set permissions
|
// Check explicitly set permissions
|
||||||
if let Some(attachment) = self.attachments.get(player_id) {
|
if let Some(attachment) = self.attachments.get(player_id) {
|
||||||
let attachment = attachment.read().await;
|
|
||||||
|
|
||||||
// Check for the exact permission match
|
// Check for the exact permission match
|
||||||
if let Some(value) = attachment.has_permission_set(permission_node) {
|
if let Some(value) = attachment.get(permission_node) {
|
||||||
return value;
|
return *value;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check parent nodes (for wildcard permissions)
|
// Check parent nodes (for wildcard permissions)
|
||||||
@ -298,23 +348,21 @@ impl PermissionManager {
|
|||||||
|
|
||||||
// Check wildcard permissions at each level
|
// Check wildcard permissions at each level
|
||||||
let mut current_node = namespace.to_string();
|
let mut current_node = namespace.to_string();
|
||||||
if let Some(value) = attachment.has_permission_set(&format!("{current_node}:*")) {
|
if let Some(value) = attachment.get(&format!("{current_node}:*")) {
|
||||||
return value;
|
return *value;
|
||||||
}
|
}
|
||||||
|
|
||||||
current_node.push(':');
|
current_node.push(':');
|
||||||
for (i, part) in key_parts.iter().enumerate() {
|
for (i, part) in key_parts.iter().enumerate() {
|
||||||
current_node.push_str(part);
|
current_node.push_str(part);
|
||||||
|
|
||||||
if let Some(value) = attachment.has_permission_set(¤t_node) {
|
if let Some(value) = attachment.get(¤t_node) {
|
||||||
return value;
|
return *value;
|
||||||
}
|
}
|
||||||
|
|
||||||
if i < key_parts.len() - 1 {
|
if i < key_parts.len() - 1 {
|
||||||
if let Some(value) =
|
if let Some(value) = attachment.get(&format!("{current_node}.*")) {
|
||||||
attachment.has_permission_set(&format!("{current_node}.*"))
|
return *value;
|
||||||
{
|
|
||||||
return value;
|
|
||||||
}
|
}
|
||||||
current_node.push('.');
|
current_node.push('.');
|
||||||
}
|
}
|
||||||
@ -322,17 +370,20 @@ impl PermissionManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check for inherited permissions from parent nodes
|
// Check for inherited permissions from parent nodes
|
||||||
for (node, value) in attachment.get_permissions() {
|
for item in attachment.iter() {
|
||||||
if let Some(permission) = reg.get_permission(node)
|
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)
|
&& let Some(child_val) = permission.children.get(permission_node)
|
||||||
{
|
{
|
||||||
return *value && *child_val;
|
return value && *child_val;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fall back to the default permission value
|
// 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 {
|
.is_some_and(|permission| match permission.default {
|
||||||
PermissionDefault::Allow => true,
|
PermissionDefault::Allow => true,
|
||||||
PermissionDefault::Deny => false,
|
PermissionDefault::Deny => false,
|
||||||
|
|||||||
@ -562,7 +562,7 @@ impl CommandExecutor for EveryAdvancementExecutor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// register the advancement command
|
/// 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(
|
registry.register_permission_or_panic(Permission::new(
|
||||||
PERMISSION,
|
PERMISSION,
|
||||||
DESCRIPTION,
|
DESCRIPTION,
|
||||||
|
|||||||
@ -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(
|
registry.register_permission_or_panic(Permission::new(
|
||||||
PERMISSION,
|
PERMISSION,
|
||||||
DESCRIPTION,
|
DESCRIPTION,
|
||||||
|
|||||||
@ -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(
|
registry.register_permission_or_panic(Permission::new(
|
||||||
PERMISSION,
|
PERMISSION,
|
||||||
DESCRIPTION,
|
DESCRIPTION,
|
||||||
|
|||||||
@ -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(
|
registry.register_permission_or_panic(Permission::new(
|
||||||
PERMISSION,
|
PERMISSION,
|
||||||
DESCRIPTION,
|
DESCRIPTION,
|
||||||
|
|||||||
@ -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(
|
registry.register_permission_or_panic(Permission::new(
|
||||||
PERMISSION,
|
PERMISSION,
|
||||||
DESCRIPTION,
|
DESCRIPTION,
|
||||||
|
|||||||
@ -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(
|
registry.register_permission_or_panic(Permission::new(
|
||||||
PERMISSION,
|
PERMISSION,
|
||||||
DESCRIPTION,
|
DESCRIPTION,
|
||||||
|
|||||||
@ -393,7 +393,7 @@ fn execute_summon_modifier<'a>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_lines)]
|
#[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(
|
registry.register_permission_or_panic(Permission::new(
|
||||||
PERMISSION,
|
PERMISSION,
|
||||||
DESCRIPTION,
|
DESCRIPTION,
|
||||||
|
|||||||
@ -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(
|
registry.register_permission_or_panic(Permission::new(
|
||||||
PERMISSION,
|
PERMISSION,
|
||||||
DESCRIPTION,
|
DESCRIPTION,
|
||||||
|
|||||||
@ -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(
|
registry.register_permission_or_panic(Permission::new(
|
||||||
PERMISSION,
|
PERMISSION,
|
||||||
DESCRIPTION,
|
DESCRIPTION,
|
||||||
|
|||||||
@ -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(
|
registry.register_permission_or_panic(Permission::new(
|
||||||
PERMISSION,
|
PERMISSION,
|
||||||
DESCRIPTION,
|
DESCRIPTION,
|
||||||
|
|||||||
@ -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(
|
registry.register_permission_or_panic(Permission::new(
|
||||||
PERMISSION,
|
PERMISSION,
|
||||||
DESCRIPTION,
|
DESCRIPTION,
|
||||||
|
|||||||
@ -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(
|
registry.register_permission_or_panic(Permission::new(
|
||||||
PERMISSION,
|
PERMISSION,
|
||||||
DESCRIPTION,
|
DESCRIPTION,
|
||||||
|
|||||||
@ -99,7 +99,7 @@ fn get_player_names_and_ids(players: &[Arc<Player>]) -> TextComponent {
|
|||||||
TextComponent::join_with_comma(names_and_ids)
|
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(
|
registry.register_permission_or_panic(Permission::new(
|
||||||
PERMISSION,
|
PERMISSION,
|
||||||
DESCRIPTION,
|
DESCRIPTION,
|
||||||
|
|||||||
@ -220,7 +220,7 @@ impl CommandExecutor for LootExecutor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[expect(clippy::too_many_lines)]
|
#[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(
|
registry.register_permission_or_panic(Permission::new(
|
||||||
PERMISSION,
|
PERMISSION,
|
||||||
DESCRIPTION,
|
DESCRIPTION,
|
||||||
|
|||||||
@ -3,9 +3,8 @@ use crate::command::tree::Command;
|
|||||||
use pumpkin_config::{BasicConfiguration, CommandsConfig};
|
use pumpkin_config::{BasicConfiguration, CommandsConfig};
|
||||||
use pumpkin_util::{
|
use pumpkin_util::{
|
||||||
PermissionLvl,
|
PermissionLvl,
|
||||||
permission::{Permission, PermissionDefault, PermissionRegistry},
|
permission::{Permission, PermissionDefault, PermissionManager, PermissionRegistry},
|
||||||
};
|
};
|
||||||
use tokio::sync::RwLock;
|
|
||||||
use tracing::{info, warn};
|
use tracing::{info, warn};
|
||||||
|
|
||||||
mod advancement;
|
mod advancement;
|
||||||
@ -87,15 +86,13 @@ mod worldborder;
|
|||||||
|
|
||||||
#[allow(clippy::too_many_lines)]
|
#[allow(clippy::too_many_lines)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub async fn default_dispatcher(
|
pub fn default_dispatcher(
|
||||||
registry: &RwLock<PermissionRegistry>,
|
permission_manager: &PermissionManager,
|
||||||
_basic_config: &BasicConfiguration,
|
_basic_config: &BasicConfiguration,
|
||||||
commands_config: &CommandsConfig,
|
commands_config: &CommandsConfig,
|
||||||
) -> CommandDispatcher {
|
) -> CommandDispatcher {
|
||||||
let mut dispatcher = crate::command::dispatcher::CommandDispatcher::default();
|
let mut dispatcher = crate::command::dispatcher::CommandDispatcher::default();
|
||||||
|
let registry = &permission_manager.registry;
|
||||||
let mut registry_lock = registry.write().await;
|
|
||||||
let registry = &mut *registry_lock;
|
|
||||||
|
|
||||||
register_permissions(registry);
|
register_permissions(registry);
|
||||||
|
|
||||||
@ -227,7 +224,7 @@ pub async fn default_dispatcher(
|
|||||||
/// this affects both the legacy and the node-based dispatchers uniformly.
|
/// this affects both the legacy and the node-based dispatchers uniformly.
|
||||||
fn apply_command_overrides(
|
fn apply_command_overrides(
|
||||||
dispatcher: &mut CommandDispatcher,
|
dispatcher: &mut CommandDispatcher,
|
||||||
registry: &mut PermissionRegistry,
|
registry: &PermissionRegistry,
|
||||||
commands_config: &CommandsConfig,
|
commands_config: &CommandsConfig,
|
||||||
) {
|
) {
|
||||||
for (raw_name, settings) in &commands_config.overrides {
|
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) {
|
if let Some(node) = resolve_permission_node(dispatcher, registry, &name) {
|
||||||
registry.set_default(&node, default);
|
if registry.set_default(&node, default) {
|
||||||
info!(
|
info!(
|
||||||
"The /{name} command now needs permission level {} to use",
|
"The /{name} command now needs permission level {} to use",
|
||||||
level as u8
|
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 {
|
} else {
|
||||||
warn!(
|
warn!(
|
||||||
"Command override for /{name} sets a permission level, but no matching permission node could be found; leaving it unchanged"
|
"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
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn register_permissions(registry: &mut PermissionRegistry) {
|
fn register_permissions(registry: &PermissionRegistry) {
|
||||||
// Register level 0 permissions (allowed by default)
|
// Register level 0 permissions (allowed by default)
|
||||||
register_level_0_permissions(registry);
|
register_level_0_permissions(registry);
|
||||||
|
|
||||||
@ -336,7 +338,7 @@ fn register_permissions(registry: &mut PermissionRegistry) {
|
|||||||
.unwrap_or_else(|e| tracing::warn!("{e}"));
|
.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
|
// Register permissions for builtin commands that are allowed for everyone
|
||||||
registry
|
registry
|
||||||
.register_permission(Permission::new(
|
.register_permission(Permission::new(
|
||||||
@ -362,7 +364,7 @@ fn register_level_0_permissions(registry: &mut PermissionRegistry) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[expect(clippy::too_many_lines)]
|
#[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
|
// Register permissions for commands with PermissionLvl::Two
|
||||||
registry
|
registry
|
||||||
.register_permission(Permission::new(
|
.register_permission(Permission::new(
|
||||||
@ -576,7 +578,7 @@ fn register_level_2_permissions(registry: &mut PermissionRegistry) {
|
|||||||
.unwrap_or_else(|e| tracing::warn!("{e}"));
|
.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
|
// Register permissions for commands with PermissionLvl::Three
|
||||||
registry
|
registry
|
||||||
.register_permission(Permission::new(
|
.register_permission(Permission::new(
|
||||||
@ -661,8 +663,7 @@ fn register_level_3_permissions(registry: &mut PermissionRegistry) {
|
|||||||
mod override_tests {
|
mod override_tests {
|
||||||
use pumpkin_config::{BasicConfiguration, CommandOverride, CommandsConfig};
|
use pumpkin_config::{BasicConfiguration, CommandOverride, CommandsConfig};
|
||||||
use pumpkin_util::PermissionLvl;
|
use pumpkin_util::PermissionLvl;
|
||||||
use pumpkin_util::permission::{PermissionDefault, PermissionRegistry};
|
use pumpkin_util::permission::{PermissionDefault, PermissionManager};
|
||||||
use tokio::sync::RwLock;
|
|
||||||
|
|
||||||
use super::default_dispatcher;
|
use super::default_dispatcher;
|
||||||
|
|
||||||
@ -686,16 +687,16 @@ mod override_tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[test]
|
||||||
async fn disabling_a_command_removes_and_hides_it() {
|
fn disabling_a_command_removes_and_hides_it() {
|
||||||
let basic = BasicConfiguration::default();
|
let basic = BasicConfiguration::default();
|
||||||
let mut commands = CommandsConfig::default();
|
let mut commands = CommandsConfig::default();
|
||||||
// `gamemode` lives on the legacy dispatcher; disabling it should remove
|
// `gamemode` lives on the legacy dispatcher; disabling it should remove
|
||||||
// it there and flag it on the wrapper.
|
// it there and flag it on the wrapper.
|
||||||
disabled(&mut commands, "gamemode");
|
disabled(&mut commands, "gamemode");
|
||||||
|
|
||||||
let registry = RwLock::new(PermissionRegistry::new());
|
let manager = PermissionManager::new();
|
||||||
let dispatcher = default_dispatcher(®istry, &basic, &commands).await;
|
let dispatcher = default_dispatcher(&manager, &basic, &commands);
|
||||||
|
|
||||||
assert!(dispatcher.is_disabled("gamemode"));
|
assert!(dispatcher.is_disabled("gamemode"));
|
||||||
assert!(
|
assert!(
|
||||||
@ -708,16 +709,16 @@ mod override_tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[test]
|
||||||
async fn disabling_an_alias_turns_off_the_whole_command() {
|
fn disabling_an_alias_turns_off_the_whole_command() {
|
||||||
let basic = BasicConfiguration::default();
|
let basic = BasicConfiguration::default();
|
||||||
let mut commands = CommandsConfig::default();
|
let mut commands = CommandsConfig::default();
|
||||||
// `tp` is an alias of `teleport`; disabling it should take the whole
|
// `tp` is an alias of `teleport`; disabling it should take the whole
|
||||||
// command down, including the primary name.
|
// command down, including the primary name.
|
||||||
disabled(&mut commands, "tp");
|
disabled(&mut commands, "tp");
|
||||||
|
|
||||||
let registry = RwLock::new(PermissionRegistry::new());
|
let manager = PermissionManager::new();
|
||||||
let dispatcher = default_dispatcher(®istry, &basic, &commands).await;
|
let dispatcher = default_dispatcher(&manager, &basic, &commands);
|
||||||
|
|
||||||
assert!(dispatcher.is_disabled("tp"));
|
assert!(dispatcher.is_disabled("tp"));
|
||||||
assert!(dispatcher.is_disabled("teleport"));
|
assert!(dispatcher.is_disabled("teleport"));
|
||||||
@ -725,60 +726,59 @@ mod override_tests {
|
|||||||
assert!(dispatcher.fallback_dispatcher.get_tree("teleport").is_err());
|
assert!(dispatcher.fallback_dispatcher.get_tree("teleport").is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[test]
|
||||||
async fn disabling_a_node_command_also_disables_its_aliases() {
|
fn disabling_a_node_command_also_disables_its_aliases() {
|
||||||
let basic = BasicConfiguration::default();
|
let basic = BasicConfiguration::default();
|
||||||
let mut commands = CommandsConfig::default();
|
let mut commands = CommandsConfig::default();
|
||||||
// `help` is a node-based command with the aliases `h` and `?`.
|
// `help` is a node-based command with the aliases `h` and `?`.
|
||||||
disabled(&mut commands, "help");
|
disabled(&mut commands, "help");
|
||||||
|
|
||||||
let registry = RwLock::new(PermissionRegistry::new());
|
let manager = PermissionManager::new();
|
||||||
let dispatcher = default_dispatcher(®istry, &basic, &commands).await;
|
let dispatcher = default_dispatcher(&manager, &basic, &commands);
|
||||||
|
|
||||||
assert!(dispatcher.is_disabled("help"));
|
assert!(dispatcher.is_disabled("help"));
|
||||||
assert!(dispatcher.is_disabled("h"));
|
assert!(dispatcher.is_disabled("h"));
|
||||||
assert!(dispatcher.is_disabled("?"));
|
assert!(dispatcher.is_disabled("?"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[test]
|
||||||
async fn override_for_unknown_command_is_ignored() {
|
fn override_for_unknown_command_is_ignored() {
|
||||||
let basic = BasicConfiguration::default();
|
let basic = BasicConfiguration::default();
|
||||||
let mut commands = CommandsConfig::default();
|
let mut commands = CommandsConfig::default();
|
||||||
// A command name that does not exist (usually a typo in the config)
|
// A command name that does not exist (usually a typo in the config)
|
||||||
// should be ignored, not silently swallow a real command or panic.
|
// should be ignored, not silently swallow a real command or panic.
|
||||||
disabled(&mut commands, "notacommand");
|
disabled(&mut commands, "notacommand");
|
||||||
|
|
||||||
let registry = RwLock::new(PermissionRegistry::new());
|
let manager = PermissionManager::new();
|
||||||
let dispatcher = default_dispatcher(®istry, &basic, &commands).await;
|
let dispatcher = default_dispatcher(&manager, &basic, &commands);
|
||||||
|
|
||||||
assert!(!dispatcher.is_disabled("notacommand"));
|
assert!(!dispatcher.is_disabled("notacommand"));
|
||||||
assert!(dispatcher.fallback_dispatcher.get_tree("gamemode").is_ok());
|
assert!(dispatcher.fallback_dispatcher.get_tree("gamemode").is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[test]
|
||||||
async fn override_is_case_insensitive() {
|
fn override_is_case_insensitive() {
|
||||||
let basic = BasicConfiguration::default();
|
let basic = BasicConfiguration::default();
|
||||||
let mut commands = CommandsConfig::default();
|
let mut commands = CommandsConfig::default();
|
||||||
disabled(&mut commands, "GameMode");
|
disabled(&mut commands, "GameMode");
|
||||||
|
|
||||||
let registry = RwLock::new(PermissionRegistry::new());
|
let manager = PermissionManager::new();
|
||||||
let dispatcher = default_dispatcher(®istry, &basic, &commands).await;
|
let dispatcher = default_dispatcher(&manager, &basic, &commands);
|
||||||
|
|
||||||
assert!(dispatcher.is_disabled("gamemode"));
|
assert!(dispatcher.is_disabled("gamemode"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[test]
|
||||||
async fn permission_level_override_rewrites_the_registry_default() {
|
fn permission_level_override_rewrites_the_registry_default() {
|
||||||
let basic = BasicConfiguration::default();
|
let basic = BasicConfiguration::default();
|
||||||
let mut commands = CommandsConfig::default();
|
let mut commands = CommandsConfig::default();
|
||||||
// `gamemode` is normally level 2; bump it to owner-only.
|
// `gamemode` is normally level 2; bump it to owner-only.
|
||||||
permission(&mut commands, "gamemode", PermissionLvl::Four);
|
permission(&mut commands, "gamemode", PermissionLvl::Four);
|
||||||
|
|
||||||
let registry = RwLock::new(PermissionRegistry::new());
|
let manager = PermissionManager::new();
|
||||||
let _dispatcher = default_dispatcher(®istry, &basic, &commands).await;
|
let _dispatcher = default_dispatcher(&manager, &basic, &commands);
|
||||||
|
|
||||||
let registry = registry.read().await;
|
let permission = manager
|
||||||
let permission = registry
|
|
||||||
.get_permission("minecraft:command.gamemode")
|
.get_permission("minecraft:command.gamemode")
|
||||||
.expect("gamemode permission should be registered");
|
.expect("gamemode permission should be registered");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -787,8 +787,8 @@ mod override_tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[test]
|
||||||
async fn permission_override_resolves_node_command_by_convention() {
|
fn permission_override_resolves_node_command_by_convention() {
|
||||||
let basic = BasicConfiguration::default();
|
let basic = BasicConfiguration::default();
|
||||||
let mut commands = CommandsConfig::default();
|
let mut commands = CommandsConfig::default();
|
||||||
// `kill` is a node-based command whose permission node is not recorded in
|
// `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.
|
// `minecraft:command.kill` naming convention.
|
||||||
permission(&mut commands, "kill", PermissionLvl::Four);
|
permission(&mut commands, "kill", PermissionLvl::Four);
|
||||||
|
|
||||||
let registry = RwLock::new(PermissionRegistry::new());
|
let manager = PermissionManager::new();
|
||||||
let _dispatcher = default_dispatcher(®istry, &basic, &commands).await;
|
let _dispatcher = default_dispatcher(&manager, &basic, &commands);
|
||||||
|
|
||||||
let registry = registry.read().await;
|
let permission = manager
|
||||||
let permission = registry
|
|
||||||
.get_permission("minecraft:command.kill")
|
.get_permission("minecraft:command.kill")
|
||||||
.expect("kill permission should be registered");
|
.expect("kill permission should be registered");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -809,17 +808,16 @@ mod override_tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[test]
|
||||||
async fn permission_level_zero_allows_everyone() {
|
fn permission_level_zero_allows_everyone() {
|
||||||
let basic = BasicConfiguration::default();
|
let basic = BasicConfiguration::default();
|
||||||
let mut commands = CommandsConfig::default();
|
let mut commands = CommandsConfig::default();
|
||||||
permission(&mut commands, "gamemode", PermissionLvl::Zero);
|
permission(&mut commands, "gamemode", PermissionLvl::Zero);
|
||||||
|
|
||||||
let registry = RwLock::new(PermissionRegistry::new());
|
let manager = PermissionManager::new();
|
||||||
let _dispatcher = default_dispatcher(®istry, &basic, &commands).await;
|
let _dispatcher = default_dispatcher(&manager, &basic, &commands);
|
||||||
|
|
||||||
let registry = registry.read().await;
|
let permission = manager
|
||||||
let permission = registry
|
|
||||||
.get_permission("minecraft:command.gamemode")
|
.get_permission("minecraft:command.gamemode")
|
||||||
.expect("gamemode permission should be registered");
|
.expect("gamemode permission should be registered");
|
||||||
assert_eq!(permission.default, PermissionDefault::Allow);
|
assert_eq!(permission.default, PermissionDefault::Allow);
|
||||||
|
|||||||
@ -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(
|
registry.register_permission_or_panic(Permission::new(
|
||||||
PERMISSION,
|
PERMISSION,
|
||||||
DESCRIPTION,
|
DESCRIPTION,
|
||||||
|
|||||||
@ -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(
|
registry.register_permission_or_panic(Permission::new(
|
||||||
PERMISSION,
|
PERMISSION,
|
||||||
DESCRIPTION,
|
DESCRIPTION,
|
||||||
|
|||||||
@ -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(
|
registry.register_permission_or_panic(Permission::new(
|
||||||
PERMISSION,
|
PERMISSION,
|
||||||
DESCRIPTION,
|
DESCRIPTION,
|
||||||
|
|||||||
@ -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(
|
registry.register_permission_or_panic(Permission::new(
|
||||||
PERMISSION,
|
PERMISSION,
|
||||||
DESCRIPTION,
|
DESCRIPTION,
|
||||||
|
|||||||
@ -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(
|
registry.register_permission_or_panic(Permission::new(
|
||||||
PERMISSION,
|
PERMISSION,
|
||||||
DESCRIPTION,
|
DESCRIPTION,
|
||||||
|
|||||||
@ -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(
|
registry.register_permission_or_panic(Permission::new(
|
||||||
PERMISSION,
|
PERMISSION,
|
||||||
DESCRIPTION,
|
DESCRIPTION,
|
||||||
|
|||||||
@ -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(
|
registry.register_permission_or_panic(Permission::new(
|
||||||
PERMISSION,
|
PERMISSION,
|
||||||
DESCRIPTION,
|
DESCRIPTION,
|
||||||
|
|||||||
@ -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(
|
registry.register_permission_or_panic(Permission::new(
|
||||||
PERMISSION,
|
PERMISSION,
|
||||||
DESCRIPTION,
|
DESCRIPTION,
|
||||||
|
|||||||
@ -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(
|
registry.register_permission_or_panic(Permission::new(
|
||||||
PERMISSION,
|
PERMISSION,
|
||||||
DESCRIPTION,
|
DESCRIPTION,
|
||||||
|
|||||||
@ -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(
|
registry.register_permission_or_panic(Permission::new(
|
||||||
PERMISSION,
|
PERMISSION,
|
||||||
DESCRIPTION,
|
DESCRIPTION,
|
||||||
|
|||||||
@ -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(
|
registry.register_permission_or_panic(Permission::new(
|
||||||
PERMISSION,
|
PERMISSION,
|
||||||
DESCRIPTION,
|
DESCRIPTION,
|
||||||
|
|||||||
@ -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(
|
registry.register_permission_or_panic(Permission::new(
|
||||||
PERMISSION,
|
PERMISSION,
|
||||||
DESCRIPTION,
|
DESCRIPTION,
|
||||||
|
|||||||
@ -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(
|
registry.register_permission_or_panic(Permission::new(
|
||||||
PERMISSION,
|
PERMISSION,
|
||||||
DESCRIPTION,
|
DESCRIPTION,
|
||||||
|
|||||||
@ -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(
|
registry.register_permission_or_panic(Permission::new(
|
||||||
PERMISSION,
|
PERMISSION,
|
||||||
DESCRIPTION,
|
DESCRIPTION,
|
||||||
|
|||||||
@ -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(
|
registry.register_permission_or_panic(Permission::new(
|
||||||
PERMISSION,
|
PERMISSION,
|
||||||
DESCRIPTION,
|
DESCRIPTION,
|
||||||
|
|||||||
@ -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(
|
registry.register_permission_or_panic(Permission::new(
|
||||||
PERMISSION,
|
PERMISSION,
|
||||||
DESCRIPTION,
|
DESCRIPTION,
|
||||||
|
|||||||
@ -366,7 +366,7 @@ const fn time_argument() -> TimeArgumentType {
|
|||||||
TimeArgumentType::new(1)
|
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(
|
registry.register_permission_or_panic(Permission::new(
|
||||||
PERMISSION,
|
PERMISSION,
|
||||||
DESCRIPTION,
|
DESCRIPTION,
|
||||||
|
|||||||
@ -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(
|
registry.register_permission_or_panic(Permission::new(
|
||||||
PERMISSION,
|
PERMISSION,
|
||||||
DESCRIPTION,
|
DESCRIPTION,
|
||||||
|
|||||||
@ -768,11 +768,10 @@ impl CommandDispatcher {
|
|||||||
mod test {
|
mod test {
|
||||||
use pumpkin_config::BasicConfiguration;
|
use pumpkin_config::BasicConfiguration;
|
||||||
use pumpkin_data::translation;
|
use pumpkin_data::translation;
|
||||||
use pumpkin_util::permission::PermissionRegistry;
|
use pumpkin_util::permission::PermissionManager;
|
||||||
use pumpkin_util::text::TextContent;
|
use pumpkin_util::text::TextContent;
|
||||||
use pumpkin_util::text::click::ClickEvent;
|
use pumpkin_util::text::click::ClickEvent;
|
||||||
use pumpkin_util::text::color::{Color, NamedColor};
|
use pumpkin_util::text::color::{Color, NamedColor};
|
||||||
use tokio::sync::RwLock;
|
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
PathParsingFailure, render_syntax_error_messages, select_parse_error,
|
PathParsingFailure, render_syntax_error_messages, select_parse_error,
|
||||||
@ -789,26 +788,24 @@ mod test {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[test]
|
||||||
async fn dynamic_command() {
|
fn dynamic_command() {
|
||||||
let config = BasicConfiguration::default();
|
let config = BasicConfiguration::default();
|
||||||
let commands_config = pumpkin_config::CommandsConfig::default();
|
let commands_config = pumpkin_config::CommandsConfig::default();
|
||||||
let registry = RwLock::new(PermissionRegistry::new());
|
let manager = PermissionManager::new();
|
||||||
let mut dispatcher = default_dispatcher(®istry, &config, &commands_config)
|
let mut dispatcher =
|
||||||
.await
|
default_dispatcher(&manager, &config, &commands_config).fallback_dispatcher;
|
||||||
.fallback_dispatcher;
|
|
||||||
let tree = CommandTree::new(["test"], "test_desc");
|
let tree = CommandTree::new(["test"], "test_desc");
|
||||||
dispatcher.register(tree, "minecraft:test");
|
dispatcher.register(tree, "minecraft:test");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[test]
|
||||||
async fn pumpkin_command_aliases() {
|
fn pumpkin_command_aliases() {
|
||||||
let config = BasicConfiguration::default();
|
let config = BasicConfiguration::default();
|
||||||
let commands_config = pumpkin_config::CommandsConfig::default();
|
let commands_config = pumpkin_config::CommandsConfig::default();
|
||||||
let registry = RwLock::new(PermissionRegistry::new());
|
let manager = PermissionManager::new();
|
||||||
let dispatcher = default_dispatcher(®istry, &config, &commands_config)
|
let dispatcher =
|
||||||
.await
|
default_dispatcher(&manager, &config, &commands_config).fallback_dispatcher;
|
||||||
.fallback_dispatcher;
|
|
||||||
|
|
||||||
let pumpkin_tree = dispatcher.get_tree("pumpkin").unwrap();
|
let pumpkin_tree = dispatcher.get_tree("pumpkin").unwrap();
|
||||||
let version_tree = dispatcher.get_tree("version").unwrap();
|
let version_tree = dispatcher.get_tree("version").unwrap();
|
||||||
|
|||||||
@ -174,8 +174,7 @@ impl CommandSender {
|
|||||||
Self::Console | Self::Rcon(_) => true, // Console and RCON always have all permissions
|
Self::Console | Self::Rcon(_) => true, // Console and RCON always have all permissions
|
||||||
Self::Player(p) => p.has_permission(server, node).await,
|
Self::Player(p) => p.has_permission(server, node).await,
|
||||||
Self::CommandBlock(..) | Self::Dummy => {
|
Self::CommandBlock(..) | Self::Dummy => {
|
||||||
let perm_reg = server.permission_registry.read().await;
|
let Some(p) = server.permission_manager.get_permission(node) else {
|
||||||
let Some(p) = perm_reg.get_permission(node) else {
|
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
match p.default {
|
match p.default {
|
||||||
|
|||||||
@ -5294,11 +5294,11 @@ impl Player {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn has_permission(self: &Arc<Self>, server: &Server, node: &str) -> bool {
|
pub async fn has_permission(self: &Arc<Self>, server: &Server, node: &str) -> bool {
|
||||||
let perm_manager = server.permission_manager.read().await;
|
let result = server.permission_manager.has_permission(
|
||||||
let result = perm_manager
|
&self.gameprofile.id,
|
||||||
.has_permission(&self.gameprofile.id, node, self.permission_lvl.load())
|
node,
|
||||||
.await;
|
self.permission_lvl.load(),
|
||||||
drop(perm_manager);
|
);
|
||||||
|
|
||||||
let mut event = PlayerPermissionCheckEvent::new(self.clone(), node.to_string(), result);
|
let mut event = PlayerPermissionCheckEvent::new(self.clone(), node.to_string(), result);
|
||||||
let server_arc = self.world().server.upgrade();
|
let server_arc = self.world().server.upgrade();
|
||||||
|
|||||||
@ -37,7 +37,7 @@ pub struct Context {
|
|||||||
pub server: Arc<Server>,
|
pub server: Arc<Server>,
|
||||||
pub handlers: Arc<RwLock<HandlerMap>>,
|
pub handlers: Arc<RwLock<HandlerMap>>,
|
||||||
pub plugin_manager: Arc<PluginManager>,
|
pub plugin_manager: Arc<PluginManager>,
|
||||||
pub permission_manager: Arc<RwLock<PermissionManager>>,
|
pub permission_manager: Arc<PermissionManager>,
|
||||||
pub logger: Arc<OnceLock<LoggerOption>>,
|
pub logger: Arc<OnceLock<LoggerOption>>,
|
||||||
}
|
}
|
||||||
impl Context {
|
impl Context {
|
||||||
@ -229,7 +229,7 @@ impl Context {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Register a permission for this plugin
|
/// 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
|
// Ensure the permission has the correct namespace
|
||||||
if !permission
|
if !permission
|
||||||
.node
|
.node
|
||||||
@ -241,23 +241,20 @@ impl Context {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let registry = &self.permission_manager.read().await.registry;
|
self.permission_manager.register_permission(permission)
|
||||||
registry.write().await.register_permission(permission)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if a player has a permission
|
/// Check if a player has a permission
|
||||||
pub async fn player_has_permission(&self, player_uuid: &uuid::Uuid, permission: &str) -> bool {
|
#[must_use]
|
||||||
let permission_manager = self.permission_manager.read().await;
|
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
|
// If the player isn't online, we need to find their op level
|
||||||
let player_op_level = self
|
let player_op_level = self
|
||||||
.server
|
.server
|
||||||
.get_player_by_uuid(*player_uuid)
|
.get_player_by_uuid(*player_uuid)
|
||||||
.map_or(PermissionLvl::Zero, |player| player.permission_lvl.load());
|
.map_or(PermissionLvl::Zero, |player| player.permission_lvl.load());
|
||||||
|
|
||||||
permission_manager
|
self.permission_manager
|
||||||
.has_permission(player_uuid, permission, player_op_level)
|
.has_permission(player_uuid, permission, player_op_level)
|
||||||
.await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Asynchronously registers an event handler for a specific event type.
|
/// Asynchronously registers an event handler for a specific event type.
|
||||||
|
|||||||
@ -1963,10 +1963,7 @@ impl pumpkin::plugin::context::HostContext for PluginHostState {
|
|||||||
let context_res = self
|
let context_res = self
|
||||||
.resource_table
|
.resource_table
|
||||||
.get_mut::<ContextResource>(&Resource::new_own(context.rep()))?;
|
.get_mut::<ContextResource>(&Resource::new_own(context.rep()))?;
|
||||||
Ok(context_res
|
Ok(context_res.provider.register_permission(util_permission))
|
||||||
.provider
|
|
||||||
.register_permission(util_permission)
|
|
||||||
.await)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_data_folder(&mut self, _context: Resource<Context>) -> wasmtime::Result<String> {
|
async fn get_data_folder(&mut self, _context: Resource<Context>) -> wasmtime::Result<String> {
|
||||||
|
|||||||
@ -1365,11 +1365,9 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState {
|
|||||||
let player = player_from_resource(self, &player)?;
|
let player = player_from_resource(self, &player)?;
|
||||||
let server = self.server.as_ref().expect("server not available");
|
let server = self.server.as_ref().expect("server not available");
|
||||||
|
|
||||||
let mut perm_manager = server.permission_manager.write().await;
|
server
|
||||||
let attachment = perm_manager.get_attachment(player.gameprofile.id);
|
.permission_manager
|
||||||
drop(perm_manager);
|
.set_permission(player.gameprofile.id, node, value);
|
||||||
|
|
||||||
attachment.write().await.set_permission(&node, value);
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -1382,11 +1380,9 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState {
|
|||||||
let player = player_from_resource(self, &player)?;
|
let player = player_from_resource(self, &player)?;
|
||||||
let server = self.server.as_ref().expect("server not available");
|
let server = self.server.as_ref().expect("server not available");
|
||||||
|
|
||||||
let mut perm_manager = server.permission_manager.write().await;
|
server
|
||||||
let attachment = perm_manager.get_attachment(player.gameprofile.id);
|
.permission_manager
|
||||||
drop(perm_manager);
|
.unset_permission(&player.gameprofile.id, &node);
|
||||||
|
|
||||||
attachment.write().await.unset_permission(&node);
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -1399,11 +1395,9 @@ impl pumpkin::plugin::player::HostPlayer for PluginHostState {
|
|||||||
let player = player_from_resource(self, &player)?;
|
let player = player_from_resource(self, &player)?;
|
||||||
let server = self.server.as_ref().expect("server not available");
|
let server = self.server.as_ref().expect("server not available");
|
||||||
|
|
||||||
let mut perm_manager = server.permission_manager.write().await;
|
Ok(server
|
||||||
let attachment = perm_manager.get_attachment(player.gameprofile.id);
|
.permission_manager
|
||||||
drop(perm_manager);
|
.has_permission_set(&player.gameprofile.id, &node))
|
||||||
|
|
||||||
Ok(attachment.read().await.has_permission_set(&node))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn has_permission(
|
async fn has_permission(
|
||||||
|
|||||||
@ -23,7 +23,7 @@ use key_store::KeyStore;
|
|||||||
use pumpkin_config::{AdvancedConfiguration, BasicConfiguration};
|
use pumpkin_config::{AdvancedConfiguration, BasicConfiguration};
|
||||||
use pumpkin_data::dimension::Dimension;
|
use pumpkin_data::dimension::Dimension;
|
||||||
use pumpkin_data::entity::EntityType;
|
use pumpkin_data::entity::EntityType;
|
||||||
use pumpkin_util::permission::{PermissionManager, PermissionRegistry};
|
use pumpkin_util::permission::PermissionManager;
|
||||||
use pumpkin_util::text::color::NamedColor;
|
use pumpkin_util::text::color::NamedColor;
|
||||||
use pumpkin_world::dimension::into_level;
|
use pumpkin_world::dimension::into_level;
|
||||||
use pumpkin_world::world::WorldPortalExt;
|
use pumpkin_world::world::WorldPortalExt;
|
||||||
@ -47,7 +47,7 @@ use std::net::IpAddr;
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::sync::atomic::{AtomicBool, AtomicI32, AtomicI64, AtomicU32};
|
use std::sync::atomic::{AtomicBool, AtomicI32, AtomicI64, AtomicU32};
|
||||||
use std::{future::Future, sync::atomic::Ordering, time::Duration};
|
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::task::{JoinHandle, JoinSet};
|
||||||
use tokio_util::task::TaskTracker;
|
use tokio_util::task::TaskTracker;
|
||||||
|
|
||||||
@ -79,9 +79,7 @@ pub struct Server {
|
|||||||
pub plugin_manager: Arc<PluginManager>,
|
pub plugin_manager: Arc<PluginManager>,
|
||||||
|
|
||||||
/// Permission manager for the server.
|
/// Permission manager for the server.
|
||||||
pub permission_manager: Arc<RwLock<PermissionManager>>,
|
pub permission_manager: Arc<PermissionManager>,
|
||||||
/// Permission registry for the server.
|
|
||||||
pub permission_registry: Arc<RwLock<PermissionRegistry>>,
|
|
||||||
|
|
||||||
/// Handles cryptographic keys for secure communication.
|
/// Handles cryptographic keys for secure communication.
|
||||||
key_store: OnceCell<Arc<KeyStore>>,
|
key_store: OnceCell<Arc<KeyStore>>,
|
||||||
@ -154,16 +152,13 @@ impl Server {
|
|||||||
advanced_config: AdvancedConfiguration,
|
advanced_config: AdvancedConfiguration,
|
||||||
vanilla_data: VanillaData,
|
vanilla_data: VanillaData,
|
||||||
) -> Arc<Self> {
|
) -> 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.
|
// First register the default commands. After that, plugins can put in their own.
|
||||||
let command_dispatcher = ArcSwap::from_pointee(
|
let command_dispatcher = ArcSwap::from_pointee(default_dispatcher(
|
||||||
default_dispatcher(
|
&permission_manager,
|
||||||
&permission_registry,
|
|
||||||
&basic_config,
|
&basic_config,
|
||||||
&advanced_config.commands,
|
&advanced_config.commands,
|
||||||
)
|
));
|
||||||
.await,
|
|
||||||
);
|
|
||||||
|
|
||||||
crate::command::set_broadcast_console_to_ops(
|
crate::command::set_broadcast_console_to_ops(
|
||||||
advanced_config.commands.broadcast_console_to_ops,
|
advanced_config.commands.broadcast_console_to_ops,
|
||||||
@ -264,10 +259,7 @@ impl Server {
|
|||||||
advanced_config,
|
advanced_config,
|
||||||
data: vanilla_data,
|
data: vanilla_data,
|
||||||
plugin_manager: Arc::new(PluginManager::new()),
|
plugin_manager: Arc::new(PluginManager::new()),
|
||||||
permission_manager: Arc::new(RwLock::new(PermissionManager::new(
|
permission_manager,
|
||||||
permission_registry.clone(),
|
|
||||||
))),
|
|
||||||
permission_registry,
|
|
||||||
container_id: 0.into(),
|
container_id: 0.into(),
|
||||||
recipe_manager: Arc::new(recipe::RecipeManager::new()),
|
recipe_manager: Arc::new(recipe::RecipeManager::new()),
|
||||||
enchantment_manager: Arc::new(enchantment::EnchantmentManager::new()),
|
enchantment_manager: Arc::new(enchantment::EnchantmentManager::new()),
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user