Purpur/patches/server/0300-Make-GUI-Great-Again.patch
granny 969064029c
Updated Upstream (Paper)
Upstream has released updates that appear to apply and compile correctly

Paper Changes:
PaperMC/Paper@d129162 Properly handle BlockBreakEvent#isDropItems (#8936)
PaperMC/Paper@67a1a8c Fix Inventory#getLocation not working for the composter (#9596)
PaperMC/Paper@5b34a09 Add option to disable chorus plant and mushroom block updates (#9442)
PaperMC/Paper@ed8401c Fix rotation for spawning display entities (#9552)
PaperMC/Paper@bd67b83 Determine lava and water fluid explosion resistance by their block explosion resistance (#9482)
PaperMC/Paper@d928dda Don't tab-complete namespaced commands if `send-namespaced` is false (#9366)
PaperMC/Paper@f43a19c Fix CME in CraftPersistentDataTypeRegistry (#6701)
PaperMC/Paper@45ba652 [ci skip] Rebuild patches
PaperMC/Paper@990765b Fire entity death event for ender dragon (#9495)
PaperMC/Paper@1259b93 Fixed CraftItemStack/CraftItemMeta enchantment level inconsistency (#8792)
PaperMC/Paper@88891c3  Add Entity Coordinate and Rotation API (#9461)
PaperMC/Paper@5bf82aa Add cancellability to PlayerTrackEntityEvent (#8605)
PaperMC/Paper@1b96c64 Improve cancelling PreCreatureSpawnEvent with per player mob spawns (#9400)
PaperMC/Paper@7232506 Add entity tracker min Y distance config option (#9406)
PaperMC/Paper@00a68b1 Player listing API (#8782)
PaperMC/Paper@af6142e [ci skip] Update Gradle wrapper
PaperMC/Paper@281855c Remove unused lag-compensate-block-breaking option (#9635)
PaperMC/Paper@35ef053 [ci skip] update editorconfig and gitattributes (#9608)
PaperMC/Paper@97b9c4a Fix NPE on Boat#getStatus (#9115)
PaperMC/Paper@2be57c6 Expand Pose API (#8781)
PaperMC/Paper@b29ecd4 Expose clicked BlockFace during BlockDamageEvent (#9632)
PaperMC/Paper@e3f29f4 MerchantRecipe: add copy constructor (#8703)
PaperMC/Paper@fed9042 More DragonBattle API (#5655)
PaperMC/Paper@27d39ca Properly clone custom nbt tags inside ItemMeta (#7849)
PaperMC/Paper@0c7385b Add PlayerPickItemEvent (#5590)
PaperMC/Paper@9395aa6 Improve performance of mass crafts (#9603)
PaperMC/Paper@7386a13 Fix endgateway teleportation offset (#9517)
PaperMC/Paper@a712766 Allow custom damage for trident (#8132)
PaperMC/Paper@3716832 Prevent overfilled bundles from duplicating items (#9633)
PaperMC/Paper@9cbad8e Expose Hand during BlockCanBuildEvent (#9636)
2023-08-22 18:41:13 -07:00

413 lines
17 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: William Blake Galbreath <blake.galbreath@gmail.com>
Date: Thu, 16 Jan 2020 14:59:16 -0600
Subject: [PATCH] Make GUI Great Again
diff --git a/src/log4jPlugins/java/org/purpurmc/purpur/gui/HighlightErrorConverter.java b/src/log4jPlugins/java/org/purpurmc/purpur/gui/HighlightErrorConverter.java
new file mode 100644
index 0000000000000000000000000000000000000000..15a226e3854d731f7724025ea3459c8ace07630c
--- /dev/null
+++ b/src/log4jPlugins/java/org/purpurmc/purpur/gui/HighlightErrorConverter.java
@@ -0,0 +1,85 @@
+package org.purpurmc.purpur.gui.util;
+
+import org.apache.logging.log4j.Level;
+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 org.apache.logging.log4j.util.PerformanceSensitive;
+
+import java.util.List;
+
+@Plugin(name = "highlightGUIError", category = PatternConverter.CATEGORY)
+@ConverterKeys({"highlightGUIError"})
+@PerformanceSensitive("allocation")
+public final class HighlightErrorConverter extends LogEventPatternConverter {
+ private static final String ERROR = "\u00A74\u00A7l"; // Bold Red
+ private static final String WARN = "\u00A7e\u00A7l"; // Bold Yellow
+
+ private final List<PatternFormatter> formatters;
+
+ private HighlightErrorConverter(List<PatternFormatter> formatters) {
+ super("highlightGUIError", null);
+ this.formatters = formatters;
+ }
+
+ @Override
+ public void format(LogEvent event, StringBuilder toAppendTo) {
+ Level level = event.getLevel();
+ if (level.isMoreSpecificThan(Level.ERROR)) {
+ format(ERROR, event, toAppendTo);
+ return;
+ } else if (level.isMoreSpecificThan(Level.WARN)) {
+ format(WARN, event, toAppendTo);
+ return;
+ }
+ for (PatternFormatter formatter : formatters) {
+ formatter.format(event, toAppendTo);
+ }
+ }
+
+ private void format(String style, LogEvent event, StringBuilder toAppendTo) {
+ int start = toAppendTo.length();
+ toAppendTo.append(style);
+ int end = toAppendTo.length();
+
+ for (PatternFormatter formatter : formatters) {
+ formatter.format(event, toAppendTo);
+ }
+
+ if (toAppendTo.length() == end) {
+ toAppendTo.setLength(start);
+ }
+ }
+
+ @Override
+ public boolean handlesThrowable() {
+ for (final PatternFormatter formatter : formatters) {
+ if (formatter.handlesThrowable()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static HighlightErrorConverter newInstance(Configuration config, String[] options) {
+ if (options.length != 1) {
+ LOGGER.error("Incorrect number of options on highlightGUIError. Expected 1 received " + options.length);
+ return null;
+ }
+
+ if (options[0] == null) {
+ LOGGER.error("No pattern supplied on highlightGUIError");
+ return null;
+ }
+
+ PatternParser parser = PatternLayout.createPatternParser(config);
+ List<PatternFormatter> formatters = parser.parse(options[0]);
+ return new HighlightErrorConverter(formatters);
+ }
+}
diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
index 41db72915baa4c02b11a701dfdde09b66ba72476..12124d6fbc0406bb62bd95a0f7bab68afa43377c 100644
--- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
+++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
@@ -99,6 +99,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
return;
}
// Paper start - Use TerminalConsoleAppender
+ if (DedicatedServer.this.gui == null || System.console() != null) // Purpur - has no GUI or has console (did not double-click)
new com.destroystokyo.paper.console.PaperConsole(DedicatedServer.this).start();
/*
jline.console.ConsoleReader bufferedreader = reader;
diff --git a/src/main/java/net/minecraft/server/gui/MinecraftServerGui.java b/src/main/java/net/minecraft/server/gui/MinecraftServerGui.java
index dd9f611efc95f7d06fd3011fedd5d0317b1d0a85..be7b3fe2dc84493dcde9e185717b0b7c7c2e9822 100644
--- a/src/main/java/net/minecraft/server/gui/MinecraftServerGui.java
+++ b/src/main/java/net/minecraft/server/gui/MinecraftServerGui.java
@@ -43,6 +43,11 @@ public class MinecraftServerGui extends JComponent {
private Thread logAppenderThread;
private final Collection<Runnable> finalizers = Lists.newArrayList();
final AtomicBoolean isClosing = new AtomicBoolean();
+ // Purpur start
+ private final CommandHistory history = new CommandHistory();
+ private String currentCommand = "";
+ private int historyIndex = 0;
+ // Purpur end
public static MinecraftServerGui showFrameFor(final DedicatedServer server) {
try {
@@ -51,7 +56,7 @@ public class MinecraftServerGui extends JComponent {
;
}
- final JFrame jframe = new JFrame("Minecraft server");
+ final JFrame jframe = new JFrame("Purpur Minecraft server"); // Purpur
final MinecraftServerGui servergui = new MinecraftServerGui(server);
jframe.setDefaultCloseOperation(2);
@@ -59,7 +64,7 @@ public class MinecraftServerGui extends JComponent {
jframe.pack();
jframe.setLocationRelativeTo((Component) null);
jframe.setVisible(true);
- jframe.setName("Minecraft server"); // Paper
+ jframe.setName("Purpur Minecraft server"); // Paper // Purpur
// Paper start - Add logo as frame image
try {
@@ -71,7 +76,7 @@ public class MinecraftServerGui extends JComponent {
jframe.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent windowevent) {
if (!servergui.isClosing.getAndSet(true)) {
- jframe.setTitle("Minecraft server - shutting down!");
+ jframe.setTitle("Purpur Minecraft server - shutting down!"); // Purpur
server.halt(true);
servergui.runFinalizers();
}
@@ -125,7 +130,7 @@ public class MinecraftServerGui extends JComponent {
private JComponent buildChatPanel() {
JPanel jpanel = new JPanel(new BorderLayout());
- JTextArea jtextarea = new JTextArea();
+ org.purpurmc.purpur.gui.JColorTextPane jtextarea = new org.purpurmc.purpur.gui.JColorTextPane(); // Purpur
JScrollPane jscrollpane = new JScrollPane(jtextarea, 22, 30);
jtextarea.setEditable(false);
@@ -137,10 +142,43 @@ public class MinecraftServerGui extends JComponent {
if (!s.isEmpty()) {
this.server.handleConsoleInput(s, this.server.createCommandSourceStack());
+ // Purpur start
+ history.add(s);
+ historyIndex = -1;
+ // Purpur end
}
jtextfield.setText("");
});
+ // Purpur start
+ jtextfield.getInputMap().put(javax.swing.KeyStroke.getKeyStroke("UP"), "up");
+ jtextfield.getInputMap().put(javax.swing.KeyStroke.getKeyStroke("DOWN"), "down");
+ jtextfield.getActionMap().put("up", new javax.swing.AbstractAction() {
+ @Override
+ public void actionPerformed(java.awt.event.ActionEvent actionEvent) {
+ if (historyIndex < 0) {
+ currentCommand = jtextfield.getText();
+ }
+ if (historyIndex < history.size() - 1) {
+ jtextfield.setText(history.get(historyIndex));
+ }
+ }
+ });
+ jtextfield.getActionMap().put("down", new javax.swing.AbstractAction() {
+ @Override
+ public void actionPerformed(java.awt.event.ActionEvent actionEvent) {
+ if (historyIndex >= 0) {
+ if (historyIndex == 0) {
+ --historyIndex;
+ jtextfield.setText(currentCommand);
+ } else {
+ --historyIndex;
+ jtextfield.setText(history.get(historyIndex));
+ }
+ }
+ }
+ });
+ // Purpur end
jtextarea.addFocusListener(new FocusAdapter() {
public void focusGained(FocusEvent focusevent) {}
});
@@ -176,7 +214,7 @@ public class MinecraftServerGui extends JComponent {
}
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) {
+ public void print(org.purpurmc.purpur.gui.JColorTextPane textArea, JScrollPane scrollPane, String message) { // Purpur
if (!SwingUtilities.isEventDispatchThread()) {
SwingUtilities.invokeLater(() -> {
this.print(textArea, scrollPane, message);
@@ -190,11 +228,14 @@ public class MinecraftServerGui extends JComponent {
flag = (double) jscrollbar.getValue() + jscrollbar.getSize().getHeight() + (double) (MinecraftServerGui.MONOSPACED.getSize() * 4) > (double) jscrollbar.getMaximum();
}
+ /* // Purpur
try {
document.insertString(document.getLength(), MinecraftServerGui.ANSI.matcher(message).replaceAll(""), (AttributeSet) null); // CraftBukkit
} catch (BadLocationException badlocationexception) {
;
}
+ */ // Purpur
+ textArea.append(message); // Purpur
if (flag) {
jscrollbar.setValue(Integer.MAX_VALUE);
@@ -202,4 +243,16 @@ public class MinecraftServerGui extends JComponent {
}
}
+
+ // Purpur start
+ public static class CommandHistory extends java.util.LinkedList<String> {
+ @Override
+ public boolean add(String command) {
+ if (size() > 1000) {
+ remove();
+ }
+ return super.offerFirst(command);
+ }
+ }
+ // Purpur end
}
diff --git a/src/main/java/org/purpurmc/purpur/gui/GUIColor.java b/src/main/java/org/purpurmc/purpur/gui/GUIColor.java
new file mode 100644
index 0000000000000000000000000000000000000000..0f2e7e0b81620c8581949bd5f0bdb567cd93c17e
--- /dev/null
+++ b/src/main/java/org/purpurmc/purpur/gui/GUIColor.java
@@ -0,0 +1,54 @@
+package org.purpurmc.purpur.gui;
+
+import net.md_5.bungee.api.ChatColor;
+
+import java.awt.Color;
+import java.util.HashMap;
+import java.util.Map;
+
+public enum GUIColor {
+ BLACK(ChatColor.BLACK, new Color(0x000000)),
+ DARK_BLUE(ChatColor.DARK_BLUE, new Color(0x0000AA)),
+ DARK_GREEN(ChatColor.DARK_GREEN, new Color(0x00AA00)),
+ DARK_AQUA(ChatColor.DARK_AQUA, new Color(0x009999)),
+ DARK_RED(ChatColor.DARK_RED, new Color(0xAA0000)),
+ DARK_PURPLE(ChatColor.DARK_PURPLE, new Color(0xAA00AA)),
+ GOLD(ChatColor.GOLD, new Color(0xBB8800)),
+ GRAY(ChatColor.GRAY, new Color(0x888888)),
+ DARK_GRAY(ChatColor.DARK_GRAY, new Color(0x444444)),
+ BLUE(ChatColor.BLUE, new Color(0x5555FF)),
+ GREEN(ChatColor.GREEN, new Color(0x55FF55)),
+ AQUA(ChatColor.AQUA, new Color(0x55DDDD)),
+ RED(ChatColor.RED, new Color(0xFF5555)),
+ LIGHT_PURPLE(ChatColor.LIGHT_PURPLE, new Color(0xFF55FF)),
+ YELLOW(ChatColor.YELLOW, new Color(0xFFBB00)),
+ WHITE(ChatColor.WHITE, new Color(0xBBBBBB));
+
+ private final ChatColor chat;
+ private final Color color;
+
+ private static final Map<ChatColor, GUIColor> BY_CHAT = new HashMap<>();
+
+ GUIColor(ChatColor chat, Color color) {
+ this.chat = chat;
+ this.color = color;
+ }
+
+ public Color getColor() {
+ return color;
+ }
+
+ public String getCode() {
+ return chat.toString();
+ }
+
+ public static GUIColor getColor(ChatColor chat) {
+ return BY_CHAT.get(chat);
+ }
+
+ static {
+ for (GUIColor color : values()) {
+ BY_CHAT.put(color.chat, color);
+ }
+ }
+}
diff --git a/src/main/java/org/purpurmc/purpur/gui/JColorTextPane.java b/src/main/java/org/purpurmc/purpur/gui/JColorTextPane.java
new file mode 100644
index 0000000000000000000000000000000000000000..33e89b4c00fa8318506b36cbe49fe4e412e0a9a1
--- /dev/null
+++ b/src/main/java/org/purpurmc/purpur/gui/JColorTextPane.java
@@ -0,0 +1,78 @@
+package org.purpurmc.purpur.gui;
+
+import com.google.common.collect.Sets;
+import net.md_5.bungee.api.ChatColor;
+import net.md_5.bungee.api.chat.BaseComponent;
+import net.md_5.bungee.api.chat.TextComponent;
+
+import javax.swing.JTextPane;
+import javax.swing.Timer;
+import javax.swing.text.AttributeSet;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.SimpleAttributeSet;
+import javax.swing.text.StyleConstants;
+import javax.swing.text.StyleContext;
+import java.util.Set;
+
+public class JColorTextPane extends JTextPane {
+ private static final GUIColor DEFAULT_COLOR = GUIColor.BLACK;
+
+ public void append(String msg) {
+ // TODO: update to use adventure instead
+ BaseComponent[] components = TextComponent.fromLegacyText(DEFAULT_COLOR.getCode() + msg, ChatColor.BLACK);
+ for (BaseComponent component : components) {
+ String text = component.toPlainText();
+ if (text == null || text.isEmpty()) {
+ continue;
+ }
+
+ GUIColor guiColor = GUIColor.getColor(component.getColor());
+
+ StyleContext context = StyleContext.getDefaultStyleContext();
+ AttributeSet attr = context.addAttribute(SimpleAttributeSet.EMPTY, StyleConstants.Foreground, guiColor.getColor());
+ attr = context.addAttribute(attr, StyleConstants.CharacterConstants.Bold, component.isBold() || guiColor != DEFAULT_COLOR);
+ attr = context.addAttribute(attr, StyleConstants.CharacterConstants.Italic, component.isItalic());
+ attr = context.addAttribute(attr, StyleConstants.CharacterConstants.Underline, component.isUnderlined());
+ attr = context.addAttribute(attr, StyleConstants.CharacterConstants.StrikeThrough, component.isStrikethrough());
+ //attr = context.addAttribute(attr, StyleConstants.CharacterConstants.Blink, component.isObfuscated()); // no such thing as Blink, sadly
+
+ try {
+ int pos = getDocument().getLength();
+ getDocument().insertString(pos, text, attr);
+
+ if (component.isObfuscated()) {
+ // dirty hack to blink some text
+ Blink blink = new Blink(pos, text.length(), attr, context.addAttribute(attr, StyleConstants.Foreground, getBackground()));
+ BLINKS.add(blink);
+ }
+ } catch (BadLocationException ignore) {
+ }
+ }
+ }
+
+ private static final Set<Blink> BLINKS = Sets.newHashSet();
+ private static boolean SYNC_BLINK;
+
+ static {
+ new Timer(500, e -> {
+ SYNC_BLINK = !SYNC_BLINK;
+ BLINKS.forEach(Blink::blink);
+ }).start();
+ }
+
+ public class Blink {
+ private final int start, length;
+ private final AttributeSet attr1, attr2;
+
+ private Blink(int start, int length, AttributeSet attr1, AttributeSet attr2) {
+ this.start = start;
+ this.length = length;
+ this.attr1 = attr1;
+ this.attr2 = attr2;
+ }
+
+ private void blink() {
+ getStyledDocument().setCharacterAttributes(start, length, SYNC_BLINK ? attr1 : attr2, true);
+ }
+ }
+}
diff --git a/src/main/resources/log4j2.xml b/src/main/resources/log4j2.xml
index 675cd61221e807aadf28322b46c3daa1370241b5..0769f5c4711a3b7f59489e611ed01ad8367e5db1 100644
--- a/src/main/resources/log4j2.xml
+++ b/src/main/resources/log4j2.xml
@@ -2,7 +2,16 @@
<Configuration status="WARN" packages="com.mojang.util" shutdownHook="disable">
<Appenders>
<Queue name="ServerGuiConsole">
- <PatternLayout pattern="[%d{HH:mm:ss} %level]: %msg%n" />
+ <!-- Purpur start - copied from TerminalConsole -->
+ <PatternLayout>
+ <LoggerNamePatternSelector defaultPattern="%highlightGUIError{[%d{HH:mm:ss} %level]: [%logger] %stripAnsi{%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="%highlightGUIError{[%d{HH:mm:ss} %level]: %stripAnsi{%msg}%n%xEx{full}}" />
+ </LoggerNamePatternSelector>
+ </PatternLayout>
+ <!-- Purpur end -->
</Queue>
<TerminalConsole name="TerminalConsole">
<PatternLayout>