mirror of
https://github.com/PaperMC/Paper.git
synced 2024-12-28 07:20:24 +01:00
a207d14a0e
Signed-off-by: Mariell Hoversholm <proximyst@proximyst.com>
130 lines
8.1 KiB
Diff
130 lines
8.1 KiB
Diff
From 0000000000000000000000000000000000000000 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/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
|
index 525728268f56470fdc24c4fd2f19d66943447778..8d0c44b6c2c99d5161c5d4b79209b79ff6db75e4 100644
|
|
--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
|
+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
|
@@ -711,10 +711,10 @@ public class ServerGamePacketListenerImpl implements ServerGamePacketListener {
|
|
|
|
@Override
|
|
public void handleCustomCommandSuggestions(ServerboundCommandSuggestionPacket packet) {
|
|
- PacketUtils.ensureRunningOnSameThread(packet, this, this.player.getLevel());
|
|
+ // PlayerConnectionUtils.ensureMainThread(packetplayintabcomplete, this, this.player.getWorldServer()); // Paper - run this async
|
|
// CraftBukkit start
|
|
if (chatSpamField.addAndGet(this, 1) > 500 && !this.server.getPlayerList().isOp(this.player.getGameProfile())) {
|
|
- this.disconnect(new TranslatableComponent("disconnect.spam", new Object[0]));
|
|
+ server.scheduleOnMain(() -> this.disconnect(new TranslatableComponent("disconnect.spam", new Object[0]))); // Paper
|
|
return;
|
|
}
|
|
// CraftBukkit end
|
|
@@ -724,12 +724,35 @@ public class ServerGamePacketListenerImpl implements ServerGamePacketListener {
|
|
stringreader.skip();
|
|
}
|
|
|
|
- ParseResults<CommandSourceStack> parseresults = this.server.getCommands().getDispatcher().parse(stringreader, this.player.createCommandSourceStack());
|
|
+ // Paper start - async tab completion
|
|
+ com.destroystokyo.paper.event.server.AsyncTabCompleteEvent event;
|
|
+ java.util.List<String> completions = new java.util.ArrayList<>();
|
|
+ String buffer = packet.getCommand();
|
|
+ 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 the event isn't handled, we can assume that we have no completions, and so we'll ask the server
|
|
+ if (!event.isHandled()) {
|
|
+ if (!event.isCancelled()) {
|
|
|
|
- this.server.getCommands().getDispatcher().getCompletionSuggestions(parseresults).thenAccept((suggestions) -> {
|
|
- if (suggestions.isEmpty()) return; // CraftBukkit - don't send through empty suggestions - prevents [<args>] from showing for plugins with nothing more to offer
|
|
- this.connection.send(new ClientboundCommandSuggestionsPacket(packet.getId(), suggestions));
|
|
- });
|
|
+ this.server.scheduleOnMain(() -> { // Paper - This needs to be on main
|
|
+ ParseResults<CommandSourceStack> parseresults = this.server.getCommands().getDispatcher().parse(stringreader, this.player.createCommandSourceStack());
|
|
+
|
|
+ this.server.getCommands().getDispatcher().getCompletionSuggestions(parseresults).thenAccept((suggestions) -> {
|
|
+ if (suggestions.isEmpty()) return; // CraftBukkit - don't send through empty suggestions - prevents [<args>] from showing for plugins with nothing more to offer
|
|
+ this.connection.send(new ClientboundCommandSuggestionsPacket(packet.getId(), suggestions));
|
|
+ });
|
|
+ });
|
|
+ }
|
|
+ } else if (!completions.isEmpty()) {
|
|
+ com.mojang.brigadier.suggestion.SuggestionsBuilder builder = new com.mojang.brigadier.suggestion.SuggestionsBuilder(packet.getCommand(), stringreader.getTotalLength());
|
|
+
|
|
+ builder = builder.createOffset(builder.getInput().lastIndexOf(' ') + 1);
|
|
+ completions.forEach(builder::suggest);
|
|
+ player.connection.send(new ClientboundCommandSuggestionsPacket(packet.getId(), builder.buildFuture().join()));
|
|
+ }
|
|
+ // Paper end - async tab completion
|
|
}
|
|
|
|
@Override
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
|
index df68599520189e2699c8521d6c6ab7235612af33..10addb128a357e7719854bf4f9d75f5def32b27d 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
|
@@ -1850,7 +1850,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 ? net.minecraft.server.MCUtil.toLocation(((CraftWorld) player.getWorld()).getHandle(), new BlockPos(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 a957695457cf3252848ce6ef37069692841b8e28..c5e00bd9e2790992202aadf8eec2002fc88c78f1 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
|