From c4b2a19e706627eff45e9f516a01b9c8e8fec0ef Mon Sep 17 00:00:00 2001 From: daoge_cmd <3523206925@qq.com> Date: Sat, 25 May 2024 00:25:59 +0800 Subject: [PATCH] feat: add memory graph --- .../org/allaymc/server/gui/Dashboard.form | 34 +++- .../org/allaymc/server/gui/Dashboard.java | 53 ++++- .../org/allaymc/server/gui/GraphPanel.java | 190 ++++++++++++++++++ 3 files changed, 273 insertions(+), 4 deletions(-) create mode 100644 Allay-Server/src/main/java/org/allaymc/server/gui/GraphPanel.java diff --git a/Allay-Server/src/main/java/org/allaymc/server/gui/Dashboard.form b/Allay-Server/src/main/java/org/allaymc/server/gui/Dashboard.form index 1c6665291..226a47c10 100644 --- a/Allay-Server/src/main/java/org/allaymc/server/gui/Dashboard.form +++ b/Allay-Server/src/main/java/org/allaymc/server/gui/Dashboard.form @@ -18,12 +18,44 @@ + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Allay-Server/src/main/java/org/allaymc/server/gui/Dashboard.java b/Allay-Server/src/main/java/org/allaymc/server/gui/Dashboard.java index 58f06b9e5..873d9d745 100644 --- a/Allay-Server/src/main/java/org/allaymc/server/gui/Dashboard.java +++ b/Allay-Server/src/main/java/org/allaymc/server/gui/Dashboard.java @@ -13,10 +13,15 @@ import javax.swing.table.DefaultTableModel; import java.awt.*; import java.net.URL; -import java.util.stream.Collectors; +import java.util.ArrayList; +import java.util.List; +import java.util.Vector; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; /** - * java-swing-playground Project 2024/5/19 + * Allay Project 2024/5/19 * * @author daoge_cmd */ @@ -31,6 +36,9 @@ public class Dashboard { private JPanel pluginTab; private JTable pluginTable; private JLabel onlinePlayerCount; + private JTabbedPane tabbedPane1; + private GraphPanel ramGraph; + private List ramValues; public static Dashboard getInstance() { if (INSTANCE != null) { @@ -43,7 +51,7 @@ public static Dashboard getInstance() { JFrame frame = new JFrame("Dashboard"); frame.setContentPane(INSTANCE.rootPane); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - frame.setSize(600, 600); + frame.setSize(700, 700); frame.setLocationRelativeTo(null); // Set icon URL image = Dashboard.class.getClassLoader().getResource("icon.png"); @@ -158,6 +166,45 @@ protected void updatePluginTable() { return rootPane; } + private static final long MEGABYTE = 1024L * 1024L; + + private void createUIComponents() { + ramGraph = new GraphPanel(); + ramValues = new ArrayList<>(); + // Set the ram graph to 0 + for (int i = 0; i < 50; i++) { + ramValues.add(0); + } + ramGraph.setValues(ramValues); + ramGraph.setXLabel("Memory Usage"); + + Runnable periodicTask = () -> { + // Update ram graph + final long freeMemory = Runtime.getRuntime().freeMemory(); + final long totalMemory = Runtime.getRuntime().totalMemory(); + final int freePercent = (int) (freeMemory * 100.0 / totalMemory + 0.5); + ramValues.add(100 - freePercent); + + ramGraph.setXLabel("Usage: " + String.format("%,d", (totalMemory - freeMemory) / MEGABYTE) + "mb (" + freePercent + "% free)"); + + // Trim the list + int k = ramValues.size(); + if (k > 50) + ramValues.subList(0, k - 50).clear(); + + // Update the graph + ramGraph.setValues(ramValues); + }; + + // SwingUtilities.invokeLater is called so that we don't run into threading issues with the GUI + Server.getInstance().getScheduler().scheduleRepeating( + Server.getInstance(), + () -> { + SwingUtilities.invokeLater(periodicTask); + return true;}, + 20); + } + private static class UneditableDefaultTableModel extends DefaultTableModel { public UneditableDefaultTableModel(String[][] data, String[] title) {super(data, title);} diff --git a/Allay-Server/src/main/java/org/allaymc/server/gui/GraphPanel.java b/Allay-Server/src/main/java/org/allaymc/server/gui/GraphPanel.java new file mode 100644 index 000000000..311219a0d --- /dev/null +++ b/Allay-Server/src/main/java/org/allaymc/server/gui/GraphPanel.java @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.allaymc.server.gui; + +import lombok.Setter; + +import javax.swing.*; +import java.awt.*; +import java.io.Serial; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * Allay Project 2024/5/24 + * + * @author daoge_cmd + */ +public final class GraphPanel extends JPanel { + + @Serial + private static final long serialVersionUID = 1L; + + private final static int padding = 10; + private final static int labelPadding = 25; + private final static int pointWidth = 4; + private final static int numberYDivisions = 20; + private final static Color textColor = Color.WHITE; + private final static Color backgroundColor = Color.DARK_GRAY; + private final static Color lineColor = new Color(44, 102, 230, 255); + private final static Color pointColor = new Color(100, 100, 100, 255); + private final static Color gridColor = Color.GRAY; + private static final Stroke graphStroke = new BasicStroke(2f); + private final List values = new ArrayList<>(50); + + @Setter + private String xLabel = ""; + + public GraphPanel() { + setPreferredSize(new Dimension(700 - (padding * 2), 700 - (padding * 2))); + } + + public void setValues(Collection newValues) { + values.clear(); + addValues(newValues); + } + + public void addValues(Collection newValues) { + values.addAll(newValues); + updateUI(); + } + + @Override + protected void paintComponent(Graphics graphics) { + super.paintComponent(graphics); + if (!(graphics instanceof final Graphics2D g)) { + graphics.drawString("Graphics is not Graphics2D, unable to render", 0, 0); + return; + } + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + + final int length = values.size(); + final int width = getWidth(); + final int height = getHeight(); + final int maxScore = getMaxScore(); + final int minScore = getMinScore(); + final int scoreRange = maxScore - minScore; + + // draw background + g.setColor(backgroundColor); + g.fillRect( + padding + labelPadding, + padding, + width - (2 * padding) - labelPadding, + height - 2 * padding - labelPadding); + g.setColor(Color.BLACK); + + final FontMetrics fontMetrics = g.getFontMetrics(); + final int fontHeight = fontMetrics.getHeight(); + + // create hatch marks and grid lines for y axis. + for (int i = 0; i < numberYDivisions + 1; i++) { + final int x1 = padding + labelPadding; + final int x2 = pointWidth + padding + labelPadding; + final int y = height - ((i * (height - padding * 2 - labelPadding)) / numberYDivisions + padding + labelPadding); + if (length > 0) { + g.setColor(gridColor); + g.drawLine(padding + labelPadding + 1 + pointWidth, y, width - padding, y); + + g.setColor(textColor); + final int tickValue = minScore + ((scoreRange * i) / numberYDivisions); + final String yLabel = tickValue + ""; + final int labelWidth = fontMetrics.stringWidth(yLabel); + g.drawString(yLabel, x1 - labelWidth - 5, y + (fontHeight / 2) - 3); + } + g.drawLine(x1, y, x2, y); + } + + // and for x axis + if (length > 1) { + for (int i = 0; i < length; i++) { + final int x = i * (width - padding * 2 - labelPadding) / (length - 1) + padding + labelPadding; + final int y1 = height - padding - labelPadding; + final int y2 = y1 - pointWidth; + if ((i % ((int) ((length / 20.0)) + 1)) == 0) { + g.setColor(gridColor); + g.drawLine(x, height - padding - labelPadding - 1 - pointWidth, x, padding); + + g.setColor(Color.BLACK); + } + g.drawLine(x, y1, x, y2); + } + } + + // create x and y axes + g.drawLine(padding + labelPadding, height - padding - labelPadding, padding + labelPadding, padding); + g.drawLine(padding + labelPadding, height - padding - labelPadding, width - padding, height - padding - labelPadding); + + g.setColor(textColor); + final int labelWidth = fontMetrics.stringWidth(xLabel); + final int labelX = ((padding + labelPadding) + (width - padding)) / 2; + final int labelY = height - padding - labelPadding; + g.drawString(xLabel, labelX - labelWidth / 2, labelY + fontHeight + 3); + + final Stroke oldStroke = g.getStroke(); + g.setColor(lineColor); + g.setStroke(graphStroke); + + final double xScale = ((double) width - (2 * padding) - labelPadding) / (length - 1); + final double yScale = ((double) height - 2 * padding - labelPadding) / scoreRange; + + final List graphPoints = new ArrayList<>(length); + for (int i = 0; i < length; i++) { + final int x1 = (int) (i * xScale + padding + labelPadding); + final int y1 = (int) ((maxScore - values.get(i)) * yScale + padding); + graphPoints.add(new Point(x1, y1)); + } + + for (int i = 0; i < graphPoints.size() - 1; i++) { + final int x1 = graphPoints.get(i).x; + final int y1 = graphPoints.get(i).y; + final int x2 = graphPoints.get(i + 1).x; + final int y2 = graphPoints.get(i + 1).y; + g.drawLine(x1, y1, x2, y2); + } + + boolean drawDots = width > (length * pointWidth); + if (drawDots) { + g.setStroke(oldStroke); + g.setColor(pointColor); + for (Point graphPoint : graphPoints) { + final int x = graphPoint.x - pointWidth / 2; + final int y = graphPoint.y - pointWidth / 2; + //noinspection SuspiciousNameCombination + g.fillOval(x, y, pointWidth, pointWidth); + } + } + } + + private int getMinScore() { + return 0; + } + + private int getMaxScore() { + return 100; + } +}