mirror of
https://github.com/PaperMC/Paper.git
synced 2024-10-30 07:19:58 +01:00
147 lines
9.1 KiB
Diff
147 lines
9.1 KiB
Diff
From 3e175150c0df5d545748e4fd74dd2c0c884a5e9b 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 619f020ba4..5778bf1c65 100644
|
|
--- a/src/main/java/net/minecraft/server/PlayerConnection.java
|
|
+++ b/src/main/java/net/minecraft/server/PlayerConnection.java
|
|
@@ -504,18 +504,60 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
|
|
}
|
|
|
|
public void a(PacketPlayInTabComplete packetplayintabcomplete) {
|
|
- PlayerConnectionUtils.ensureMainThread(packetplayintabcomplete, this, this.player.getWorldServer());
|
|
+ // PlayerConnectionUtils.ensureMainThread(packetplayintabcomplete, this, this.player.getWorldServer()); // Paper - run this async
|
|
// CraftBukkit start
|
|
if (chatSpamField.addAndGet(this, 2) > 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]))); // Paper
|
|
return;
|
|
}
|
|
- // CraftBukkit end
|
|
- ParseResults parseresults = this.minecraftServer.getCommandDispatcher().a().parse(packetplayintabcomplete.c(), this.player.getCommandListener());
|
|
+ // Paper start
|
|
+ com.destroystokyo.paper.event.server.AsyncTabCompleteEvent event;
|
|
+ java.util.List<String> completions = new java.util.ArrayList<>();
|
|
+ String buffer = packetplayintabcomplete.c();
|
|
+ event = new com.destroystokyo.paper.event.server.AsyncTabCompleteEvent(this.getPlayer(), 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() && 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, true, 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();
|
|
+ }
|
|
+ }
|
|
+ java.util.List<String> otherSuggestions = completions;
|
|
+ minecraftServer.postToMainThread(() -> sendSuggestions(packetplayintabcomplete, otherSuggestions));
|
|
+ return;
|
|
+ }
|
|
+ minecraftServer.postToMainThread(() -> sendSuggestions(packetplayintabcomplete, null));
|
|
+ }
|
|
+ public void sendSuggestions(PacketPlayInTabComplete packetplayintabcomplete, java.util.List<String> otherSuggestions) {
|
|
|
|
- this.minecraftServer.getCommandDispatcher().a().getCompletionSuggestions(parseresults).thenAccept((suggestions) -> {
|
|
- if (((Suggestions) suggestions).isEmpty()) return; // CraftBukkit - don't send through empty suggestions - prevents [<args>] from showing for plugins with nothing more to offer
|
|
- this.networkManager.sendPacket(new PacketPlayOutTabComplete(packetplayintabcomplete.b(), (Suggestions) suggestions)); // CraftBukkit - decompile error
|
|
+ ParseResults parseresults = this.minecraftServer.getCommandDispatcher().a().parse(packetplayintabcomplete.c(), this.player.getCommandListener());
|
|
+ //noinspection unchecked
|
|
+ java.util.concurrent.CompletableFuture<Suggestions> completionSuggestions = this.minecraftServer.getCommandDispatcher().a().getCompletionSuggestions(parseresults);
|
|
+ completionSuggestions.thenAccept((Suggestions suggestions) -> {
|
|
+ if (otherSuggestions != null && !otherSuggestions.isEmpty()) {
|
|
+ com.mojang.brigadier.suggestion.SuggestionsBuilder builder = new com.mojang.brigadier.suggestion.SuggestionsBuilder(packetplayintabcomplete.c(), packetplayintabcomplete.b());
|
|
+ otherSuggestions.forEach(builder::suggest);
|
|
+ suggestions.getList().addAll(builder.build().getList());
|
|
+ }
|
|
+ // Paper end
|
|
+ if (suggestions.isEmpty()) return; // CraftBukkit - don't send through empty suggestions - prevents [<args>] from showing for plugins with nothing more to offer
|
|
+ this.networkManager.sendPacket(new PacketPlayOutTabComplete(packetplayintabcomplete.b(), suggestions)); // CraftBukkit - decompile error
|
|
});
|
|
}
|
|
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
|
index 202c6c4927..c74831b3d7 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
|
@@ -1667,7 +1667,7 @@ public final class CraftServer implements Server {
|
|
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(), new BlockPosition(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 1e3aae3b8f..95d13c146b 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.18.0
|
|
|