aboutsummaryrefslogtreecommitdiffhomepage
path: root/patches/server/0601-Enhance-console-tab-completions-for-brigadier-comman.patch
diff options
context:
space:
mode:
Diffstat (limited to 'patches/server/0601-Enhance-console-tab-completions-for-brigadier-comman.patch')
-rw-r--r--patches/server/0601-Enhance-console-tab-completions-for-brigadier-comman.patch295
1 files changed, 295 insertions, 0 deletions
diff --git a/patches/server/0601-Enhance-console-tab-completions-for-brigadier-comman.patch b/patches/server/0601-Enhance-console-tab-completions-for-brigadier-comman.patch
new file mode 100644
index 0000000000..f20da4badc
--- /dev/null
+++ b/patches/server/0601-Enhance-console-tab-completions-for-brigadier-comman.patch
@@ -0,0 +1,295 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Jason Penilla <[email protected]>
+Date: Tue, 30 Mar 2021 16:06:08 -0700
+Subject: [PATCH] Enhance console tab completions for brigadier commands
+
+
+diff --git a/src/main/java/com/destroystokyo/paper/console/PaperConsole.java b/src/main/java/com/destroystokyo/paper/console/PaperConsole.java
+index a4070b59e261f0f1ac4beec47b11492f4724bf27..c5d5648f4ca603ef2b1df723b58f9caf4dd3c722 100644
+--- a/src/main/java/com/destroystokyo/paper/console/PaperConsole.java
++++ b/src/main/java/com/destroystokyo/paper/console/PaperConsole.java
+@@ -16,11 +16,15 @@ public final class PaperConsole extends SimpleTerminalConsole {
+
+ @Override
+ protected LineReader buildReader(LineReaderBuilder builder) {
+- return super.buildReader(builder
++ builder
+ .appName("Paper")
+ .variable(LineReader.HISTORY_FILE, java.nio.file.Paths.get(".console_history"))
+ .completer(new ConsoleCommandCompleter(this.server))
+- );
++ .option(LineReader.Option.COMPLETE_IN_WORD, true);
++ if (io.papermc.paper.configuration.GlobalConfiguration.get().console.enableBrigadierHighlighting) {
++ builder.highlighter(new io.papermc.paper.console.BrigadierCommandHighlighter(this.server));
++ }
++ return super.buildReader(builder);
+ }
+
+ @Override
+diff --git a/src/main/java/io/papermc/paper/console/BrigadierCommandCompleter.java b/src/main/java/io/papermc/paper/console/BrigadierCommandCompleter.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..0627c98cae0b5ebdd71a849ae1299d7d3d581850
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/console/BrigadierCommandCompleter.java
+@@ -0,0 +1,99 @@
++package io.papermc.paper.console;
++
++import com.destroystokyo.paper.event.server.AsyncTabCompleteEvent.Completion;
++import com.google.common.base.Suppliers;
++import com.mojang.brigadier.CommandDispatcher;
++import com.mojang.brigadier.ParseResults;
++import com.mojang.brigadier.StringReader;
++import com.mojang.brigadier.suggestion.Suggestion;
++import io.papermc.paper.adventure.PaperAdventure;
++import java.util.ArrayList;
++import java.util.Collections;
++import java.util.List;
++import java.util.function.Supplier;
++import net.minecraft.commands.CommandSourceStack;
++import net.minecraft.network.chat.ComponentUtils;
++import net.minecraft.server.dedicated.DedicatedServer;
++import org.checkerframework.checker.nullness.qual.NonNull;
++import org.jline.reader.Candidate;
++import org.jline.reader.LineReader;
++import org.jline.reader.ParsedLine;
++
++import static com.destroystokyo.paper.event.server.AsyncTabCompleteEvent.Completion.completion;
++
++public final class BrigadierCommandCompleter {
++ private final Supplier<CommandSourceStack> commandSourceStack;
++ private final DedicatedServer server;
++
++ public BrigadierCommandCompleter(final @NonNull DedicatedServer server) {
++ this.server = server;
++ this.commandSourceStack = Suppliers.memoize(this.server::createCommandSourceStack);
++ }
++
++ public void complete(final @NonNull LineReader reader, final @NonNull ParsedLine line, final @NonNull List<Candidate> candidates, final @NonNull List<Completion> existing) {
++ //noinspection ConstantConditions
++ if (this.server.overworld() == null) { // check if overworld is null, as worlds haven't been loaded yet
++ return;
++ } else if (!io.papermc.paper.configuration.GlobalConfiguration.get().console.enableBrigadierCompletions) {
++ this.addCandidates(candidates, Collections.emptyList(), existing);
++ return;
++ }
++ final CommandDispatcher<CommandSourceStack> dispatcher = this.server.getCommands().getDispatcher();
++ final ParseResults<CommandSourceStack> results = dispatcher.parse(prepareStringReader(line.line()), this.commandSourceStack.get());
++ this.addCandidates(
++ candidates,
++ dispatcher.getCompletionSuggestions(results, line.cursor()).join().getList(),
++ existing
++ );
++ }
++
++ private void addCandidates(
++ final @NonNull List<Candidate> candidates,
++ final @NonNull List<Suggestion> brigSuggestions,
++ final @NonNull List<Completion> existing
++ ) {
++ final List<Completion> completions = new ArrayList<>();
++ brigSuggestions.forEach(it -> completions.add(toCompletion(it)));
++ for (final Completion completion : existing) {
++ if (completion.suggestion().isEmpty() || brigSuggestions.stream().anyMatch(it -> it.getText().equals(completion.suggestion()))) {
++ continue;
++ }
++ completions.add(completion);
++ }
++ for (final Completion completion : completions) {
++ if (completion.suggestion().isEmpty()) {
++ continue;
++ }
++ candidates.add(toCandidate(completion));
++ }
++ }
++
++ private static @NonNull Candidate toCandidate(final @NonNull Completion completion) {
++ final String suggestionText = completion.suggestion();
++ final String suggestionTooltip = PaperAdventure.PLAIN.serializeOr(completion.tooltip(), null);
++ return new Candidate(
++ suggestionText,
++ suggestionText,
++ null,
++ suggestionTooltip,
++ null,
++ null,
++ false
++ );
++ }
++
++ private static @NonNull Completion toCompletion(final @NonNull Suggestion suggestion) {
++ if (suggestion.getTooltip() == null) {
++ return completion(suggestion.getText());
++ }
++ return completion(suggestion.getText(), PaperAdventure.asAdventure(ComponentUtils.fromMessage(suggestion.getTooltip())));
++ }
++
++ static @NonNull StringReader prepareStringReader(final @NonNull String line) {
++ final StringReader stringReader = new StringReader(line);
++ if (stringReader.canRead() && stringReader.peek() == '/') {
++ stringReader.skip();
++ }
++ return stringReader;
++ }
++}
+diff --git a/src/main/java/io/papermc/paper/console/BrigadierCommandHighlighter.java b/src/main/java/io/papermc/paper/console/BrigadierCommandHighlighter.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..dd9d77d7c7f1a5a130a1f4c15e5b1e68ae3753e1
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/console/BrigadierCommandHighlighter.java
+@@ -0,0 +1,70 @@
++package io.papermc.paper.console;
++
++import com.google.common.base.Suppliers;
++import com.mojang.brigadier.ParseResults;
++import com.mojang.brigadier.context.ParsedCommandNode;
++import com.mojang.brigadier.tree.LiteralCommandNode;
++import java.util.function.Supplier;
++import java.util.regex.Pattern;
++import net.minecraft.commands.CommandSourceStack;
++import net.minecraft.server.dedicated.DedicatedServer;
++import org.checkerframework.checker.nullness.qual.NonNull;
++import org.jline.reader.Highlighter;
++import org.jline.reader.LineReader;
++import org.jline.utils.AttributedString;
++import org.jline.utils.AttributedStringBuilder;
++import org.jline.utils.AttributedStyle;
++
++public final class BrigadierCommandHighlighter implements Highlighter {
++ private static final int[] COLORS = {AttributedStyle.CYAN, AttributedStyle.YELLOW, AttributedStyle.GREEN, AttributedStyle.MAGENTA, /* Client uses GOLD here, not BLUE, however there is no GOLD AttributedStyle. */ AttributedStyle.BLUE};
++ private final Supplier<CommandSourceStack> commandSourceStack;
++ private final DedicatedServer server;
++
++ public BrigadierCommandHighlighter(final @NonNull DedicatedServer server) {
++ this.server = server;
++ this.commandSourceStack = Suppliers.memoize(this.server::createCommandSourceStack);
++ }
++
++ @Override
++ public AttributedString highlight(final @NonNull LineReader reader, final @NonNull String buffer) {
++ //noinspection ConstantConditions
++ if (this.server.overworld() == null) { // check if overworld is null, as worlds haven't been loaded yet
++ return new AttributedString(buffer, AttributedStyle.DEFAULT.foreground(AttributedStyle.RED));
++ }
++ final AttributedStringBuilder builder = new AttributedStringBuilder();
++ final ParseResults<CommandSourceStack> results = this.server.getCommands().getDispatcher().parse(BrigadierCommandCompleter.prepareStringReader(buffer), this.commandSourceStack.get());
++ int pos = 0;
++ if (buffer.startsWith("/")) {
++ builder.append("/", AttributedStyle.DEFAULT);
++ pos = 1;
++ }
++ int component = -1;
++ for (final ParsedCommandNode<CommandSourceStack> node : results.getContext().getLastChild().getNodes()) {
++ if (node.getRange().getStart() >= buffer.length()) {
++ break;
++ }
++ final int start = node.getRange().getStart();
++ final int end = Math.min(node.getRange().getEnd(), buffer.length());
++ builder.append(buffer.substring(pos, start), AttributedStyle.DEFAULT);
++ if (node.getNode() instanceof LiteralCommandNode) {
++ builder.append(buffer.substring(start, end), AttributedStyle.DEFAULT);
++ } else {
++ if (++component >= COLORS.length) {
++ component = 0;
++ }
++ builder.append(buffer.substring(start, end), AttributedStyle.DEFAULT.foreground(COLORS[component]));
++ }
++ pos = end;
++ }
++ if (pos < buffer.length()) {
++ builder.append((buffer.substring(pos)), AttributedStyle.DEFAULT.foreground(AttributedStyle.RED));
++ }
++ return builder.toAttributedString();
++ }
++
++ @Override
++ public void setErrorPattern(final Pattern errorPattern) {}
++
++ @Override
++ public void setErrorIndex(final int errorIndex) {}
++}
+diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
+index f4378558557c36cdbbaf43481b679f86583bdf16..2b79f5cd92a442f47324e13b5bf1b803e730197c 100644
+--- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
++++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
+@@ -178,7 +178,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
+
+ thread.setDaemon(true);
+ thread.setUncaughtExceptionHandler(new DefaultUncaughtExceptionHandler(DedicatedServer.LOGGER));
+- thread.start();
++ // thread.start(); // Paper - moved down
+ DedicatedServer.LOGGER.info("Starting minecraft server version {}", SharedConstants.getCurrentVersion().getName());
+ if (Runtime.getRuntime().maxMemory() / 1024L / 1024L < 512L) {
+ DedicatedServer.LOGGER.warn("To start the server with more ram, launch it as \"java -Xmx1024M -Xms1024M -jar minecraft_server.jar\"");
+@@ -210,6 +210,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
+ this.getPlayerList().loadAndSaveFiles(); // Must be after convertNames
+ // Paper end - moved up
+ org.spigotmc.WatchdogThread.doStart(org.spigotmc.SpigotConfig.timeoutTime, org.spigotmc.SpigotConfig.restartOnCrash);
++ thread.start(); // Paper - start console thread after MinecraftServer.console & PaperConfig are initialized
+ io.papermc.paper.command.PaperCommands.registerCommands(this);
+ com.destroystokyo.paper.Metrics.PaperMetrics.startMetrics();
+ com.destroystokyo.paper.VersionHistoryManager.INSTANCE.getClass(); // load version history now
+diff --git a/src/main/java/org/bukkit/craftbukkit/command/ConsoleCommandCompleter.java b/src/main/java/org/bukkit/craftbukkit/command/ConsoleCommandCompleter.java
+index 14cd8ae69d9b25dc5edad4ff96ff4a9acb1f22cb..cd4ad8261e56365850068db1d83d6a8454026737 100644
+--- a/src/main/java/org/bukkit/craftbukkit/command/ConsoleCommandCompleter.java
++++ b/src/main/java/org/bukkit/craftbukkit/command/ConsoleCommandCompleter.java
+@@ -18,9 +18,11 @@ import org.bukkit.event.server.TabCompleteEvent;
+
+ public class ConsoleCommandCompleter implements Completer {
+ private final DedicatedServer server; // Paper - CraftServer -> DedicatedServer
++ private final io.papermc.paper.console.BrigadierCommandCompleter brigadierCompleter; // Paper
+
+ public ConsoleCommandCompleter(DedicatedServer server) { // Paper - CraftServer -> DedicatedServer
+ this.server = server;
++ this.brigadierCompleter = new io.papermc.paper.console.BrigadierCommandCompleter(this.server); // Paper
+ }
+
+ // Paper start - Change method signature for JLine update
+@@ -64,7 +66,7 @@ public class ConsoleCommandCompleter implements Completer {
+ }
+ }
+
+- if (!completions.isEmpty()) {
++ if (false && !completions.isEmpty()) {
+ for (final com.destroystokyo.paper.event.server.AsyncTabCompleteEvent.Completion completion : completions) {
+ if (completion.suggestion().isEmpty()) {
+ continue;
+@@ -80,6 +82,7 @@ public class ConsoleCommandCompleter implements Completer {
+ ));
+ }
+ }
++ this.addCompletions(reader, line, candidates, completions);
+ return;
+ }
+
+@@ -99,10 +102,12 @@ public class ConsoleCommandCompleter implements Completer {
+ try {
+ List<String> offers = waitable.get();
+ if (offers == null) {
++ this.addCompletions(reader, line, candidates, Collections.emptyList()); // Paper
+ return; // Paper - Method returns void
+ }
+
+ // Paper start - JLine update
++ /*
+ for (String completion : offers) {
+ if (completion.isEmpty()) {
+ continue;
+@@ -110,6 +115,8 @@ public class ConsoleCommandCompleter implements Completer {
+
+ candidates.add(new Candidate(completion));
+ }
++ */
++ this.addCompletions(reader, line, candidates, offers.stream().map(com.destroystokyo.paper.event.server.AsyncTabCompleteEvent.Completion::completion).collect(java.util.stream.Collectors.toList()));
+ // Paper end
+
+ // Paper start - JLine handles cursor now
+@@ -138,5 +145,9 @@ public class ConsoleCommandCompleter implements Completer {
+ }
+ return false;
+ }
++
++ private void addCompletions(final LineReader reader, final ParsedLine line, final List<Candidate> candidates, final List<com.destroystokyo.paper.event.server.AsyncTabCompleteEvent.Completion> existing) {
++ this.brigadierCompleter.complete(reader, line, candidates, existing);
++ }
+ // Paper end
+ }