mirror of
https://github.com/PurpurMC/Purpur.git
synced 2026-02-17 16:37:43 +01:00
1189 lines
42 KiB
Diff
1189 lines
42 KiB
Diff
From d9d93f9f020802a8d08bc42879814775142b39e1 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 the GUI better
|
|
|
|
---
|
|
.../net/minecraft/server/DedicatedServer.java | 6 +-
|
|
.../net/minecraft/server/MinecraftServer.java | 1 +
|
|
.../java/net/pl3x/purpur/gui/ServerGUI.java | 123 +++++++++++++++
|
|
.../purpur/gui/console/JColorTextPane.java | 81 ++++++++++
|
|
.../purpur/gui/console/JConsolePanel.java | 128 ++++++++++++++++
|
|
.../gui/info/DetailsListSelectionModel.java | 21 +++
|
|
.../net/pl3x/purpur/gui/info/JInfoPanel.java | 42 +++++
|
|
.../net/pl3x/purpur/gui/info/RAMDetails.java | 59 +++++++
|
|
.../purpur/gui/info/graph/GraphColor.java | 44 ++++++
|
|
.../pl3x/purpur/gui/info/graph/GraphData.java | 47 ++++++
|
|
.../pl3x/purpur/gui/info/graph/RAMGraph.java | 144 ++++++++++++++++++
|
|
.../purpur/gui/playerlist/JPlayerList.java | 57 +++++++
|
|
.../playerlist/PlayerListCellRenderer.java | 22 +++
|
|
.../gui/playerlist/PlayerListModel.java | 47 ++++++
|
|
.../playerlist/PlayerListMouseAdapter.java | 32 ++++
|
|
.../net/pl3x/purpur/gui/util/GUIColor.java | 54 +++++++
|
|
.../purpur/util/HighlightErrorConverter.java | 86 +++++++++++
|
|
src/main/resources/log4j2.xml | 13 +-
|
|
18 files changed, 1002 insertions(+), 5 deletions(-)
|
|
create mode 100644 src/main/java/net/pl3x/purpur/gui/ServerGUI.java
|
|
create mode 100644 src/main/java/net/pl3x/purpur/gui/console/JColorTextPane.java
|
|
create mode 100644 src/main/java/net/pl3x/purpur/gui/console/JConsolePanel.java
|
|
create mode 100644 src/main/java/net/pl3x/purpur/gui/info/DetailsListSelectionModel.java
|
|
create mode 100644 src/main/java/net/pl3x/purpur/gui/info/JInfoPanel.java
|
|
create mode 100644 src/main/java/net/pl3x/purpur/gui/info/RAMDetails.java
|
|
create mode 100644 src/main/java/net/pl3x/purpur/gui/info/graph/GraphColor.java
|
|
create mode 100644 src/main/java/net/pl3x/purpur/gui/info/graph/GraphData.java
|
|
create mode 100644 src/main/java/net/pl3x/purpur/gui/info/graph/RAMGraph.java
|
|
create mode 100644 src/main/java/net/pl3x/purpur/gui/playerlist/JPlayerList.java
|
|
create mode 100644 src/main/java/net/pl3x/purpur/gui/playerlist/PlayerListCellRenderer.java
|
|
create mode 100644 src/main/java/net/pl3x/purpur/gui/playerlist/PlayerListModel.java
|
|
create mode 100644 src/main/java/net/pl3x/purpur/gui/playerlist/PlayerListMouseAdapter.java
|
|
create mode 100644 src/main/java/net/pl3x/purpur/gui/util/GUIColor.java
|
|
create mode 100644 src/main/java/net/pl3x/purpur/util/HighlightErrorConverter.java
|
|
|
|
diff --git a/src/main/java/net/minecraft/server/DedicatedServer.java b/src/main/java/net/minecraft/server/DedicatedServer.java
|
|
index 8c7156bb52..5337c0be08 100644
|
|
--- a/src/main/java/net/minecraft/server/DedicatedServer.java
|
|
+++ b/src/main/java/net/minecraft/server/DedicatedServer.java
|
|
@@ -51,7 +51,7 @@ public class DedicatedServer extends MinecraftServer implements IMinecraftServer
|
|
public DedicatedServerSettings propertyManager;
|
|
private EnumGamemode o;
|
|
@Nullable
|
|
- private ServerGUI p;
|
|
+ private net.pl3x.purpur.gui.ServerGUI p; // Purpur
|
|
|
|
// CraftBukkit start - Signature changed
|
|
public DedicatedServer(joptsimple.OptionSet options, DedicatedServerSettings dedicatedserversettings, DataFixer datafixer, YggdrasilAuthenticationService yggdrasilauthenticationservice, MinecraftSessionService minecraftsessionservice, GameProfileRepository gameprofilerepository, UserCache usercache, WorldLoadListenerFactory worldloadlistenerfactory, String s) {
|
|
@@ -421,7 +421,7 @@ public class DedicatedServer extends MinecraftServer implements IMinecraftServer
|
|
@Override
|
|
public void exit() {
|
|
if (this.p != null) {
|
|
- this.p.b();
|
|
+ this.p.close(); // Purpur
|
|
}
|
|
|
|
if (this.remoteControlListener != null) {
|
|
@@ -520,7 +520,7 @@ public class DedicatedServer extends MinecraftServer implements IMinecraftServer
|
|
|
|
public void bc() {
|
|
if (this.p == null) {
|
|
- this.p = ServerGUI.a(this);
|
|
+ this.p = net.pl3x.purpur.gui.ServerGUI.create(this); // Purpur
|
|
}
|
|
|
|
}
|
|
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
|
|
index af5dd9f2d3..f2a8af48df 100644
|
|
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
|
|
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
|
|
@@ -1289,6 +1289,7 @@ public abstract class MinecraftServer extends IAsyncTaskHandlerReentrant<TickTas
|
|
return true;
|
|
}
|
|
|
|
+ public void addTickable(Runnable tickable) { b(tickable); } // Purpur - OBFHELPER
|
|
public void b(Runnable runnable) {
|
|
this.tickables.add(runnable);
|
|
}
|
|
diff --git a/src/main/java/net/pl3x/purpur/gui/ServerGUI.java b/src/main/java/net/pl3x/purpur/gui/ServerGUI.java
|
|
new file mode 100644
|
|
index 0000000000..973b5efef5
|
|
--- /dev/null
|
|
+++ b/src/main/java/net/pl3x/purpur/gui/ServerGUI.java
|
|
@@ -0,0 +1,123 @@
|
|
+package net.pl3x.purpur.gui;
|
|
+
|
|
+import com.google.common.collect.Lists;
|
|
+import net.minecraft.server.DedicatedServer;
|
|
+import net.pl3x.purpur.gui.console.JConsolePanel;
|
|
+import net.pl3x.purpur.gui.info.JInfoPanel;
|
|
+import net.pl3x.purpur.gui.playerlist.JPlayerList;
|
|
+import org.apache.logging.log4j.LogManager;
|
|
+import org.apache.logging.log4j.Logger;
|
|
+
|
|
+import javax.swing.JComponent;
|
|
+import javax.swing.JFrame;
|
|
+import javax.swing.JPanel;
|
|
+import javax.swing.JScrollPane;
|
|
+import javax.swing.ScrollPaneConstants;
|
|
+import javax.swing.UIManager;
|
|
+import javax.swing.WindowConstants;
|
|
+import javax.swing.border.EtchedBorder;
|
|
+import javax.swing.border.TitledBorder;
|
|
+import java.awt.BorderLayout;
|
|
+import java.awt.Dimension;
|
|
+import java.awt.event.WindowAdapter;
|
|
+import java.awt.event.WindowEvent;
|
|
+import java.util.Collection;
|
|
+import java.util.concurrent.atomic.AtomicBoolean;
|
|
+
|
|
+public class ServerGUI extends JComponent {
|
|
+ private static final Logger LOGGER = LogManager.getLogger();
|
|
+
|
|
+ private final DedicatedServer server;
|
|
+
|
|
+ private final Collection<Runnable> finalizers = Lists.newArrayList();
|
|
+ private final AtomicBoolean isClosing = new AtomicBoolean();
|
|
+
|
|
+ private JConsolePanel consolePanel;
|
|
+
|
|
+ public static ServerGUI create(final DedicatedServer dedicatedserver) {
|
|
+ try {
|
|
+ UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
|
|
+ } catch (Exception ignore) {
|
|
+ }
|
|
+
|
|
+ JFrame window = new JFrame("Purpur Minecraft Server");
|
|
+ ServerGUI serverGUI = new ServerGUI(dedicatedserver);
|
|
+
|
|
+ window.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
|
|
+ window.add(serverGUI);
|
|
+ window.pack();
|
|
+ window.setLocationRelativeTo(null);
|
|
+ window.setVisible(true);
|
|
+
|
|
+ window.addWindowListener(new WindowAdapter() {
|
|
+ @Override
|
|
+ public void windowClosing(WindowEvent windowevent) {
|
|
+ if (!serverGUI.isClosing.getAndSet(true)) {
|
|
+ window.setTitle("Purpur Minecraft Server - Shutting Down!");
|
|
+ dedicatedserver.safeShutdown(true);
|
|
+ serverGUI.runFinalizers();
|
|
+ }
|
|
+
|
|
+ }
|
|
+ });
|
|
+
|
|
+ serverGUI.addFinalizer(window::dispose);
|
|
+ serverGUI.start();
|
|
+
|
|
+ return serverGUI;
|
|
+ }
|
|
+
|
|
+ private ServerGUI(DedicatedServer dedicatedserver) {
|
|
+ this.server = dedicatedserver;
|
|
+
|
|
+ setPreferredSize(new Dimension(854, 480));
|
|
+ setLayout(new BorderLayout());
|
|
+
|
|
+ consolePanel = new JConsolePanel(server);
|
|
+
|
|
+ try {
|
|
+ add(consolePanel, "Center");
|
|
+ add(buildInfoPanel(), "West");
|
|
+ } catch (Exception exception) {
|
|
+ LOGGER.error("Couldn't build server GUI", exception);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private void addFinalizer(Runnable runnable) {
|
|
+ finalizers.add(runnable);
|
|
+ }
|
|
+
|
|
+ private JComponent buildInfoPanel() {
|
|
+ JPanel jpanel = new JPanel(new BorderLayout());
|
|
+
|
|
+ JInfoPanel serverInfo = new JInfoPanel(server);
|
|
+ finalizers.add(serverInfo::stop);
|
|
+
|
|
+ jpanel.add(serverInfo, "North");
|
|
+ jpanel.add(buildPlayerPanel(), "Center");
|
|
+
|
|
+ jpanel.setBorder(new TitledBorder(new EtchedBorder(), "Stats"));
|
|
+ return jpanel;
|
|
+ }
|
|
+
|
|
+ private JComponent buildPlayerPanel() {
|
|
+ JScrollPane jscrollpane = new JScrollPane(new JPlayerList(server), ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
|
|
+ jscrollpane.setBorder(new TitledBorder(new EtchedBorder(), "Players"));
|
|
+ return jscrollpane;
|
|
+ }
|
|
+
|
|
+ public void start() {
|
|
+ consolePanel.start();
|
|
+ }
|
|
+
|
|
+ public void close() {
|
|
+ if (!isClosing.getAndSet(true)) {
|
|
+ runFinalizers();
|
|
+ }
|
|
+
|
|
+ }
|
|
+
|
|
+ private void runFinalizers() {
|
|
+ finalizers.forEach(Runnable::run);
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/net/pl3x/purpur/gui/console/JColorTextPane.java b/src/main/java/net/pl3x/purpur/gui/console/JColorTextPane.java
|
|
new file mode 100644
|
|
index 0000000000..55feec811f
|
|
--- /dev/null
|
|
+++ b/src/main/java/net/pl3x/purpur/gui/console/JColorTextPane.java
|
|
@@ -0,0 +1,81 @@
|
|
+package net.pl3x.purpur.gui.console;
|
|
+
|
|
+import net.md_5.bungee.api.ChatColor;
|
|
+import net.md_5.bungee.api.chat.BaseComponent;
|
|
+import net.md_5.bungee.api.chat.TextComponent;
|
|
+import net.pl3x.purpur.gui.util.GUIColor;
|
|
+
|
|
+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.HashSet;
|
|
+import java.util.Set;
|
|
+
|
|
+public class JColorTextPane extends JTextPane {
|
|
+ private static final GUIColor DEFAULT_COLOR = GUIColor.BLACK;
|
|
+
|
|
+ public void append(String str) {
|
|
+ BaseComponent[] components = TextComponent.fromLegacyText(DEFAULT_COLOR.getCode() + str, 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 e) {
|
|
+ e.printStackTrace();
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private static final Set<Blink> BLINKS = new HashSet<>();
|
|
+ private static boolean SYNC_BLINK;
|
|
+
|
|
+ static {
|
|
+ Timer timer = new Timer(500, e -> {
|
|
+ SYNC_BLINK = !SYNC_BLINK;
|
|
+ BLINKS.forEach(Blink::blink);
|
|
+ });
|
|
+ timer.start();
|
|
+ }
|
|
+
|
|
+ public class Blink {
|
|
+ private final int start, length;
|
|
+ private final AttributeSet attr1;
|
|
+ private final AttributeSet 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/java/net/pl3x/purpur/gui/console/JConsolePanel.java b/src/main/java/net/pl3x/purpur/gui/console/JConsolePanel.java
|
|
new file mode 100644
|
|
index 0000000000..fb017a0708
|
|
--- /dev/null
|
|
+++ b/src/main/java/net/pl3x/purpur/gui/console/JConsolePanel.java
|
|
@@ -0,0 +1,128 @@
|
|
+package net.pl3x.purpur.gui.console;
|
|
+
|
|
+import com.mojang.util.QueueLogAppender;
|
|
+import net.minecraft.server.DedicatedServer;
|
|
+import net.minecraft.server.DefaultUncaughtExceptionHandler;
|
|
+import org.apache.logging.log4j.LogManager;
|
|
+import org.apache.logging.log4j.Logger;
|
|
+
|
|
+import javax.swing.AbstractAction;
|
|
+import javax.swing.JPanel;
|
|
+import javax.swing.JScrollBar;
|
|
+import javax.swing.JScrollPane;
|
|
+import javax.swing.JTextField;
|
|
+import javax.swing.KeyStroke;
|
|
+import javax.swing.ScrollPaneConstants;
|
|
+import javax.swing.SwingUtilities;
|
|
+import javax.swing.border.EtchedBorder;
|
|
+import javax.swing.border.TitledBorder;
|
|
+import java.awt.BorderLayout;
|
|
+import java.awt.Font;
|
|
+import java.awt.event.ActionEvent;
|
|
+import java.util.LinkedList;
|
|
+
|
|
+public class JConsolePanel extends JPanel {
|
|
+ private static final Font MONOSPACED = new Font("Monospaced", Font.PLAIN, 12);
|
|
+ private static final Logger LOGGER = LogManager.getLogger();
|
|
+
|
|
+ private final CommandHistory history = new CommandHistory();
|
|
+ private String currentCommand = "";
|
|
+ private int historyIndex = 0;
|
|
+
|
|
+ private Thread logAppenderThread;
|
|
+
|
|
+ public JConsolePanel(DedicatedServer server) {
|
|
+ super(new BorderLayout());
|
|
+
|
|
+ JColorTextPane console = new JColorTextPane();
|
|
+ console.setEditable(false);
|
|
+ console.setFont(MONOSPACED);
|
|
+
|
|
+ JTextField jtextfield = new JTextField();
|
|
+ jtextfield.addActionListener((actionevent) -> {
|
|
+ String msg = jtextfield.getText().trim();
|
|
+ if (!msg.isEmpty()) {
|
|
+ server.issueCommand(msg, server.getServerCommandListener());
|
|
+ history.add(msg);
|
|
+ historyIndex = -1;
|
|
+ }
|
|
+ jtextfield.setText("");
|
|
+ });
|
|
+ jtextfield.getInputMap().put(KeyStroke.getKeyStroke("UP"), "up");
|
|
+ jtextfield.getInputMap().put(KeyStroke.getKeyStroke("DOWN"), "down");
|
|
+ jtextfield.getActionMap().put("up", new AbstractAction() {
|
|
+ @Override
|
|
+ public void actionPerformed(ActionEvent actionEvent) {
|
|
+ if (historyIndex < 0) {
|
|
+ currentCommand = jtextfield.getText();
|
|
+ }
|
|
+ if (historyIndex < history.size() - 1) {
|
|
+ jtextfield.setText(history.get(++historyIndex));
|
|
+ }
|
|
+ }
|
|
+ });
|
|
+ jtextfield.getActionMap().put("down", new AbstractAction() {
|
|
+ @Override
|
|
+ public void actionPerformed(ActionEvent actionEvent) {
|
|
+ if (historyIndex >= 0) {
|
|
+ if (historyIndex == 0) {
|
|
+ --historyIndex;
|
|
+ jtextfield.setText(currentCommand);
|
|
+ } else {
|
|
+ --historyIndex;
|
|
+ jtextfield.setText(history.get(historyIndex));
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ });
|
|
+
|
|
+ JScrollPane jscrollpane = new JScrollPane(console, ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
|
|
+
|
|
+ add(jscrollpane, "Center");
|
|
+ add(jtextfield, "South");
|
|
+ setBorder(new TitledBorder(new EtchedBorder(), "Console"));
|
|
+
|
|
+ logAppenderThread = new Thread(() -> {
|
|
+ String msg;
|
|
+ while ((msg = QueueLogAppender.getNextLogEvent("ServerGuiConsole")) != null) {
|
|
+ this.print(console, jscrollpane, msg);
|
|
+ }
|
|
+
|
|
+ });
|
|
+ logAppenderThread.setUncaughtExceptionHandler(new DefaultUncaughtExceptionHandler(LOGGER));
|
|
+ logAppenderThread.setDaemon(true);
|
|
+ }
|
|
+
|
|
+ public void start() {
|
|
+ logAppenderThread.start();
|
|
+ }
|
|
+
|
|
+ private void print(JColorTextPane console, JScrollPane jscrollpane, String str) {
|
|
+ if (!SwingUtilities.isEventDispatchThread()) {
|
|
+ SwingUtilities.invokeLater(() -> print(console, jscrollpane, str));
|
|
+ } else {
|
|
+ JScrollBar jscrollbar = jscrollpane.getVerticalScrollBar();
|
|
+ boolean scrollToBottom = false;
|
|
+
|
|
+ if (jscrollpane.getViewport().getView() == console) {
|
|
+ scrollToBottom = (double) jscrollbar.getValue() + jscrollbar.getSize().getHeight() + (double) (MONOSPACED.getSize() * 4) > (double) jscrollbar.getMaximum();
|
|
+ }
|
|
+
|
|
+ console.append(str);
|
|
+
|
|
+ if (scrollToBottom) {
|
|
+ jscrollbar.setValue(Integer.MAX_VALUE);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public static class CommandHistory extends LinkedList<String> {
|
|
+ @Override
|
|
+ public boolean add(String command) {
|
|
+ if (size() > 1000) {
|
|
+ remove();
|
|
+ }
|
|
+ return super.offerFirst(command);
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/net/pl3x/purpur/gui/info/DetailsListSelectionModel.java b/src/main/java/net/pl3x/purpur/gui/info/DetailsListSelectionModel.java
|
|
new file mode 100644
|
|
index 0000000000..4e0b80fddb
|
|
--- /dev/null
|
|
+++ b/src/main/java/net/pl3x/purpur/gui/info/DetailsListSelectionModel.java
|
|
@@ -0,0 +1,21 @@
|
|
+package net.pl3x.purpur.gui.info;
|
|
+
|
|
+import javax.swing.DefaultListSelectionModel;
|
|
+
|
|
+public class DetailsListSelectionModel extends DefaultListSelectionModel {
|
|
+ @Override
|
|
+ public void setAnchorSelectionIndex(final int anchorIndex) {
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void setLeadAnchorNotificationEnabled(final boolean flag) {
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void setLeadSelectionIndex(final int leadIndex) {
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void setSelectionInterval(final int index0, final int index1) {
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/net/pl3x/purpur/gui/info/JInfoPanel.java b/src/main/java/net/pl3x/purpur/gui/info/JInfoPanel.java
|
|
new file mode 100644
|
|
index 0000000000..c4903c7db6
|
|
--- /dev/null
|
|
+++ b/src/main/java/net/pl3x/purpur/gui/info/JInfoPanel.java
|
|
@@ -0,0 +1,42 @@
|
|
+package net.pl3x.purpur.gui.info;
|
|
+
|
|
+import net.minecraft.server.MinecraftServer;
|
|
+import net.pl3x.purpur.gui.info.graph.RAMGraph;
|
|
+
|
|
+import javax.swing.JPanel;
|
|
+import javax.swing.Timer;
|
|
+import java.awt.BorderLayout;
|
|
+import java.awt.Dimension;
|
|
+
|
|
+public class JInfoPanel extends JPanel {
|
|
+ private final Timer timer;
|
|
+ private final RAMGraph ramGraph;
|
|
+
|
|
+ public JInfoPanel(MinecraftServer server) {
|
|
+ super(new BorderLayout());
|
|
+
|
|
+ setOpaque(false);
|
|
+
|
|
+ ramGraph = new RAMGraph();
|
|
+ RAMDetails ramDetails = new RAMDetails(server);
|
|
+
|
|
+ add(ramGraph, "North");
|
|
+ add(ramDetails, "Center");
|
|
+
|
|
+ timer = new Timer(500, (event) -> {
|
|
+ ramGraph.update();
|
|
+ ramDetails.update();
|
|
+ });
|
|
+ timer.start();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public Dimension getPreferredSize() {
|
|
+ return new Dimension(350, 200);
|
|
+ }
|
|
+
|
|
+ public void stop() {
|
|
+ timer.stop();
|
|
+ ramGraph.stop();
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/net/pl3x/purpur/gui/info/RAMDetails.java b/src/main/java/net/pl3x/purpur/gui/info/RAMDetails.java
|
|
new file mode 100644
|
|
index 0000000000..1e049d575b
|
|
--- /dev/null
|
|
+++ b/src/main/java/net/pl3x/purpur/gui/info/RAMDetails.java
|
|
@@ -0,0 +1,59 @@
|
|
+package net.pl3x.purpur.gui.info;
|
|
+
|
|
+import net.minecraft.server.MinecraftServer;
|
|
+import net.minecraft.server.SystemUtils;
|
|
+import net.pl3x.purpur.gui.info.graph.GraphData;
|
|
+import net.pl3x.purpur.gui.info.graph.RAMGraph;
|
|
+import org.bukkit.Bukkit;
|
|
+
|
|
+import javax.swing.DefaultListCellRenderer;
|
|
+import javax.swing.JList;
|
|
+import javax.swing.border.EmptyBorder;
|
|
+import java.awt.Dimension;
|
|
+import java.text.DecimalFormat;
|
|
+import java.text.DecimalFormatSymbols;
|
|
+import java.util.Locale;
|
|
+import java.util.Vector;
|
|
+
|
|
+public class RAMDetails extends JList<String> {
|
|
+ private static final DecimalFormat DECIMAL_FORMAT = SystemUtils.a(new DecimalFormat("########0.000"), (format)
|
|
+ -> format.setDecimalFormatSymbols(DecimalFormatSymbols.getInstance(Locale.ROOT)));
|
|
+
|
|
+ private final MinecraftServer server;
|
|
+
|
|
+ public RAMDetails(MinecraftServer server) {
|
|
+ this.server = server;
|
|
+
|
|
+ setBorder(new EmptyBorder(0, 10, 0, 0));
|
|
+ setFixedCellHeight(20);
|
|
+ setOpaque(false);
|
|
+
|
|
+ DefaultListCellRenderer renderer = new DefaultListCellRenderer();
|
|
+ renderer.setOpaque(false);
|
|
+ setCellRenderer(renderer);
|
|
+
|
|
+ setSelectionModel(new DetailsListSelectionModel());
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public Dimension getPreferredSize() {
|
|
+ return new Dimension(350, 100);
|
|
+ }
|
|
+
|
|
+ public void update() {
|
|
+ GraphData data = RAMGraph.DATA.peekLast();
|
|
+ Vector<String> vector = new Vector<>();
|
|
+ vector.add("Memory use: " + (data.getUsedMem() / 1024L / 1024L) + " mb (" + (data.getFree() * 100L / data.getMax()) + "% free)");
|
|
+ vector.add("Heap: " + (data.getTotal() / 1024L / 1024L) + " / " + (data.getMax() / 1024L / 1024L) + " mb");
|
|
+ vector.add("Avg tick: " + DECIMAL_FORMAT.format(server.tickTimes5s.getAverage()) + " ms");
|
|
+ setListData(vector);
|
|
+ }
|
|
+
|
|
+ private double getAverage(long[] values) {
|
|
+ long total = 0L;
|
|
+ for (long value : values) {
|
|
+ total += value;
|
|
+ }
|
|
+ return (double) total / (double) values.length;
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/net/pl3x/purpur/gui/info/graph/GraphColor.java b/src/main/java/net/pl3x/purpur/gui/info/graph/GraphColor.java
|
|
new file mode 100644
|
|
index 0000000000..2fdb9bdd0d
|
|
--- /dev/null
|
|
+++ b/src/main/java/net/pl3x/purpur/gui/info/graph/GraphColor.java
|
|
@@ -0,0 +1,44 @@
|
|
+package net.pl3x.purpur.gui.info.graph;
|
|
+
|
|
+import java.awt.Color;
|
|
+
|
|
+public class GraphColor {
|
|
+ private static final Color[] colorLine = new Color[101];
|
|
+ private static final Color[] colorFill = new Color[101];
|
|
+
|
|
+ static {
|
|
+ for (int i = 0; i < 101; i++) {
|
|
+ Color color = createColor(i);
|
|
+ colorLine[i] = new Color(color.getRed() / 2, color.getGreen() / 2, color.getBlue() / 2, 255);
|
|
+ colorFill[i] = new Color(colorLine[i].getRed(), colorLine[i].getGreen(), colorLine[i].getBlue(), 125);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public static Color getLineColor(int percent) {
|
|
+ return colorLine[percent];
|
|
+ }
|
|
+
|
|
+ public static Color getFillColor(int percent) {
|
|
+ return colorFill[percent];
|
|
+ }
|
|
+
|
|
+ private static Color createColor(int percent) {
|
|
+ if (percent <= 50) {
|
|
+ return new Color(0X00FF00);
|
|
+ }
|
|
+
|
|
+ int value = 510 - (int) (Math.min(Math.max(0, ((percent - 50) / 50F)), 1) * 510);
|
|
+
|
|
+ int red, green;
|
|
+ if (value < 255) {
|
|
+ red = 255;
|
|
+ green = (int) (Math.sqrt(value) * 16);
|
|
+ } else {
|
|
+ green = 255;
|
|
+ value = value - 255;
|
|
+ red = 255 - (value * value / 255);
|
|
+ }
|
|
+
|
|
+ return new Color(red, green, 0);
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/net/pl3x/purpur/gui/info/graph/GraphData.java b/src/main/java/net/pl3x/purpur/gui/info/graph/GraphData.java
|
|
new file mode 100644
|
|
index 0000000000..85babad9cd
|
|
--- /dev/null
|
|
+++ b/src/main/java/net/pl3x/purpur/gui/info/graph/GraphData.java
|
|
@@ -0,0 +1,47 @@
|
|
+package net.pl3x.purpur.gui.info.graph;
|
|
+
|
|
+import java.awt.Color;
|
|
+
|
|
+public class GraphData {
|
|
+ private long total;
|
|
+ private long free;
|
|
+ private long max;
|
|
+ private long usedMem;
|
|
+ private int usedPercent;
|
|
+
|
|
+ public GraphData(long total, long free, long max) {
|
|
+ this.total = total;
|
|
+ this.free = free;
|
|
+ this.max = max;
|
|
+ this.usedMem = total - free;
|
|
+ this.usedPercent = usedMem == 0 ? 0 : (int) (usedMem * 100L / max);
|
|
+ }
|
|
+
|
|
+ public long getTotal() {
|
|
+ return total;
|
|
+ }
|
|
+
|
|
+ public long getFree() {
|
|
+ return free;
|
|
+ }
|
|
+
|
|
+ public long getMax() {
|
|
+ return max;
|
|
+ }
|
|
+
|
|
+ public long getUsedMem() {
|
|
+ return usedMem;
|
|
+ }
|
|
+
|
|
+ public int getUsedPercent() {
|
|
+ return usedPercent;
|
|
+ }
|
|
+
|
|
+ public Color getFillColor() {
|
|
+ return GraphColor.getFillColor(usedPercent);
|
|
+ }
|
|
+
|
|
+ public Color getLineColor() {
|
|
+ return GraphColor.getLineColor(usedPercent);
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/net/pl3x/purpur/gui/info/graph/RAMGraph.java b/src/main/java/net/pl3x/purpur/gui/info/graph/RAMGraph.java
|
|
new file mode 100644
|
|
index 0000000000..0c0c73beeb
|
|
--- /dev/null
|
|
+++ b/src/main/java/net/pl3x/purpur/gui/info/graph/RAMGraph.java
|
|
@@ -0,0 +1,144 @@
|
|
+package net.pl3x.purpur.gui.info.graph;
|
|
+
|
|
+import javax.swing.JComponent;
|
|
+import javax.swing.SwingUtilities;
|
|
+import javax.swing.Timer;
|
|
+import javax.swing.ToolTipManager;
|
|
+import java.awt.Color;
|
|
+import java.awt.Dimension;
|
|
+import java.awt.Graphics;
|
|
+import java.awt.MouseInfo;
|
|
+import java.awt.Point;
|
|
+import java.awt.PointerInfo;
|
|
+import java.awt.event.MouseAdapter;
|
|
+import java.awt.event.MouseEvent;
|
|
+import java.text.SimpleDateFormat;
|
|
+import java.util.Date;
|
|
+import java.util.LinkedList;
|
|
+import java.util.concurrent.TimeUnit;
|
|
+
|
|
+public class RAMGraph extends JComponent {
|
|
+ public static final LinkedList<GraphData> DATA = new LinkedList<GraphData>() {
|
|
+ @Override
|
|
+ public boolean add(GraphData data) {
|
|
+ if (size() >= 348) {
|
|
+ remove();
|
|
+ }
|
|
+ return super.add(data);
|
|
+ }
|
|
+ };
|
|
+
|
|
+ static {
|
|
+ GraphData empty = new GraphData(0, 0, 0);
|
|
+ for (int i = 0; i < 350; i++) {
|
|
+ DATA.add(empty);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private final Timer timer;
|
|
+ private final SimpleDateFormat TIME_FORMAT = new SimpleDateFormat("HH:mm:ss");
|
|
+
|
|
+ private int currentTick;
|
|
+
|
|
+ public RAMGraph() {
|
|
+ ToolTipManager.sharedInstance().setInitialDelay(0);
|
|
+
|
|
+ addMouseListener(new MouseAdapter() {
|
|
+ final int defaultDismissTimeout = ToolTipManager.sharedInstance().getDismissDelay();
|
|
+ final int dismissDelayMinutes = (int) TimeUnit.MINUTES.toMillis(10);
|
|
+
|
|
+ @Override
|
|
+ public void mouseEntered(MouseEvent me) {
|
|
+ ToolTipManager.sharedInstance().setDismissDelay(dismissDelayMinutes);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void mouseExited(MouseEvent me) {
|
|
+ ToolTipManager.sharedInstance().setDismissDelay(defaultDismissTimeout);
|
|
+ }
|
|
+ });
|
|
+
|
|
+ timer = new Timer(50, (event) -> repaint());
|
|
+ timer.start();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public Dimension getPreferredSize() {
|
|
+ return new Dimension(350, 110);
|
|
+ }
|
|
+
|
|
+ public void update() {
|
|
+ Runtime jvm = Runtime.getRuntime();
|
|
+ DATA.add(new GraphData(jvm.totalMemory(), jvm.freeMemory(), jvm.maxMemory()));
|
|
+
|
|
+ PointerInfo pointerInfo = MouseInfo.getPointerInfo();
|
|
+ if (pointerInfo != null) {
|
|
+ Point point = pointerInfo.getLocation();
|
|
+ if (point != null) {
|
|
+ Point loc = new Point(point);
|
|
+ SwingUtilities.convertPointFromScreen(loc, this);
|
|
+ if (this.contains(loc)) {
|
|
+ ToolTipManager.sharedInstance().mouseMoved(
|
|
+ new MouseEvent(this, -1, System.currentTimeMillis(), 0, loc.x, loc.y,
|
|
+ point.x, point.y, 0, false, 0));
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ currentTick++;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void paint(Graphics graphics) {
|
|
+ graphics.setColor(new Color(0xFFFFFFFF));
|
|
+ graphics.fillRect(0, 0, 350, 100);
|
|
+
|
|
+ graphics.setColor(new Color(0x888888));
|
|
+ graphics.drawLine(1, 25, 348, 25);
|
|
+ graphics.drawLine(1, 50, 348, 50);
|
|
+ graphics.drawLine(1, 75, 348, 75);
|
|
+
|
|
+ int i = 0;
|
|
+ for (GraphData data : DATA) {
|
|
+ i++;
|
|
+ if ((i + currentTick) % 120 == 0) {
|
|
+ graphics.setColor(new Color(0x888888));
|
|
+ graphics.drawLine(i, 1, i, 99);
|
|
+ }
|
|
+ int used = data.getUsedPercent();
|
|
+ if (used > 0) {
|
|
+ Color color = data.getLineColor();
|
|
+ graphics.setColor(data.getFillColor());
|
|
+ graphics.fillRect(i, 100 - used, 1, used);
|
|
+ graphics.setColor(color);
|
|
+ graphics.fillRect(i, 100 - used, 1, 1);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ graphics.setColor(new Color(0xFF000000));
|
|
+ graphics.drawRect(0, 0, 348, 100);
|
|
+
|
|
+ Point m = getMousePosition();
|
|
+ if (m != null && m.x > 0 && m.x < 348 && m.y > 0 && m.y < 100) {
|
|
+ GraphData data = DATA.get(m.x);
|
|
+ int used = data.getUsedPercent();
|
|
+ graphics.setColor(new Color(0x000000));
|
|
+ graphics.drawLine(m.x, 1, m.x, 99);
|
|
+ graphics.drawOval(m.x - 2, 100 - used - 2, 5, 5);
|
|
+ graphics.setColor(data.getLineColor());
|
|
+ graphics.fillOval(m.x - 2, 100 - used - 2, 5, 5);
|
|
+ setToolTipText(String.format("<html><body>Used: %s mb (%s%%)<br/>%s</body></html>",
|
|
+ Math.round(data.getUsedMem() / 1024F / 1024F),
|
|
+ used, getTime(m.x)));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public String getTime(int halfSeconds) {
|
|
+ int millis = (348 - halfSeconds) / 2 * 1000;
|
|
+ return TIME_FORMAT.format(new Date((System.currentTimeMillis() - millis)));
|
|
+ }
|
|
+
|
|
+ public void stop() {
|
|
+ timer.stop();
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/net/pl3x/purpur/gui/playerlist/JPlayerList.java b/src/main/java/net/pl3x/purpur/gui/playerlist/JPlayerList.java
|
|
new file mode 100644
|
|
index 0000000000..17e75a2070
|
|
--- /dev/null
|
|
+++ b/src/main/java/net/pl3x/purpur/gui/playerlist/JPlayerList.java
|
|
@@ -0,0 +1,57 @@
|
|
+package net.pl3x.purpur.gui.playerlist;
|
|
+
|
|
+import net.minecraft.server.EntityPlayer;
|
|
+import net.minecraft.server.MinecraftServer;
|
|
+
|
|
+import javax.swing.JList;
|
|
+import javax.swing.JMenuItem;
|
|
+import javax.swing.JPopupMenu;
|
|
+import javax.swing.ListSelectionModel;
|
|
+import java.awt.Dimension;
|
|
+
|
|
+public class JPlayerList extends JList<EntityPlayer> {
|
|
+ private final MinecraftServer server;
|
|
+ private final PlayerListModel model;
|
|
+ private final JPopupMenu popupMenu;
|
|
+ private int currentSelection = -1;
|
|
+ private int tickCount;
|
|
+
|
|
+ public JPlayerList(MinecraftServer server) {
|
|
+ this.server = server;
|
|
+ server.addTickable(this::tick);
|
|
+
|
|
+ setModel(model = new PlayerListModel(this));
|
|
+ setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
|
+ addListSelectionListener(event -> currentSelection = event.getFirstIndex());
|
|
+ addMouseListener(new PlayerListMouseAdapter(this));
|
|
+ setCellRenderer(new PlayerListCellRenderer());
|
|
+
|
|
+ popupMenu = new JPopupMenu();
|
|
+ popupMenu.add(new JMenuItem("Details"));
|
|
+ popupMenu.add(new JPopupMenu.Separator());
|
|
+ popupMenu.add(new JMenuItem("Kick"));
|
|
+ popupMenu.add(new JMenuItem("Ban"));
|
|
+ popupMenu.add(new JPopupMenu.Separator());
|
|
+ popupMenu.add(new JMenuItem("Op"));
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public Dimension getPreferredSize() {
|
|
+ Dimension superPref = super.getPreferredSize();
|
|
+ return new Dimension(330, superPref.height);
|
|
+ }
|
|
+
|
|
+ public void tick() {
|
|
+ if (tickCount++ % 20 == 0) {
|
|
+ model.update(server.getPlayerList().getPlayers());
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public int getCurrentSelection() {
|
|
+ return currentSelection;
|
|
+ }
|
|
+
|
|
+ public JPopupMenu getPopupMenu() {
|
|
+ return popupMenu;
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/net/pl3x/purpur/gui/playerlist/PlayerListCellRenderer.java b/src/main/java/net/pl3x/purpur/gui/playerlist/PlayerListCellRenderer.java
|
|
new file mode 100644
|
|
index 0000000000..2f02e9adc7
|
|
--- /dev/null
|
|
+++ b/src/main/java/net/pl3x/purpur/gui/playerlist/PlayerListCellRenderer.java
|
|
@@ -0,0 +1,22 @@
|
|
+package net.pl3x.purpur.gui.playerlist;
|
|
+
|
|
+import com.mojang.authlib.GameProfile;
|
|
+import net.minecraft.server.EntityPlayer;
|
|
+
|
|
+import javax.swing.DefaultListCellRenderer;
|
|
+import javax.swing.JList;
|
|
+import java.awt.Component;
|
|
+
|
|
+public class PlayerListCellRenderer extends DefaultListCellRenderer {
|
|
+ @Override
|
|
+ public Component getListCellRendererComponent(JList<?> list, Object player, int index, boolean isSelected, boolean cellHasFocus) {
|
|
+ super.getListCellRendererComponent(list, player, index, isSelected, cellHasFocus);
|
|
+ if (player instanceof EntityPlayer) {
|
|
+ GameProfile profile = ((EntityPlayer) player).getProfile();
|
|
+ setText(profile.getName());
|
|
+ setToolTipText(String.format("<html><body>%s<br/>%s</body></html>",
|
|
+ profile.getName(), profile.getId()));
|
|
+ }
|
|
+ return this;
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/net/pl3x/purpur/gui/playerlist/PlayerListModel.java b/src/main/java/net/pl3x/purpur/gui/playerlist/PlayerListModel.java
|
|
new file mode 100644
|
|
index 0000000000..edbf454163
|
|
--- /dev/null
|
|
+++ b/src/main/java/net/pl3x/purpur/gui/playerlist/PlayerListModel.java
|
|
@@ -0,0 +1,47 @@
|
|
+package net.pl3x.purpur.gui.playerlist;
|
|
+
|
|
+import net.minecraft.server.EntityPlayer;
|
|
+
|
|
+import javax.swing.DefaultListModel;
|
|
+import java.util.Collection;
|
|
+import java.util.HashSet;
|
|
+import java.util.Set;
|
|
+
|
|
+public class PlayerListModel extends DefaultListModel<EntityPlayer> {
|
|
+ private final Set<EntityPlayer> datas = new HashSet<>();
|
|
+ private final JPlayerList parent;
|
|
+
|
|
+ public PlayerListModel(JPlayerList parent) {
|
|
+ this.parent = parent;
|
|
+ }
|
|
+
|
|
+ public void update(Collection<EntityPlayer> players) {
|
|
+ boolean hadFocus = parent.hasFocus();
|
|
+
|
|
+ players.forEach(this::add);
|
|
+
|
|
+ Set<EntityPlayer> result = new HashSet<>(datas);
|
|
+ result.removeIf(players::contains);
|
|
+ result.forEach(this::remove);
|
|
+
|
|
+ if (parent.getCurrentSelection() >= 0) {
|
|
+ parent.setSelectedIndex(parent.getCurrentSelection());
|
|
+ }
|
|
+
|
|
+ if (hadFocus) {
|
|
+ parent.grabFocus();
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public void add(EntityPlayer player) {
|
|
+ if (datas.add(player)) {
|
|
+ addElement(player);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public void remove(EntityPlayer player) {
|
|
+ if (datas.remove(player)) {
|
|
+ removeElement(player);
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/net/pl3x/purpur/gui/playerlist/PlayerListMouseAdapter.java b/src/main/java/net/pl3x/purpur/gui/playerlist/PlayerListMouseAdapter.java
|
|
new file mode 100644
|
|
index 0000000000..13a5eb3ad3
|
|
--- /dev/null
|
|
+++ b/src/main/java/net/pl3x/purpur/gui/playerlist/PlayerListMouseAdapter.java
|
|
@@ -0,0 +1,32 @@
|
|
+package net.pl3x.purpur.gui.playerlist;
|
|
+
|
|
+import javax.swing.SwingUtilities;
|
|
+import java.awt.event.MouseAdapter;
|
|
+import java.awt.event.MouseEvent;
|
|
+
|
|
+public class PlayerListMouseAdapter extends MouseAdapter {
|
|
+ private final JPlayerList parent;
|
|
+
|
|
+ public PlayerListMouseAdapter(JPlayerList parent) {
|
|
+ this.parent = parent;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void mousePressed(MouseEvent event) {
|
|
+ if (!SwingUtilities.isRightMouseButton(event)) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ parent.setSelectedIndex(parent.locationToIndex(event.getPoint()));
|
|
+
|
|
+ if (parent.isSelectionEmpty()) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (parent.locationToIndex(event.getPoint()) != parent.getSelectedIndex()) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ parent.getPopupMenu().show(parent, event.getX(), event.getY());
|
|
+ }
|
|
+}
|
|
diff --git a/src/main/java/net/pl3x/purpur/gui/util/GUIColor.java b/src/main/java/net/pl3x/purpur/gui/util/GUIColor.java
|
|
new file mode 100644
|
|
index 0000000000..973c8ddf93
|
|
--- /dev/null
|
|
+++ b/src/main/java/net/pl3x/purpur/gui/util/GUIColor.java
|
|
@@ -0,0 +1,54 @@
|
|
+package net.pl3x.purpur.gui.util;
|
|
+
|
|
+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/net/pl3x/purpur/util/HighlightErrorConverter.java b/src/main/java/net/pl3x/purpur/util/HighlightErrorConverter.java
|
|
new file mode 100644
|
|
index 0000000000..4b340b88a2
|
|
--- /dev/null
|
|
+++ b/src/main/java/net/pl3x/purpur/util/HighlightErrorConverter.java
|
|
@@ -0,0 +1,86 @@
|
|
+package net.pl3x.purpur.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;
|
|
+
|
|
+ protected 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/resources/log4j2.xml b/src/main/resources/log4j2.xml
|
|
index a9bb987652..4be7613d0f 100644
|
|
--- a/src/main/resources/log4j2.xml
|
|
+++ b/src/main/resources/log4j2.xml
|
|
@@ -1,8 +1,17 @@
|
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
-<Configuration status="WARN" packages="com.mojang.util">
|
|
+<Configuration status="WARN" packages="com.mojang.util,net.pl3x.purpur.util"><!-- Purpur -->
|
|
<Appenders>
|
|
<Queue name="ServerGuiConsole">
|
|
- <PatternLayout pattern="[%d{HH:mm:ss} %level]: %msg%n" />
|
|
+ <!-- Purpur start -->
|
|
+ <PatternLayout>
|
|
+ <LoggerNamePatternSelector defaultPattern="%highlightGUIError{[%d{HH:mm:ss} %level]: [%logger] %msg%n%xEx}">
|
|
+ <!-- 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]: %msg%n%xEx}" />
|
|
+ </LoggerNamePatternSelector>
|
|
+ </PatternLayout>
|
|
+ <!-- Purpur end -->
|
|
</Queue>
|
|
<TerminalConsole name="TerminalConsole">
|
|
<PatternLayout>
|
|
--
|
|
2.24.0
|
|
|