144 lines
8.1 KiB
Diff
144 lines
8.1 KiB
Diff
From 68df71e092d912014c7cde5c7504e016474fdb65 Mon Sep 17 00:00:00 2001
|
|
From: Aikar <aikar@aikar.co>
|
|
Date: Sun, 26 Nov 2017 13:19:58 -0500
|
|
Subject: [PATCH] AsyncTabCompleteEvent
|
|
|
|
Let plugins be able to control tab completion of commands and chat async.
|
|
|
|
This will be useful for frameworks like ACF so we can define async safe completion handlers,
|
|
and avoid going to main for tab completions.
|
|
|
|
Especially useful if you need to query a database in order to obtain the results for tab
|
|
completion, such as offline players.
|
|
|
|
Also adds isCommand and getLocation to the sync TabCompleteEvent
|
|
|
|
diff --git a/src/main/java/net/minecraft/server/PlayerConnection.java b/src/main/java/net/minecraft/server/PlayerConnection.java
|
|
index d0ab87d0f..ca054afcf 100644
|
|
--- a/src/main/java/net/minecraft/server/PlayerConnection.java
|
|
+++ b/src/main/java/net/minecraft/server/PlayerConnection.java
|
|
@@ -2276,24 +2276,51 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
|
|
// CraftBukkit end
|
|
}
|
|
|
|
- public void a(PacketPlayInTabComplete packetplayintabcomplete) {
|
|
- PlayerConnectionUtils.ensureMainThread(packetplayintabcomplete, this, this.player.x());
|
|
+ // Paper start - async tab completion
|
|
+ public void a(PacketPlayInTabComplete packet) {
|
|
// CraftBukkit start
|
|
if (chatSpamField.addAndGet(this, 10) > 500 && !this.minecraftServer.getPlayerList().isOp(this.player.getProfile())) {
|
|
- this.disconnect(new ChatMessage("disconnect.spam", new Object[0]));
|
|
+ minecraftServer.postToMainThread(() -> this.disconnect(new ChatMessage("disconnect.spam", new Object[0])));
|
|
return;
|
|
}
|
|
// CraftBukkit end
|
|
- ArrayList arraylist = Lists.newArrayList();
|
|
- Iterator iterator = this.minecraftServer.tabCompleteCommand(this.player, packetplayintabcomplete.a(), packetplayintabcomplete.b(), packetplayintabcomplete.c()).iterator();
|
|
|
|
- while (iterator.hasNext()) {
|
|
- String s = (String) iterator.next();
|
|
+ com.destroystokyo.paper.event.server.AsyncTabCompleteEvent event;
|
|
+ java.util.List<String> completions = new ArrayList<>();
|
|
+ BlockPosition blockpos = packet.b();
|
|
+ String buffer = packet.a();
|
|
+ boolean isCommand = buffer.startsWith("/") || packet.c();
|
|
+ event = new com.destroystokyo.paper.event.server.AsyncTabCompleteEvent(this.getPlayer(), completions,
|
|
+ buffer, isCommand, blockpos != null ? MCUtil.toLocation(player.world, blockpos) : null);
|
|
+ event.callEvent();
|
|
+ completions = event.isCancelled() ? com.google.common.collect.ImmutableList.of() : event.getCompletions();
|
|
+ if (event.isCancelled() || event.isHandled()) {
|
|
+ // Still fire sync event with the provided completions, if someone is listening
|
|
+ if (!event.isCancelled() && org.bukkit.event.server.TabCompleteEvent.getHandlerList().getRegisteredListeners().length > 0) {
|
|
+ java.util.List<String> finalCompletions = completions;
|
|
+ Waitable<java.util.List<String>> syncCompletions = new Waitable<java.util.List<String>>() {
|
|
+ @Override
|
|
+ protected java.util.List<String> evaluate() {
|
|
+ org.bukkit.event.server.TabCompleteEvent syncEvent = new org.bukkit.event.server.TabCompleteEvent(PlayerConnection.this.getPlayer(), buffer, finalCompletions, isCommand, blockpos != null ? MCUtil.toLocation(player.world, blockpos) : null);
|
|
+ return syncEvent.callEvent() ? syncEvent.getCompletions() : com.google.common.collect.ImmutableList.of();
|
|
+ }
|
|
+ };
|
|
+ server.getServer().processQueue.add(syncCompletions);
|
|
+ try {
|
|
+ completions = syncCompletions.get();
|
|
+ } catch (InterruptedException | ExecutionException e1) {
|
|
+ e1.printStackTrace();
|
|
+ }
|
|
+ }
|
|
|
|
- arraylist.add(s);
|
|
+ this.player.playerConnection.sendPacket(new PacketPlayOutTabComplete(completions.toArray(new String[completions.size()])));
|
|
+ return;
|
|
}
|
|
-
|
|
- this.player.playerConnection.sendPacket(new PacketPlayOutTabComplete((String[]) arraylist.toArray(new String[arraylist.size()])));
|
|
+ minecraftServer.postToMainThread(() -> {
|
|
+ java.util.List<String> syncCompletions = this.minecraftServer.tabCompleteCommand(this.player, buffer, blockpos, isCommand);
|
|
+ this.player.playerConnection.sendPacket(new PacketPlayOutTabComplete(syncCompletions.toArray(new String[syncCompletions.size()])));
|
|
+ });
|
|
+ // Paper end
|
|
}
|
|
|
|
public void a(PacketPlayInSettings packetplayinsettings) {
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
|
index f0ae65f08..f01408d55 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
|
@@ -1643,8 +1643,8 @@ public final class CraftServer implements Server {
|
|
} else {
|
|
offers = tabCompleteChat(player, message);
|
|
}
|
|
-
|
|
- TabCompleteEvent tabEvent = new TabCompleteEvent(player, message, offers);
|
|
+
|
|
+ TabCompleteEvent tabEvent = new TabCompleteEvent(player, message, offers, message.startsWith("/") || forceCommand, pos != null ? MCUtil.toLocation(((CraftWorld) player.getWorld()).getHandle(), pos) : null); // Paper
|
|
getPluginManager().callEvent(tabEvent);
|
|
|
|
return tabEvent.isCancelled() ? Collections.EMPTY_LIST : tabEvent.getCompletions();
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/command/ConsoleCommandCompleter.java b/src/main/java/org/bukkit/craftbukkit/command/ConsoleCommandCompleter.java
|
|
index 1e3aae3b8..95d13c146 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/command/ConsoleCommandCompleter.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/command/ConsoleCommandCompleter.java
|
|
@@ -28,6 +28,39 @@ public class ConsoleCommandCompleter implements Completer {
|
|
public void complete(LineReader reader, ParsedLine line, List<Candidate> candidates) {
|
|
final CraftServer server = this.server.server;
|
|
final String buffer = line.line();
|
|
+ // Async Tab Complete
|
|
+ com.destroystokyo.paper.event.server.AsyncTabCompleteEvent event;
|
|
+ java.util.List<String> completions = new java.util.ArrayList<>();
|
|
+ event = new com.destroystokyo.paper.event.server.AsyncTabCompleteEvent(server.getConsoleSender(), completions,
|
|
+ buffer, true, null);
|
|
+ event.callEvent();
|
|
+ completions = event.isCancelled() ? com.google.common.collect.ImmutableList.of() : event.getCompletions();
|
|
+
|
|
+ if (event.isCancelled() || event.isHandled()) {
|
|
+ // Still fire sync event with the provided completions, if someone is listening
|
|
+ if (!event.isCancelled() && TabCompleteEvent.getHandlerList().getRegisteredListeners().length > 0) {
|
|
+ List<String> finalCompletions = completions;
|
|
+ Waitable<List<String>> syncCompletions = new Waitable<List<String>>() {
|
|
+ @Override
|
|
+ protected List<String> evaluate() {
|
|
+ org.bukkit.event.server.TabCompleteEvent syncEvent = new org.bukkit.event.server.TabCompleteEvent(server.getConsoleSender(), buffer, finalCompletions);
|
|
+ return syncEvent.callEvent() ? syncEvent.getCompletions() : com.google.common.collect.ImmutableList.of();
|
|
+ }
|
|
+ };
|
|
+ server.getServer().processQueue.add(syncCompletions);
|
|
+ try {
|
|
+ completions = syncCompletions.get();
|
|
+ } catch (InterruptedException | ExecutionException e1) {
|
|
+ e1.printStackTrace();
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (!completions.isEmpty()) {
|
|
+ candidates.addAll(completions.stream().map(Candidate::new).collect(java.util.stream.Collectors.toList()));
|
|
+ }
|
|
+ return;
|
|
+ }
|
|
+
|
|
// Paper end
|
|
Waitable<List<String>> waitable = new Waitable<List<String>>() {
|
|
@Override
|
|
--
|
|
2.16.1
|
|
|