Make the GUI graph fancier

This commit is contained in:
William Blake Galbreath 2020-02-02 04:00:40 -06:00
parent 196c153504
commit fa7c667a3e
6 changed files with 366 additions and 2 deletions

View file

@ -1,10 +1,12 @@
--- a/net/minecraft/server/gui/MinecraftServerGui.java
+++ b/net/minecraft/server/gui/MinecraftServerGui.java
@@ -96,7 +96,7 @@
@@ -95,8 +95,8 @@
private JComponent buildInfoPanel() {
JPanel jpanel = new JPanel(new BorderLayout());
StatsComponent guistatscomponent = new StatsComponent(this.server);
- StatsComponent guistatscomponent = new StatsComponent(this.server);
- Collection collection = this.finalizers;
+ com.destroystokyo.paper.gui.GuiStatsComponent guistatscomponent = new com.destroystokyo.paper.gui.GuiStatsComponent(this.server); // Paper - Make GUI graph fancier
+ Collection<Runnable> collection = this.finalizers; // CraftBukkit - decompile error
Objects.requireNonNull(guistatscomponent);

View file

@ -0,0 +1,44 @@
package com.destroystokyo.paper.gui;
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);
}
}

View file

@ -0,0 +1,47 @@
package com.destroystokyo.paper.gui;
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);
}
}

View file

@ -0,0 +1,41 @@
package com.destroystokyo.paper.gui;
import net.minecraft.server.MinecraftServer;
import javax.swing.JPanel;
import javax.swing.Timer;
import java.awt.BorderLayout;
import java.awt.Dimension;
public class GuiStatsComponent extends JPanel {
private final Timer timer;
private final RAMGraph ramGraph;
public GuiStatsComponent(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 close() {
timer.stop();
ramGraph.stop();
}
}

View file

@ -0,0 +1,74 @@
package com.destroystokyo.paper.gui;
import net.minecraft.Util;
import net.minecraft.server.MinecraftServer;
import net.minecraft.util.TimeUtil;
import javax.swing.DefaultListCellRenderer;
import javax.swing.DefaultListSelectionModel;
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> {
public static final DecimalFormat DECIMAL_FORMAT = Util.make(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 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) {
}
});
}
@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((double)this.server.getAverageTickTimeNanos() / (double) TimeUtil.NANOSECONDS_PER_MILLISECOND) + " ms");
setListData(vector);
}
public double getAverage(long[] tickTimes) {
long total = 0L;
for (long value : tickTimes) {
total += value * 1000;
}
return ((double) total / (double) tickTimes.length) * 1.0E-6D;
}
}

View file

@ -0,0 +1,156 @@
package com.destroystokyo.paper.gui;
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 = null;
// I think I recall spotting a bug report where this throwed an exception once
// not sure it's of concern here
try {
pointerInfo = MouseInfo.getPointerInfo();
} catch (NullPointerException | ArrayIndexOutOfBoundsException ignored) {
// https://bugs.openjdk.org/browse/JDK-6840067
}
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 = null;
try {
m = getMousePosition();
} catch (NullPointerException ignored) {
// https://bugs.openjdk.org/browse/JDK-6840067
}
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();
}
}