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;
+ }
+}