chore: switch command_dispatcher to ArcSwap
i need this for patchbukkit, but this is also safer in terms of deadlocks
This commit is contained in:
parent
399bf14150
commit
489403c58b
@ -141,8 +141,7 @@ impl CommandBlock {
|
|||||||
|
|
||||||
server
|
server
|
||||||
.command_dispatcher
|
.command_dispatcher
|
||||||
.read()
|
.load()
|
||||||
.await
|
|
||||||
.handle_command(&source, command)
|
.handle_command(&source, command)
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -40,7 +40,7 @@ impl ArgumentConsumer for CommandTreeArgumentConsumer {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let dispatcher = server.command_dispatcher.read().await;
|
let dispatcher = server.command_dispatcher.load();
|
||||||
|
|
||||||
dispatcher
|
dispatcher
|
||||||
.fallback_dispatcher
|
.fallback_dispatcher
|
||||||
@ -61,7 +61,7 @@ impl ArgumentConsumer for CommandTreeArgumentConsumer {
|
|||||||
return Ok(None);
|
return Ok(None);
|
||||||
};
|
};
|
||||||
|
|
||||||
let dispatcher = server.command_dispatcher.read().await;
|
let dispatcher = server.command_dispatcher.load();
|
||||||
let suggestions = dispatcher
|
let suggestions = dispatcher
|
||||||
.fallback_dispatcher
|
.fallback_dispatcher
|
||||||
.commands
|
.commands
|
||||||
|
|||||||
@ -41,7 +41,7 @@ impl CommandExecutor for Executor {
|
|||||||
succeeded_deops += 1;
|
succeeded_deops += 1;
|
||||||
|
|
||||||
if let Some(player) = server.get_player_by_uuid(profile.id) {
|
if let Some(player) = server.get_player_by_uuid(profile.id) {
|
||||||
let command_dispatcher = server.command_dispatcher.read().await;
|
let command_dispatcher = server.command_dispatcher.load();
|
||||||
player
|
player
|
||||||
.set_permission_lvl(
|
.set_permission_lvl(
|
||||||
server,
|
server,
|
||||||
|
|||||||
@ -116,7 +116,7 @@ impl HelpCommandExecutor {
|
|||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let server = context.server();
|
let server = context.server();
|
||||||
|
|
||||||
let dispatcher = server.command_dispatcher.read().await;
|
let dispatcher = server.command_dispatcher.load();
|
||||||
let commands = dispatcher
|
let commands = dispatcher
|
||||||
.get_all_permitted_commands_usage(&context.source)
|
.get_all_permitted_commands_usage(&context.source)
|
||||||
.await;
|
.await;
|
||||||
@ -206,7 +206,7 @@ impl HelpCommandExecutor {
|
|||||||
|
|
||||||
fn command<'a>(context: &'a CommandContext, command: &'a str) -> CommandExecutorResult<'a> {
|
fn command<'a>(context: &'a CommandContext, command: &'a str) -> CommandExecutorResult<'a> {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let dispatcher = context.server().command_dispatcher.read().await;
|
let dispatcher = context.server().command_dispatcher.load();
|
||||||
|
|
||||||
let Some((description, usage)) = dispatcher
|
let Some((description, usage)) = dispatcher
|
||||||
.get_permitted_command_usage(&context.source, command)
|
.get_permitted_command_usage(&context.source, command)
|
||||||
@ -274,7 +274,7 @@ impl HelpCommandExecutor {
|
|||||||
fn plugin<'a>(context: &'a CommandContext, plugin_name: &'a str) -> CommandExecutorResult<'a> {
|
fn plugin<'a>(context: &'a CommandContext, plugin_name: &'a str) -> CommandExecutorResult<'a> {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let server = context.server();
|
let server = context.server();
|
||||||
let dispatcher = server.command_dispatcher.read().await;
|
let dispatcher = server.command_dispatcher.load();
|
||||||
let commands = dispatcher
|
let commands = dispatcher
|
||||||
.get_all_permitted_commands_usage_by_plugin(&context.source, plugin_name)
|
.get_all_permitted_commands_usage_by_plugin(&context.source, plugin_name)
|
||||||
.await;
|
.await;
|
||||||
@ -335,7 +335,7 @@ impl HelpCommandExecutor {
|
|||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
// Prioritize commands ig
|
// Prioritize commands ig
|
||||||
{
|
{
|
||||||
let dispatcher = context.server().command_dispatcher.read().await;
|
let dispatcher = context.server().command_dispatcher.load();
|
||||||
if dispatcher
|
if dispatcher
|
||||||
.get_permitted_command_usage(&context.source, input)
|
.get_permitted_command_usage(&context.source, input)
|
||||||
.await
|
.await
|
||||||
@ -346,7 +346,7 @@ impl HelpCommandExecutor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
let dispatcher = context.server().command_dispatcher.read().await;
|
let dispatcher = context.server().command_dispatcher.load();
|
||||||
let plugin_commands = dispatcher
|
let plugin_commands = dispatcher
|
||||||
.get_all_permitted_commands_usage_by_plugin(&context.source, input)
|
.get_all_permitted_commands_usage_by_plugin(&context.source, input)
|
||||||
.await;
|
.await;
|
||||||
|
|||||||
@ -50,7 +50,7 @@ impl CommandExecutor for OpCommandExecutor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(player) = server.get_player_by_uuid(profile.id) {
|
if let Some(player) = server.get_player_by_uuid(profile.id) {
|
||||||
let command_dispatcher = server.command_dispatcher.read().await;
|
let command_dispatcher = server.command_dispatcher.load();
|
||||||
player
|
player
|
||||||
.set_permission_lvl(server, new_level, &command_dispatcher)
|
.set_permission_lvl(server, new_level, &command_dispatcher)
|
||||||
.await;
|
.await;
|
||||||
|
|||||||
@ -228,7 +228,7 @@ fn path_failure(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default, Clone)]
|
||||||
pub struct CommandDispatcher {
|
pub struct CommandDispatcher {
|
||||||
pub commands: FxHashMap<String, Command>,
|
pub commands: FxHashMap<String, Command>,
|
||||||
pub permissions: FxHashMap<String, String>,
|
pub permissions: FxHashMap<String, String>,
|
||||||
|
|||||||
@ -95,6 +95,7 @@ pub static RESULT_DEFERRER: LazyLock<Arc<ResultDeferrer>> =
|
|||||||
///
|
///
|
||||||
/// Internally, this dispatcher stores a [`Tree`]. Refer to its documentation
|
/// Internally, this dispatcher stores a [`Tree`]. Refer to its documentation
|
||||||
/// for more information about nodes.
|
/// for more information about nodes.
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct CommandDispatcher {
|
pub struct CommandDispatcher {
|
||||||
pub tree: Tree,
|
pub tree: Tree,
|
||||||
pub consumer: Arc<dyn ResultConsumer>,
|
pub consumer: Arc<dyn ResultConsumer>,
|
||||||
|
|||||||
@ -67,6 +67,7 @@ impl Debug for NodeType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
pub enum Command {
|
pub enum Command {
|
||||||
Tree(CommandTree),
|
Tree(CommandTree),
|
||||||
Alias(String),
|
Alias(String),
|
||||||
|
|||||||
@ -722,8 +722,7 @@ fn setup_stdin_console(server: Arc<Server>) {
|
|||||||
if !event.cancelled {
|
if !event.cancelled {
|
||||||
server
|
server
|
||||||
.command_dispatcher
|
.command_dispatcher
|
||||||
.read()
|
.load()
|
||||||
.await
|
|
||||||
.handle_command(
|
.handle_command(
|
||||||
&command::CommandSender::Console.into_source(&server).await,
|
&command::CommandSender::Console.into_source(&server).await,
|
||||||
command.as_str(),
|
command.as_str(),
|
||||||
@ -795,8 +794,7 @@ fn setup_console(mut rl: Editor<PumpkinCommandCompleter, FileHistory>, server: A
|
|||||||
if !event.cancelled {
|
if !event.cancelled {
|
||||||
server
|
server
|
||||||
.command_dispatcher
|
.command_dispatcher
|
||||||
.read()
|
.load()
|
||||||
.await
|
|
||||||
.handle_command(
|
.handle_command(
|
||||||
&command::CommandSender::Console.into_source(&server).await,
|
&command::CommandSender::Console.into_source(&server).await,
|
||||||
&line,
|
&line,
|
||||||
|
|||||||
@ -476,7 +476,7 @@ impl Completer for PumpkinCommandCompleter {
|
|||||||
let ends_with_space = cmd.ends_with(' ');
|
let ends_with_space = cmd.ends_with(' ');
|
||||||
|
|
||||||
handle.block_on(async {
|
handle.block_on(async {
|
||||||
let dispatcher = server.command_dispatcher.read().await;
|
let dispatcher = server.command_dispatcher.load();
|
||||||
let source = CommandSender::Console.into_source(server).await;
|
let source = CommandSender::Console.into_source(server).await;
|
||||||
|
|
||||||
// Temporary setups to unify both dispatchers for now:
|
// Temporary setups to unify both dispatchers for now:
|
||||||
|
|||||||
@ -31,7 +31,7 @@ impl BedrockClient {
|
|||||||
// Some commands can take a long time to execute. If they do, they block packet processing for the player.
|
// Some commands can take a long time to execute. If they do, they block packet processing for the player.
|
||||||
// That's why we will spawn a task instead.
|
// That's why we will spawn a task instead.
|
||||||
server.spawn_task(async move {
|
server.spawn_task(async move {
|
||||||
let dispatcher = server_clone.command_dispatcher.read().await;
|
let dispatcher = server_clone.command_dispatcher.load();
|
||||||
dispatcher.handle_command(
|
dispatcher.handle_command(
|
||||||
&player_clone.get_command_source(&server_clone).await,
|
&player_clone.get_command_source(&server_clone).await,
|
||||||
&command_clone
|
&command_clone
|
||||||
|
|||||||
@ -28,7 +28,7 @@ impl JavaClient {
|
|||||||
// Some commands can take a long time to execute. If they do, they block packet processing for the player.
|
// Some commands can take a long time to execute. If they do, they block packet processing for the player.
|
||||||
// That's why we will spawn a task instead.
|
// That's why we will spawn a task instead.
|
||||||
server.spawn_task(async move {
|
server.spawn_task(async move {
|
||||||
let dispatcher = server_clone.command_dispatcher.read().await;
|
let dispatcher = server_clone.command_dispatcher.load();
|
||||||
dispatcher.handle_command(
|
dispatcher.handle_command(
|
||||||
&player_clone.get_command_source(&server_clone).await,
|
&player_clone.get_command_source(&server_clone).await,
|
||||||
&command_clone
|
&command_clone
|
||||||
|
|||||||
@ -19,8 +19,7 @@ impl JavaClient {
|
|||||||
|
|
||||||
let suggestions = server
|
let suggestions = server
|
||||||
.command_dispatcher
|
.command_dispatcher
|
||||||
.read()
|
.load()
|
||||||
.await
|
|
||||||
.suggest(cmd, &player.get_command_source(server).await)
|
.suggest(cmd, &player.get_command_source(server).await)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
|
|||||||
@ -146,8 +146,7 @@ impl RCONClient {
|
|||||||
let _ = tokio::spawn(async move {
|
let _ = tokio::spawn(async move {
|
||||||
server_clone
|
server_clone
|
||||||
.command_dispatcher
|
.command_dispatcher
|
||||||
.read()
|
.load()
|
||||||
.await
|
|
||||||
.handle_command(&command_source, &packet_body)
|
.handle_command(&command_source, &packet_body)
|
||||||
.await;
|
.await;
|
||||||
})
|
})
|
||||||
|
|||||||
@ -175,12 +175,13 @@ impl Context {
|
|||||||
format!("{}:{permission}", self.metadata.name)
|
format!("{}:{permission}", self.metadata.name)
|
||||||
};
|
};
|
||||||
|
|
||||||
{
|
self.server.command_dispatcher.rcu(|dispatcher| {
|
||||||
let mut dispatcher_lock = self.server.command_dispatcher.write().await;
|
let mut new_dispatcher = (**dispatcher).clone();
|
||||||
dispatcher_lock
|
new_dispatcher
|
||||||
.fallback_dispatcher
|
.fallback_dispatcher
|
||||||
.register(tree, full_permission_node);
|
.register(tree.clone(), full_permission_node.clone());
|
||||||
};
|
Arc::new(new_dispatcher)
|
||||||
|
});
|
||||||
|
|
||||||
self.reload_commands_for_everyone().await;
|
self.reload_commands_for_everyone().await;
|
||||||
}
|
}
|
||||||
@ -190,10 +191,11 @@ impl Context {
|
|||||||
/// # Arguments
|
/// # Arguments
|
||||||
/// - `name`: The name of the command to unregister.
|
/// - `name`: The name of the command to unregister.
|
||||||
pub async fn unregister_command(&self, name: &str) {
|
pub async fn unregister_command(&self, name: &str) {
|
||||||
{
|
self.server.command_dispatcher.rcu(|dispatcher| {
|
||||||
let mut dispatcher_lock = self.server.command_dispatcher.write().await;
|
let mut new_dispatcher = (**dispatcher).clone();
|
||||||
dispatcher_lock.fallback_dispatcher.unregister(name);
|
new_dispatcher.fallback_dispatcher.unregister(name);
|
||||||
};
|
Arc::new(new_dispatcher)
|
||||||
|
});
|
||||||
|
|
||||||
self.reload_commands_for_everyone().await;
|
self.reload_commands_for_everyone().await;
|
||||||
}
|
}
|
||||||
@ -212,7 +214,7 @@ impl Context {
|
|||||||
/// # Arguments
|
/// # Arguments
|
||||||
/// - `player`: The player for which the commands will be reloaded.
|
/// - `player`: The player for which the commands will be reloaded.
|
||||||
pub async fn reload_commands_for(&self, player: &Arc<Player>) {
|
pub async fn reload_commands_for(&self, player: &Arc<Player>) {
|
||||||
let command_dispatcher = self.server.command_dispatcher.read().await;
|
let command_dispatcher = self.server.command_dispatcher.load();
|
||||||
if let ClientPlatform::Bedrock(_) = player.client.as_ref() {
|
if let ClientPlatform::Bedrock(_) = player.client.as_ref() {
|
||||||
client_suggestions::send_bedrock_commands_packet(
|
client_suggestions::send_bedrock_commands_packet(
|
||||||
player,
|
player,
|
||||||
|
|||||||
@ -1349,7 +1349,7 @@ 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 level = from_wit_permission_level(level);
|
let level = from_wit_permission_level(level);
|
||||||
let command_dispatcher = server.command_dispatcher.read().await;
|
let command_dispatcher = server.command_dispatcher.load();
|
||||||
player
|
player
|
||||||
.set_permission_lvl(server, level, &command_dispatcher)
|
.set_permission_lvl(server, level, &command_dispatcher)
|
||||||
.await;
|
.await;
|
||||||
|
|||||||
@ -348,7 +348,7 @@ impl pumpkin::plugin::server::HostServer for PluginHostState {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let dispatcher = server.command_dispatcher.read().await;
|
let dispatcher = server.command_dispatcher.load();
|
||||||
dispatcher
|
dispatcher
|
||||||
.handle_command(&native_sender.into_source(server).await, &command)
|
.handle_command(&native_sender.into_source(server).await, &command)
|
||||||
.await;
|
.await;
|
||||||
@ -700,7 +700,7 @@ impl pumpkin::plugin::server::HostOpManager for PluginHostState {
|
|||||||
drop(config);
|
drop(config);
|
||||||
|
|
||||||
if let Some(player) = server.get_player_by_uuid(uuid) {
|
if let Some(player) = server.get_player_by_uuid(uuid) {
|
||||||
let command_dispatcher = server.command_dispatcher.read().await;
|
let command_dispatcher = server.command_dispatcher.load();
|
||||||
player
|
player
|
||||||
.set_permission_lvl(server, internal_level, &command_dispatcher)
|
.set_permission_lvl(server, internal_level, &command_dispatcher)
|
||||||
.await;
|
.await;
|
||||||
@ -727,7 +727,7 @@ impl pumpkin::plugin::server::HostOpManager for PluginHostState {
|
|||||||
drop(config);
|
drop(config);
|
||||||
|
|
||||||
if let Some(player) = server.get_player_by_uuid(uuid) {
|
if let Some(player) = server.get_player_by_uuid(uuid) {
|
||||||
let command_dispatcher = server.command_dispatcher.read().await;
|
let command_dispatcher = server.command_dispatcher.load();
|
||||||
player
|
player
|
||||||
.set_permission_lvl(
|
.set_permission_lvl(
|
||||||
server,
|
server,
|
||||||
|
|||||||
@ -94,7 +94,7 @@ pub struct Server {
|
|||||||
/// Saves server branding information.
|
/// Saves server branding information.
|
||||||
branding: CachedBranding,
|
branding: CachedBranding,
|
||||||
/// Saves and dispatches commands to appropriate handlers.
|
/// Saves and dispatches commands to appropriate handlers.
|
||||||
pub command_dispatcher: RwLock<CommandDispatcher>,
|
pub command_dispatcher: ArcSwap<CommandDispatcher>,
|
||||||
/// Block behaviour.
|
/// Block behaviour.
|
||||||
pub block_registry: Arc<BlockRegistry>,
|
pub block_registry: Arc<BlockRegistry>,
|
||||||
/// Item behaviour.
|
/// Item behaviour.
|
||||||
@ -156,7 +156,7 @@ impl Server {
|
|||||||
) -> Arc<Self> {
|
) -> Arc<Self> {
|
||||||
let permission_registry = Arc::new(RwLock::new(PermissionRegistry::new()));
|
let permission_registry = Arc::new(RwLock::new(PermissionRegistry::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 = RwLock::new(
|
let command_dispatcher = ArcSwap::from_pointee(
|
||||||
default_dispatcher(
|
default_dispatcher(
|
||||||
&permission_registry,
|
&permission_registry,
|
||||||
&basic_config,
|
&basic_config,
|
||||||
|
|||||||
@ -2714,7 +2714,7 @@ impl World {
|
|||||||
player.send_abilities_update().await;
|
player.send_abilities_update().await;
|
||||||
|
|
||||||
{
|
{
|
||||||
let command_dispatcher = server.command_dispatcher.read().await;
|
let command_dispatcher = server.command_dispatcher.load();
|
||||||
client_suggestions::send_bedrock_commands_packet(&player, server, &command_dispatcher)
|
client_suggestions::send_bedrock_commands_packet(&player, server, &command_dispatcher)
|
||||||
.await;
|
.await;
|
||||||
};
|
};
|
||||||
@ -3092,7 +3092,7 @@ impl World {
|
|||||||
// Difficulty of the world
|
// Difficulty of the world
|
||||||
player.send_difficulty_update().await;
|
player.send_difficulty_update().await;
|
||||||
{
|
{
|
||||||
let command_dispatcher = server.command_dispatcher.read().await;
|
let command_dispatcher = server.command_dispatcher.load();
|
||||||
|
|
||||||
client_suggestions::send_c_commands_packet(player, server, &command_dispatcher).await;
|
client_suggestions::send_c_commands_packet(player, server, &command_dispatcher).await;
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user