Paper/patches/unapplied/server/0576-Add-support-for-hex-color-codes-in-console.patch

328 lines
17 KiB
Diff
Raw Normal View History

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.
diff --git a/src/main/java/com/destroystokyo/paper/console/TerminalConsoleCommandSender.java b/src/main/java/com/destroystokyo/paper/console/TerminalConsoleCommandSender.java
index 685deaa0e5d1ddc13e3a7c0471b1cfcf1710c869..8f07539a82f449ad217e316a7513a1708781fb63 100644
--- a/src/main/java/com/destroystokyo/paper/console/TerminalConsoleCommandSender.java
+++ b/src/main/java/com/destroystokyo/paper/console/TerminalConsoleCommandSender.java
@@ -1,16 +1,25 @@
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.apache.logging.log4j.Logger;
import org.bukkit.craftbukkit.command.CraftConsoleCommandSender;
public class TerminalConsoleCommandSender extends CraftConsoleCommandSender {
- private static final Logger LOGGER = LogManager.getRootLogger();
+ private static final ComponentLogger LOGGER = ComponentLogger.logger(LogManager.getRootLogger().getName());
@Override
public void sendRawMessage(String message) {
- // TerminalConsoleAppender supports color codes directly in log messages
+ 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/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()));
}
}
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
index 0000000000000000000000000000000000000000..b9922b07cb105618390187d98acdf89e728e1f5a
2021-06-11 14:02:28 +02:00
--- /dev/null
+++ b/src/main/java/io/papermc/paper/console/HexFormattingConverter.java
@@ -0,0 +1,213 @@
2021-06-11 14:02:28 +02:00
+package io.papermc.paper.console;
+
+import io.papermc.paper.configuration.GlobalConfiguration;
2022-03-13 02:47:04 +01:00
+import io.papermc.paper.adventure.PaperAdventure;
+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;
+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">
+ * 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)
+@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 String RESET_RGB_ANSI = ANSI_RESET + RGB_ANSI;
2021-06-11 14:02:28 +02:00
+ private static final Pattern NAMED_PATTERN = Pattern.compile(COLOR_CHAR + "[0-9a-fk-orA-FK-OR]");
+ private static final Pattern RGB_PATTERN = Pattern.compile(COLOR_CHAR + "#([0-9a-fA-F]){6}");
2021-06-11 14:02:28 +02: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[1m", // 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[]{
+ ANSI_RESET + "\u001B[0;30m", // Black §0
+ ANSI_RESET + "\u001B[0;34m", // Dark Blue §1
+ ANSI_RESET + "\u001B[0;32m", // Dark Green §2
+ ANSI_RESET + "\u001B[0;36m", // Dark Aqua §3
+ ANSI_RESET + "\u001B[0;31m", // Dark Red §4
+ ANSI_RESET + "\u001B[0;35m", // Dark Purple §5
+ ANSI_RESET + "\u001B[0;33m", // Gold §6
+ ANSI_RESET + "\u001B[0;37m", // Gray §7
+ ANSI_RESET + "\u001B[0;30;1m", // Dark Gray §8
+ ANSI_RESET + "\u001B[0;34;1m", // Blue §9
+ ANSI_RESET + "\u001B[0;32;1m", // Green §a
+ ANSI_RESET + "\u001B[0;36;1m", // Aqua §b
+ ANSI_RESET + "\u001B[0;31;1m", // Red §c
+ ANSI_RESET + "\u001B[0;35;1m", // Light Purple §d
+ ANSI_RESET + "\u001B[0;33;1m", // Yellow §e
+ ANSI_RESET + "\u001B[0;37;1m", // White §f
+ "\u001B[5m", // Obfuscated §k
+ "\u001B[1m", // Bold §l
+ "\u001B[9m", // Strikethrough §m
+ "\u001B[4m", // Underline §n
+ "\u001B[3m", // Italic §o
+ ANSI_RESET, // Reset §r
2021-06-11 14:02:28 +02:00
+ };
+
+ 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);
+ }
+
+ 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
+ }
+
+ 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(RESET_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);
+ StringBuilder buffer = new StringBuilder();
+ 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);
+ 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-11-03 22:03:31 +01:00
index 2f8026fffde9976e28d3263cbc4296a51fce8221..64dd0211ac2ceed34dba900b5af3a56ac5a4a65a 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
Rewrite chunk system (#8177) Patch documentation to come Issues with the old system that are fixed now: - World generation does not scale with cpu cores effectively. - Relies on the main thread for scheduling and maintaining chunk state, dropping chunk load/generate rates at lower tps. - Unreliable prioritisation of chunk gen/load calls that block the main thread. - Shutdown logic is utterly unreliable, as it has to wait for all chunks to unload - is it guaranteed that the chunk system is in a state on shutdown that it can reliably do this? Watchdog shutdown also typically failed due to thread checks, which is now resolved. - Saving of data is not unified (i.e can save chunk data without saving entity data, poses problems for desync if shutdown is really abnormal. - Entities are not loaded with chunks. This caused quite a bit of headache for Chunk#getEntities API, but now the new chunk system loads entities with chunks so that they are ready whenever the chunk loads in. Effectively brings the behavior back to 1.16 era, but still storing entities in their own separate regionfiles. The above list is not complete. The patch documentation will complete it. New chunk system hard relies on starlight and dataconverter, and most importantly the new concurrent utilities in ConcurrentUtil. Some of the old async chunk i/o interface (i.e the old file io thread reroutes _some_ calls to the new file io thread) is kept for plugin compat reasons. It will be removed in the next major version of minecraft. The old legacy chunk system patches have been moved to the removed folder in case we need them again.
2022-09-26 10:02:51 +02:00
@@ -1682,7 +1682,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) {
- 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
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>