From d54bcadd915c15d95c9be4c3291ddd5ad4826b77 Mon Sep 17 00:00:00 2001 From: CraftBukkit/Spigot Date: Sun, 7 Apr 2024 11:54:28 +1000 Subject: [PATCH] SPIGOT-6705: Re-print tab completion "Display all" message in console, when another message is being logged By: DerFrZocker --- .../server/dedicated/DedicatedServer.patch | 30 ++++++----- .../util/TerminalCompletionHandler.java | 53 +++++++++++++++++++ .../util/TerminalConsoleWriterThread.java | 15 ++++++ 3 files changed, 85 insertions(+), 13 deletions(-) create mode 100644 paper-server/src/main/java/org/bukkit/craftbukkit/util/TerminalCompletionHandler.java diff --git a/paper-server/nms-patches/net/minecraft/server/dedicated/DedicatedServer.patch b/paper-server/nms-patches/net/minecraft/server/dedicated/DedicatedServer.patch index 6f24e16e42..c54c205b8e 100644 --- a/paper-server/nms-patches/net/minecraft/server/dedicated/DedicatedServer.patch +++ b/paper-server/nms-patches/net/minecraft/server/dedicated/DedicatedServer.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/server/dedicated/DedicatedServer.java +++ b/net/minecraft/server/dedicated/DedicatedServer.java -@@ -53,6 +53,16 @@ +@@ -53,6 +53,18 @@ import net.minecraft.world.level.storage.Convertable; import org.slf4j.Logger; @@ -10,6 +10,8 @@ +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.io.IoBuilder; +import org.bukkit.command.CommandSender; ++import org.bukkit.craftbukkit.util.TerminalCompletionHandler; ++import org.bukkit.craftbukkit.util.TerminalConsoleWriterThread; +import org.bukkit.event.server.ServerCommandEvent; +import org.bukkit.event.server.RemoteServerCommandEvent; +// CraftBukkit end @@ -17,7 +19,7 @@ public class DedicatedServer extends MinecraftServer implements IMinecraftServer { static final Logger LOGGER = LogUtils.getLogger(); -@@ -61,7 +71,7 @@ +@@ -61,7 +73,7 @@ private final List consoleInput = Collections.synchronizedList(Lists.newArrayList()); @Nullable private RemoteStatusListener queryThreadGs4; @@ -26,7 +28,7 @@ @Nullable private RemoteControlListener rconThread; public DedicatedServerSettings settings; -@@ -70,10 +80,12 @@ +@@ -70,10 +82,12 @@ @Nullable private final TextFilter textFilterClient; @@ -42,7 +44,7 @@ this.textFilterClient = TextFilter.createFromConfig(dedicatedserversettings.getProperties().textFilteringConfig); } -@@ -81,13 +93,44 @@ +@@ -81,13 +95,44 @@ public boolean initServer() throws IOException { Thread thread = new Thread("Server console handler") { public void run() { @@ -90,7 +92,7 @@ } } catch (IOException ioexception) { DedicatedServer.LOGGER.error("Exception handling console input", ioexception); -@@ -96,6 +139,27 @@ +@@ -96,6 +141,29 @@ } }; @@ -109,7 +111,9 @@ + } + } + -+ new org.bukkit.craftbukkit.util.TerminalConsoleWriterThread(System.out, this.reader).start(); ++ TerminalConsoleWriterThread writerThread = new TerminalConsoleWriterThread(System.out, this.reader); ++ this.reader.setCompletionHandler(new TerminalCompletionHandler(writerThread, this.reader.getCompletionHandler())); ++ writerThread.start(); + + System.setOut(IoBuilder.forLogger(logger).setLevel(Level.INFO).buildPrintStream()); + System.setErr(IoBuilder.forLogger(logger).setLevel(Level.WARN).buildPrintStream()); @@ -118,7 +122,7 @@ thread.setDaemon(true); thread.setUncaughtExceptionHandler(new DefaultUncaughtExceptionHandler(DedicatedServer.LOGGER)); thread.start(); -@@ -120,7 +184,7 @@ +@@ -120,7 +188,7 @@ this.setMotd(dedicatedserverproperties.motd); super.setPlayerIdleTimeout((Integer) dedicatedserverproperties.playerIdleTimeout.get()); this.setEnforceWhitelist(dedicatedserverproperties.enforceWhitelist); @@ -127,7 +131,7 @@ DedicatedServer.LOGGER.info("Default game type: {}", dedicatedserverproperties.gamemode); InetAddress inetaddress = null; -@@ -144,6 +208,12 @@ +@@ -144,6 +212,12 @@ return false; } @@ -140,7 +144,7 @@ if (!this.usesAuthentication()) { DedicatedServer.LOGGER.warn("**** SERVER IS RUNNING IN OFFLINE/INSECURE MODE!"); DedicatedServer.LOGGER.warn("The server will make no attempt to authenticate usernames. Beware."); -@@ -158,19 +228,19 @@ +@@ -158,19 +232,19 @@ if (!NameReferencingFileConverter.serverReadyAfterUserconversion(this)) { return false; } else { @@ -163,7 +167,7 @@ } if (dedicatedserverproperties.enableQuery) { -@@ -296,6 +366,7 @@ +@@ -296,6 +370,7 @@ this.queryThreadGs4.stop(); } @@ -171,7 +175,7 @@ } @Override -@@ -317,7 +388,15 @@ +@@ -317,7 +392,15 @@ while (!this.consoleInput.isEmpty()) { ServerCommand servercommand = (ServerCommand) this.consoleInput.remove(0); @@ -188,7 +192,7 @@ } } -@@ -544,16 +623,52 @@ +@@ -544,16 +627,52 @@ @Override public String getPluginNames() { @@ -245,7 +249,7 @@ } public void storeUsingWhiteList(boolean flag) { -@@ -604,4 +719,15 @@ +@@ -604,4 +723,15 @@ public Optional getServerResourcePack() { return this.settings.getProperties().serverResourcePackInfo; } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/util/TerminalCompletionHandler.java b/paper-server/src/main/java/org/bukkit/craftbukkit/util/TerminalCompletionHandler.java new file mode 100644 index 0000000000..beaa54b562 --- /dev/null +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/util/TerminalCompletionHandler.java @@ -0,0 +1,53 @@ +package org.bukkit.craftbukkit.util; + +import java.io.IOException; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import jline.console.ConsoleReader; +import jline.console.completer.CompletionHandler; + +/** + * SPIGOT-6705: Make sure we print the display line again on tab completion, so that the user does not get stuck on it + * e.g. The user needs to press y / n to continue + */ +public class TerminalCompletionHandler implements CompletionHandler { + + private final TerminalConsoleWriterThread writerThread; + private final CompletionHandler delegate; + + public TerminalCompletionHandler(TerminalConsoleWriterThread writerThread, CompletionHandler delegate) { + this.writerThread = writerThread; + this.delegate = delegate; + } + + @Override + public boolean complete(ConsoleReader reader, List candidates, int position) throws IOException { + // First check normal list, so that we do not unnecessarily create a new HashSet if the not distinct list is already lower + if (candidates.size() <= reader.getAutoprintThreshold()) { + return delegate.complete(reader, candidates, position); + } + + Set distinct = new HashSet<>(candidates); + if (distinct.size() <= reader.getAutoprintThreshold()) { + return delegate.complete(reader, candidates, position); + } + + writerThread.setCompletion(distinct.size()); + + // FIXME: Potential concurrency issue, when terminal writer prints the display message before the delegate does it + // resulting in two display message being present, until a new message gets logged or the user presses y / n + // But the probability of this happening are probably lower than the effort needed to fix this + // And seeing the display message at all should be a higher priority than seeing it two times in rare cases. + boolean result = delegate.complete(reader, candidates, position); + + writerThread.setCompletion(-1); + // draw line to prevent concurrency issue, + // where terminal write would print the display message between delegate#complete finished and the completion set back to -1 + // Resulting in the display message being present even after pressing y / n + reader.drawLine(); + reader.flush(); + + return result; + } +} diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/util/TerminalConsoleWriterThread.java b/paper-server/src/main/java/org/bukkit/craftbukkit/util/TerminalConsoleWriterThread.java index 327a38ea67..3a4eb59486 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/util/TerminalConsoleWriterThread.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/util/TerminalConsoleWriterThread.java @@ -3,16 +3,21 @@ package org.bukkit.craftbukkit.util; import com.mojang.logging.LogQueues; import java.io.IOException; import java.io.OutputStream; +import java.util.Locale; +import java.util.ResourceBundle; import java.util.logging.Level; import java.util.logging.Logger; import jline.console.ConsoleReader; +import jline.console.completer.CandidateListCompletionHandler; import org.bukkit.craftbukkit.Main; import org.fusesource.jansi.Ansi; import org.fusesource.jansi.Ansi.Erase; public class TerminalConsoleWriterThread extends Thread { + private final ResourceBundle bundle = ResourceBundle.getBundle(CandidateListCompletionHandler.class.getName(), Locale.getDefault()); private final ConsoleReader reader; private final OutputStream output; + private volatile int completion = -1; public TerminalConsoleWriterThread(OutputStream output, ConsoleReader reader) { super("TerminalConsoleWriter"); @@ -45,6 +50,12 @@ public class TerminalConsoleWriterThread extends Thread { } catch (Throwable ex) { reader.getCursorBuffer().clear(); } + + if (completion > -1) { + // SPIGOT-6705: Make sure we print the display line again on tab completion, so that the user does not get stuck on it + reader.print(String.format(bundle.getString("DISPLAY_CANDIDATES"), completion)); + } + reader.flush(); } else { output.write(message.getBytes()); @@ -55,4 +66,8 @@ public class TerminalConsoleWriterThread extends Thread { } } } + + void setCompletion(int completion) { + this.completion = completion; + } }