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

View File

@ -1,11 +1,14 @@
package org.patchbukkit.entity;
import java.lang.ref.WeakReference;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.time.Duration;
import java.time.Instant;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@ -83,6 +86,8 @@ public class PatchBukkitPlayer
extends PatchBukkitHumanEntity
implements Player {
private final Map<UUID, Set<WeakReference<Plugin>>> invertedVisibilityEntities = new HashMap<>();
public PatchBukkitPlayer(UUID uuid, String name) {
super(uuid, name);
}
@ -1213,26 +1218,42 @@ public class PatchBukkitPlayer
@Override
public boolean canSee(Player player) {
// TODO Auto-generated method stub
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'");
return this.canSee((org.bukkit.entity.Entity) player);
}
@Override
public boolean canSee(Entity entity) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'canSee'");
return this.equals(entity) || entity.isVisibleByDefault() ^ this.invertedVisibilityEntities.containsKey(entity.getUniqueId());
}
@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

View File

@ -1,7 +1,10 @@
use std::borrow::Cow;
use pumpkin::{
command::{
CommandExecutor, CommandSender,
args::{Arg, ArgumentConsumer, GetClientSideArgParser, SuggestResult},
tree::{CommandTree, builder::argument},
},
entity::EntityBase,
};
@ -12,6 +15,8 @@ use tokio::sync::{mpsc, oneshot};
use crate::java::jvm::commands::{JvmCommand, Location, Rotation};
const ARG_ANY: &str = "any";
pub struct JavaCommandExecutor {
pub cmd_name: String,
pub command_tx: mpsc::Sender<JvmCommand>,
@ -25,7 +30,6 @@ pub enum SimpleCommandSender {
}
pub struct AnyCommandNode {
command_name: String,
command_tx: mpsc::Sender<JvmCommand>,
}
@ -86,7 +90,6 @@ impl ArgumentConsumer for AnyCommandNode {
None
};
let command_name = self.command_name.clone();
let command_sender: SimpleCommandSender = sender.into();
Box::pin(async move {
@ -94,15 +97,15 @@ impl ArgumentConsumer for AnyCommandNode {
self.command_tx
.send(JvmCommand::GetCommandTabComplete {
command_sender,
cmd_name: command_name,
full_command: input.to_string(),
respond_to: tx,
args: input.split(' ').map(|arg| arg.to_string()).collect(),
location,
})
.await
.unwrap();
rx.await.unwrap()
let suggestion = rx.await.unwrap();
suggestion
})
}
}
@ -125,16 +128,20 @@ impl CommandExecutor for JavaCommandExecutor {
&'a self,
sender: &'a pumpkin::command::CommandSender,
_server: &'a pumpkin::server::Server,
_args: &'a pumpkin::command::args::ConsumedArgs<'a>,
args: &'a pumpkin::command::args::ConsumedArgs<'a>,
) -> pumpkin::command::CommandResult<'a> {
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();
self.command_tx
.send(JvmCommand::TriggerCommand {
cmd_name: self.cmd_name.clone(),
full_command: full_command,
respond_to: tx,
command_sender: sender.into(),
args: vec![self.cmd_name.clone()],
})
.await
.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 {
fn handle_blocking<'a>(
&self,
server: &Arc<Server>,
_server: &Arc<Server>,
event: &'a mut PlayerJoinEvent,
) -> BoxFuture<'a, ()> {
let command_tx = self.command_tx.clone();
Box::pin(async move {
let event: &mut PlayerJoinEvent = event;
let (tx, rx) = oneshot::channel();
let (tx, _rx) = oneshot::channel();
let sent_event = event.clone();
command_tx
.send(JvmCommand::TriggerEvent {

View File

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

View File

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

View File

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