aboutsummaryrefslogtreecommitdiffhomepage
path: root/patches/server/0128-Use-TerminalConsoleAppender-for-console-improvements.patch
diff options
context:
space:
mode:
Diffstat (limited to 'patches/server/0128-Use-TerminalConsoleAppender-for-console-improvements.patch')
-rw-r--r--patches/server/0128-Use-TerminalConsoleAppender-for-console-improvements.patch667
1 files changed, 667 insertions, 0 deletions
diff --git a/patches/server/0128-Use-TerminalConsoleAppender-for-console-improvements.patch b/patches/server/0128-Use-TerminalConsoleAppender-for-console-improvements.patch
new file mode 100644
index 0000000000..be1dff00d0
--- /dev/null
+++ b/patches/server/0128-Use-TerminalConsoleAppender-for-console-improvements.patch
@@ -0,0 +1,667 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Minecrell <[email protected]>
+Date: Fri, 9 Jun 2017 19:03:43 +0200
+Subject: [PATCH] Use TerminalConsoleAppender for console improvements
+
+Rewrite console improvements (console colors, tab completion,
+persistent input line, ...) using JLine 3.x and TerminalConsoleAppender.
+
+Also uses the new ANSIComponentSerializer to serialize components when
+logging them via the ComponentLogger, or when sending messages to the
+console, for hex color support.
+
+New features:
+ - Support console colors for Vanilla commands
+ - Add console colors for warnings and errors
+ - Server can now be turned off safely using CTRL + C. JLine catches
+ the signal and the implementation shuts down the server cleanly.
+ - Support console colors and persistent input line when running in
+ IntelliJ IDEA
+
+Other changes:
+ - Server starts 1-2 seconds faster thanks to optimizations in Log4j
+ configuration
+
+Co-Authored-By: Emilia Kond <[email protected]>
+
+diff --git a/build.gradle.kts b/build.gradle.kts
+index 65e9d5918d46b123fb4f8122344a7d3863aec758..7a3c96318f95fcd6cf6fd94415958382d1193ec6 100644
+--- a/build.gradle.kts
++++ b/build.gradle.kts
+@@ -6,9 +6,30 @@ plugins {
+ id("com.github.johnrengelman.shadow")
+ }
+
++val log4jPlugins = sourceSets.create("log4jPlugins")
++configurations.named(log4jPlugins.compileClasspathConfigurationName) {
++ extendsFrom(configurations.compileClasspath.get())
++}
++val alsoShade: Configuration by configurations.creating
++
+ dependencies {
+ implementation(project(":paper-api"))
+- implementation("jline:jline:2.12.1")
++ // Paper start
++ implementation("org.jline:jline-terminal-jansi:3.21.0")
++ implementation("net.minecrell:terminalconsoleappender:1.3.0")
++ implementation("net.kyori:adventure-text-serializer-ansi:4.14.0") // Keep in sync with adventureVersion from Paper-API build file
++ implementation("net.kyori:ansi:1.0.3") // Manually bump beyond above transitive dep
++ /*
++ Required to add the missing Log4j2Plugins.dat file from log4j-core
++ which has been removed by Mojang. Without it, log4j has to classload
++ all its classes to check if they are plugins.
++ Scanning takes about 1-2 seconds so adding this speeds up the server start.
++ */
++ runtimeOnly("org.apache.logging.log4j:log4j-core:2.19.0")
++ log4jPlugins.annotationProcessorConfigurationName("org.apache.logging.log4j:log4j-core:2.19.0") // Paper - Needed to generate meta for our Log4j plugins
++ runtimeOnly(log4jPlugins.output)
++ alsoShade(log4jPlugins.output)
++ // Paper end
+ implementation("org.apache.logging.log4j:log4j-iostreams:2.19.0") // Paper - remove exclusion
+ implementation("org.ow2.asm:asm-commons:9.5")
+ implementation("org.spongepowered:configurate-yaml:4.2.0-SNAPSHOT") // Paper - config files
+@@ -78,7 +99,7 @@ relocation {
+ }
+
+ tasks.shadowJar {
+- configurations = listOf(project.configurations.vanillaServer.get())
++ configurations = listOf(project.configurations.vanillaServer.get(), alsoShade)
+ archiveClassifier.set("mojang-mapped")
+
+ for (relocation in relocation.relocations.get()) {
+diff --git a/src/log4jPlugins/java/io/papermc/paper/console/StripANSIConverter.java b/src/log4jPlugins/java/io/papermc/paper/console/StripANSIConverter.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..91547f6e6fe90006713beb2818da634304bdd236
+--- /dev/null
++++ b/src/log4jPlugins/java/io/papermc/paper/console/StripANSIConverter.java
+@@ -0,0 +1,51 @@
++package io.papermc.paper.console;
++
++import org.apache.logging.log4j.core.LogEvent;
++import org.apache.logging.log4j.core.config.Configuration;
++import org.apache.logging.log4j.core.config.plugins.Plugin;
++import org.apache.logging.log4j.core.layout.PatternLayout;
++import org.apache.logging.log4j.core.pattern.ConverterKeys;
++import org.apache.logging.log4j.core.pattern.LogEventPatternConverter;
++import org.apache.logging.log4j.core.pattern.PatternConverter;
++import org.apache.logging.log4j.core.pattern.PatternFormatter;
++import org.apache.logging.log4j.core.pattern.PatternParser;
++
++import java.util.List;
++import java.util.regex.Pattern;
++
++@Plugin(name = "stripAnsi", category = PatternConverter.CATEGORY)
++@ConverterKeys({"stripAnsi"})
++public final class StripANSIConverter extends LogEventPatternConverter {
++ final private Pattern ANSI_PATTERN = Pattern.compile("\\e\\[[\\d;]*[^\\d;]");
++
++ private final List<PatternFormatter> formatters;
++
++ private StripANSIConverter(List<PatternFormatter> formatters) {
++ super("stripAnsi", null);
++ this.formatters = formatters;
++ }
++
++ @Override
++ public void format(LogEvent event, StringBuilder toAppendTo) {
++ int start = toAppendTo.length();
++ for (PatternFormatter formatter : formatters) {
++ formatter.format(event, toAppendTo);
++ }
++ String content = toAppendTo.substring(start);
++ content = ANSI_PATTERN.matcher(content).replaceAll("");
++
++ toAppendTo.setLength(start);
++ toAppendTo.append(content);
++ }
++
++ public static StripANSIConverter newInstance(Configuration config, String[] options) {
++ if (options.length != 1) {
++ LOGGER.error("Incorrect number of options on stripAnsi. Expected exactly 1, received " + options.length);
++ return null;
++ }
++
++ PatternParser parser = PatternLayout.createPatternParser(config);
++ List<PatternFormatter> formatters = parser.parse(options[0]);
++ return new StripANSIConverter(formatters);
++ }
++}
+diff --git a/src/main/java/com/destroystokyo/paper/console/PaperConsole.java b/src/main/java/com/destroystokyo/paper/console/PaperConsole.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..a4070b59e261f0f1ac4beec47b11492f4724bf27
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/console/PaperConsole.java
+@@ -0,0 +1,41 @@
++package com.destroystokyo.paper.console;
++
++import net.minecraft.server.dedicated.DedicatedServer;
++import net.minecrell.terminalconsole.SimpleTerminalConsole;
++import org.bukkit.craftbukkit.command.ConsoleCommandCompleter;
++import org.jline.reader.LineReader;
++import org.jline.reader.LineReaderBuilder;
++
++public final class PaperConsole extends SimpleTerminalConsole {
++
++ private final DedicatedServer server;
++
++ public PaperConsole(DedicatedServer server) {
++ this.server = server;
++ }
++
++ @Override
++ protected LineReader buildReader(LineReaderBuilder builder) {
++ return super.buildReader(builder
++ .appName("Paper")
++ .variable(LineReader.HISTORY_FILE, java.nio.file.Paths.get(".console_history"))
++ .completer(new ConsoleCommandCompleter(this.server))
++ );
++ }
++
++ @Override
++ protected boolean isRunning() {
++ return !this.server.isStopped() && this.server.isRunning();
++ }
++
++ @Override
++ protected void runCommand(String command) {
++ this.server.handleConsoleInput(command, this.server.createCommandSourceStack());
++ }
++
++ @Override
++ protected void shutdown() {
++ this.server.halt(false);
++ }
++
++}
+diff --git a/src/main/java/com/destroystokyo/paper/console/TerminalConsoleCommandSender.java b/src/main/java/com/destroystokyo/paper/console/TerminalConsoleCommandSender.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..8f07539a82f449ad217e316a7513a1708781fb63
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/console/TerminalConsoleCommandSender.java
+@@ -0,0 +1,26 @@
++package com.destroystokyo.paper.console;
++
++import net.kyori.adventure.audience.MessageType;
++import net.kyori.adventure.identity.Identity;
++import net.kyori.adventure.text.Component;
++import net.kyori.adventure.text.logger.slf4j.ComponentLogger;
++import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
++import org.apache.logging.log4j.LogManager;
++import org.bukkit.craftbukkit.command.CraftConsoleCommandSender;
++
++public class TerminalConsoleCommandSender extends CraftConsoleCommandSender {
++
++ private static final ComponentLogger LOGGER = ComponentLogger.logger(LogManager.getRootLogger().getName());
++
++ @Override
++ public void sendRawMessage(String message) {
++ final Component msg = LegacyComponentSerializer.legacySection().deserialize(message);
++ this.sendMessage(Identity.nil(), msg, MessageType.SYSTEM);
++ }
++
++ @Override
++ public void sendMessage(Identity identity, Component message, MessageType type) {
++ LOGGER.info(message);
++ }
++
++}
+diff --git a/src/main/java/io/papermc/paper/adventure/PaperAdventure.java b/src/main/java/io/papermc/paper/adventure/PaperAdventure.java
+index 2e757cd9b01ac7eba1e4723a6e21dcea9d062483..ca80cbe422d766b3d044a5b53ce40bb7f92558e4 100644
+--- a/src/main/java/io/papermc/paper/adventure/PaperAdventure.java
++++ b/src/main/java/io/papermc/paper/adventure/PaperAdventure.java
+@@ -24,6 +24,7 @@ import net.kyori.adventure.text.TranslationArgument;
+ import net.kyori.adventure.text.flattener.ComponentFlattener;
+ import net.kyori.adventure.text.format.TextColor;
+ import net.kyori.adventure.text.serializer.ComponentSerializer;
++import net.kyori.adventure.text.serializer.ansi.ANSIComponentSerializer;
+ import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
+ import net.kyori.adventure.text.serializer.plain.PlainComponentSerializer;
+ import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
+@@ -111,6 +112,7 @@ public final class PaperAdventure {
+ public static final AttributeKey<Locale> LOCALE_ATTRIBUTE = AttributeKey.valueOf("adventure:locale"); // init after FLATTENER because classloading triggered here might create a logger
+ @Deprecated
+ public static final PlainComponentSerializer PLAIN = PlainComponentSerializer.builder().flattener(FLATTENER).build();
++ public static final ANSIComponentSerializer ANSI_SERIALIZER = ANSIComponentSerializer.builder().flattener(FLATTENER).build();
+ static final Codec<CompoundTag, String, IOException, IOException> NBT_CODEC = new Codec<CompoundTag, String, IOException, IOException>() {
+ @Override
+ public @NotNull CompoundTag decode(final @NotNull String encoded) throws IOException {
+diff --git a/src/main/java/io/papermc/paper/adventure/providers/ComponentLoggerProviderImpl.java b/src/main/java/io/papermc/paper/adventure/providers/ComponentLoggerProviderImpl.java
+index 8323f135d6bf2e1f12525e05094ffa3f2420e7e1..a143ea1e58464a3122fbd8ccafe417bdb3c31c78 100644
+--- a/src/main/java/io/papermc/paper/adventure/providers/ComponentLoggerProviderImpl.java
++++ b/src/main/java/io/papermc/paper/adventure/providers/ComponentLoggerProviderImpl.java
+@@ -1,9 +1,11 @@
+ package io.papermc.paper.adventure.providers;
+
+ import io.papermc.paper.adventure.PaperAdventure;
++import java.util.Locale;
+ import net.kyori.adventure.text.Component;
+ import net.kyori.adventure.text.logger.slf4j.ComponentLogger;
+ import net.kyori.adventure.text.logger.slf4j.ComponentLoggerProvider;
++import net.kyori.adventure.translation.GlobalTranslator;
+ import org.jetbrains.annotations.NotNull;
+ import org.slf4j.LoggerFactory;
+
+@@ -15,6 +17,6 @@ public class ComponentLoggerProviderImpl implements ComponentLoggerProvider {
+ }
+
+ private String serialize(final Component message) {
+- return PaperAdventure.asPlain(message, null);
++ return PaperAdventure.ANSI_SERIALIZER.serialize(GlobalTranslator.render(message, Locale.getDefault()));
+ }
+ }
+diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
+index 1d8e17f4b862e71cc5ef8c5eabfb80dc427aec51..6587cd9ea257d0f6f1308ea8ca4d0245178b2870 100644
+--- a/src/main/java/net/minecraft/server/MinecraftServer.java
++++ b/src/main/java/net/minecraft/server/MinecraftServer.java
+@@ -155,7 +155,7 @@ import org.slf4j.Logger;
+ import com.mojang.serialization.Dynamic;
+ import com.mojang.serialization.Lifecycle;
+ import java.util.Random;
+-import jline.console.ConsoleReader;
++// import jline.console.ConsoleReader; // Paper
+ import joptsimple.OptionSet;
+ import net.minecraft.nbt.NbtException;
+ import net.minecraft.nbt.ReportedNbtException;
+@@ -284,7 +284,6 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+ public org.bukkit.craftbukkit.CraftServer server;
+ public OptionSet options;
+ public org.bukkit.command.ConsoleCommandSender console;
+- public ConsoleReader reader;
+ public static int currentTick; // Paper - improve tick loop
+ public java.util.Queue<Runnable> processQueue = new java.util.concurrent.ConcurrentLinkedQueue<Runnable>();
+ public int autosavePeriod;
+@@ -371,7 +370,9 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+ this.options = options;
+ this.worldLoader = worldLoader;
+ this.vanillaCommandDispatcher = worldstem.dataPackResources().commands; // CraftBukkit
++ // Paper start - Handled by TerminalConsoleAppender
+ // Try to see if we're actually running in a terminal, disable jline if not
++ /*
+ if (System.console() == null && System.getProperty("jline.terminal") == null) {
+ System.setProperty("jline.terminal", "jline.UnsupportedTerminal");
+ Main.useJline = false;
+@@ -392,6 +393,8 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+ MinecraftServer.LOGGER.warn((String) null, ex);
+ }
+ }
++ */
++ // Paper end
+ Runtime.getRuntime().addShutdownHook(new org.bukkit.craftbukkit.util.ServerShutdownThread(this));
+ // CraftBukkit end
+ this.paperConfigurations = services.paperConfigurations(); // Paper - add paper configuration files
+@@ -1187,7 +1190,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+ org.spigotmc.WatchdogThread.doStop(); // Spigot
+ // CraftBukkit start - Restore terminal to original settings
+ try {
+- this.reader.getTerminal().restore();
++ net.minecrell.terminalconsole.TerminalConsoleAppender.close(); // Paper - Use TerminalConsoleAppender
+ } catch (Exception ignored) {
+ }
+ // CraftBukkit end
+@@ -1685,7 +1688,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+
+ @Override
+ public void sendSystemMessage(Component message) {
+- MinecraftServer.LOGGER.info(message.getString());
++ MinecraftServer.LOGGER.info(io.papermc.paper.adventure.PaperAdventure.ANSI_SERIALIZER.serialize(io.papermc.paper.adventure.PaperAdventure.asAdventure(message))); // Paper - Log message with colors
+ }
+
+ public KeyPair getKeyPair() {
+diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
+index c8163a8645248765c0fa6c15bc8c37facdc70dc7..8edf2f049aa077792bfb507384f4cf5bb716cb64 100644
+--- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
++++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
+@@ -98,6 +98,9 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
+ if (!org.bukkit.craftbukkit.Main.useConsole) {
+ return;
+ }
++ // Paper start - Use TerminalConsoleAppender
++ new com.destroystokyo.paper.console.PaperConsole(DedicatedServer.this).start();
++ /*
+ jline.console.ConsoleReader bufferedreader = DedicatedServer.this.reader;
+
+ // MC-33041, SPIGOT-5538: if System.in is not valid due to javaw, then return
+@@ -129,7 +132,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
+ continue;
+ }
+ if (s.trim().length() > 0) { // Trim to filter lines which are just spaces
+- DedicatedServer.this.handleConsoleInput(s, DedicatedServer.this.createCommandSourceStack());
++ DedicatedServer.this.issueCommand(s, DedicatedServer.this.getServerCommandListener());
+ }
+ // CraftBukkit end
+ }
+@@ -137,6 +140,8 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
+ DedicatedServer.LOGGER.error("Exception handling console input", ioexception);
+ }
+
++ */
++ // Paper end
+ }
+ };
+
+@@ -148,6 +153,9 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
+ }
+ global.addHandler(new org.bukkit.craftbukkit.util.ForwardLogHandler());
+
++ // Paper start - Not needed with TerminalConsoleAppender
++ final org.apache.logging.log4j.Logger logger = LogManager.getRootLogger();
++ /*
+ final org.apache.logging.log4j.core.Logger logger = ((org.apache.logging.log4j.core.Logger) LogManager.getRootLogger());
+ for (org.apache.logging.log4j.core.Appender appender : logger.getAppenders().values()) {
+ if (appender instanceof org.apache.logging.log4j.core.appender.ConsoleAppender) {
+@@ -156,6 +164,8 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
+ }
+
+ new org.bukkit.craftbukkit.util.TerminalConsoleWriterThread(System.out, this.reader).start();
++ */
++ // Paper end
+
+ System.setOut(IoBuilder.forLogger(logger).setLevel(Level.INFO).buildPrintStream());
+ System.setErr(IoBuilder.forLogger(logger).setLevel(Level.WARN).buildPrintStream());
+diff --git a/src/main/java/net/minecraft/server/gui/MinecraftServerGui.java b/src/main/java/net/minecraft/server/gui/MinecraftServerGui.java
+index 75083eeb9b413e6dd5375007360dce6857a08fff..d292fdb165436f0b9b46b32110f5e09ad0e517a1 100644
+--- a/src/main/java/net/minecraft/server/gui/MinecraftServerGui.java
++++ b/src/main/java/net/minecraft/server/gui/MinecraftServerGui.java
+@@ -166,7 +166,7 @@ public class MinecraftServerGui extends JComponent {
+ this.finalizers.forEach(Runnable::run);
+ }
+
+- private static final java.util.regex.Pattern ANSI = java.util.regex.Pattern.compile("\\x1B\\[([0-9]{1,2}(;[0-9]{1,2})*)?[m|K]"); // CraftBukkit
++ private static final java.util.regex.Pattern ANSI = java.util.regex.Pattern.compile("\\e\\[[\\d;]*[^\\d;]"); // CraftBukkit // Paper
+ public void print(JTextArea textArea, JScrollPane scrollPane, String message) {
+ if (!SwingUtilities.isEventDispatchThread()) {
+ SwingUtilities.invokeLater(() -> {
+diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
+index 40af2325afea3e4831a9d8795ce1932a6a5663bf..db4480778e4b917a073c61f29cd45663ed859597 100644
+--- a/src/main/java/net/minecraft/server/players/PlayerList.java
++++ b/src/main/java/net/minecraft/server/players/PlayerList.java
+@@ -160,8 +160,7 @@ public abstract class PlayerList {
+
+ public PlayerList(MinecraftServer server, LayeredRegistryAccess<RegistryLayer> registryManager, PlayerDataStorage saveHandler, int maxPlayers) {
+ this.cserver = server.server = new CraftServer((DedicatedServer) server, this);
+- server.console = org.bukkit.craftbukkit.command.ColouredConsoleSender.getInstance();
+- server.reader.addCompleter(new org.bukkit.craftbukkit.command.ConsoleCommandCompleter(server.server));
++ server.console = new com.destroystokyo.paper.console.TerminalConsoleCommandSender(); // Paper
+ // CraftBukkit end
+
+ this.bans = new UserBanList(PlayerList.USERBANLIST_FILE);
+diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+index 827579f59d34b61912a67b40624f0f41524185fd..f7d937c6a11e24afe767411428210f3c042a6f78 100644
+--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
++++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+@@ -42,7 +42,7 @@ import java.util.logging.Level;
+ import java.util.logging.Logger;
+ import java.util.stream.Collectors;
+ import javax.imageio.ImageIO;
+-import jline.console.ConsoleReader;
++// import jline.console.ConsoleReader;
+ import net.minecraft.advancements.AdvancementHolder;
+ import net.minecraft.commands.CommandSourceStack;
+ import net.minecraft.commands.Commands;
+@@ -1341,9 +1341,13 @@ public final class CraftServer implements Server {
+ return this.logger;
+ }
+
++ // Paper start - JLine update
++ /*
+ public ConsoleReader getReader() {
+ return this.console.reader;
+ }
++ */
++ // Paper end
+
+ @Override
+ public PluginCommand getPluginCommand(String name) {
+diff --git a/src/main/java/org/bukkit/craftbukkit/Main.java b/src/main/java/org/bukkit/craftbukkit/Main.java
+index 4fb377d967d13ed920ea1246e84b3b94cda25be6..659b32d49016a23475f3bbda1548a78101b468ce 100644
+--- a/src/main/java/org/bukkit/craftbukkit/Main.java
++++ b/src/main/java/org/bukkit/craftbukkit/Main.java
+@@ -13,7 +13,6 @@ import java.util.logging.Logger;
+ import joptsimple.OptionParser;
+ import joptsimple.OptionSet;
+ import joptsimple.util.PathConverter;
+-import org.fusesource.jansi.AnsiConsole;
+
+ public class Main {
+ public static boolean useJline = true;
+@@ -220,6 +219,8 @@ public class Main {
+ }
+
+ try {
++ // Paper start - Handled by TerminalConsoleAppender
++ /*
+ // This trick bypasses Maven Shade's clever rewriting of our getProperty call when using String literals
+ String jline_UnsupportedTerminal = new String(new char[]{'j', 'l', 'i', 'n', 'e', '.', 'U', 'n', 's', 'u', 'p', 'p', 'o', 'r', 't', 'e', 'd', 'T', 'e', 'r', 'm', 'i', 'n', 'a', 'l'});
+ String jline_terminal = new String(new char[]{'j', 'l', 'i', 'n', 'e', '.', 't', 'e', 'r', 'm', 'i', 'n', 'a', 'l'});
+@@ -237,9 +238,18 @@ public class Main {
+ // This ensures the terminal literal will always match the jline implementation
+ System.setProperty(jline.TerminalFactory.JLINE_TERMINAL, jline.UnsupportedTerminal.class.getName());
+ }
++ */
++
++ if (options.has("nojline")) {
++ System.setProperty(net.minecrell.terminalconsole.TerminalConsoleAppender.JLINE_OVERRIDE_PROPERTY, "false");
++ useJline = false;
++ }
++ // Paper end
+
+ if (options.has("noconsole")) {
+ Main.useConsole = false;
++ useJline = false; // Paper
++ System.setProperty(net.minecrell.terminalconsole.TerminalConsoleAppender.JLINE_OVERRIDE_PROPERTY, "false"); // Paper
+ }
+
+ if (Main.class.getPackage().getImplementationVendor() != null && System.getProperty("IReallyKnowWhatIAmDoingISwear") == null) {
+@@ -268,6 +278,7 @@ public class Main {
+ }
+ // Paper end - Log Java and OS versioning to help with debugging plugin issues
+
++ System.setProperty("library.jansi.version", "Paper"); // Paper - set meaningless jansi version to prevent git builds from crashing on Windows
+ System.out.println("Loading libraries, please wait...");
+ net.minecraft.server.Main.main(options);
+ } catch (Throwable t) {
+diff --git a/src/main/java/org/bukkit/craftbukkit/command/ColouredConsoleSender.java b/src/main/java/org/bukkit/craftbukkit/command/ColouredConsoleSender.java
+index bcf1c36d07b79520a39643d3a01020a67b1c9ef2..217e7e3b9db04c7fc5f6518f39cc9d3488f9128d 100644
+--- a/src/main/java/org/bukkit/craftbukkit/command/ColouredConsoleSender.java
++++ b/src/main/java/org/bukkit/craftbukkit/command/ColouredConsoleSender.java
+@@ -5,15 +5,13 @@ import java.util.EnumMap;
+ import java.util.Map;
+ import java.util.regex.Matcher;
+ import java.util.regex.Pattern;
+-import jline.Terminal;
++//import jline.Terminal;
+ import org.bukkit.Bukkit;
+ import org.bukkit.ChatColor;
+ import org.bukkit.command.ConsoleCommandSender;
+ import org.bukkit.craftbukkit.CraftServer;
+-import org.fusesource.jansi.Ansi;
+-import org.fusesource.jansi.Ansi.Attribute;
+
+-public class ColouredConsoleSender extends CraftConsoleCommandSender {
++public class ColouredConsoleSender /*extends CraftConsoleCommandSender */{/* // Paper - disable
+ private final Terminal terminal;
+ private final Map<ChatColor, String> replacements = new EnumMap<ChatColor, String>(ChatColor.class);
+ private final ChatColor[] colors = ChatColor.values();
+@@ -93,5 +91,5 @@ public class ColouredConsoleSender extends CraftConsoleCommandSender {
+ } else {
+ return new ColouredConsoleSender();
+ }
+- }
++ }*/ // Paper
+ }
+diff --git a/src/main/java/org/bukkit/craftbukkit/command/ConsoleCommandCompleter.java b/src/main/java/org/bukkit/craftbukkit/command/ConsoleCommandCompleter.java
+index 0b4c62387c1093652ac15b64a8703249de4cf088..d24acf28f5ed023acc550bcf877e4b9800ec8c9f 100644
+--- a/src/main/java/org/bukkit/craftbukkit/command/ConsoleCommandCompleter.java
++++ b/src/main/java/org/bukkit/craftbukkit/command/ConsoleCommandCompleter.java
+@@ -4,50 +4,73 @@ import java.util.Collections;
+ import java.util.List;
+ import java.util.concurrent.ExecutionException;
+ import java.util.logging.Level;
+-import jline.console.completer.Completer;
++import net.minecraft.server.dedicated.DedicatedServer;
+ import org.bukkit.craftbukkit.CraftServer;
+ import org.bukkit.craftbukkit.util.Waitable;
++
++// Paper start - JLine update
++import org.jline.reader.Candidate;
++import org.jline.reader.Completer;
++import org.jline.reader.LineReader;
++import org.jline.reader.ParsedLine;
++// Paper end
+ import org.bukkit.event.server.TabCompleteEvent;
+
+ public class ConsoleCommandCompleter implements Completer {
+- private final CraftServer server;
++ private final DedicatedServer server; // Paper - CraftServer -> DedicatedServer
+
+- public ConsoleCommandCompleter(CraftServer server) {
++ public ConsoleCommandCompleter(DedicatedServer server) { // Paper - CraftServer -> DedicatedServer
+ this.server = server;
+ }
+
++ // Paper start - Change method signature for JLine update
+ @Override
+- public int complete(final String buffer, final int cursor, final List<CharSequence> candidates) {
++ public void complete(LineReader reader, ParsedLine line, List<Candidate> candidates) {
++ final CraftServer server = this.server.server;
++ final String buffer = line.line();
++ // Paper end
+ Waitable<List<String>> waitable = new Waitable<List<String>>() {
+ @Override
+ protected List<String> evaluate() {
+- List<String> offers = ConsoleCommandCompleter.this.server.getCommandMap().tabComplete(ConsoleCommandCompleter.this.server.getConsoleSender(), buffer);
++ List<String> offers = server.getCommandMap().tabComplete(server.getConsoleSender(), buffer); // Paper - Remove "this."
+
+- TabCompleteEvent tabEvent = new TabCompleteEvent(ConsoleCommandCompleter.this.server.getConsoleSender(), buffer, (offers == null) ? Collections.EMPTY_LIST : offers);
+- ConsoleCommandCompleter.this.server.getPluginManager().callEvent(tabEvent);
++ TabCompleteEvent tabEvent = new TabCompleteEvent(server.getConsoleSender(), buffer, (offers == null) ? Collections.EMPTY_LIST : offers); // Paper - Remove "this."
++ server.getPluginManager().callEvent(tabEvent); // Paper - Remove "this."
+
+ return tabEvent.isCancelled() ? Collections.EMPTY_LIST : tabEvent.getCompletions();
+ }
+ };
+- this.server.getServer().processQueue.add(waitable);
++ server.getServer().processQueue.add(waitable); // Paper - Remove "this."
+ try {
+ List<String> offers = waitable.get();
+ if (offers == null) {
+- return cursor;
++ return; // Paper - Method returns void
++ }
++
++ // Paper start - JLine update
++ for (String completion : offers) {
++ if (completion.isEmpty()) {
++ continue;
++ }
++
++ candidates.add(new Candidate(completion));
+ }
+- candidates.addAll(offers);
++ // Paper end
+
++ // Paper start - JLine handles cursor now
++ /*
+ final int lastSpace = buffer.lastIndexOf(' ');
+ if (lastSpace == -1) {
+ return cursor - buffer.length();
+ } else {
+ return cursor - (buffer.length() - lastSpace - 1);
+ }
++ */
++ // Paper end
+ } catch (ExecutionException e) {
+- this.server.getLogger().log(Level.WARNING, "Unhandled exception when tab completing", e);
++ server.getLogger().log(Level.WARNING, "Unhandled exception when tab completing", e); // Paper - Remove "this."
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+- return cursor;
+ }
+ }
+diff --git a/src/main/java/org/bukkit/craftbukkit/util/ServerShutdownThread.java b/src/main/java/org/bukkit/craftbukkit/util/ServerShutdownThread.java
+index 8390f5b5b957b5435efece26507a89756d0a7b3c..c6e8441e299f477ddb22c1ce2618710763978f1a 100644
+--- a/src/main/java/org/bukkit/craftbukkit/util/ServerShutdownThread.java
++++ b/src/main/java/org/bukkit/craftbukkit/util/ServerShutdownThread.java
+@@ -16,7 +16,7 @@ public class ServerShutdownThread extends Thread {
+ this.server.close();
+ } finally {
+ try {
+- this.server.reader.getTerminal().restore();
++ net.minecrell.terminalconsole.TerminalConsoleAppender.close(); // Paper - Use TerminalConsoleAppender
+ } catch (Exception e) {
+ }
+ }
+diff --git a/src/main/java/org/bukkit/craftbukkit/util/TerminalConsoleWriterThread.java b/src/main/java/org/bukkit/craftbukkit/util/TerminalConsoleWriterThread.java
+index 1d8b279f3cbe6fde6bb1bfc4985c4133b0d4a95d..cdc52bbe5c6ae4688615a7732b10071f7f51718e 100644
+--- a/src/main/java/org/bukkit/craftbukkit/util/TerminalConsoleWriterThread.java
++++ b/src/main/java/org/bukkit/craftbukkit/util/TerminalConsoleWriterThread.java
+@@ -5,12 +5,12 @@ import java.io.IOException;
+ import java.io.OutputStream;
+ import java.util.logging.Level;
+ import java.util.logging.Logger;
+-import jline.console.ConsoleReader;
++//import jline.console.ConsoleReader;
+ import org.bukkit.craftbukkit.Main;
+-import org.fusesource.jansi.Ansi;
+-import org.fusesource.jansi.Ansi.Erase;
++//import org.fusesource.jansi.Ansi;
++//import org.fusesource.jansi.Ansi.Erase;
+
+-public class TerminalConsoleWriterThread extends Thread {
++public class TerminalConsoleWriterThread /*extends Thread*/ {/* // Paper - disable
+ private final ConsoleReader reader;
+ private final OutputStream output;
+
+@@ -54,5 +54,5 @@ public class TerminalConsoleWriterThread extends Thread {
+ Logger.getLogger(TerminalConsoleWriterThread.class.getName()).log(Level.SEVERE, null, ex);
+ }
+ }
+- }
++ }*/
+ }
+diff --git a/src/main/resources/log4j2.component.properties b/src/main/resources/log4j2.component.properties
+new file mode 100644
+index 0000000000000000000000000000000000000000..0694b21465fb9e4164e71862ff24b62241b191f2
+--- /dev/null
++++ b/src/main/resources/log4j2.component.properties
+@@ -0,0 +1 @@
++log4j.skipJansi=true
+diff --git a/src/main/resources/log4j2.xml b/src/main/resources/log4j2.xml
+index 722ca84968cbbbdeffd09939abff0cccd0a84010..a994ec0f8621b1f267b40049306f63479c050e2f 100644
+--- a/src/main/resources/log4j2.xml
++++ b/src/main/resources/log4j2.xml
+@@ -1,17 +1,14 @@
+ <?xml version="1.0" encoding="UTF-8"?>
+ <Configuration status="WARN" packages="com.mojang.util">
+ <Appenders>
+- <Console name="SysOut" target="SYSTEM_OUT">
+- <PatternLayout pattern="[%d{HH:mm:ss}] [%t/%level]: %msg%n" />
+- </Console>
+ <Queue name="ServerGuiConsole">
+ <PatternLayout pattern="[%d{HH:mm:ss} %level]: %msg%n" />
+ </Queue>
+- <Queue name="TerminalConsole">
+- <PatternLayout pattern="[%d{HH:mm:ss}] [%t/%level]: %msg%n" />
+- </Queue>
++ <TerminalConsole name="TerminalConsole">
++ <PatternLayout pattern="%highlightError{[%d{HH:mm:ss} %level]: %msg%n%xEx}" />
++ </TerminalConsole>
+ <RollingRandomAccessFile name="File" fileName="logs/latest.log" filePattern="logs/%d{yyyy-MM-dd}-%i.log.gz">
+- <PatternLayout pattern="[%d{HH:mm:ss}] [%t/%level]: %msg%n" />
++ <PatternLayout pattern="[%d{HH:mm:ss}] [%t/%level]: %stripAnsi{%msg}%n" />
+ <Policies>
+ <TimeBasedTriggeringPolicy />
+ <OnStartupTriggeringPolicy />
+@@ -24,10 +21,9 @@
+ <filters>
+ <MarkerFilter marker="NETWORK_PACKETS" onMatch="DENY" onMismatch="NEUTRAL" />
+ </filters>
+- <AppenderRef ref="SysOut" level="info"/>
+ <AppenderRef ref="File"/>
+- <AppenderRef ref="ServerGuiConsole" level="info"/>
+ <AppenderRef ref="TerminalConsole" level="info"/>
++ <AppenderRef ref="ServerGuiConsole" level="info"/>
+ </Root>
+ </Loggers>
+ </Configuration>