From 196acf8865db368023cccee8964f50e7537d1202 Mon Sep 17 00:00:00 2001 From: Bjorn Beishline <75190918+BjornTheProgrammer@users.noreply.github.com> Date: Mon, 2 Feb 2026 03:25:17 -0700 Subject: [PATCH] Refactor command code --- ...ava => PatchBukkitBlockCommandSender.java} | 12 +- .../patchbukkit/world/PatchBukkitWorld.java | 3 + rust/src/commands/mod.rs | 144 +++++++++ rust/src/java/jvm/command_executor.rs | 56 ---- rust/src/java/jvm/commands.rs | 54 +++- rust/src/java/jvm/mod.rs | 1 - rust/src/java/jvm/worker.rs | 57 +++- rust/src/java/mod.rs | 1 + rust/src/java/plugin/command_manager/mod.rs | 301 ++++++++++++++++++ rust/src/{ => java}/plugin/event_manager.rs | 0 rust/src/{ => java}/plugin/manager.rs | 183 ++--------- rust/src/{ => java}/plugin/mod.rs | 1 + rust/src/lib.rs | 2 +- 13 files changed, 572 insertions(+), 243 deletions(-) rename java/patchbukkit/src/main/java/org/patchbukkit/command/{PatchBukkitCommandSender.java => PatchBukkitBlockCommandSender.java} (93%) create mode 100644 rust/src/commands/mod.rs delete mode 100644 rust/src/java/jvm/command_executor.rs create mode 100644 rust/src/java/plugin/command_manager/mod.rs rename rust/src/{ => java}/plugin/event_manager.rs (100%) rename rust/src/{ => java}/plugin/manager.rs (54%) rename rust/src/{ => java}/plugin/mod.rs (61%) diff --git a/java/patchbukkit/src/main/java/org/patchbukkit/command/PatchBukkitCommandSender.java b/java/patchbukkit/src/main/java/org/patchbukkit/command/PatchBukkitBlockCommandSender.java similarity index 93% rename from java/patchbukkit/src/main/java/org/patchbukkit/command/PatchBukkitCommandSender.java rename to java/patchbukkit/src/main/java/org/patchbukkit/command/PatchBukkitBlockCommandSender.java index 4cc9a26..ccd92c2 100644 --- a/java/patchbukkit/src/main/java/org/patchbukkit/command/PatchBukkitCommandSender.java +++ b/java/patchbukkit/src/main/java/org/patchbukkit/command/PatchBukkitBlockCommandSender.java @@ -4,7 +4,8 @@ import java.util.Set; import java.util.UUID; import org.bukkit.Server; -import org.bukkit.command.CommandSender; +import org.bukkit.block.Block; +import org.bukkit.command.BlockCommandSender; import org.bukkit.permissions.Permission; import org.bukkit.permissions.PermissionAttachment; import org.bukkit.permissions.PermissionAttachmentInfo; @@ -14,7 +15,12 @@ import org.jetbrains.annotations.Nullable; import net.kyori.adventure.text.Component; -public class PatchBukkitCommandSender implements CommandSender { +public class PatchBukkitBlockCommandSender implements BlockCommandSender { + @NotNull + public Block getBlock() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'getBlock'"); + } @Override public boolean isPermissionSet(@NotNull String name) { @@ -141,5 +147,5 @@ public class PatchBukkitCommandSender implements CommandSender { // TODO Auto-generated method stub throw new UnsupportedOperationException("Unimplemented method 'name'"); } - + } diff --git a/java/patchbukkit/src/main/java/org/patchbukkit/world/PatchBukkitWorld.java b/java/patchbukkit/src/main/java/org/patchbukkit/world/PatchBukkitWorld.java index 0fcf55f..8a27e19 100644 --- a/java/patchbukkit/src/main/java/org/patchbukkit/world/PatchBukkitWorld.java +++ b/java/patchbukkit/src/main/java/org/patchbukkit/world/PatchBukkitWorld.java @@ -44,6 +44,9 @@ public class PatchBukkitWorld return instances.computeIfAbsent(uuid, PatchBukkitWorld::new); } + public static PatchBukkitWorld getOrCreate(String uuid) { + return getOrCreate(UUID.fromString(uuid)); + } @Override public boolean isVoidDamageEnabled() { diff --git a/rust/src/commands/mod.rs b/rust/src/commands/mod.rs new file mode 100644 index 0000000..a99af9e --- /dev/null +++ b/rust/src/commands/mod.rs @@ -0,0 +1,144 @@ +use pumpkin::{ + command::{ + CommandExecutor, CommandSender, + args::{Arg, ArgumentConsumer, GetClientSideArgParser, SuggestResult}, + }, + entity::EntityBase, +}; +use pumpkin_protocol::java::client::play::{ + ArgumentType, StringProtoArgBehavior, SuggestionProviders, +}; +use tokio::sync::{mpsc, oneshot}; + +use crate::java::jvm::commands::{JvmCommand, Location, Rotation}; + +pub struct JavaCommandExecutor { + pub cmd_name: String, + pub command_tx: mpsc::Sender, +} + +#[derive(Clone)] +pub enum SimpleCommandSender { + Console, + /// UUID + Player(String), +} + +pub struct AnyCommandNode { + command_name: String, + command_tx: mpsc::Sender, +} + +impl GetClientSideArgParser for AnyCommandNode { + fn get_client_side_parser(&self) -> ArgumentType<'_> { + ArgumentType::String(StringProtoArgBehavior::GreedyPhrase) + } + + fn get_client_side_suggestion_type_override(&self) -> Option { + Some(SuggestionProviders::AskServer) + } +} + +impl ArgumentConsumer for AnyCommandNode { + fn consume<'a>( + &'a self, + _sender: &'a pumpkin::command::CommandSender, + _server: &'a pumpkin::server::Server, + args: &mut pumpkin::command::tree::RawArgs<'a>, + ) -> pumpkin::command::args::ConsumeResult<'a> { + let first_word_opt = args.pop(); + + let mut msg = match first_word_opt { + Some(word) => word.to_string(), + None => return Box::pin(async { None }), + }; + + while let Some(word) = args.pop() { + msg.push(' '); + msg.push_str(word); + } + + Box::pin(async move { Some(Arg::Msg(msg)) }) + } + + fn suggest<'a>( + &'a self, + sender: &pumpkin::command::CommandSender, + _server: &'a pumpkin::server::Server, + input: &'a str, + ) -> SuggestResult<'a> { + let location = if let Some(position) = sender.position() + && let Some(world) = sender.world() + { + let rotation = if let Some(player) = sender.as_player() { + let entity = player.get_entity(); + let yaw = entity.yaw.load(); + let pitch = entity.pitch.load(); + Some(Rotation::new(yaw, pitch)) + } else { + None + }; + + Some(Location::new( + world.uuid, position.x, position.y, position.z, rotation, + )) + } else { + None + }; + + let command_name = self.command_name.clone(); + let command_sender: SimpleCommandSender = sender.into(); + + Box::pin(async move { + let (tx, rx) = oneshot::channel(); + self.command_tx + .send(JvmCommand::GetCommandTabComplete { + command_sender, + cmd_name: command_name, + respond_to: tx, + args: input.split(' ').map(|arg| arg.to_string()).collect(), + location, + }) + .await + .unwrap(); + + rx.await.unwrap() + }) + } +} + +impl Into for &CommandSender { + fn into(self) -> SimpleCommandSender { + match self { + CommandSender::Rcon(_mutex) => todo!(), + CommandSender::Console => SimpleCommandSender::Console, + CommandSender::Player(player) => { + SimpleCommandSender::Player(player.gameprofile.id.to_string()) + } + CommandSender::CommandBlock(_block_entity, _world) => todo!(), + } + } +} + +impl CommandExecutor for JavaCommandExecutor { + fn execute<'a>( + &'a self, + sender: &'a pumpkin::command::CommandSender, + _server: &'a pumpkin::server::Server, + _args: &'a pumpkin::command::args::ConsumedArgs<'a>, + ) -> pumpkin::command::CommandResult<'a> { + Box::pin(async move { + let (tx, _rx) = oneshot::channel(); + self.command_tx + .send(JvmCommand::TriggerCommand { + cmd_name: self.cmd_name.clone(), + respond_to: tx, + command_sender: sender.into(), + args: vec![self.cmd_name.clone()], + }) + .await + .unwrap(); + Ok(()) + }) + } +} diff --git a/rust/src/java/jvm/command_executor.rs b/rust/src/java/jvm/command_executor.rs deleted file mode 100644 index 9bee99a..0000000 --- a/rust/src/java/jvm/command_executor.rs +++ /dev/null @@ -1,56 +0,0 @@ -use std::sync::{Arc, Mutex}; - -use j4rs::Instance; -use pumpkin::command::CommandExecutor; -use tokio::sync::{mpsc, oneshot}; - -use crate::java::jvm::commands::JvmCommand; - -pub struct JavaCommandExecutor { - pub cmd_name: String, - pub command_tx: mpsc::Sender, - pub command: Arc>, -} - -#[derive(Clone)] -pub enum SimpleCommandSender { - Console, - // UUID - Player(String), -} - -impl CommandExecutor for JavaCommandExecutor { - fn execute<'a>( - &'a self, - sender: &'a pumpkin::command::CommandSender, - _server: &'a pumpkin::server::Server, - _args: &'a pumpkin::command::args::ConsumedArgs<'a>, - ) -> pumpkin::command::CommandResult<'a> { - Box::pin(async move { - let (tx, _rx) = oneshot::channel(); - let sender = match sender { - pumpkin::command::CommandSender::Rcon(_mutex) => todo!(), - pumpkin::command::CommandSender::Console => SimpleCommandSender::Console, - pumpkin::command::CommandSender::Player(player) => { - SimpleCommandSender::Player(player.gameprofile.id.to_string()) - } - pumpkin::command::CommandSender::CommandBlock(_block_entity, _world) => todo!(), - }; - self.command_tx - .send(JvmCommand::TriggerCommand { - cmd_name: self.cmd_name.clone(), - respond_to: tx, - command_sender: sender.clone(), - command: self.command.clone(), - }) - .await - .unwrap(); - - // match rx.await.unwrap().unwrap() { - // Event::PlayerJoinEvent(player_join_event) => *event = player_join_event, - // _ => unreachable!(), - // }; - Ok(()) - }) - } -} diff --git a/rust/src/java/jvm/commands.rs b/rust/src/java/jvm/commands.rs index b5a1a21..7a5363c 100644 --- a/rust/src/java/jvm/commands.rs +++ b/rust/src/java/jvm/commands.rs @@ -1,14 +1,12 @@ -use std::{ - path::PathBuf, - sync::{Arc, Mutex}, -}; +use std::{path::PathBuf, sync::Arc}; use anyhow::Result; -use j4rs::Instance; -use pumpkin::plugin::Context; +use pumpkin::{command::dispatcher::CommandError, plugin::Context}; +use pumpkin_protocol::java::client::play::CommandSuggestion; use tokio::sync::{mpsc, oneshot}; +use uuid::Uuid; -use crate::{events::Event, java::jvm::command_executor::SimpleCommandSender}; +use crate::{commands::SimpleCommandSender, events::Event}; pub enum LoadPluginResult { SuccessfullyLoadedSpigot, @@ -50,7 +48,45 @@ pub enum JvmCommand { TriggerCommand { cmd_name: String, command_sender: SimpleCommandSender, - respond_to: oneshot::Sender>, - command: Arc>, + respond_to: oneshot::Sender>, + args: Vec, + }, + GetCommandTabComplete { + command_sender: SimpleCommandSender, + cmd_name: String, + respond_to: oneshot::Sender>, CommandError>>, + args: Vec, + location: Option, }, } + +pub struct Rotation { + pub yaw: f32, + pub pitch: f32, +} + +impl Rotation { + pub fn new(yaw: f32, pitch: f32) -> Self { + Rotation { yaw, pitch } + } +} + +pub struct Location { + pub world: Uuid, + pub x: f64, + pub y: f64, + pub z: f64, + pub rotation: Option, +} + +impl Location { + pub fn new(world: Uuid, x: f64, y: f64, z: f64, rotation: Option) -> Self { + Location { + world, + x, + y, + z, + rotation, + } + } +} diff --git a/rust/src/java/jvm/mod.rs b/rust/src/java/jvm/mod.rs index 2b0d9e1..1cdb224 100644 --- a/rust/src/java/jvm/mod.rs +++ b/rust/src/java/jvm/mod.rs @@ -1,3 +1,2 @@ -pub mod command_executor; pub mod commands; pub mod worker; diff --git a/rust/src/java/jvm/worker.rs b/rust/src/java/jvm/worker.rs index 8943d2a..760f504 100644 --- a/rust/src/java/jvm/worker.rs +++ b/rust/src/java/jvm/worker.rs @@ -10,14 +10,17 @@ use crate::{ jar::read_configs_from_jar, jvm::commands::{JvmCommand, LoadPluginResult}, native_callbacks::{init_callback_context, initialize_callbacks}, + plugin::{ + command_manager::CommandManager, event_manager::EventManager, manager::PluginManager, + }, }, - plugin::{event_manager::EventManager, manager::PluginManager}, }; pub struct JvmWorker { command_rx: mpsc::Receiver, pub plugin_manager: PluginManager, pub event_manager: EventManager, + pub command_manager: CommandManager, jvm: Option, context: Option>, } @@ -28,6 +31,7 @@ impl JvmWorker { command_rx, plugin_manager: PluginManager::new(), event_manager: EventManager::new(), + command_manager: CommandManager::new(), jvm: None, context: None, } @@ -99,7 +103,12 @@ impl JvmWorker { let _ = respond_to.send( self.plugin_manager - .instantiate_all_plugins(jvm, &server, command_tx) + .instantiate_all_plugins( + jvm, + &server, + command_tx, + &mut self.command_manager, + ) .await, ); } @@ -123,7 +132,10 @@ impl JvmWorker { let _ = respond_to.send(self.plugin_manager.unload_all_plugins()); break; } - JvmCommand::TriggerEvent { event, respond_to } => { + JvmCommand::TriggerEvent { + event, + respond_to: _, + } => { let jvm = match self.jvm { Some(ref jvm) => jvm, None => &Jvm::attach_thread().unwrap(), @@ -221,21 +233,40 @@ impl JvmWorker { cmd_name, command_sender, respond_to, - command, + args, } => { let jvm = match self.jvm { Some(ref jvm) => jvm, None => &Jvm::attach_thread().unwrap(), }; - self.plugin_manager - .trigger_command( - jvm, - &cmd_name, - command, - command_sender, - vec![cmd_name.clone()], - ) - .unwrap(); + + let result = + self.command_manager + .trigger_command(jvm, &cmd_name, command_sender, args); + + let _ = respond_to.send(result); + } + JvmCommand::GetCommandTabComplete { + command_sender, + cmd_name, + respond_to, + args, + location, + } => { + let jvm = match self.jvm { + Some(ref jvm) => jvm, + None => &Jvm::attach_thread().unwrap(), + }; + + let result = self.command_manager.get_tab_complete( + jvm, + command_sender, + &cmd_name, + args, + location, + ); + + let _ = respond_to.send(result); } } } diff --git a/rust/src/java/mod.rs b/rust/src/java/mod.rs index f0d8b02..d1ad7bb 100644 --- a/rust/src/java/mod.rs +++ b/rust/src/java/mod.rs @@ -1,4 +1,5 @@ pub mod jar; pub mod jvm; pub mod native_callbacks; +pub mod plugin; pub mod resources; diff --git a/rust/src/java/plugin/command_manager/mod.rs b/rust/src/java/plugin/command_manager/mod.rs new file mode 100644 index 0000000..61620f5 --- /dev/null +++ b/rust/src/java/plugin/command_manager/mod.rs @@ -0,0 +1,301 @@ +use std::sync::{Arc, Mutex}; + +use anyhow::Result; +use j4rs::{Instance, InvocationArg, JavaClass, Jvm}; +use pumpkin::{ + command::{ + dispatcher::CommandError, + tree::{CommandTree, builder::literal}, + }, + plugin::Context, +}; +use pumpkin_protocol::java::client::play::CommandSuggestion; +use pumpkin_util::permission::{Permission, PermissionDefault}; +use tokio::sync::mpsc; + +use crate::{ + commands::{JavaCommandExecutor, SimpleCommandSender}, + config, + java::{ + jvm::commands::{JvmCommand, Location}, + plugin::manager::Plugin, + }, +}; + +pub struct CommandManager { + command_map: Option, +} + +impl CommandManager { + pub fn new() -> Self { + Self { command_map: None } + } + + pub fn init(&mut self, jvm: &Jvm) -> Result<()> { + let server_instance = + jvm.invoke_static("org.bukkit.Bukkit", "getServer", InvocationArg::empty())?; + + let command_map = jvm.invoke(&server_instance, "getCommandMap", InvocationArg::empty())?; + + self.command_map = Some(command_map); + + Ok(()) + } + + pub fn get_tab_complete( + &mut self, + jvm: &Jvm, + sender: SimpleCommandSender, + cmd_name: &str, + args: Vec, + location: Option, + ) -> Result>, CommandError> { + match self.try_tab_complete(jvm, sender, cmd_name, args, location) { + Ok(suggestions) => Ok(suggestions), + Err(e) => { + log::warn!("Tab completion failed: {e}"); + Ok(None) + } + } + } + + fn try_tab_complete( + &mut self, + jvm: &Jvm, + sender: SimpleCommandSender, + cmd_name: &str, + args: Vec, + location: Option, + ) -> Result>> { + let command_map = match self.command_map { + Some(ref command_map) => command_map, + None => match self.init(jvm) { + Ok(_) => self.command_map.as_ref().unwrap(), + Err(_) => return Ok(None), + }, + }; + + let sender = Self::sender_to_jsender(jvm, sender)?; + + let completions = if let Some(location) = location { + let world = jvm.invoke_static( + "org.patchbukkit.world.PatchBukkitWorld", + "getOrCreate", + &[InvocationArg::try_from(location.world.to_string())?], + )?; + + let location = match location.rotation { + Some(rotation) => jvm.create_instance( + "org.bukkit.Location", + &[ + InvocationArg::try_from(world)?, + InvocationArg::try_from(location.x)?.into_primitive()?, + InvocationArg::try_from(location.y)?.into_primitive()?, + InvocationArg::try_from(location.z)?.into_primitive()?, + InvocationArg::try_from(rotation.yaw)?.into_primitive()?, + InvocationArg::try_from(rotation.pitch)?.into_primitive()?, + ], + )?, + None => jvm.create_instance( + "org.bukkit.Location", + &[ + InvocationArg::try_from(world)?, + InvocationArg::try_from(location.x)?.into_primitive()?, + InvocationArg::try_from(location.y)?.into_primitive()?, + InvocationArg::try_from(location.z)?.into_primitive()?, + ], + )?, + }; + + jvm.invoke( + command_map, + "tabComplete", + &[ + InvocationArg::try_from(sender)?, + InvocationArg::try_from(cmd_name)?, + InvocationArg::try_from(jvm.java_list(JavaClass::String, args)?)?, + InvocationArg::try_from(location)?, + ], + )? + } else { + jvm.invoke( + command_map, + "tabComplete", + &[ + InvocationArg::try_from(sender)?, + InvocationArg::try_from(cmd_name)?, + InvocationArg::try_from(jvm.java_list(JavaClass::String, args)?)?, + ], + )? + }; + + let completions: Vec = jvm.to_rust(completions)?; + + Ok(Some( + completions + .into_iter() + .map(|completion| CommandSuggestion::new(completion, None)) + .collect(), + )) + } + + pub async fn register_command( + &mut self, + jvm: &Jvm, + context: &Arc, + plugin: &Plugin, + cmd_name: String, + cmd_data: &config::spigot::Command, + command_tx: mpsc::Sender, + ) -> Result<()> { + let command_map = match self.command_map { + Some(ref command_map) => command_map, + None => match self.init(jvm) { + Ok(_) => self.command_map.as_ref().unwrap(), + Err(err) => return Err(err), + }, + }; + + let plugin_instance = + jvm.clone_instance(plugin.instance.as_ref().expect( + "This function should never be called with a plugin that has no instance", + ))?; + let j_plugin_arg = InvocationArg::from(plugin_instance); + + let j_plugin_cmd = Arc::new(Mutex::new(jvm.invoke_static( + "org.patchbukkit.command.CommandFactory", + "create", + &[InvocationArg::try_from(&cmd_name)?, j_plugin_arg], + )?)); + log::info!("Registering Bukkit command: {}", &cmd_name); + { + let cmd_lock = j_plugin_cmd.lock().unwrap(); + let j_plugin_cmd_owned = jvm.clone_instance(&*cmd_lock)?; + jvm.invoke( + command_map, + "register", + &[ + InvocationArg::try_from(&cmd_name)?, + InvocationArg::try_from(&plugin.name)?, + InvocationArg::from(j_plugin_cmd_owned), + ], + )?; + } + let j_sender = jvm + .invoke_static( + "org.bukkit.Bukkit", + "getConsoleSender", + InvocationArg::empty(), + ) + .map_err(|e| e.to_string()) + .unwrap(); + + // Make the tab working, at least the first thingy + let dispatch_result = jvm + .invoke( + command_map, + "tabComplete", + &[ + InvocationArg::from(j_sender), + InvocationArg::try_from(format!("{} ", &cmd_name)).unwrap(), + ], + ) + .unwrap(); + + let tab_list: Vec = jvm.to_rust(dispatch_result).unwrap(); + + let mut node = CommandTree::new( + [&cmd_name], + cmd_data.description.clone().unwrap_or_default(), + ) + .execute(JavaCommandExecutor { + cmd_name: cmd_name.clone(), + command_tx: command_tx.clone(), + }); + for tab in tab_list { + node = node.then(literal(tab).execute(JavaCommandExecutor { + cmd_name: cmd_name.clone(), + command_tx: command_tx.clone(), + })); + } + // TODO + // let permission = if let Some(perm) = cmd_data.permission.clone() { + // perm + // } else { + // format!("patchbukkit:{}", cmd_name) // TODO + // }; + let permission = format!("patchbukkit:{}", cmd_name); + + context + .register_permission(Permission::new( + &permission, + &permission, + PermissionDefault::Allow, + )) + .await + .unwrap(); + + context.register_command(node, permission).await; + + Ok(()) + } + + pub fn trigger_command( + &mut self, + jvm: &Jvm, + cmd_name: &str, + sender: SimpleCommandSender, + _args: Vec, + ) -> Result<()> { + let command_map = match self.command_map { + Some(ref command_map) => command_map, + None => match self.init(jvm) { + Ok(_) => self.command_map.as_ref().unwrap(), + Err(err) => return Err(err), + }, + }; + + let j_sender = Self::sender_to_jsender(jvm, sender)?; + + let dispatch_result = jvm.invoke( + command_map, + "dispatch", + &[ + InvocationArg::from(j_sender), + InvocationArg::try_from(cmd_name)?, + ], + )?; + + let handled: bool = jvm.to_rust(dispatch_result)?; + + if !handled { + //log::warn!("Command was not handled by any Java plugin: {}", cmd_name); + } + + Ok(()) + } + + pub fn sender_to_jsender(jvm: &Jvm, sender: SimpleCommandSender) -> Result { + match sender { + SimpleCommandSender::Console => Ok(jvm.invoke_static( + "org.bukkit.Bukkit", + "getConsoleSender", + InvocationArg::empty(), + )?), + + SimpleCommandSender::Player(uuid_str) => { + let server = + jvm.invoke_static("org.bukkit.Bukkit", "getServer", InvocationArg::empty())?; + let patch_server = jvm.cast(&server, "org.patchbukkit.PatchBukkitServer")?; + + let j_uuid = jvm.invoke_static( + "java.util.UUID", + "fromString", + &[InvocationArg::try_from(uuid_str)?], + )?; + + Ok(jvm.invoke(&patch_server, "getPlayer", &[InvocationArg::from(j_uuid)])?) + } + } + } +} diff --git a/rust/src/plugin/event_manager.rs b/rust/src/java/plugin/event_manager.rs similarity index 100% rename from rust/src/plugin/event_manager.rs rename to rust/src/java/plugin/event_manager.rs diff --git a/rust/src/plugin/manager.rs b/rust/src/java/plugin/manager.rs similarity index 54% rename from rust/src/plugin/manager.rs rename to rust/src/java/plugin/manager.rs index 2184124..5f0f9f7 100644 --- a/rust/src/plugin/manager.rs +++ b/rust/src/java/plugin/manager.rs @@ -1,16 +1,12 @@ use std::{ collections::HashMap, path::{Path, PathBuf}, - sync::{Arc, Mutex}, + sync::Arc, }; use anyhow::Result; use j4rs::{Instance, InvocationArg, Jvm}; -use pumpkin::{ - command::tree::{CommandTree, builder::literal}, - plugin::Context, -}; -use pumpkin_util::permission::{Permission, PermissionDefault}; +use pumpkin::plugin::Context; use tokio::sync::mpsc; use crate::{ @@ -18,10 +14,7 @@ use crate::{ paper::PaperPluginYml, spigot::{Command, SpigotPluginYml}, }, - java::jvm::{ - command_executor::{JavaCommandExecutor, SimpleCommandSender}, - commands::JvmCommand, - }, + java::{jvm::commands::JvmCommand, plugin::command_manager::CommandManager}, }; #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -219,84 +212,13 @@ impl PluginManager { Ok(()) } - pub fn trigger_command( - &self, - jvm: &Jvm, - cmd_name: &str, - _command: Arc>, - sender: SimpleCommandSender, - _args: Vec, - ) -> Result<(), String> { - let j_sender = match sender { - SimpleCommandSender::Console => jvm - .invoke_static( - "org.bukkit.Bukkit", - "getConsoleSender", - InvocationArg::empty(), - ) - .map_err(|e| e.to_string())?, - - SimpleCommandSender::Player(uuid_str) => { - // Get the server and cast it to your implementation - let server = jvm - .invoke_static("org.bukkit.Bukkit", "getServer", InvocationArg::empty()) - .map_err(|e| e.to_string())?; - let patch_server = jvm - .cast(&server, "org.patchbukkit.PatchBukkitServer") - .map_err(|e| e.to_string())?; - - // Create Java UUID object - let j_uuid = jvm - .invoke_static( - "java.util.UUID", - "fromString", - &[InvocationArg::try_from(uuid_str).map_err(|e| e.to_string())?], - ) - .map_err(|e| e.to_string())?; - - // Call your getPlayer(UUID) method on the server - jvm.invoke(&patch_server, "getPlayer", &[InvocationArg::from(j_uuid)]) - .map_err(|e| e.to_string())? - } - }; - - let server_instance = jvm - .invoke_static("org.bukkit.Bukkit", "getServer", InvocationArg::empty()) - .unwrap(); - - let command_map = jvm - .invoke(&server_instance, "getCommandMap", InvocationArg::empty()) - .unwrap(); - - let dispatch_result = jvm - .invoke( - &command_map, - "dispatch", - &[ - InvocationArg::from(j_sender), - InvocationArg::try_from(cmd_name).unwrap(), - ], - ) - .unwrap(); - - let handled: bool = jvm.to_rust(dispatch_result).unwrap(); - - if !handled { - //log::warn!("Command was not handled by any Java plugin: {}", cmd_name); - } - - Ok(()) - } - pub async fn instantiate_all_plugins( &mut self, jvm: &Jvm, server: &Arc, command_tx: mpsc::Sender, + command_manager: &mut CommandManager, ) -> Result<()> { - let server_instance = - jvm.invoke_static("org.bukkit.Bukkit", "getServer", InvocationArg::empty())?; - for (_plugin_name, plugin) in &mut self.plugins { let plugin_instance = jvm.invoke_static( "org.patchbukkit.loader.PatchBukkitPluginLoader", @@ -308,88 +230,29 @@ impl PluginManager { )?; plugin.instance = Some(plugin_instance); - let instance_ref = plugin.instance.as_ref().unwrap(); - - let command_map = - jvm.invoke(&server_instance, "getCommandMap", InvocationArg::empty())?; for (cmd_name, cmd_data) in &plugin.commands { - let cloned_plugin_instance = jvm.clone_instance(instance_ref)?; - let j_plugin_arg = InvocationArg::from(cloned_plugin_instance); - - let j_plugin_cmd = Arc::new(Mutex::new(jvm.invoke_static( - "org.patchbukkit.command.CommandFactory", - "create", - &[InvocationArg::try_from(cmd_name)?, j_plugin_arg], - )?)); - log::info!("Registering Bukkit command: {}", cmd_name); - { - let cmd_lock = j_plugin_cmd.lock().unwrap(); - let j_plugin_cmd_owned = jvm.clone_instance(&*cmd_lock)?; - jvm.invoke( - &command_map, - "register", - &[ - InvocationArg::try_from(cmd_name)?, - InvocationArg::try_from(&plugin.name)?, - InvocationArg::from(j_plugin_cmd_owned), - ], - )?; - } - let j_sender = jvm - .invoke_static( - "org.bukkit.Bukkit", - "getConsoleSender", - InvocationArg::empty(), + match command_manager + .register_command( + &jvm, + server, + &plugin, + cmd_name.clone(), + cmd_data, + command_tx.clone(), ) - .map_err(|e| e.to_string()) - .unwrap(); - - // Make the tab working, at least the first thingy - let dispatch_result = jvm - .invoke( - &command_map, - "tabComplete", - &[ - InvocationArg::from(j_sender), - InvocationArg::try_from(format!("{} ", cmd_name)).unwrap(), - ], - ) - .unwrap(); - - let tab_list: Vec = jvm.to_rust(dispatch_result).unwrap(); - - let mut node = - CommandTree::new([cmd_name], cmd_data.description.clone().unwrap_or_default()) - .execute(JavaCommandExecutor { - cmd_name: cmd_name.clone(), - command_tx: command_tx.clone(), - command: j_plugin_cmd.clone(), - }); - for tab in tab_list { - node = node.then(literal(tab).execute(JavaCommandExecutor { - cmd_name: cmd_name.clone(), - command_tx: command_tx.clone(), - command: j_plugin_cmd.clone(), - })); - } - // TODO - // let permission = if let Some(perm) = cmd_data.permission.clone() { - // perm - // } else { - // format!("patchbukkit:{}", cmd_name) // TODO - // }; - let permission = format!("patchbukkit:{}", cmd_name); - - server - .register_permission(Permission::new( - &permission, - &permission, - PermissionDefault::Allow, - )) .await - .unwrap(); - server.register_command(node, permission).await + { + Ok(_) => (), + Err(e) => { + log::error!( + "Failed to register command {} for plugin {}: {:?}", + cmd_name, + plugin.name, + e + ); + } + } } plugin.state = PluginState::Loaded; diff --git a/rust/src/plugin/mod.rs b/rust/src/java/plugin/mod.rs similarity index 61% rename from rust/src/plugin/mod.rs rename to rust/src/java/plugin/mod.rs index c6a7ad5..7f052d7 100644 --- a/rust/src/plugin/mod.rs +++ b/rust/src/java/plugin/mod.rs @@ -1,2 +1,3 @@ +pub mod command_manager; pub mod event_manager; pub mod manager; diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 3ff0d81..f05bbae 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -3,11 +3,11 @@ use std::sync::Arc; use pumpkin::plugin::Context; use pumpkin_api_macros::{plugin_impl, plugin_method}; +pub mod commands; pub mod config; pub mod directories; pub mod events; pub mod java; -pub mod plugin; use directories::setup_directories; use java::{