Refactor command code

This commit is contained in:
Bjorn Beishline 2026-02-02 03:25:17 -07:00
parent d2ee270e82
commit 196acf8865
13 changed files with 572 additions and 243 deletions

View File

@ -4,7 +4,8 @@ import java.util.Set;
import java.util.UUID; import java.util.UUID;
import org.bukkit.Server; 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.Permission;
import org.bukkit.permissions.PermissionAttachment; import org.bukkit.permissions.PermissionAttachment;
import org.bukkit.permissions.PermissionAttachmentInfo; import org.bukkit.permissions.PermissionAttachmentInfo;
@ -14,7 +15,12 @@ import org.jetbrains.annotations.Nullable;
import net.kyori.adventure.text.Component; 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 @Override
public boolean isPermissionSet(@NotNull String name) { public boolean isPermissionSet(@NotNull String name) {

View File

@ -44,6 +44,9 @@ public class PatchBukkitWorld
return instances.computeIfAbsent(uuid, PatchBukkitWorld::new); return instances.computeIfAbsent(uuid, PatchBukkitWorld::new);
} }
public static PatchBukkitWorld getOrCreate(String uuid) {
return getOrCreate(UUID.fromString(uuid));
}
@Override @Override
public boolean isVoidDamageEnabled() { public boolean isVoidDamageEnabled() {

144
rust/src/commands/mod.rs Normal file
View File

@ -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<JvmCommand>,
}
#[derive(Clone)]
pub enum SimpleCommandSender {
Console,
/// UUID
Player(String),
}
pub struct AnyCommandNode {
command_name: String,
command_tx: mpsc::Sender<JvmCommand>,
}
impl GetClientSideArgParser for AnyCommandNode {
fn get_client_side_parser(&self) -> ArgumentType<'_> {
ArgumentType::String(StringProtoArgBehavior::GreedyPhrase)
}
fn get_client_side_suggestion_type_override(&self) -> Option<SuggestionProviders> {
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<SimpleCommandSender> 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(())
})
}
}

View File

@ -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<JvmCommand>,
pub command: Arc<Mutex<Instance>>,
}
#[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(())
})
}
}

View File

@ -1,14 +1,12 @@
use std::{ use std::{path::PathBuf, sync::Arc};
path::PathBuf,
sync::{Arc, Mutex},
};
use anyhow::Result; use anyhow::Result;
use j4rs::Instance; use pumpkin::{command::dispatcher::CommandError, plugin::Context};
use pumpkin::plugin::Context; use pumpkin_protocol::java::client::play::CommandSuggestion;
use tokio::sync::{mpsc, oneshot}; 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 { pub enum LoadPluginResult {
SuccessfullyLoadedSpigot, SuccessfullyLoadedSpigot,
@ -50,7 +48,45 @@ pub enum JvmCommand {
TriggerCommand { TriggerCommand {
cmd_name: String, cmd_name: String,
command_sender: SimpleCommandSender, command_sender: SimpleCommandSender,
respond_to: oneshot::Sender<Result<Event>>, respond_to: oneshot::Sender<Result<()>>,
command: Arc<Mutex<Instance>>, args: Vec<String>,
},
GetCommandTabComplete {
command_sender: SimpleCommandSender,
cmd_name: String,
respond_to: oneshot::Sender<Result<Option<Vec<CommandSuggestion>>, CommandError>>,
args: Vec<String>,
location: Option<Location>,
}, },
} }
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<Rotation>,
}
impl Location {
pub fn new(world: Uuid, x: f64, y: f64, z: f64, rotation: Option<Rotation>) -> Self {
Location {
world,
x,
y,
z,
rotation,
}
}
}

View File

@ -1,3 +1,2 @@
pub mod command_executor;
pub mod commands; pub mod commands;
pub mod worker; pub mod worker;

View File

@ -10,14 +10,17 @@ use crate::{
jar::read_configs_from_jar, jar::read_configs_from_jar,
jvm::commands::{JvmCommand, LoadPluginResult}, jvm::commands::{JvmCommand, LoadPluginResult},
native_callbacks::{init_callback_context, initialize_callbacks}, 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 { pub struct JvmWorker {
command_rx: mpsc::Receiver<JvmCommand>, command_rx: mpsc::Receiver<JvmCommand>,
pub plugin_manager: PluginManager, pub plugin_manager: PluginManager,
pub event_manager: EventManager, pub event_manager: EventManager,
pub command_manager: CommandManager,
jvm: Option<j4rs::Jvm>, jvm: Option<j4rs::Jvm>,
context: Option<Arc<Context>>, context: Option<Arc<Context>>,
} }
@ -28,6 +31,7 @@ impl JvmWorker {
command_rx, command_rx,
plugin_manager: PluginManager::new(), plugin_manager: PluginManager::new(),
event_manager: EventManager::new(), event_manager: EventManager::new(),
command_manager: CommandManager::new(),
jvm: None, jvm: None,
context: None, context: None,
} }
@ -99,7 +103,12 @@ impl JvmWorker {
let _ = respond_to.send( let _ = respond_to.send(
self.plugin_manager self.plugin_manager
.instantiate_all_plugins(jvm, &server, command_tx) .instantiate_all_plugins(
jvm,
&server,
command_tx,
&mut self.command_manager,
)
.await, .await,
); );
} }
@ -123,7 +132,10 @@ impl JvmWorker {
let _ = respond_to.send(self.plugin_manager.unload_all_plugins()); let _ = respond_to.send(self.plugin_manager.unload_all_plugins());
break; break;
} }
JvmCommand::TriggerEvent { event, respond_to } => { JvmCommand::TriggerEvent {
event,
respond_to: _,
} => {
let jvm = match self.jvm { let jvm = match self.jvm {
Some(ref jvm) => jvm, Some(ref jvm) => jvm,
None => &Jvm::attach_thread().unwrap(), None => &Jvm::attach_thread().unwrap(),
@ -221,21 +233,40 @@ impl JvmWorker {
cmd_name, cmd_name,
command_sender, command_sender,
respond_to, respond_to,
command, args,
} => { } => {
let jvm = match self.jvm { let jvm = match self.jvm {
Some(ref jvm) => jvm, Some(ref jvm) => jvm,
None => &Jvm::attach_thread().unwrap(), None => &Jvm::attach_thread().unwrap(),
}; };
self.plugin_manager
.trigger_command( let result =
jvm, self.command_manager
&cmd_name, .trigger_command(jvm, &cmd_name, command_sender, args);
command,
command_sender, let _ = respond_to.send(result);
vec![cmd_name.clone()], }
) JvmCommand::GetCommandTabComplete {
.unwrap(); 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);
} }
} }
} }

View File

@ -1,4 +1,5 @@
pub mod jar; pub mod jar;
pub mod jvm; pub mod jvm;
pub mod native_callbacks; pub mod native_callbacks;
pub mod plugin;
pub mod resources; pub mod resources;

View File

@ -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<Instance>,
}
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<String>,
location: Option<Location>,
) -> Result<Option<Vec<CommandSuggestion>>, 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<String>,
location: Option<Location>,
) -> Result<Option<Vec<CommandSuggestion>>> {
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<String> = 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<Context>,
plugin: &Plugin,
cmd_name: String,
cmd_data: &config::spigot::Command,
command_tx: mpsc::Sender<JvmCommand>,
) -> 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<String> = 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<String>,
) -> 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<Instance> {
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)])?)
}
}
}
}

View File

@ -1,16 +1,12 @@
use std::{ use std::{
collections::HashMap, collections::HashMap,
path::{Path, PathBuf}, path::{Path, PathBuf},
sync::{Arc, Mutex}, sync::Arc,
}; };
use anyhow::Result; use anyhow::Result;
use j4rs::{Instance, InvocationArg, Jvm}; use j4rs::{Instance, InvocationArg, Jvm};
use pumpkin::{ use pumpkin::plugin::Context;
command::tree::{CommandTree, builder::literal},
plugin::Context,
};
use pumpkin_util::permission::{Permission, PermissionDefault};
use tokio::sync::mpsc; use tokio::sync::mpsc;
use crate::{ use crate::{
@ -18,10 +14,7 @@ use crate::{
paper::PaperPluginYml, paper::PaperPluginYml,
spigot::{Command, SpigotPluginYml}, spigot::{Command, SpigotPluginYml},
}, },
java::jvm::{ java::{jvm::commands::JvmCommand, plugin::command_manager::CommandManager},
command_executor::{JavaCommandExecutor, SimpleCommandSender},
commands::JvmCommand,
},
}; };
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
@ -219,84 +212,13 @@ impl PluginManager {
Ok(()) Ok(())
} }
pub fn trigger_command(
&self,
jvm: &Jvm,
cmd_name: &str,
_command: Arc<Mutex<Instance>>,
sender: SimpleCommandSender,
_args: Vec<String>,
) -> 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( pub async fn instantiate_all_plugins(
&mut self, &mut self,
jvm: &Jvm, jvm: &Jvm,
server: &Arc<Context>, server: &Arc<Context>,
command_tx: mpsc::Sender<JvmCommand>, command_tx: mpsc::Sender<JvmCommand>,
command_manager: &mut CommandManager,
) -> Result<()> { ) -> Result<()> {
let server_instance =
jvm.invoke_static("org.bukkit.Bukkit", "getServer", InvocationArg::empty())?;
for (_plugin_name, plugin) in &mut self.plugins { for (_plugin_name, plugin) in &mut self.plugins {
let plugin_instance = jvm.invoke_static( let plugin_instance = jvm.invoke_static(
"org.patchbukkit.loader.PatchBukkitPluginLoader", "org.patchbukkit.loader.PatchBukkitPluginLoader",
@ -308,88 +230,29 @@ impl PluginManager {
)?; )?;
plugin.instance = Some(plugin_instance); 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 { for (cmd_name, cmd_data) in &plugin.commands {
let cloned_plugin_instance = jvm.clone_instance(instance_ref)?; match command_manager
let j_plugin_arg = InvocationArg::from(cloned_plugin_instance); .register_command(
&jvm,
let j_plugin_cmd = Arc::new(Mutex::new(jvm.invoke_static( server,
"org.patchbukkit.command.CommandFactory", &plugin,
"create", cmd_name.clone(),
&[InvocationArg::try_from(cmd_name)?, j_plugin_arg], cmd_data,
)?)); command_tx.clone(),
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<String> = 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 .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; plugin.state = PluginState::Loaded;

View File

@ -1,2 +1,3 @@
pub mod command_manager;
pub mod event_manager; pub mod event_manager;
pub mod manager; pub mod manager;

View File

@ -3,11 +3,11 @@ use std::sync::Arc;
use pumpkin::plugin::Context; use pumpkin::plugin::Context;
use pumpkin_api_macros::{plugin_impl, plugin_method}; use pumpkin_api_macros::{plugin_impl, plugin_method};
pub mod commands;
pub mod config; pub mod config;
pub mod directories; pub mod directories;
pub mod events; pub mod events;
pub mod java; pub mod java;
pub mod plugin;
use directories::setup_directories; use directories::setup_directories;
use java::{ use java::{