From 1ef1f86e4ffacc1e104cb34f404d9caff58d51cd Mon Sep 17 00:00:00 2001 From: William Blake Galbreath 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 | 3 +- .../java/net/pl3x/purpur/gui/ColorPane.java | 80 ++++++++ .../net/pl3x/purpur/gui/ConsolePanel.java | 128 +++++++++++++ .../java/net/pl3x/purpur/gui/GUIColor.java | 58 ++++++ .../pl3x/purpur/gui/PlayerListComponent.java | 27 +++ .../java/net/pl3x/purpur/gui/ServerGUI.java | 121 ++++++++++++ .../net/pl3x/purpur/gui/info/RAMDetails.java | 52 +++++ .../net/pl3x/purpur/gui/info/RAMGraph.java | 179 ++++++++++++++++++ .../pl3x/purpur/gui/info/ServerInfoPanel.java | 42 ++++ .../purpur/util/HighlightErrorConverter.java | 86 +++++++++ src/main/resources/log4j2.xml | 13 +- 12 files changed, 789 insertions(+), 6 deletions(-) create mode 100644 src/main/java/net/pl3x/purpur/gui/ColorPane.java create mode 100644 src/main/java/net/pl3x/purpur/gui/ConsolePanel.java create mode 100644 src/main/java/net/pl3x/purpur/gui/GUIColor.java create mode 100644 src/main/java/net/pl3x/purpur/gui/PlayerListComponent.java create mode 100644 src/main/java/net/pl3x/purpur/gui/ServerGUI.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/RAMGraph.java create mode 100644 src/main/java/net/pl3x/purpur/gui/info/ServerInfoPanel.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 d70c8cab2b..61ccb6079e 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) { @@ -420,7 +420,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) { @@ -519,7 +519,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 9d5ef40a03..105ac8a040 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java @@ -105,7 +105,7 @@ public abstract class MinecraftServer extends IAsyncTaskHandlerReentrant 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(); + } + + private 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/ConsolePanel.java b/src/main/java/net/pl3x/purpur/gui/ConsolePanel.java new file mode 100644 index 0000000000..29d237d9e7 --- /dev/null +++ b/src/main/java/net/pl3x/purpur/gui/ConsolePanel.java @@ -0,0 +1,128 @@ +package net.pl3x.purpur.gui; + +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 ConsolePanel 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; + + ConsolePanel(DedicatedServer server) { + super(new BorderLayout()); + + ColorPane console = new ColorPane(); + 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(ColorPane 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 { + @Override + public boolean add(String command) { + if (size() > 1000) { + remove(); + } + return super.offerFirst(command); + } + } +} diff --git a/src/main/java/net/pl3x/purpur/gui/GUIColor.java b/src/main/java/net/pl3x/purpur/gui/GUIColor.java new file mode 100644 index 0000000000..2a280ae48a --- /dev/null +++ b/src/main/java/net/pl3x/purpur/gui/GUIColor.java @@ -0,0 +1,58 @@ +package net.pl3x.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 BY_CHAT = new HashMap<>(); + + GUIColor(ChatColor chat, Color color) { + this.chat = chat; + this.color = color; + } + + public Color getColor() { + return color; + } + + public ChatColor getChatColor() { + return chat; + } + + 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/gui/PlayerListComponent.java b/src/main/java/net/pl3x/purpur/gui/PlayerListComponent.java new file mode 100644 index 0000000000..c97a6f7dc3 --- /dev/null +++ b/src/main/java/net/pl3x/purpur/gui/PlayerListComponent.java @@ -0,0 +1,27 @@ +package net.pl3x.purpur.gui; + +import net.minecraft.server.EntityPlayer; +import net.minecraft.server.MinecraftServer; + +import javax.swing.JList; +import java.util.Vector; + +public class PlayerListComponent extends JList { + private final MinecraftServer server; + private int tickCount; + + PlayerListComponent(MinecraftServer server) { + this.server = server; + server.addTickable(this::tick); + } + + public void tick() { + if (tickCount++ % 20 == 0) { + Vector vector = new Vector<>(); + for (EntityPlayer player : server.getPlayerList().getPlayers()) { + vector.add(player.getProfile().getName()); + } + setListData(vector); + } + } +} 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..a779fea689 --- /dev/null +++ b/src/main/java/net/pl3x/purpur/gui/ServerGUI.java @@ -0,0 +1,121 @@ +package net.pl3x.purpur.gui; + +import com.google.common.collect.Lists; +import net.minecraft.server.DedicatedServer; +import net.pl3x.purpur.gui.info.ServerInfoPanel; +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 finalizers = Lists.newArrayList(); + private final AtomicBoolean isClosing = new AtomicBoolean(); + + private ConsolePanel 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 ConsolePanel(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()); + + ServerInfoPanel serverInfo = new ServerInfoPanel(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 PlayerListComponent(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/info/RAMDetails.java b/src/main/java/net/pl3x/purpur/gui/info/RAMDetails.java new file mode 100644 index 0000000000..b1ea91b49b --- /dev/null +++ b/src/main/java/net/pl3x/purpur/gui/info/RAMDetails.java @@ -0,0 +1,52 @@ +package net.pl3x.purpur.gui.info; + +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.SystemUtils; + +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 { + private static final DecimalFormat DECIMAL_FORMAT = SystemUtils.a(new DecimalFormat("########0.000"), (format) + -> format.setDecimalFormatSymbols(DecimalFormatSymbols.getInstance(Locale.ROOT))); + private final MinecraftServer server; + + 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); + } + + @Override + public Dimension getPreferredSize() { + return new Dimension(350, 100); + } + + public void update(RAMGraph graph) { + Vector vector = new Vector<>(); + vector.add("Memory use: " + (graph.usedMem / 1024L / 1024L) + " mb (" + (graph.free * 100L / graph.max) + "% free)"); + vector.add("Heap: " + (graph.total / 1024L / 1024L) + " / " + (graph.max / 1024L / 1024L) + " mb"); + vector.add("Avg tick: " + DECIMAL_FORMAT.format(getAverage(server.getTickTimes()) * 1.0E-6D) + " 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/RAMGraph.java b/src/main/java/net/pl3x/purpur/gui/info/RAMGraph.java new file mode 100644 index 0000000000..7623088bae --- /dev/null +++ b/src/main/java/net/pl3x/purpur/gui/info/RAMGraph.java @@ -0,0 +1,179 @@ +package net.pl3x.purpur.gui.info; + +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.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 { + private final Timer timer; + + private final SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss"); + + private final Color[] colors = new Color[101]; + private final Color[] colorsAlpha = new Color[101]; + + private final RAMQueue usedMemQueue = new RAMQueue<>(); + private final RAMQueue usedPercentQueue = new RAMQueue<>(); + + private int currentTick; + + protected long total; + protected long free; + protected long max; + + long usedMem; + + RAMGraph() { + for (int i = 0; i < 350; i++) { + usedMemQueue.add(0L); + usedPercentQueue.add(0); + } + + for (int i = 0; i < 101; i++) { + Color color = getColor(i); + colors[i] = new Color(color.getRed() / 2, color.getGreen() / 2, color.getBlue() / 2, 255); + colorsAlpha[i] = new Color(colors[i].getRed(), colors[i].getGreen(), colors[i].getBlue(), 125); + } + + ToolTipManager.sharedInstance().setInitialDelay(0); + + addMouseListener(new MouseAdapter() { + final int defaultDismissTimeout = ToolTipManager.sharedInstance().getDismissDelay(); + final int dismissDelayMinutes = (int) TimeUnit.MINUTES.toMillis(10); // 10 minutes + + @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); + } + + protected void update() { + total = Runtime.getRuntime().totalMemory(); + free = Runtime.getRuntime().freeMemory(); + max = Runtime.getRuntime().maxMemory(); + usedMem = total - free; + + usedMemQueue.add(usedMem); + usedPercentQueue.add((int) (usedMem * 100L / max)); + + Point scr = MouseInfo.getPointerInfo().getLocation(); + Point loc = new Point(scr); + SwingUtilities.convertPointFromScreen(loc, this); + if (this.contains(loc)) { + ToolTipManager.sharedInstance().mouseMoved( + new MouseEvent(this, -1, System.currentTimeMillis(), 0, loc.x, loc.y, + scr.x, scr.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 (Integer used : usedPercentQueue) { + i++; + if ((i + currentTick) % 120 == 0) { + graphics.setColor(new Color(0x888888)); + graphics.drawLine(i, 1, i, 99); + } + + if (used > 0) { + Color color = colors[used]; + graphics.setColor(colorsAlpha[used]); + 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) { + Integer used = usedPercentQueue.get(m.x); + 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(colors[used]); + graphics.fillOval(m.x - 2, 100 - used - 2, 5, 5); + setToolTipText(String.format("Used: %s mb (%s%%)
%s", + Math.round(usedMemQueue.get(m.x) / 1024F / 1024F), + used, getTime(m.x))); + } + } + + protected void stop() { + timer.stop(); + } + + private String getTime(int halfSeconds) { + int millis = (348 - halfSeconds) / 2 * 1000; + return sdf.format(new Date((System.currentTimeMillis() - millis))); + } + + private Color getColor(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); + } + + public static class RAMQueue extends LinkedList { + @Override + public boolean add(E e) { + if (size() >= 348) { + remove(); + } + return super.add(e); + } + } +} diff --git a/src/main/java/net/pl3x/purpur/gui/info/ServerInfoPanel.java b/src/main/java/net/pl3x/purpur/gui/info/ServerInfoPanel.java new file mode 100644 index 0000000000..c4519794c9 --- /dev/null +++ b/src/main/java/net/pl3x/purpur/gui/info/ServerInfoPanel.java @@ -0,0 +1,42 @@ +package net.pl3x.purpur.gui.info; + +import net.minecraft.server.MinecraftServer; + +import javax.swing.JPanel; +import javax.swing.Timer; +import java.awt.BorderLayout; +import java.awt.Dimension; + +public class ServerInfoPanel extends JPanel { + private final Timer timer; + private final RAMGraph ramGraph; + private final RAMDetails ramDetails; + + public ServerInfoPanel(MinecraftServer server) { + super(new BorderLayout()); + + setOpaque(false); + + ramGraph = new RAMGraph(); + ramDetails = new RAMDetails(server); + + add(ramGraph, "North"); + add(ramDetails, "Center"); + + timer = new Timer(500, (event) -> { + ramGraph.update(); + ramDetails.update(ramGraph); + }); + 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/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 formatters; + + protected HighlightErrorConverter(List 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 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 @@ - + - + + + + + + + + + -- 2.24.0