Get suggestions working

This commit is contained in:
Bjorn Beishline 2026-02-02 04:35:02 -07:00
parent 196acf8865
commit 288c45537c
7 changed files with 109 additions and 98 deletions

View File

@ -53,6 +53,7 @@ public class PatchBukkitEntity implements Entity {
protected final UUID uuid; protected final UUID uuid;
private final String name; private final String name;
private static PermissibleBase perm; private static PermissibleBase perm;
private boolean visibleByDefault = true;
public PatchBukkitEntity( public PatchBukkitEntity(
UUID uuid, UUID uuid,
@ -511,8 +512,7 @@ public class PatchBukkitEntity implements Entity {
@Override @Override
public @NotNull Server getServer() { public @NotNull Server getServer() {
// TODO Auto-generated method stub return Bukkit.getServer();
throw new UnsupportedOperationException("Unimplemented method 'getServer'");
} }
@Override @Override
@ -678,14 +678,12 @@ public class PatchBukkitEntity implements Entity {
@Override @Override
public void setVisibleByDefault(boolean visible) { public void setVisibleByDefault(boolean visible) {
// TODO Auto-generated method stub this.visibleByDefault = visible;
throw new UnsupportedOperationException("Unimplemented method 'setVisibleByDefault'");
} }
@Override @Override
public boolean isVisibleByDefault() { public boolean isVisibleByDefault() {
// TODO Auto-generated method stub return this.visibleByDefault;
throw new UnsupportedOperationException("Unimplemented method 'isVisibleByDefault'");
} }
@Override @Override

View File

@ -1,11 +1,14 @@
package org.patchbukkit.entity; package org.patchbukkit.entity;
import java.lang.ref.WeakReference;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.time.Duration; import java.time.Duration;
import java.time.Instant; import java.time.Instant;
import java.util.Collection; import java.util.Collection;
import java.util.Date; import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
@ -83,6 +86,8 @@ public class PatchBukkitPlayer
extends PatchBukkitHumanEntity extends PatchBukkitHumanEntity
implements Player { implements Player {
private final Map<UUID, Set<WeakReference<Plugin>>> invertedVisibilityEntities = new HashMap<>();
public PatchBukkitPlayer(UUID uuid, String name) { public PatchBukkitPlayer(UUID uuid, String name) {
super(uuid, name); super(uuid, name);
} }
@ -1213,26 +1218,42 @@ public class PatchBukkitPlayer
@Override @Override
public boolean canSee(Player player) { public boolean canSee(Player player) {
// TODO Auto-generated method stub return this.canSee((org.bukkit.entity.Entity) player);
throw new UnsupportedOperationException("Unimplemented method 'canSee'");
}
@Override
public void hideEntity(Plugin plugin, Entity entity) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'hideEntity'");
}
@Override
public void showEntity(Plugin plugin, Entity entity) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'showEntity'");
} }
@Override @Override
public boolean canSee(Entity entity) { public boolean canSee(Entity entity) {
// TODO Auto-generated method stub return this.equals(entity) || entity.isVisibleByDefault() ^ this.invertedVisibilityEntities.containsKey(entity.getUniqueId());
throw new UnsupportedOperationException("Unimplemented method 'canSee'"); }
@Override
public void hideEntity(Plugin plugin, Entity entity) {
Preconditions.checkNotNull(plugin, "Plugin cannot be null");
Preconditions.checkNotNull(entity, "Entity cannot be null");
if (this.equals(entity)) return;
Set<WeakReference<Plugin>> plugins = invertedVisibilityEntities
.computeIfAbsent(entity.getUniqueId(), k -> new HashSet<>());
plugins.add(new WeakReference<>(plugin));
}
@Override
public void showEntity(Plugin plugin, Entity entity) {
Preconditions.checkNotNull(plugin, "Plugin cannot be null");
Preconditions.checkNotNull(entity, "Entity cannot be null");
if (this.equals(entity)) return;
Set<WeakReference<Plugin>> plugins = invertedVisibilityEntities.get(entity.getUniqueId());
if (plugins == null) return;
plugins.removeIf(ref -> {
Plugin p = ref.get();
return p == null || p.equals(plugin);
});
if (plugins.isEmpty()) {
invertedVisibilityEntities.remove(entity.getUniqueId());
}
} }
@Override @Override

View File

@ -1,7 +1,10 @@
use std::borrow::Cow;
use pumpkin::{ use pumpkin::{
command::{ command::{
CommandExecutor, CommandSender, CommandExecutor, CommandSender,
args::{Arg, ArgumentConsumer, GetClientSideArgParser, SuggestResult}, args::{Arg, ArgumentConsumer, GetClientSideArgParser, SuggestResult},
tree::{CommandTree, builder::argument},
}, },
entity::EntityBase, entity::EntityBase,
}; };
@ -12,6 +15,8 @@ use tokio::sync::{mpsc, oneshot};
use crate::java::jvm::commands::{JvmCommand, Location, Rotation}; use crate::java::jvm::commands::{JvmCommand, Location, Rotation};
const ARG_ANY: &str = "any";
pub struct JavaCommandExecutor { pub struct JavaCommandExecutor {
pub cmd_name: String, pub cmd_name: String,
pub command_tx: mpsc::Sender<JvmCommand>, pub command_tx: mpsc::Sender<JvmCommand>,
@ -25,7 +30,6 @@ pub enum SimpleCommandSender {
} }
pub struct AnyCommandNode { pub struct AnyCommandNode {
command_name: String,
command_tx: mpsc::Sender<JvmCommand>, command_tx: mpsc::Sender<JvmCommand>,
} }
@ -86,7 +90,6 @@ impl ArgumentConsumer for AnyCommandNode {
None None
}; };
let command_name = self.command_name.clone();
let command_sender: SimpleCommandSender = sender.into(); let command_sender: SimpleCommandSender = sender.into();
Box::pin(async move { Box::pin(async move {
@ -94,15 +97,15 @@ impl ArgumentConsumer for AnyCommandNode {
self.command_tx self.command_tx
.send(JvmCommand::GetCommandTabComplete { .send(JvmCommand::GetCommandTabComplete {
command_sender, command_sender,
cmd_name: command_name, full_command: input.to_string(),
respond_to: tx, respond_to: tx,
args: input.split(' ').map(|arg| arg.to_string()).collect(),
location, location,
}) })
.await .await
.unwrap(); .unwrap();
rx.await.unwrap() let suggestion = rx.await.unwrap();
suggestion
}) })
} }
} }
@ -125,16 +128,20 @@ impl CommandExecutor for JavaCommandExecutor {
&'a self, &'a self,
sender: &'a pumpkin::command::CommandSender, sender: &'a pumpkin::command::CommandSender,
_server: &'a pumpkin::server::Server, _server: &'a pumpkin::server::Server,
_args: &'a pumpkin::command::args::ConsumedArgs<'a>, args: &'a pumpkin::command::args::ConsumedArgs<'a>,
) -> pumpkin::command::CommandResult<'a> { ) -> pumpkin::command::CommandResult<'a> {
Box::pin(async move { Box::pin(async move {
let full_command = match args.get(ARG_ANY) {
Some(Arg::Msg(msg)) => format!("/{} {}", self.cmd_name, msg),
_ => format!("/{}", self.cmd_name),
};
let (tx, _rx) = oneshot::channel(); let (tx, _rx) = oneshot::channel();
self.command_tx self.command_tx
.send(JvmCommand::TriggerCommand { .send(JvmCommand::TriggerCommand {
cmd_name: self.cmd_name.clone(), full_command: full_command,
respond_to: tx, respond_to: tx,
command_sender: sender.into(), command_sender: sender.into(),
args: vec![self.cmd_name.clone()],
}) })
.await .await
.unwrap(); .unwrap();
@ -142,3 +149,29 @@ impl CommandExecutor for JavaCommandExecutor {
}) })
} }
} }
pub fn init_java_command(
cmd_name: impl Into<String>,
command_tx: mpsc::Sender<JvmCommand>,
names: impl IntoIterator<Item: Into<String>>,
description: impl Into<Cow<'static, str>>,
) -> CommandTree {
let cmd_name = cmd_name.into();
CommandTree::new(names, description)
.execute(JavaCommandExecutor {
cmd_name: cmd_name.clone(),
command_tx: command_tx.clone(),
})
.then(
argument(
ARG_ANY,
AnyCommandNode {
command_tx: command_tx.clone(),
},
)
.execute(JavaCommandExecutor {
cmd_name,
command_tx,
}),
)
}

View File

@ -17,14 +17,14 @@ pub struct PatchBukkitJoinHandler {
impl EventHandler<PlayerJoinEvent> for PatchBukkitJoinHandler { impl EventHandler<PlayerJoinEvent> for PatchBukkitJoinHandler {
fn handle_blocking<'a>( fn handle_blocking<'a>(
&self, &self,
server: &Arc<Server>, _server: &Arc<Server>,
event: &'a mut PlayerJoinEvent, event: &'a mut PlayerJoinEvent,
) -> BoxFuture<'a, ()> { ) -> BoxFuture<'a, ()> {
let command_tx = self.command_tx.clone(); let command_tx = self.command_tx.clone();
Box::pin(async move { Box::pin(async move {
let event: &mut PlayerJoinEvent = event; let event: &mut PlayerJoinEvent = event;
let (tx, rx) = oneshot::channel(); let (tx, _rx) = oneshot::channel();
let sent_event = event.clone(); let sent_event = event.clone();
command_tx command_tx
.send(JvmCommand::TriggerEvent { .send(JvmCommand::TriggerEvent {

View File

@ -46,16 +46,14 @@ pub enum JvmCommand {
respond_to: oneshot::Sender<Result<Event>>, respond_to: oneshot::Sender<Result<Event>>,
}, },
TriggerCommand { TriggerCommand {
cmd_name: String, full_command: String,
command_sender: SimpleCommandSender, command_sender: SimpleCommandSender,
respond_to: oneshot::Sender<Result<()>>, respond_to: oneshot::Sender<Result<()>>,
args: Vec<String>,
}, },
GetCommandTabComplete { GetCommandTabComplete {
command_sender: SimpleCommandSender, command_sender: SimpleCommandSender,
cmd_name: String, full_command: String,
respond_to: oneshot::Sender<Result<Option<Vec<CommandSuggestion>>, CommandError>>, respond_to: oneshot::Sender<Result<Option<Vec<CommandSuggestion>>, CommandError>>,
args: Vec<String>,
location: Option<Location>, location: Option<Location>,
}, },
} }

View File

@ -230,10 +230,9 @@ impl JvmWorker {
} }
} }
JvmCommand::TriggerCommand { JvmCommand::TriggerCommand {
cmd_name, full_command,
command_sender, command_sender,
respond_to, respond_to,
args,
} => { } => {
let jvm = match self.jvm { let jvm = match self.jvm {
Some(ref jvm) => jvm, Some(ref jvm) => jvm,
@ -242,15 +241,14 @@ impl JvmWorker {
let result = let result =
self.command_manager self.command_manager
.trigger_command(jvm, &cmd_name, command_sender, args); .trigger_command(jvm, full_command, command_sender);
let _ = respond_to.send(result); let _ = respond_to.send(result);
} }
JvmCommand::GetCommandTabComplete { JvmCommand::GetCommandTabComplete {
command_sender, command_sender,
cmd_name, full_command,
respond_to, respond_to,
args,
location, location,
} => { } => {
let jvm = match self.jvm { let jvm = match self.jvm {
@ -261,8 +259,7 @@ impl JvmWorker {
let result = self.command_manager.get_tab_complete( let result = self.command_manager.get_tab_complete(
jvm, jvm,
command_sender, command_sender,
&cmd_name, full_command,
args,
location, location,
); );

View File

@ -1,20 +1,14 @@
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use anyhow::Result; use anyhow::Result;
use j4rs::{Instance, InvocationArg, JavaClass, Jvm}; use j4rs::{Instance, InvocationArg, Jvm};
use pumpkin::{ use pumpkin::{command::dispatcher::CommandError, plugin::Context};
command::{
dispatcher::CommandError,
tree::{CommandTree, builder::literal},
},
plugin::Context,
};
use pumpkin_protocol::java::client::play::CommandSuggestion; use pumpkin_protocol::java::client::play::CommandSuggestion;
use pumpkin_util::permission::{Permission, PermissionDefault}; use pumpkin_util::permission::{Permission, PermissionDefault};
use tokio::sync::mpsc; use tokio::sync::mpsc;
use crate::{ use crate::{
commands::{JavaCommandExecutor, SimpleCommandSender}, commands::{SimpleCommandSender, init_java_command},
config, config,
java::{ java::{
jvm::commands::{JvmCommand, Location}, jvm::commands::{JvmCommand, Location},
@ -36,6 +30,10 @@ impl CommandManager {
jvm.invoke_static("org.bukkit.Bukkit", "getServer", InvocationArg::empty())?; jvm.invoke_static("org.bukkit.Bukkit", "getServer", InvocationArg::empty())?;
let command_map = jvm.invoke(&server_instance, "getCommandMap", InvocationArg::empty())?; let command_map = jvm.invoke(&server_instance, "getCommandMap", InvocationArg::empty())?;
let command_map = jvm.cast(
&command_map,
"org.patchbukkit.command.PatchBukkitCommandMap",
)?;
self.command_map = Some(command_map); self.command_map = Some(command_map);
@ -46,11 +44,10 @@ impl CommandManager {
&mut self, &mut self,
jvm: &Jvm, jvm: &Jvm,
sender: SimpleCommandSender, sender: SimpleCommandSender,
cmd_name: &str, full_command: String,
args: Vec<String>,
location: Option<Location>, location: Option<Location>,
) -> Result<Option<Vec<CommandSuggestion>>, CommandError> { ) -> Result<Option<Vec<CommandSuggestion>>, CommandError> {
match self.try_tab_complete(jvm, sender, cmd_name, args, location) { match self.try_tab_complete(jvm, sender, full_command, location) {
Ok(suggestions) => Ok(suggestions), Ok(suggestions) => Ok(suggestions),
Err(e) => { Err(e) => {
log::warn!("Tab completion failed: {e}"); log::warn!("Tab completion failed: {e}");
@ -63,8 +60,7 @@ impl CommandManager {
&mut self, &mut self,
jvm: &Jvm, jvm: &Jvm,
sender: SimpleCommandSender, sender: SimpleCommandSender,
cmd_name: &str, full_command: String,
args: Vec<String>,
location: Option<Location>, location: Option<Location>,
) -> Result<Option<Vec<CommandSuggestion>>> { ) -> Result<Option<Vec<CommandSuggestion>>> {
let command_map = match self.command_map { let command_map = match self.command_map {
@ -112,8 +108,7 @@ impl CommandManager {
"tabComplete", "tabComplete",
&[ &[
InvocationArg::try_from(sender)?, InvocationArg::try_from(sender)?,
InvocationArg::try_from(cmd_name)?, InvocationArg::try_from(full_command)?,
InvocationArg::try_from(jvm.java_list(JavaClass::String, args)?)?,
InvocationArg::try_from(location)?, InvocationArg::try_from(location)?,
], ],
)? )?
@ -123,8 +118,7 @@ impl CommandManager {
"tabComplete", "tabComplete",
&[ &[
InvocationArg::try_from(sender)?, InvocationArg::try_from(sender)?,
InvocationArg::try_from(cmd_name)?, InvocationArg::try_from(full_command)?,
InvocationArg::try_from(jvm.java_list(JavaClass::String, args)?)?,
], ],
)? )?
}; };
@ -181,43 +175,14 @@ impl CommandManager {
], ],
)?; )?;
} }
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 node = init_java_command(
let dispatch_result = jvm cmd_name.clone(),
.invoke( command_tx.clone(),
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_name],
cmd_data.description.clone().unwrap_or_default(), 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 // TODO
// let permission = if let Some(perm) = cmd_data.permission.clone() { // let permission = if let Some(perm) = cmd_data.permission.clone() {
// perm // perm
@ -243,9 +208,8 @@ impl CommandManager {
pub fn trigger_command( pub fn trigger_command(
&mut self, &mut self,
jvm: &Jvm, jvm: &Jvm,
cmd_name: &str, full_command: String,
sender: SimpleCommandSender, sender: SimpleCommandSender,
_args: Vec<String>,
) -> Result<()> { ) -> Result<()> {
let command_map = match self.command_map { let command_map = match self.command_map {
Some(ref command_map) => command_map, Some(ref command_map) => command_map,
@ -262,7 +226,7 @@ impl CommandManager {
"dispatch", "dispatch",
&[ &[
InvocationArg::from(j_sender), InvocationArg::from(j_sender),
InvocationArg::try_from(cmd_name)?, InvocationArg::try_from(full_command)?,
], ],
)?; )?;