2021-06-11 14:02:28 +02:00
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Josh Roy <10731363+JRoy@users.noreply.github.com>
Date: Sat, 20 Feb 2021 13:09:59 -0500
Subject: [PATCH] Add support for hex color codes in console
Converts upstream's hex color code legacy format into actual hex color codes in the console.
2022-03-12 22:31:21 +01:00
diff --git a/src/main/java/com/destroystokyo/paper/console/TerminalConsoleCommandSender.java b/src/main/java/com/destroystokyo/paper/console/TerminalConsoleCommandSender.java
2022-06-12 23:07:40 +02:00
index 685deaa0e5d1ddc13e3a7c0471b1cfcf1710c869..8f07539a82f449ad217e316a7513a1708781fb63 100644
2022-03-12 22:31:21 +01:00
--- a/src/main/java/com/destroystokyo/paper/console/TerminalConsoleCommandSender.java
+++ b/src/main/java/com/destroystokyo/paper/console/TerminalConsoleCommandSender.java
2022-06-12 23:07:40 +02:00
@@ -1,16 +1,25 @@
2022-03-12 22:31:21 +01:00
package com.destroystokyo.paper.console;
+import net.kyori.adventure.audience.MessageType;
+import net.kyori.adventure.identity.Identity;
+import net.kyori.adventure.text.Component;
2022-06-12 23:07:40 +02:00
+import net.kyori.adventure.text.logger.slf4j.ComponentLogger;
2022-05-26 21:12:14 +02:00
+import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
2022-03-12 22:31:21 +01:00
import org.apache.logging.log4j.LogManager;
2022-06-12 23:07:40 +02:00
-import org.apache.logging.log4j.Logger;
2022-03-12 22:31:21 +01:00
import org.bukkit.craftbukkit.command.CraftConsoleCommandSender;
2022-06-12 23:07:40 +02:00
public class TerminalConsoleCommandSender extends CraftConsoleCommandSender {
- private static final Logger LOGGER = LogManager.getRootLogger();
+ private static final ComponentLogger LOGGER = ComponentLogger.logger(LogManager.getRootLogger().getName());
2022-03-12 22:31:21 +01:00
@Override
public void sendRawMessage(String message) {
- // TerminalConsoleAppender supports color codes directly in log messages
2022-05-26 21:12:14 +02:00
+ final Component msg = LegacyComponentSerializer.legacySection().deserialize(message);
2022-03-12 22:31:21 +01:00
+ this.sendMessage(Identity.nil(), msg, MessageType.SYSTEM);
+ }
+
+ @Override
+ public void sendMessage(Identity identity, Component message, MessageType type) {
2022-06-12 23:07:40 +02:00
LOGGER.info(message);
2022-03-12 22:31:21 +01:00
}
2022-06-12 23:07:40 +02:00
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 c3631efda9c7fa531a8a9f18fbee7b5f8655382b..9a3c1314d5a0aa20380662595359580b1a97be89 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 io.papermc.paper.console.HexFormattingConverter;
+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;
@@ -14,6 +16,6 @@ public class ComponentLoggerProviderImpl implements ComponentLoggerProvider {
}
private String serialize(final Component message) {
- return PaperAdventure.asPlain(message, null);
+ return HexFormattingConverter.SERIALIZER.serialize(GlobalTranslator.render(message, Locale.getDefault()));
}
2022-03-12 22:31:21 +01:00
}
2021-06-11 14:02:28 +02:00
diff --git a/src/main/java/io/papermc/paper/console/HexFormattingConverter.java b/src/main/java/io/papermc/paper/console/HexFormattingConverter.java
new file mode 100644
2022-06-09 10:51:45 +02:00
index 0000000000000000000000000000000000000000..f39ae450e316255d315aeb362bf967eadef6ba23
2021-06-11 14:02:28 +02:00
--- /dev/null
+++ b/src/main/java/io/papermc/paper/console/HexFormattingConverter.java
2022-03-13 02:47:04 +01:00
@@ -0,0 +1,212 @@
2021-06-11 14:02:28 +02:00
+package io.papermc.paper.console;
+
2022-06-09 10:51:45 +02:00
+import io.papermc.paper.configuration.GlobalConfiguration;
2022-03-13 02:47:04 +01:00
+import io.papermc.paper.adventure.PaperAdventure;
2022-03-12 22:31:21 +01:00
+import net.kyori.adventure.text.format.NamedTextColor;
+import net.kyori.adventure.text.format.TextColor;
2022-03-13 02:47:04 +01:00
+import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
2021-06-11 14:02:28 +02:00
+import net.minecrell.terminalconsole.TerminalConsoleAppender;
+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;
2022-03-12 22:31:21 +01:00
+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;
2021-06-11 14:02:28 +02:00
+import org.apache.logging.log4j.util.PerformanceSensitive;
+import org.apache.logging.log4j.util.PropertiesUtil;
+
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import static net.minecrell.terminalconsole.MinecraftFormattingConverter.KEEP_FORMATTING_PROPERTY;
+
+/**
+ * Modified version of <a href="https://github.com/Minecrell/TerminalConsoleAppender/blob/master/src/main/java/net/minecrell/terminalconsole/MinecraftFormattingConverter.java">
2022-03-12 22:31:21 +01:00
+ * TerminalConsoleAppender's MinecraftFormattingConverter</a> to support hex color codes using the Adventure [char]#rrggbb format.
2021-06-11 14:02:28 +02:00
+ */
+@Plugin(name = "paperMinecraftFormatting", category = PatternConverter.CATEGORY)
2022-03-12 22:31:21 +01:00
+@ConverterKeys({"paperMinecraftFormatting"})
2021-06-11 14:02:28 +02:00
+@PerformanceSensitive("allocation")
+public final class HexFormattingConverter extends LogEventPatternConverter {
+
+ private static final boolean KEEP_FORMATTING = PropertiesUtil.getProperties().getBooleanProperty(KEEP_FORMATTING_PROPERTY);
+
+ private static final String ANSI_RESET = "\u001B[m";
+
2022-03-13 02:47:04 +01:00
+ private static final char COLOR_CHAR = 0x7f;
+ public static final LegacyComponentSerializer SERIALIZER = LegacyComponentSerializer.builder()
+ .hexColors()
+ .flattener(PaperAdventure.FLATTENER)
+ .character(HexFormattingConverter.COLOR_CHAR)
+ .build();
2021-06-11 14:02:28 +02:00
+ private static final String LOOKUP = "0123456789abcdefklmnor";
+
+ private static final String RGB_ANSI = "\u001B[38;2;%d;%d;%dm";
+ private static final Pattern NAMED_PATTERN = Pattern.compile(COLOR_CHAR + "[0-9a-fk-orA-FK-OR]");
2022-03-12 22:31:21 +01:00
+ private static final Pattern RGB_PATTERN = Pattern.compile(COLOR_CHAR + "#([0-9a-fA-F]){6}");
2021-06-11 14:02:28 +02:00
+
2022-03-12 22:31:21 +01:00
+ private static final String[] RGB_ANSI_CODES = new String[]{
+ formatHexAnsi(NamedTextColor.BLACK), // Black §0
+ formatHexAnsi(NamedTextColor.DARK_BLUE), // Dark Blue §1
+ formatHexAnsi(NamedTextColor.DARK_GREEN), // Dark Green §2
+ formatHexAnsi(NamedTextColor.DARK_AQUA), // Dark Aqua §3
+ formatHexAnsi(NamedTextColor.DARK_RED), // Dark Red §4
+ formatHexAnsi(NamedTextColor.DARK_PURPLE), // Dark Purple §5
+ formatHexAnsi(NamedTextColor.GOLD), // Gold §6
+ formatHexAnsi(NamedTextColor.GRAY), // Gray §7
+ formatHexAnsi(NamedTextColor.DARK_GRAY), // Dark Gray §8
+ formatHexAnsi(NamedTextColor.BLUE), // Blue §9
+ formatHexAnsi(NamedTextColor.GREEN), // Green §a
+ formatHexAnsi(NamedTextColor.AQUA), // Aqua §b
+ formatHexAnsi(NamedTextColor.RED), // Red §c
+ formatHexAnsi(NamedTextColor.LIGHT_PURPLE), // Light Purple §d
+ formatHexAnsi(NamedTextColor.YELLOW), // Yellow §e
+ formatHexAnsi(NamedTextColor.WHITE), // White §f
+ "\u001B[5m", // Obfuscated §k
+ "\u001B[21m", // Bold §l
+ "\u001B[9m", // Strikethrough §m
+ "\u001B[4m", // Underline §n
+ "\u001B[3m", // Italic §o
+ ANSI_RESET, // Reset §r
+ };
+ private static final String[] ANSI_ANSI_CODES = new String[]{
2021-06-11 14:02:28 +02:00
+ "\u001B[0;30m", // Black §0
+ "\u001B[0;34m", // Dark Blue §1
+ "\u001B[0;32m", // Dark Green §2
+ "\u001B[0;36m", // Dark Aqua §3
+ "\u001B[0;31m", // Dark Red §4
+ "\u001B[0;35m", // Dark Purple §5
+ "\u001B[0;33m", // Gold §6
+ "\u001B[0;37m", // Gray §7
+ "\u001B[0;30;1m", // Dark Gray §8
+ "\u001B[0;34;1m", // Blue §9
+ "\u001B[0;32;1m", // Green §a
+ "\u001B[0;36;1m", // Aqua §b
+ "\u001B[0;31;1m", // Red §c
+ "\u001B[0;35;1m", // Light Purple §d
+ "\u001B[0;33;1m", // Yellow §e
+ "\u001B[0;37;1m", // White §f
+ "\u001B[5m", // Obfuscated §k
+ "\u001B[21m", // Bold §l
+ "\u001B[9m", // Strikethrough §m
+ "\u001B[4m", // Underline §n
+ "\u001B[3m", // Italic §o
+ ANSI_RESET, // Reset §r
+ };
+
+ private final boolean ansi;
+ private final List<PatternFormatter> formatters;
+
+ /**
+ * Construct the converter.
+ *
+ * @param formatters The pattern formatters to generate the text to manipulate
+ * @param strip If true, the converter will strip all formatting codes
+ */
+ protected HexFormattingConverter(List<PatternFormatter> formatters, boolean strip) {
+ super("paperMinecraftFormatting", null);
+ this.formatters = formatters;
+ this.ansi = !strip;
+ }
+
+ @Override
+ public void format(LogEvent event, StringBuilder toAppendTo) {
+ int start = toAppendTo.length();
+ //noinspection ForLoopReplaceableByForEach
+ for (int i = 0, size = formatters.size(); i < size; i++) {
+ formatters.get(i).format(event, toAppendTo);
+ }
+
+ if (KEEP_FORMATTING || toAppendTo.length() == start) {
+ // Skip replacement if disabled or if the content is empty
+ return;
+ }
+
+ boolean useAnsi = ansi && TerminalConsoleAppender.isAnsiSupported();
+ String content = toAppendTo.substring(start);
+ content = useAnsi ? convertRGBColors(content) : stripRGBColors(content);
+ format(content, toAppendTo, start, useAnsi);
+ }
+
2022-03-12 22:31:21 +01:00
+ private static String convertRGBColors(final String input) {
+ return RGB_PATTERN.matcher(input).replaceAll(result -> {
+ final int hex = Integer.decode(result.group().substring(1));
+ return formatHexAnsi(hex);
+ });
2021-06-11 14:02:28 +02:00
+ }
+
2022-03-12 22:31:21 +01:00
+ private static String formatHexAnsi(final TextColor color) {
+ return formatHexAnsi(color.value());
+ }
+
+ private static String formatHexAnsi(final int color) {
+ final int red = color >> 16 & 0xFF;
+ final int green = color >> 8 & 0xFF;
+ final int blue = color & 0xFF;
+ return String.format(RGB_ANSI, red, green, blue);
+ }
+
+ private static String stripRGBColors(final String input) {
+ return RGB_PATTERN.matcher(input).replaceAll("");
2021-06-11 14:02:28 +02:00
+ }
+
+ static void format(String content, StringBuilder result, int start, boolean ansi) {
+ int next = content.indexOf(COLOR_CHAR);
+ int last = content.length() - 1;
+ if (next == -1 || next == last) {
+ result.setLength(start);
+ result.append(content);
+ if (ansi) {
+ result.append(ANSI_RESET);
+ }
+ return;
+ }
+
+ Matcher matcher = NAMED_PATTERN.matcher(content);
2022-03-12 22:31:21 +01:00
+ StringBuilder buffer = new StringBuilder();
2022-06-09 10:51:45 +02:00
+ final String[] ansiCodes = GlobalConfiguration.get().logging.useRgbForNamedTextColors ? RGB_ANSI_CODES : ANSI_ANSI_CODES;
2021-06-11 14:02:28 +02:00
+ while (matcher.find()) {
+ int format = LOOKUP.indexOf(Character.toLowerCase(matcher.group().charAt(1)));
+ if (format != -1) {
+ matcher.appendReplacement(buffer, ansi ? ansiCodes[format] : "");
+ }
+ }
+ matcher.appendTail(buffer);
+
+ result.setLength(start);
2022-03-12 22:31:21 +01:00
+ result.append(buffer);
2021-06-11 14:02:28 +02:00
+ if (ansi) {
+ result.append(ANSI_RESET);
+ }
+ }
+
+ /**
+ * Gets a new instance of the {@link HexFormattingConverter} with the
+ * specified options.
+ *
+ * @param config The current configuration
+ * @param options The pattern options
+ * @return The new instance
+ * @see HexFormattingConverter
+ */
+ public static HexFormattingConverter newInstance(Configuration config, String[] options) {
+ if (options.length < 1 || options.length > 2) {
+ LOGGER.error("Incorrect number of options on paperMinecraftFormatting. Expected at least 1, max 2 received " + options.length);
+ return null;
+ }
+ if (options[0] == null) {
+ LOGGER.error("No pattern supplied on paperMinecraftFormatting");
+ return null;
+ }
+
+ PatternParser parser = PatternLayout.createPatternParser(config);
+ List<PatternFormatter> formatters = parser.parse(options[0]);
+ boolean strip = options.length > 1 && "strip".equals(options[1]);
+ return new HexFormattingConverter(formatters, strip);
+ }
+
+}
2022-03-13 02:47:04 +01:00
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
2022-06-12 23:07:40 +02:00
index 9e943c5d3a5ee91bc8a3f77d14e1413d0a1ecdb7..d61acd95810f735245b15555f9bf2022e5bc3683 100644
2022-03-13 02:47:04 +01:00
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
2022-06-08 09:40:34 +02:00
@@ -1670,7 +1670,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
2022-03-13 02:47:04 +01:00
@Override
2022-06-08 09:40:34 +02:00
public void sendSystemMessage(Component message) {
2022-05-26 21:12:14 +02:00
- MinecraftServer.LOGGER.info(net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().serialize(io.papermc.paper.adventure.PaperAdventure.asAdventure(message))); // Paper - Log message with colors
2022-03-13 02:47:04 +01:00
+ MinecraftServer.LOGGER.info(io.papermc.paper.console.HexFormattingConverter.SERIALIZER.serialize(io.papermc.paper.adventure.PaperAdventure.asAdventure(message))); // Paper - Log message with colors
}
public KeyPair getKeyPair() {
2021-06-11 14:02:28 +02:00
diff --git a/src/main/resources/log4j2.xml b/src/main/resources/log4j2.xml
2021-09-09 18:57:16 +02:00
index 1a05d23ff886b015fb9396f119822c678a47ec6f..2e421eaac80cf251b32e0bb504dd54a73edf4986 100644
2021-06-11 14:02:28 +02:00
--- a/src/main/resources/log4j2.xml
+++ b/src/main/resources/log4j2.xml
@@ -6,21 +6,21 @@
</Queue>
<TerminalConsole name="TerminalConsole">
<PatternLayout>
- <LoggerNamePatternSelector defaultPattern="%highlightError{[%d{HH:mm:ss} %level]: [%logger] %minecraftFormatting{%msg}%n%xEx{full}}">
+ <LoggerNamePatternSelector defaultPattern="%highlightError{[%d{HH:mm:ss} %level]: [%logger] %paperMinecraftFormatting{%msg}%n%xEx{full}}">
<!-- Log root, Minecraft, Mojang and Bukkit loggers without prefix -->
<!-- Disable prefix for various plugins that bypass the plugin logger -->
<PatternMatch key=",net.minecraft.,Minecraft,com.mojang.,com.sk89q.,ru.tehkode.,Minecraft.AWE"
- pattern="%highlightError{[%d{HH:mm:ss} %level]: %minecraftFormatting{%msg}%n%xEx{full}}" />
+ pattern="%highlightError{[%d{HH:mm:ss} %level]: %paperMinecraftFormatting{%msg}%n%xEx{full}}" />
</LoggerNamePatternSelector>
</PatternLayout>
</TerminalConsole>
<RollingRandomAccessFile name="File" fileName="logs/latest.log" filePattern="logs/%d{yyyy-MM-dd}-%i.log.gz">
<PatternLayout>
- <LoggerNamePatternSelector defaultPattern="[%d{HH:mm:ss}] [%t/%level]: [%logger] %minecraftFormatting{%msg}{strip}%n%xEx{full}">
+ <LoggerNamePatternSelector defaultPattern="[%d{HH:mm:ss}] [%t/%level]: [%logger] %paperMinecraftFormatting{%msg}{strip}%n%xEx{full}">
<!-- Log root, Minecraft, Mojang and Bukkit loggers without prefix -->
<!-- Disable prefix for various plugins that bypass the plugin logger -->
<PatternMatch key=",net.minecraft.,Minecraft,com.mojang.,com.sk89q.,ru.tehkode.,Minecraft.AWE"
- pattern="[%d{HH:mm:ss}] [%t/%level]: %minecraftFormatting{%msg}{strip}%n%xEx{full}" />
+ pattern="[%d{HH:mm:ss}] [%t/%level]: %paperMinecraftFormatting{%msg}{strip}%n%xEx{full}" />
</LoggerNamePatternSelector>
</PatternLayout>
<Policies>