diff --git a/Allay-API/src/main/java/org/allaymc/api/server/ServerSettings.java b/Allay-API/src/main/java/org/allaymc/api/server/ServerSettings.java index 29f4fc5d3..6bfda3e8b 100644 --- a/Allay-API/src/main/java/org/allaymc/api/server/ServerSettings.java +++ b/Allay-API/src/main/java/org/allaymc/api/server/ServerSettings.java @@ -62,6 +62,9 @@ public static class GenericSettings extends OkaeriConfig { @CustomKey("is-whitelisted") private boolean isWhitelisted = false; + + @Comment("Whether or not to display the GUI") + private boolean enableGui = true; } @Getter diff --git a/Allay-Server/build.gradle.kts b/Allay-Server/build.gradle.kts index a951c7bea..523c5c76a 100644 --- a/Allay-Server/build.gradle.kts +++ b/Allay-Server/build.gradle.kts @@ -39,6 +39,8 @@ dependencies { implementation(libs.js) implementation(libs.bundles.graalvm) implementation(libs.oshi) + implementation(libs.flatlaf) + implementation(libs.formsrt) testImplementation(libs.bytebuddy) } diff --git a/Allay-Server/src/main/java/org/allaymc/server/AllayServer.java b/Allay-Server/src/main/java/org/allaymc/server/AllayServer.java index 0b664e16d..8a88dbee1 100644 --- a/Allay-Server/src/main/java/org/allaymc/server/AllayServer.java +++ b/Allay-Server/src/main/java/org/allaymc/server/AllayServer.java @@ -41,6 +41,7 @@ import org.allaymc.server.client.storage.AllayNBTFilePlayerStorage; import org.allaymc.server.command.AllayCommandRegistry; import org.allaymc.server.eventbus.AllayEventBus; +import org.allaymc.server.gui.Dashboard; import org.allaymc.server.network.AllayNetworkServer; import org.allaymc.server.plugin.AllayPluginManager; import org.allaymc.server.scheduler.AllayScheduler; @@ -143,6 +144,8 @@ public final class AllayServer implements Server { }) .build(); + private Dashboard dashboard; + private AllayServer() {} public static AllayServer getInstance() { @@ -190,6 +193,9 @@ public void run() { networkServer.start(); startTime = System.currentTimeMillis(); sendTr(TrKeys.A_NETWORK_SERVER_STARTED, SETTINGS.networkSettings().ip(), String.valueOf(SETTINGS.networkSettings().port()), String.valueOf(startTime - timeMillis)); + if (SETTINGS.genericSettings().enableGui()) { + dashboard = Dashboard.getInstance(); + } gameLoop.startLoop(); } @@ -286,8 +292,6 @@ public void onLoggedIn(EntityPlayer player) { @Override public void onDisconnect(EntityPlayer player, String reason) { - var event = new PlayerQuitEvent(player, reason); - eventBus.callEvent(event); sendTr(TrKeys.A_NETWORK_CLIENT_DISCONNECTED, player.getClientSession().getSocketAddress().toString()); if (player.isInitialized()) { broadcastTr("§e%" + TrKeys.M_MULTIPLAYER_PLAYER_LEFT, player.getOriginName()); @@ -303,6 +307,8 @@ public void onDisconnect(EntityPlayer player, String reason) { } players.remove(player.getUUID()); networkServer.setPlayerCount(players.size()); + var event = new PlayerQuitEvent(player, reason); + eventBus.callEvent(event); } @Override 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 new file mode 100644 index 000000000..1c6665291 --- /dev/null +++ b/Allay-Server/src/main/java/org/allaymc/server/gui/Dashboard.form @@ -0,0 +1,92 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
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 new file mode 100644 index 000000000..d067519e2 --- /dev/null +++ b/Allay-Server/src/main/java/org/allaymc/server/gui/Dashboard.java @@ -0,0 +1,161 @@ +package org.allaymc.server.gui; + +import com.formdev.flatlaf.themes.FlatMacDarkLaf; +import com.intellij.uiDesigner.core.GridConstraints; +import com.intellij.uiDesigner.core.GridLayoutManager; +import com.jgoodies.forms.layout.FormLayout; +import org.allaymc.api.eventbus.EventHandler; +import org.allaymc.api.eventbus.event.server.player.PlayerInitializedEvent; +import org.allaymc.api.eventbus.event.server.player.PlayerQuitEvent; +import org.allaymc.api.server.Server; + +import javax.swing.*; +import javax.swing.table.DefaultTableModel; +import java.awt.*; +import java.util.stream.Collectors; + +/** + * java-swing-playground Project 2024/5/19 + * + * @author daoge_cmd + */ +public class Dashboard { + private static Dashboard INSTANCE; + + private JPanel rootPane; + private JTabbedPane tabbedPane; + private JPanel playerTab; + private JPanel perfTab; + private JTable playerTable; + private JPanel pluginTab; + private JTable pluginTable; + private JLabel onlinePlayerCount; + + public static Dashboard getInstance() { + if (INSTANCE != null) { + return INSTANCE; + } + FlatMacDarkLaf.setup(); + + // Init the frame + INSTANCE = new Dashboard(); + JFrame frame = new JFrame("Dashboard"); + frame.setContentPane(INSTANCE.rootPane); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.setSize(600, 600); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + + var server = Server.getInstance(); + server.getEventBus().registerListener(INSTANCE); + // TODO: Update the table if any plugin disabled or enabled + INSTANCE.updatePluginTable(); + + return INSTANCE; + } + + @EventHandler + protected void onPlayerJoin(PlayerInitializedEvent event) { + updateOnlinePlayerCount(); + updateOnlinePlayerTable(); + } + + @EventHandler + protected void onPlayerQuit(PlayerQuitEvent event) { + updateOnlinePlayerCount(); + updateOnlinePlayerTable(); + } + + protected void updateOnlinePlayerCount() { + SwingUtilities.invokeLater(() -> onlinePlayerCount.setText("Online: " + Server.getInstance().getOnlinePlayerCount())); + } + + protected void updateOnlinePlayerTable() { + var title = new String[] {"Name", "Address", "UUID"}; + var players = Server.getInstance().getOnlinePlayers().values(); + var data = new String[3][players.size() - 1]; + int row = 0; + for (var player : players) { + data[row] = new String[] { + player.getOriginName(), + player.getClientSession().getSocketAddress().toString(), + player.getUUID().toString() + }; + row++; + } + var model = new UneditableDefaultTableModel(data, title); + SwingUtilities.invokeLater(() -> playerTable.setModel(model)); + } + + protected void updatePluginTable() { + var title = new String[] {"Name", "Description", "Version", "Author"}; + var plugins = Server.getInstance().getPluginManager().getPlugins().values(); + var data = new String[4][plugins.size() - 1]; + int row = 0; + for (var plugin : plugins) { + var descriptor = plugin.descriptor(); + data[row] = new String[] { + descriptor.getName(), + descriptor.getDescription().isBlank() ? "N/A" : descriptor.getDescription(), + descriptor.getVersion(), + String.join(", ", descriptor.getAuthors()) + }; + row++; + } + var model = new UneditableDefaultTableModel(data, title); + SwingUtilities.invokeLater(() -> pluginTable.setModel(model)); + } + + { +// GUI initializer generated by IntelliJ IDEA GUI Designer +// >>> IMPORTANT!! <<< +// DO NOT EDIT OR ADD ANY CODE HERE! + $$$setupUI$$$(); + } + + /** + * Method generated by IntelliJ IDEA GUI Designer + * >>> IMPORTANT!! <<< + * DO NOT edit this method OR call it in your code! + * + * @noinspection ALL + */ + private void $$$setupUI$$$() { + rootPane = new JPanel(); + rootPane.setLayout(new GridLayoutManager(1, 1, new Insets(8, 8, 8, 8), -1, -1)); + tabbedPane = new JTabbedPane(); + rootPane.add(tabbedPane, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, new Dimension(200, 200), null, 0, false)); + playerTab = new JPanel(); + playerTab.setLayout(new GridLayoutManager(2, 1, new Insets(0, 0, 0, 0), -1, -1)); + tabbedPane.addTab("Players", playerTab); + final JLabel label1 = new JLabel(); + label1.setEnabled(true); + label1.setHorizontalAlignment(2); + label1.setText("Online: "); + playerTab.add(label1, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_NORTH, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false)); + final JScrollPane scrollPane1 = new JScrollPane(); + playerTab.add(scrollPane1, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, null, null, null, 0, false)); + playerTable = new JTable(); + playerTable.setAutoCreateRowSorter(true); + scrollPane1.setViewportView(playerTable); + perfTab = new JPanel(); + perfTab.setLayout(new FormLayout("", "")); + tabbedPane.addTab("Performance", perfTab); + } + + /** + * @noinspection ALL + */ + public JComponent $$$getRootComponent$$$() { + return rootPane; + } + + private static class UneditableDefaultTableModel extends DefaultTableModel { + public UneditableDefaultTableModel(String[][] data, String[] title) {super(data, title);} + + @Override + public boolean isCellEditable(int row, int column) { + return false; + } + } +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c5c478f99..b970c9d6b 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -73,6 +73,10 @@ chromeinspector-tool = { group = "org.graalvm.tools", name = "chromeinspector-to profiler-tool = { group = "org.graalvm.tools", name = "profiler-tool", version.ref = "graalvm" } # Native Operating System and Hardware Information oshi = { group = "com.github.oshi", name = "oshi-core", version = "6.6.0" } +# GUI +flatlaf = { group = "com.formdev", name = "flatlaf", version = "3.4.1" } +formsrt = { group = "com.intellij", name = "forms_rt", version = "7.0.3" } + [bundles] logging = ["log4j-slf4j2-impl", "log4j-core"]