diff --git a/pom.xml b/pom.xml
index f4a08ac..d0c890e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -64,6 +64,26 @@
mcmanage.Main
+
+ org.apache.maven.plugins
+ maven-shade-plugin
+ 3.2.4
+
+
+ package
+
+ shade
+
+
+
+
+ mcmanage.Main
+
+
+
+
+
+
diff --git a/src/main/java/mcmanage/EnhancedServerManagerUI.java b/src/main/java/mcmanage/EnhancedServerManagerUI.java
index 0f78335..e2cd2f8 100644
--- a/src/main/java/mcmanage/EnhancedServerManagerUI.java
+++ b/src/main/java/mcmanage/EnhancedServerManagerUI.java
@@ -7,6 +7,86 @@ import java.util.List;
import java.util.ArrayList;
import java.util.concurrent.ExecutionException;
import com.formdev.flatlaf.FlatLightLaf;
+import javax.swing.border.TitledBorder;
+import java.io.File;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.Map;
+import java.util.HashMap;
+import javax.swing.ListCellRenderer;
+import java.awt.image.BufferedImage;
+
+class AddServerWizard extends JDialog {
+ private JTextField nameField = new JTextField(15);
+ private JTextField portField = new JTextField(5);
+ private JTextField ramField = new JTextField(3);
+ private JComboBox versionsComboBox;
+ private boolean confirmed = false;
+
+ // Standard wizard for normal use
+ public AddServerWizard(JFrame parent, List versions) {
+ this(parent, versions, null);
+ }
+ // Recovery wizard with version
+ public AddServerWizard(JFrame parent, List versions, String fixedVersion) {
+ super(parent, "Add Server", true);
+ setLayout(new BorderLayout(10, 10));
+ JPanel panel = new JPanel();
+ panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
+ panel.setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15));
+ panel.add(new JLabel("Server Name:"));
+ panel.add(nameField);
+ panel.add(Box.createRigidArea(new Dimension(0, 5)));
+ panel.add(new JLabel("Port:"));
+ panel.add(portField);
+ panel.add(Box.createRigidArea(new Dimension(0, 5)));
+ panel.add(new JLabel("RAM (GB):"));
+ panel.add(ramField);
+ panel.add(Box.createRigidArea(new Dimension(0, 5)));
+ panel.add(new JLabel("Paper Version:"));
+ versionsComboBox = new JComboBox<>(versions.toArray(new String[0]));
+ panel.add(versionsComboBox);
+ if (fixedVersion != null) {
+ boolean found = false;
+ for (int i = 0; i < versionsComboBox.getItemCount(); i++) {
+ if (fixedVersion.equals(versionsComboBox.getItemAt(i))) {
+ versionsComboBox.setSelectedItem(fixedVersion);
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ versionsComboBox.addItem(fixedVersion);
+ versionsComboBox.setSelectedItem(fixedVersion);
+ }
+ versionsComboBox.setEnabled(false);
+ }
+ panel.add(Box.createRigidArea(new Dimension(0, 10)));
+ JButton confirmButton = new JButton("Create");
+ confirmButton.addActionListener(e -> {
+ boolean versionRequired = versionsComboBox.isEnabled();
+ if (nameField.getText().trim().isEmpty() ||
+ portField.getText().trim().isEmpty() ||
+ ramField.getText().trim().isEmpty() ||
+ (versionRequired && versionsComboBox.getSelectedItem() == null)) {
+ JOptionPane.showMessageDialog(this, "Please fill all fields" + (versionRequired ? " and select a version" : ""), "Input Error", JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+ confirmed = true;
+ setVisible(false);
+ });
+ panel.add(confirmButton);
+ add(panel, BorderLayout.CENTER);
+ pack();
+ setLocationRelativeTo(parent);
+ }
+ public boolean isConfirmed() { return confirmed; }
+ public String getNameField() { return nameField.getText().trim(); }
+ public String getPortField() { return portField.getText().trim(); }
+ public String getRamField() { return ramField.getText().trim(); }
+ public String getVersion() { return (String) versionsComboBox.getSelectedItem(); }
+ public void setNameField(String name) { nameField.setText(name); }
+}
public class EnhancedServerManagerUI extends JFrame {
static {
@@ -26,70 +106,122 @@ public class EnhancedServerManagerUI extends JFrame {
private JButton addButton = new JButton("Add Server");
private JButton saveButton = new JButton("Save Servers");
private JButton fetchVersionsButton = new JButton("Fetch Paper Versions");
- private JButton downloadJarButton = new JButton("Download JAR");
private JButton startServerButton = new JButton("Start Server");
private JButton stopServerButton = new JButton("Stop Server");
private JButton sendCommandButton = new JButton("Send Command");
private JTextField commandField = new JTextField(20);
private JTextArea consoleOutput = new JTextArea(20, 60);
private JScrollPane consoleScrollPane = new JScrollPane(consoleOutput);
+ private JButton playerManagerButton = new JButton("Player Manager");
+
+ private final Map> playerListCache = new HashMap<>();
+ private final Map> playerListListeners = new HashMap<>();
+
+ private static class ServerStatusCellRenderer extends JLabel implements ListCellRenderer {
+ private static final Icon ICON_RUNNING = createStatusIcon(new Color(0, 180, 0), '✔');
+ private static final Icon ICON_STOPPED = createStatusIcon(new Color(160, 160, 160), '●');
+ private static final Icon ICON_CRASHED = createStatusIcon(new Color(200, 0, 0), '✖');
+ public ServerStatusCellRenderer() {
+ setOpaque(true);
+ setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 14));
+ setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
+ }
+ @Override
+ public Component getListCellRendererComponent(JList extends ServerManager.ServerInstance> list, ServerManager.ServerInstance value, int index, boolean isSelected, boolean cellHasFocus) {
+ setText(value.name + " (" + value.version + ":" + value.port + ", " + value.ramGB + "GB)");
+ if (value.isRunning) {
+ setIcon(ICON_RUNNING);
+ } else {
+ setIcon(ICON_STOPPED);
+ }
+ setBackground(isSelected ? new Color(220, 240, 255) : Color.WHITE);
+ setForeground(Color.BLACK);
+ return this;
+ }
+ private static Icon createStatusIcon(Color color, char symbol) {
+ int size = 18;
+ BufferedImage img = new BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB);
+ Graphics2D g = img.createGraphics();
+ g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+ g.setColor(color);
+ g.setFont(new Font(Font.SANS_SERIF, Font.BOLD, 16));
+ if (symbol == '✔') {
+ g.drawString("✔", 2, size-4);
+ } else if (symbol == '✖') {
+ g.drawString("✖", 2, size-4);
+ } else {
+ g.fillOval(3, 3, size-6, size-6);
+ }
+ g.dispose();
+ return new ImageIcon(img);
+ }
+ }
public EnhancedServerManagerUI() {
super("Enhanced Minecraft Server Manager");
-
- setLayout(new BorderLayout());
+ setLayout(new BorderLayout(10, 10));
+ getContentPane().setBackground(new Color(245, 245, 245));
// Initialize directories
ServerManager.initializeDirectories();
+ // Fetch Paper versions at startup
+ fetchVersionsAtStartup();
+
// Left panel - server list and controls
- JPanel leftPanel = new JPanel(new BorderLayout());
-
- // Server list
- leftPanel.add(new JLabel("Server Instances:"), BorderLayout.NORTH);
- leftPanel.add(new JScrollPane(serverList), BorderLayout.CENTER);
-
- // Control buttons
- JPanel controlPanel = new JPanel(new GridLayout(2, 2, 5, 5));
+ JPanel leftPanel = new JPanel(new BorderLayout(10, 10));
+ leftPanel.setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 10));
+ leftPanel.setBackground(new Color(250, 250, 250));
+
+ JLabel serverListLabel = new JLabel("Server Instances:");
+ serverListLabel.setFont(serverListLabel.getFont().deriveFont(Font.BOLD, 16f));
+ leftPanel.add(serverListLabel, BorderLayout.NORTH);
+
+ JScrollPane serverListScroll = new JScrollPane(serverList);
+ serverListScroll.setBorder(BorderFactory.createTitledBorder("Servers"));
+ leftPanel.add(serverListScroll, BorderLayout.CENTER);
+
+ JPanel controlPanel = new JPanel(new GridLayout(2, 2, 10, 10));
+ controlPanel.setBorder(BorderFactory.createTitledBorder("Server Controls"));
+ controlPanel.setBackground(new Color(250, 250, 250));
controlPanel.add(startServerButton);
controlPanel.add(stopServerButton);
- controlPanel.add(downloadJarButton);
controlPanel.add(sendCommandButton);
+ controlPanel.add(playerManagerButton);
leftPanel.add(controlPanel, BorderLayout.SOUTH);
// Right panel - input and console
- JPanel rightPanel = new JPanel(new BorderLayout());
-
+ JPanel rightPanel = new JPanel(new BorderLayout(10, 10));
+ rightPanel.setBorder(BorderFactory.createEmptyBorder(15, 10, 15, 15));
+ rightPanel.setBackground(new Color(245, 245, 245));
+
// Input panel
JPanel inputPanel = new JPanel();
inputPanel.setLayout(new BoxLayout(inputPanel, BoxLayout.Y_AXIS));
- inputPanel.add(new JLabel("Server Name:"));
- inputPanel.add(nameField);
- inputPanel.add(new JLabel("Port:"));
- inputPanel.add(portField);
- inputPanel.add(new JLabel("RAM (GB):"));
- inputPanel.add(ramField);
- inputPanel.add(new JLabel("Paper Version:"));
- inputPanel.add(versionsComboBox);
-
- JPanel buttonPanel = new JPanel(new GridLayout(3, 1, 5, 5));
+ inputPanel.setBorder(BorderFactory.createTitledBorder("Server Actions"));
+ inputPanel.setBackground(new Color(250, 250, 250));
+ inputPanel.add(Box.createRigidArea(new Dimension(0, 10)));
+ JPanel buttonPanel = new JPanel(new GridLayout(1, 3, 10, 10));
+ buttonPanel.setBackground(new Color(250, 250, 250));
buttonPanel.add(addButton);
buttonPanel.add(saveButton);
buttonPanel.add(fetchVersionsButton);
inputPanel.add(buttonPanel);
-
rightPanel.add(inputPanel, BorderLayout.NORTH);
-
+
// Console panel
- JPanel consolePanel = new JPanel(new BorderLayout());
- consolePanel.add(new JLabel("Server Console:"), BorderLayout.NORTH);
+ JPanel consolePanel = new JPanel(new BorderLayout(5, 5));
+ consolePanel.setBorder(BorderFactory.createTitledBorder("Server Console"));
+ consolePanel.setBackground(new Color(250, 250, 250));
+ JLabel consoleHeader = new JLabel("Console Output");
+ consoleHeader.setFont(consoleHeader.getFont().deriveFont(Font.BOLD, 15f));
+ consolePanel.add(consoleHeader, BorderLayout.NORTH);
consolePanel.add(consoleScrollPane, BorderLayout.CENTER);
-
- JPanel commandPanel = new JPanel(new BorderLayout());
+ JPanel commandPanel = new JPanel(new BorderLayout(5, 5));
+ commandPanel.setBackground(new Color(250, 250, 250));
commandPanel.add(new JLabel("Command:"), BorderLayout.WEST);
commandPanel.add(commandField, BorderLayout.CENTER);
consolePanel.add(commandPanel, BorderLayout.SOUTH);
-
rightPanel.add(consolePanel, BorderLayout.CENTER);
add(leftPanel, BorderLayout.WEST);
@@ -98,15 +230,17 @@ public class EnhancedServerManagerUI extends JFrame {
// Load saved servers
List savedServers = JSONManager.loadServerInstances();
savedServers.forEach(serverListModel::addElement);
+ // Autodiscover unsaved servers
+ SwingUtilities.invokeLater(this::recoverUnsavedServers);
// Button listeners
- addButton.addActionListener(e -> addServer());
+ addButton.addActionListener(e -> openAddServerWizard());
saveButton.addActionListener(e -> saveServers());
fetchVersionsButton.addActionListener(e -> fetchVersions());
- downloadJarButton.addActionListener(e -> downloadJar());
startServerButton.addActionListener(e -> startServer());
stopServerButton.addActionListener(e -> stopServer());
sendCommandButton.addActionListener(e -> sendCommand());
+ playerManagerButton.addActionListener(e -> openPlayerManager());
// Enter key in command field
commandField.addKeyListener(new KeyAdapter() {
@@ -122,10 +256,10 @@ public class EnhancedServerManagerUI extends JFrame {
addButton.setToolTipText("Add a new server instance");
saveButton.setToolTipText("Save all server configurations");
fetchVersionsButton.setToolTipText("Fetch available PaperMC versions");
- downloadJarButton.setToolTipText("Download the selected PaperMC JAR");
startServerButton.setToolTipText("Start the selected server");
stopServerButton.setToolTipText("Send 'stop' command to the selected server");
sendCommandButton.setToolTipText("Send a command to the server console");
+ playerManagerButton.setToolTipText("Manage online players (kick/ban)");
nameField.setToolTipText("Server name (used as directory)");
portField.setToolTipText("Server port (default 25565)");
ramField.setToolTipText("RAM in GB (e.g. 2, 4, 8)");
@@ -137,34 +271,49 @@ public class EnhancedServerManagerUI extends JFrame {
setSize(1200, 800);
setLocationRelativeTo(null);
setVisible(true);
+
+ // Set cell renderer and fixed height for the server list
+ serverList.setCellRenderer(new ServerStatusCellRenderer());
+ serverList.setFixedCellHeight(28);
+ serverList.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
+
+ // Add a Swing Timer to refresh the server list state every 10 minutes (600,000 ms):
+ new javax.swing.Timer(600_000, e -> {
+ // Optionally, check process state for each server and update isRunning if needed
+ for (int i = 0; i < serverListModel.size(); i++) {
+ ServerManager.ServerInstance s = serverListModel.getElementAt(i);
+ if (s.process != null) {
+ boolean running = s.process.isAlive();
+ if (s.isRunning != running) {
+ s.isRunning = running;
+ }
+ } else {
+ s.isRunning = false;
+ }
+ }
+ serverList.repaint();
+ }).start();
}
- private void addServer() {
- String name = nameField.getText().trim();
- String portStr = portField.getText().trim();
- String ramStr = ramField.getText().trim();
- String version = (String) versionsComboBox.getSelectedItem();
-
- if (name.isEmpty() || portStr.isEmpty() || ramStr.isEmpty() || version == null) {
- JOptionPane.showMessageDialog(this, "Please fill all fields and select a version", "Input Error", JOptionPane.ERROR_MESSAGE);
- return;
+ private void openAddServerWizard() {
+ List versions = new ArrayList<>();
+ for (int i = 0; i < versionsComboBox.getItemCount(); i++) {
+ versions.add(versionsComboBox.getItemAt(i));
}
-
- int port, ram;
- try {
- port = Integer.parseInt(portStr);
- ram = Integer.parseInt(ramStr);
- } catch (NumberFormatException ex) {
- JOptionPane.showMessageDialog(this, "Invalid port or RAM number", "Input Error", JOptionPane.ERROR_MESSAGE);
- return;
+ AddServerWizard wizard = new AddServerWizard(this, versions);
+ wizard.setVisible(true);
+ if (wizard.isConfirmed()) {
+ try {
+ int port = Integer.parseInt(wizard.getPortField());
+ int ram = Integer.parseInt(wizard.getRamField());
+ ServerManager.ServerInstance server = new ServerManager.ServerInstance(wizard.getNameField(), wizard.getVersion(), port, ram);
+ serverListModel.addElement(server);
+ autosaveServers();
+ JOptionPane.showMessageDialog(this, "Server added and autosaved!", "Success", JOptionPane.INFORMATION_MESSAGE);
+ } catch (NumberFormatException ex) {
+ JOptionPane.showMessageDialog(this, "Invalid port or RAM number", "Input Error", JOptionPane.ERROR_MESSAGE);
+ }
}
-
- ServerManager.ServerInstance server = new ServerManager.ServerInstance(name, version, port, ram);
- serverListModel.addElement(server);
-
- nameField.setText("");
- portField.setText("");
- ramField.setText("");
}
private void saveServers() {
@@ -202,72 +351,41 @@ public class EnhancedServerManagerUI extends JFrame {
worker.execute();
}
- private void downloadJar() {
- ServerManager.ServerInstance selected = serverList.getSelectedValue();
- if (selected == null) {
- JOptionPane.showMessageDialog(this, "Please select a server first", "Error", JOptionPane.ERROR_MESSAGE);
- return;
- }
-
- downloadJarButton.setEnabled(false);
- consoleOutput.append("Downloading JAR for version " + selected.version + "...\n");
-
- SwingWorker worker = new SwingWorker() {
- @Override
- protected String doInBackground() throws Exception {
- return ServerManager.downloadPaperJar(selected.version);
- }
-
- @Override
- protected void done() {
- try {
- String jarPath = get();
- selected.jarPath = jarPath;
- consoleOutput.append("JAR downloaded successfully: " + jarPath + "\n");
-
- // Setup server directory
- ServerManager.setupServerDirectory(selected);
- consoleOutput.append("Server directory setup complete for: " + selected.name + "\n");
-
- } catch (Exception e) {
- consoleOutput.append("Error downloading JAR: " + e.getMessage() + "\n");
- e.printStackTrace();
- } finally {
- downloadJarButton.setEnabled(true);
- }
- }
- };
-
- worker.execute();
- }
-
private void startServer() {
ServerManager.ServerInstance selected = serverList.getSelectedValue();
if (selected == null) {
JOptionPane.showMessageDialog(this, "Please select a server first", "Error", JOptionPane.ERROR_MESSAGE);
return;
}
-
if (selected.jarPath == null || selected.jarPath.isEmpty()) {
- JOptionPane.showMessageDialog(this, "Please download the JAR first", "Error", JOptionPane.ERROR_MESSAGE);
- return;
+ // Determine expected JAR path
+ String expectedJar = "jars/paper-" + selected.version + "-latest.jar"; // or use build if available
+ selected.jarPath = expectedJar;
}
-
+ File jarFile = new File(selected.jarPath);
+ if (!jarFile.exists()) {
+ JOptionPane.showMessageDialog(this, "JAR not found, downloading...", "Info", JOptionPane.INFORMATION_MESSAGE);
+ try {
+ selected.jarPath = ServerManager.downloadPaperJar(selected.version);
+ autosaveServers(); // Save the new jarPath
+ JOptionPane.showMessageDialog(this, "JAR downloaded successfully!", "Success", JOptionPane.INFORMATION_MESSAGE);
+ } catch (Exception e) {
+ JOptionPane.showMessageDialog(this, "Failed to download JAR: " + e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+ }
+ // Now start the server as before
startServerButton.setEnabled(false);
consoleOutput.append("Starting server: " + selected.name + "\n");
-
SwingWorker worker = new SwingWorker() {
@Override
protected Void doInBackground() throws Exception {
ServerManager.startServer(selected, consoleOutput);
return null;
}
-
@Override
protected void done() {
- try {
- get();
- } catch (Exception e) {
+ try { get(); } catch (Exception e) {
consoleOutput.append("Error starting server: " + e.getMessage() + "\n");
e.printStackTrace();
} finally {
@@ -275,7 +393,6 @@ public class EnhancedServerManagerUI extends JFrame {
}
}
};
-
worker.execute();
}
@@ -307,6 +424,170 @@ public class EnhancedServerManagerUI extends JFrame {
commandField.setText("");
}
+ private void autosaveServers() {
+ List servers = new ArrayList<>();
+ for (int i = 0; i < serverListModel.size(); i++) {
+ servers.add(serverListModel.getElementAt(i));
+ }
+ JSONManager.saveServerInstances(servers);
+ }
+
+ private void fetchVersionsAtStartup() {
+ fetchVersionsButton.setEnabled(false);
+ versionsComboBox.removeAllItems();
+ SwingWorker, Void> worker = new SwingWorker, Void>() {
+ @Override
+ protected List doInBackground() throws Exception {
+ return PaperAPIClient.fetchPaperVersions();
+ }
+ @Override
+ protected void done() {
+ try {
+ List versions = get();
+ versions.forEach(versionsComboBox::addItem);
+ } catch (Exception e) {
+ JOptionPane.showMessageDialog(EnhancedServerManagerUI.this, "Failed to fetch Paper versions at startup: " + e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
+ } finally {
+ fetchVersionsButton.setEnabled(true);
+ }
+ }
+ };
+ worker.execute();
+ }
+
+ private void recoverUnsavedServers() {
+ File serversDir = new File("servers");
+ if (!serversDir.exists() || !serversDir.isDirectory()) return;
+ // Build a set of known server names
+ Set knownNames = new HashSet<>();
+ for (int i = 0; i < serverListModel.size(); i++) {
+ knownNames.add(serverListModel.getElementAt(i).name);
+ }
+ File[] dirs = serversDir.listFiles(File::isDirectory);
+ if (dirs == null) return;
+ for (File dir : dirs) {
+ if (!knownNames.contains(dir.getName())) {
+ // Try to find a paper--.jar
+ String foundVersion = null;
+ File[] files = dir.listFiles((d, name) -> name.startsWith("paper-") && name.endsWith(".jar"));
+ if (files != null && files.length > 0) {
+ String jarName = files[0].getName();
+ // Extract version from paper--.jar
+ // e.g. paper-1.20.1-123.jar
+ int dash1 = jarName.indexOf('-');
+ int dash2 = jarName.indexOf('-', dash1 + 1);
+ int dotJar = jarName.lastIndexOf(".jar");
+ if (dash1 != -1 && dash2 != -1 && dotJar != -1) {
+ foundVersion = jarName.substring(dash1 + 1, dash2);
+ }
+ }
+ List versions = new ArrayList<>();
+ for (int i = 0; i < versionsComboBox.getItemCount(); i++) {
+ versions.add(versionsComboBox.getItemAt(i));
+ }
+ AddServerWizard wizard = new AddServerWizard(this, versions, foundVersion);
+ wizard.setNameField(dir.getName());
+ JOptionPane.showMessageDialog(this, "Recovered server directory: " + dir.getName() + "\nPlease enter the missing info." + (foundVersion != null ? "\nVersion detected: " + foundVersion : "\nNo JAR found, please select version."), "Recovery", JOptionPane.WARNING_MESSAGE);
+ wizard.setVisible(true);
+ if (wizard.isConfirmed()) {
+ try {
+ int port = Integer.parseInt(wizard.getPortField());
+ int ram = Integer.parseInt(wizard.getRamField());
+ ServerManager.ServerInstance server = new ServerManager.ServerInstance(wizard.getNameField(), wizard.getVersion(), port, ram);
+ serverListModel.addElement(server);
+ autosaveServers();
+ JOptionPane.showMessageDialog(this, "Recovered and saved server: " + dir.getName(), "Recovery Success", JOptionPane.INFORMATION_MESSAGE);
+ } catch (NumberFormatException ex) {
+ JOptionPane.showMessageDialog(this, "Invalid port or RAM number for recovered server: " + dir.getName(), "Input Error", JOptionPane.ERROR_MESSAGE);
+ }
+ }
+ }
+ }
+ }
+
+ private void openPlayerManager() {
+ ServerManager.ServerInstance selected = serverList.getSelectedValue();
+ if (selected == null || !selected.isRunning) {
+ JOptionPane.showMessageDialog(this, "Please select a running server first", "Error", JOptionPane.ERROR_MESSAGE);
+ return;
+ }
+ JDialog dialog = new JDialog(this, "Player Manager", true);
+ dialog.setLayout(new BorderLayout(10, 10));
+ dialog.setSize(350, 300);
+ dialog.setLocationRelativeTo(this);
+ JPanel panel = new JPanel();
+ panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
+ panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
+ JLabel label = new JLabel("Online players:");
+ DefaultListModel playerListModel = new DefaultListModel<>();
+ JList playerList = new JList<>(playerListModel);
+ JScrollPane playerScroll = new JScrollPane(playerList);
+ playerScroll.setPreferredSize(new Dimension(200, 100));
+ JButton refreshButton = new JButton("Refresh");
+ JButton kickButton = new JButton("Kick");
+ JButton banButton = new JButton("Ban");
+ panel.add(label);
+ panel.add(playerScroll);
+ panel.add(Box.createRigidArea(new Dimension(0, 10)));
+ JPanel btnPanel = new JPanel(new GridLayout(1, 3, 10, 10));
+ btnPanel.add(refreshButton);
+ btnPanel.add(kickButton);
+ btnPanel.add(banButton);
+ panel.add(btnPanel);
+ dialog.add(panel, BorderLayout.CENTER);
+ // Loading indicator
+ label.setText("Loading player list...");
+ playerListModel.clear();
+ // Register a listener for player list update
+ playerListListeners.computeIfAbsent(selected, k -> new ArrayList<>()).add(() -> {
+ List players = playerListCache.getOrDefault(selected, List.of());
+ SwingUtilities.invokeLater(() -> {
+ label.setText("Online players:");
+ playerListModel.clear();
+ for (String p : players) playerListModel.addElement(p);
+ });
+ });
+ // Send 'list' command to server
+ ServerManager.sendCommand(selected, "list");
+ // Refresh button re-sends 'list'
+ refreshButton.addActionListener(ev -> {
+ label.setText("Loading player list...");
+ playerListModel.clear();
+ playerListListeners.computeIfAbsent(selected, k -> new ArrayList<>()).add(() -> {
+ List players = playerListCache.getOrDefault(selected, List.of());
+ SwingUtilities.invokeLater(() -> {
+ label.setText("Online players:");
+ playerListModel.clear();
+ for (String p : players) playerListModel.addElement(p);
+ });
+ });
+ ServerManager.sendCommand(selected, "list");
+ });
+ kickButton.addActionListener(ev -> {
+ String player = playerList.getSelectedValue();
+ if (player != null && !player.isEmpty()) {
+ ServerManager.sendCommand(selected, "kick " + player);
+ JOptionPane.showMessageDialog(dialog, "Kick command sent for: " + player, "Info", JOptionPane.INFORMATION_MESSAGE);
+ }
+ });
+ banButton.addActionListener(ev -> {
+ String player = playerList.getSelectedValue();
+ if (player != null && !player.isEmpty()) {
+ ServerManager.sendCommand(selected, "ban " + player);
+ JOptionPane.showMessageDialog(dialog, "Ban command sent for: " + player, "Info", JOptionPane.INFORMATION_MESSAGE);
+ }
+ });
+ dialog.setVisible(true);
+ }
+
+ public void updatePlayerList(ServerManager.ServerInstance server, List players) {
+ playerListCache.put(server, players);
+ if (playerListListeners.containsKey(server)) {
+ for (Runnable r : playerListListeners.get(server)) r.run();
+ playerListListeners.get(server).clear();
+ }
+ }
+
public static void main(String[] args) {
SwingUtilities.invokeLater(EnhancedServerManagerUI::new);
}
diff --git a/src/main/java/mcmanage/JSONManager.java b/src/main/java/mcmanage/JSONManager.java
index c55d684..4521a9e 100644
--- a/src/main/java/mcmanage/JSONManager.java
+++ b/src/main/java/mcmanage/JSONManager.java
@@ -9,6 +9,8 @@ import java.io.File;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
+import java.util.HashSet;
+import java.util.Set;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
@@ -73,4 +75,27 @@ public class JSONManager {
System.err.println("Failed to rename temp file to server_instances.json");
}
}
+
+ private static final String DOWNLOADED_JARS_FILE = "downloaded_jars.json";
+ public static Set loadDownloadedJars() {
+ File file = new File(DOWNLOADED_JARS_FILE);
+ if (!file.exists()) return new HashSet<>();
+ try (Reader reader = new FileReader(file)) {
+ Gson gson = new Gson();
+ Type setType = new TypeToken>(){}.getType();
+ Set set = gson.fromJson(reader, setType);
+ return set != null ? set : new HashSet<>();
+ } catch (Exception e) {
+ e.printStackTrace();
+ return new HashSet<>();
+ }
+ }
+ public static void saveDownloadedJars(Set jars) {
+ try (Writer writer = new FileWriter(DOWNLOADED_JARS_FILE)) {
+ Gson gson = new Gson();
+ gson.toJson(jars, writer);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
}
diff --git a/src/main/java/mcmanage/ServerManager.java b/src/main/java/mcmanage/ServerManager.java
index 9f6685f..92be5b2 100644
--- a/src/main/java/mcmanage/ServerManager.java
+++ b/src/main/java/mcmanage/ServerManager.java
@@ -7,6 +7,7 @@ import java.nio.file.*;
import java.util.*;
import java.util.concurrent.*;
import javax.swing.*;
+import java.awt.Frame;
public class ServerManager {
@@ -35,8 +36,7 @@ public class ServerManager {
@Override
public String toString() {
- String status = isRunning ? " [RUNNING]" : " [STOPPED]";
- return name + " (" + version + ":" + port + ", " + ramGB + "GB)" + status;
+ return name;
}
}
@@ -181,12 +181,42 @@ public class ServerManager {
CompletableFuture.runAsync(() -> {
try {
String line;
+ boolean[] expectPlayerListLine = {false};
while ((line = server.consoleReader.readLine()) != null) {
final String outputLine = line;
SwingUtilities.invokeLater(() -> {
consoleOutput.append(outputLine + "\n");
consoleOutput.setCaretPosition(consoleOutput.getDocument().getLength());
});
+ // Add logic to detect and parse player list lines, and call updatePlayerList on the main UI if found.
+ // Example:
+ if (outputLine.matches(".*There are \\d+/\\d+ players online:.*")) {
+ expectPlayerListLine[0] = true;
+ // Optionally, clear previous player list
+ SwingUtilities.invokeLater(() -> {
+ for (Frame f : Frame.getFrames()) {
+ if (f instanceof EnhancedServerManagerUI) {
+ ((EnhancedServerManagerUI) f).updatePlayerList(server, new ArrayList<>());
+ }
+ }
+ });
+ } else if (expectPlayerListLine[0]) {
+ String names = outputLine.replaceFirst("^\\[[^\\]]*\\] \\w+: ", "").trim();
+ if (!names.isEmpty()) {
+ List players = new ArrayList<>();
+ for (String p : names.split(", ")) {
+ if (!p.isBlank()) players.add(p.trim());
+ }
+ SwingUtilities.invokeLater(() -> {
+ for (Frame f : Frame.getFrames()) {
+ if (f instanceof EnhancedServerManagerUI) {
+ ((EnhancedServerManagerUI) f).updatePlayerList(server, players);
+ }
+ }
+ });
+ }
+ expectPlayerListLine[0] = false;
+ }
}
} catch (IOException e) {
System.err.println("Error reading console output: " + e.getMessage());