diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..e0f15db --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "java.configuration.updateBuildConfiguration": "automatic" +} \ No newline at end of file diff --git a/README.md b/README.md index 1bf0069..534b75f 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,139 @@ +<<<<<<< HEAD # mvn_MCMANAGE -manage mc servers with java \ No newline at end of file +manage mc servers with java +======= +# Enhanced Minecraft Server Manager + +A comprehensive Java Swing application for managing Minecraft Paper servers with automatic JAR downloading, server launching, and console management. + +## Features + +- **Server Management**: Create and manage multiple Minecraft server instances +- **Paper Integration**: Fetch and download Paper versions automatically from the official API +- **JAR Management**: Automatic downloading of Paper JAR files with latest builds +- **Server Launching**: Start servers with custom RAM allocation and port configuration +- **Console Integration**: Real-time server console output and command input +- **EULA Handling**: Automatic EULA acceptance and server.properties generation +- **Persistent Storage**: JSON-based configuration saving in the root directory +- **Multi-Server Support**: Manage multiple servers simultaneously + +## Requirements + +- Java 11 or higher +- Maven 3.6 or higher +- Internet connection (for JAR downloads) + +## Building the Project + +```bash +mvn clean compile +``` + +## Running the Application + +### Option 1: Using Maven +```bash +mvn exec:java +``` + +### Option 2: Using JAR file +```bash +mvn clean package +java -jar target/minecraft-server-manager-1.0-SNAPSHOT.jar +``` + +## Usage Guide + +### 1. Creating a Server +1. **Fetch Versions**: Click "Fetch Paper Versions" to load available Paper versions +2. **Add Server**: Fill in the server details: + - **Name**: Server instance name + - **Port**: Server port (default: 25565) + - **RAM**: Memory allocation in GB + - **Version**: Select a Paper version from the dropdown +3. **Save**: Click "Save Servers" to persist your configuration + +### 2. Setting Up a Server +1. **Select Server**: Choose a server from the list +2. **Download JAR**: Click "Download JAR" to fetch the Paper server JAR +3. **Automatic Setup**: The system will: + - Create server directory structure + - Generate `eula.txt` (automatically accepted) + - Create `server.properties` with your port + - Copy the JAR to the server directory + +### 3. Running a Server +1. **Select Server**: Choose the server you want to start +2. **Start Server**: Click "Start Server" to launch the Minecraft server +3. **Monitor Console**: Watch real-time output in the console area +4. **Send Commands**: Use the command field to send server commands +5. **Stop Server**: Click "Stop Server" to shut down the server + +## Project Structure + +- `Main.java` - Application entry point +- `EnhancedServerManagerUI.java` - Main GUI interface with server management +- `ServerManager.java` - Core server management and JAR downloading logic +- `ServerLocation.java` - Legacy server location model +- `JSONManager.java` - JSON persistence utilities +- `PaperAPIClient.java` - API client for fetching Paper versions + +## Directory Structure + +``` +minecraft-server-manager/ +├── servers.json # Legacy server locations +├── server_instances.json # Enhanced server configurations +├── servers/ # Server instance directories +│ ├── server1/ +│ │ ├── paper-1.20.1-123.jar +│ │ ├── eula.txt +│ │ ├── server.properties +│ │ └── world/ +│ └── server2/ +└── jars/ # Downloaded JAR files + ├── paper-1.20.1-123.jar + └── paper-1.21.1-456.jar +``` + +## Server Configuration + +### Automatic Files Generated +- **eula.txt**: Automatically set to `eula=true` +- **server.properties**: Basic configuration with your specified port +- **JAR files**: Downloaded from Paper API with latest builds + +### Memory Configuration +- **Xmx**: Maximum heap size (RAM GB × 1024 MB) +- **Xms**: Initial heap size (RAM GB × 512 MB) + +## API Integration + +The application integrates with the official Paper API: +- **Versions**: Fetches available Paper versions +- **Builds**: Downloads latest builds for selected versions +- **JARs**: Automatic JAR file management + +## Troubleshooting + +### Common Issues +1. **Port Already in Use**: Change the port number in server configuration +2. **Insufficient Memory**: Reduce RAM allocation or close other applications +3. **JAR Download Fails**: Check internet connection and try again +4. **Server Won't Start**: Check console output for specific error messages + +### Console Commands +Use the command field to send server commands: +- `stop` - Stop the server gracefully +- `save-all` - Save all worlds +- `list` - List online players +- `op ` - Give operator status +- `whitelist add ` - Add player to whitelist + +## Data Storage + +- **Server Configurations**: Stored in `server_instances.json` in the root directory +- **Server Files**: Each server has its own directory under `servers/` +- **JAR Files**: Cached in `jars/` directory to avoid re-downloading +>>>>>>> df9ed6a (first shit) diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..f4a08ac --- /dev/null +++ b/pom.xml @@ -0,0 +1,69 @@ + + 4.0.0 + mcmanage + minecraft-server-manager + jar + 1.0-SNAPSHOT + minecraft-server-manager + http://maven.apache.org + + + 11 + 11 + UTF-8 + + + + + junit + junit + 3.8.1 + test + + + com.google.code.gson + gson + 2.13.1 + + + com.formdev + flatlaf + 3.2 + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + 11 + 11 + + + + org.apache.maven.plugins + maven-jar-plugin + 3.2.0 + + + + mcmanage.Main + + + + + + org.codehaus.mojo + exec-maven-plugin + 3.0.0 + + mcmanage.Main + + + + + diff --git a/server_instances.json b/server_instances.json new file mode 100644 index 0000000..0bd8e64 --- /dev/null +++ b/server_instances.json @@ -0,0 +1 @@ +[{"name":"shit","version":"1.12.2","port":25565,"ramGB":8}] \ No newline at end of file diff --git a/src/main/java/mcmanage/EnhancedServerManagerUI.java b/src/main/java/mcmanage/EnhancedServerManagerUI.java new file mode 100644 index 0000000..0f78335 --- /dev/null +++ b/src/main/java/mcmanage/EnhancedServerManagerUI.java @@ -0,0 +1,313 @@ +package mcmanage; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.*; +import java.util.List; +import java.util.ArrayList; +import java.util.concurrent.ExecutionException; +import com.formdev.flatlaf.FlatLightLaf; + +public class EnhancedServerManagerUI extends JFrame { + static { + try { + UIManager.setLookAndFeel(new FlatLightLaf()); + } catch (Exception e) { + System.err.println("Failed to initialize FlatLaf"); + } + } + + private DefaultListModel serverListModel = new DefaultListModel<>(); + private JList serverList = new JList<>(serverListModel); + private JTextField nameField = new JTextField(15); + private JTextField portField = new JTextField(5); + private JTextField ramField = new JTextField(3); + private JComboBox versionsComboBox = new JComboBox<>(); + 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); + + public EnhancedServerManagerUI() { + super("Enhanced Minecraft Server Manager"); + + setLayout(new BorderLayout()); + + // Initialize directories + ServerManager.initializeDirectories(); + + // 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)); + controlPanel.add(startServerButton); + controlPanel.add(stopServerButton); + controlPanel.add(downloadJarButton); + controlPanel.add(sendCommandButton); + leftPanel.add(controlPanel, BorderLayout.SOUTH); + + // Right panel - input and console + JPanel rightPanel = new JPanel(new BorderLayout()); + + // 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)); + 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); + consolePanel.add(consoleScrollPane, BorderLayout.CENTER); + + JPanel commandPanel = new JPanel(new BorderLayout()); + 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); + add(rightPanel, BorderLayout.CENTER); + + // Load saved servers + List savedServers = JSONManager.loadServerInstances(); + savedServers.forEach(serverListModel::addElement); + + // Button listeners + addButton.addActionListener(e -> addServer()); + saveButton.addActionListener(e -> saveServers()); + fetchVersionsButton.addActionListener(e -> fetchVersions()); + downloadJarButton.addActionListener(e -> downloadJar()); + startServerButton.addActionListener(e -> startServer()); + stopServerButton.addActionListener(e -> stopServer()); + sendCommandButton.addActionListener(e -> sendCommand()); + + // Enter key in command field + commandField.addKeyListener(new KeyAdapter() { + @Override + public void keyPressed(KeyEvent e) { + if (e.getKeyCode() == KeyEvent.VK_ENTER) { + sendCommand(); + } + } + }); + + // Add tooltips and padding to buttons and fields + 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"); + nameField.setToolTipText("Server name (used as directory)"); + portField.setToolTipText("Server port (default 25565)"); + ramField.setToolTipText("RAM in GB (e.g. 2, 4, 8)"); + versionsComboBox.setToolTipText("Select PaperMC version"); + consoleOutput.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 12)); + inputPanel.setBorder(BorderFactory.createEmptyBorder(10,10,10,10)); + + setDefaultCloseOperation(EXIT_ON_CLOSE); + setSize(1200, 800); + setLocationRelativeTo(null); + setVisible(true); + } + + 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; + } + + 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; + } + + ServerManager.ServerInstance server = new ServerManager.ServerInstance(name, version, port, ram); + serverListModel.addElement(server); + + nameField.setText(""); + portField.setText(""); + ramField.setText(""); + } + + private void saveServers() { + List servers = new ArrayList<>(); + for (int i = 0; i < serverListModel.size(); i++) { + servers.add(serverListModel.getElementAt(i)); + } + JSONManager.saveServerInstances(servers); + JOptionPane.showMessageDialog(this, "Servers saved!"); + } + + private void fetchVersions() { + 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 (InterruptedException | ExecutionException e) { + JOptionPane.showMessageDialog(EnhancedServerManagerUI.this, "Failed to fetch versions: " + e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE); + } finally { + fetchVersionsButton.setEnabled(true); + } + } + }; + + 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; + } + + 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) { + consoleOutput.append("Error starting server: " + e.getMessage() + "\n"); + e.printStackTrace(); + } finally { + startServerButton.setEnabled(true); + } + } + }; + + worker.execute(); + } + + private void stopServer() { + ServerManager.ServerInstance selected = serverList.getSelectedValue(); + if (selected == null) { + JOptionPane.showMessageDialog(this, "Please select a server first", "Error", JOptionPane.ERROR_MESSAGE); + return; + } + + ServerManager.stopServer(selected); + consoleOutput.append("Stopping server: " + selected.name + "\n"); + } + + private void sendCommand() { + ServerManager.ServerInstance selected = serverList.getSelectedValue(); + if (selected == null) { + JOptionPane.showMessageDialog(this, "Please select a server first", "Error", JOptionPane.ERROR_MESSAGE); + return; + } + + String command = commandField.getText().trim(); + if (command.isEmpty()) { + return; + } + + ServerManager.sendCommand(selected, command); + consoleOutput.append("> " + command + "\n"); + commandField.setText(""); + } + + public static void main(String[] args) { + SwingUtilities.invokeLater(EnhancedServerManagerUI::new); + } +} \ No newline at end of file diff --git a/src/main/java/mcmanage/JSONManager.java b/src/main/java/mcmanage/JSONManager.java new file mode 100644 index 0000000..c55d684 --- /dev/null +++ b/src/main/java/mcmanage/JSONManager.java @@ -0,0 +1,76 @@ +package mcmanage; + +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.Reader; +import java.io.Writer; +import java.io.File; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.List; + +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; + +public class JSONManager { + + private static final String FILE = "servers.json"; + + public static List load() { + File file = new File(FILE); + if (!file.exists()) { + return new ArrayList<>(); + } + try (Reader reader = new FileReader(file)) { + Gson gson = new Gson(); + Type listType = new TypeToken>(){}.getType(); + return gson.fromJson(reader, listType); + } catch (IOException e) { + e.printStackTrace(); + return new ArrayList<>(); + } + } + + public static void save(List servers) { + try (Writer writer = new FileWriter(FILE)) { + Gson gson = new Gson(); + gson.toJson(servers, writer); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public static List loadServerInstances() { + File file = new File("server_instances.json"); + if (!file.exists() || file.length() == 0) { + return new ArrayList<>(); + } + try (Reader reader = new FileReader(file)) { + Gson gson = new Gson(); + Type listType = new TypeToken>(){}.getType(); + List list = gson.fromJson(reader, listType); + return list != null ? list : new ArrayList<>(); + } catch (Exception e) { + e.printStackTrace(); + // If parsing fails, return an empty list and optionally log a warning + return new ArrayList<>(); + } + } + + public static void saveServerInstances(List servers) { + File tempFile = new File("server_instances.json.tmp"); + File targetFile = new File("server_instances.json"); + try (Writer writer = new FileWriter(tempFile)) { + Gson gson = new Gson(); + gson.toJson(servers != null ? servers : new ArrayList<>(), writer); + } catch (IOException e) { + e.printStackTrace(); + return; + } + // Atomic move to avoid partial/corrupt files + if (!tempFile.renameTo(targetFile)) { + System.err.println("Failed to rename temp file to server_instances.json"); + } + } +} diff --git a/src/main/java/mcmanage/Main.java b/src/main/java/mcmanage/Main.java new file mode 100644 index 0000000..13fc303 --- /dev/null +++ b/src/main/java/mcmanage/Main.java @@ -0,0 +1,11 @@ +package mcmanage; + +import javax.swing.SwingUtilities; + +public class Main +{ + public static void main( String[] args ) + { + SwingUtilities.invokeLater(() -> new EnhancedServerManagerUI()); + } +} diff --git a/src/main/java/mcmanage/PaperAPIClient.java b/src/main/java/mcmanage/PaperAPIClient.java new file mode 100644 index 0000000..24d78ed --- /dev/null +++ b/src/main/java/mcmanage/PaperAPIClient.java @@ -0,0 +1,47 @@ +package mcmanage; + +import com.google.gson.*; +import java.io.IOException; +import java.net.URI; +import java.net.http.*; +import java.util.ArrayList; +import java.util.List; + +public class PaperAPIClient { + + private static final String API_URL = "https://api.papermc.io/v2/projects/paper"; + + public static List fetchPaperVersions() throws IOException, InterruptedException { + HttpClient client = HttpClient.newHttpClient(); + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(API_URL)) + .GET() + .build(); + + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); + + List versions = new ArrayList<>(); + + if (response.statusCode() == 200) { + try { + JsonObject json = JsonParser.parseString(response.body()).getAsJsonObject(); + + if (json.has("versions")) { + JsonArray versionsJson = json.getAsJsonArray("versions"); + + for (JsonElement v : versionsJson) { + String version = v.getAsString(); + versions.add(version); + } + } + } catch (Exception e) { + System.err.println("Error parsing JSON: " + e.getMessage()); + e.printStackTrace(); + } + } else { + System.err.println("HTTP Error: " + response.statusCode()); + } + + return versions; + } +} diff --git a/src/main/java/mcmanage/ServerLocation.java b/src/main/java/mcmanage/ServerLocation.java new file mode 100644 index 0000000..752f866 --- /dev/null +++ b/src/main/java/mcmanage/ServerLocation.java @@ -0,0 +1,18 @@ +package mcmanage; + +public class ServerLocation { + public String name; + public String ip; + public int port; + + public ServerLocation(String name, String ip, int port) { + this.name = name; + this.ip = ip; + this.port = port; + } + + @Override + public String toString() { + return name + " (" + ip + ":" + port + ")"; + } +} \ No newline at end of file diff --git a/src/main/java/mcmanage/ServerManager.java b/src/main/java/mcmanage/ServerManager.java new file mode 100644 index 0000000..9f6685f --- /dev/null +++ b/src/main/java/mcmanage/ServerManager.java @@ -0,0 +1,226 @@ +package mcmanage; + +import java.io.*; +import java.net.URI; +import java.net.http.*; +import java.nio.file.*; +import java.util.*; +import java.util.concurrent.*; +import javax.swing.*; + +public class ServerManager { + + private static final String PAPER_API_BASE = "https://api.papermc.io/v2/projects/paper"; + private static final String SERVERS_DIR = "servers"; + private static final String JARS_DIR = "jars"; + + public static class ServerInstance { + public String name; + public String version; + public int port; + public int ramGB; + public String jarPath; + + public transient Process process; + public transient BufferedReader consoleReader; + public transient boolean isRunning; + + public ServerInstance(String name, String version, int port, int ramGB) { + this.name = name; + this.version = version; + this.port = port; + this.ramGB = ramGB; + this.isRunning = false; + } + + @Override + public String toString() { + String status = isRunning ? " [RUNNING]" : " [STOPPED]"; + return name + " (" + version + ":" + port + ", " + ramGB + "GB)" + status; + } + } + + public static void initializeDirectories() { + try { + Files.createDirectories(Paths.get(SERVERS_DIR)); + Files.createDirectories(Paths.get(JARS_DIR)); + } catch (IOException e) { + System.err.println("Error creating directories: " + e.getMessage()); + } + } + + public static String downloadPaperJar(String version) throws IOException, InterruptedException { + // First, get the builds for the version + String buildsUrl = PAPER_API_BASE + "/versions/" + version + "/builds"; + HttpClient client = HttpClient.newHttpClient(); + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(buildsUrl)) + .GET() + .build(); + + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); + + if (response.statusCode() != 200) { + throw new IOException("Failed to get builds for version " + version + ": " + response.statusCode()); + } + + // Parse the builds response to get the latest build number + com.google.gson.JsonObject buildsJson = com.google.gson.JsonParser.parseString(response.body()).getAsJsonObject(); + com.google.gson.JsonArray builds = buildsJson.getAsJsonArray("builds"); + + if (builds.size() == 0) { + throw new IOException("No builds found for version " + version); + } + + // Get the latest build (last in the array) + com.google.gson.JsonObject latestBuild = builds.get(builds.size() - 1).getAsJsonObject(); + int buildNumber = latestBuild.get("build").getAsInt(); + + // Download the JAR + String jarName = "paper-" + version + "-" + buildNumber + ".jar"; + String jarUrl = PAPER_API_BASE + "/versions/" + version + "/builds/" + buildNumber + "/downloads/" + jarName; + String jarPath = JARS_DIR + "/" + jarName; + + Path jarFile = Paths.get(jarPath); + if (Files.exists(jarFile)) { + System.out.println("JAR already exists: " + jarPath); + return jarPath; + } + + System.out.println("Downloading JAR: " + jarUrl); + HttpRequest downloadRequest = HttpRequest.newBuilder() + .uri(URI.create(jarUrl)) + .GET() + .build(); + + HttpResponse downloadResponse = client.send(downloadRequest, HttpResponse.BodyHandlers.ofInputStream()); + + if (downloadResponse.statusCode() != 200) { + throw new IOException("Failed to download JAR: " + downloadResponse.statusCode()); + } + + Files.copy(downloadResponse.body(), jarFile, StandardCopyOption.REPLACE_EXISTING); + System.out.println("Downloaded JAR to: " + jarPath); + + return jarPath; + } + + public static void setupServerDirectory(ServerInstance server) throws IOException { + String serverDir = SERVERS_DIR + "/" + server.name; + Path serverPath = Paths.get(serverDir); + Files.createDirectories(serverPath); + + // Copy JAR to server directory + Path jarSource = Paths.get(server.jarPath); + Path jarDest = serverPath.resolve(jarSource.getFileName()); + Files.copy(jarSource, jarDest, StandardCopyOption.REPLACE_EXISTING); + + // Create eula.txt if it doesn't exist + Path eulaFile = serverPath.resolve("eula.txt"); + if (!Files.exists(eulaFile)) { + String eulaContent = "#By changing the setting below to TRUE you are indicating your agreement to our EULA (https://account.mojang.com/documents/minecraft_eula).\n" + + "#Generated on " + new Date() + "\n" + + "eula=true\n"; + Files.write(eulaFile, eulaContent.getBytes()); + System.out.println("Created eula.txt for server: " + server.name); + } + + // Create server.properties if it doesn't exist + Path propertiesFile = serverPath.resolve("server.properties"); + if (!Files.exists(propertiesFile)) { + String propertiesContent = "#Minecraft server properties\n" + + "#Generated on " + new Date() + "\n" + + "server-port=" + server.port + "\n" + + "gamemode=survival\n" + + "difficulty=easy\n" + + "max-players=20\n" + + "online-mode=false\n" + + "enable-command-block=false\n" + + "spawn-protection=16\n"; + Files.write(propertiesFile, propertiesContent.getBytes()); + System.out.println("Created server.properties for server: " + server.name); + } + } + + public static void startServer(ServerInstance server, JTextArea consoleOutput) throws IOException { + if (server.isRunning) { + consoleOutput.append("Server " + server.name + " is already running!\n"); + return; + } + + String serverDir = SERVERS_DIR + "/" + server.name; + Path jarFile = Paths.get(serverDir).resolve(Paths.get(server.jarPath).getFileName()); + + if (!Files.exists(jarFile)) { + throw new IOException("Server JAR not found: " + jarFile); + } + + // Build the command + List command = new ArrayList<>(); + command.add("java"); + command.add("-Xmx" + (server.ramGB * 1024) + "M"); + command.add("-Xms" + (server.ramGB * 512) + "M"); + command.add("-jar"); + command.add(jarFile.toAbsolutePath().toString()); + command.add("nogui"); + + ProcessBuilder pb = new ProcessBuilder(command); + pb.directory(new File(serverDir)); + pb.redirectErrorStream(true); + + consoleOutput.append("Starting server " + server.name + " with " + server.ramGB + "GB RAM...\n"); + consoleOutput.append("Command: " + String.join(" ", command) + "\n"); + + server.process = pb.start(); + server.isRunning = true; + + // Start console reader + server.consoleReader = new BufferedReader(new InputStreamReader(server.process.getInputStream())); + + // Start a thread to read console output + CompletableFuture.runAsync(() -> { + try { + String line; + while ((line = server.consoleReader.readLine()) != null) { + final String outputLine = line; + SwingUtilities.invokeLater(() -> { + consoleOutput.append(outputLine + "\n"); + consoleOutput.setCaretPosition(consoleOutput.getDocument().getLength()); + }); + } + } catch (IOException e) { + System.err.println("Error reading console output: " + e.getMessage()); + } finally { + server.isRunning = false; + SwingUtilities.invokeLater(() -> { + consoleOutput.append("Server " + server.name + " has stopped.\n"); + }); + } + }); + } + + public static void stopServer(ServerInstance server) { + if (server.process != null && server.isRunning) { + try { + PrintWriter writer = new PrintWriter(server.process.getOutputStream(), true); + writer.println("stop"); + writer.flush(); + System.out.println("Sent 'stop' command to server: " + server.name); + } catch (Exception e) { + System.err.println("Error sending 'stop' command: " + e.getMessage()); + } + } + } + + public static void sendCommand(ServerInstance server, String command) { + if (server.process != null && server.isRunning) { + try { + PrintWriter writer = new PrintWriter(server.process.getOutputStream()); + writer.println(command); + writer.flush(); + } catch (Exception e) { + System.err.println("Error sending command: " + e.getMessage()); + } + } + } +} \ No newline at end of file diff --git a/src/main/java/mcmanage/ServerManagerUI.java b/src/main/java/mcmanage/ServerManagerUI.java new file mode 100644 index 0000000..9225c89 --- /dev/null +++ b/src/main/java/mcmanage/ServerManagerUI.java @@ -0,0 +1,141 @@ +package mcmanage; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.*; +import java.util.List; +import java.util.ArrayList; +import java.util.concurrent.ExecutionException; + +public class ServerManagerUI extends JFrame { + + private DefaultListModel serverListModel = new DefaultListModel<>(); + private JList serverList = new JList<>(serverListModel); + private JTextField nameField = new JTextField(15); + private JTextField ipField = new JTextField(15); + private JTextField portField = new JTextField(5); + private JButton addButton = new JButton("Add Server"); + private JButton saveButton = new JButton("Save Servers"); + private JButton fetchVersionsButton = new JButton("Fetch Paper Versions"); + private JComboBox versionsComboBox = new JComboBox<>(); + + public ServerManagerUI() { + super("Minecraft Server Manager"); + + setLayout(new BorderLayout()); + + // Left panel - list of servers + JPanel leftPanel = new JPanel(new BorderLayout()); + leftPanel.add(new JLabel("Saved Servers:"), BorderLayout.NORTH); + leftPanel.add(new JScrollPane(serverList), BorderLayout.CENTER); + + // Right panel - input and buttons + JPanel rightPanel = new JPanel(); + rightPanel.setLayout(new BoxLayout(rightPanel, BoxLayout.Y_AXIS)); + rightPanel.add(new JLabel("Name:")); + rightPanel.add(nameField); + rightPanel.add(new JLabel("IP Address:")); + rightPanel.add(ipField); + rightPanel.add(new JLabel("Port:")); + rightPanel.add(portField); + + rightPanel.add(addButton); + rightPanel.add(saveButton); + rightPanel.add(new JLabel("Paper Versions:")); + rightPanel.add(versionsComboBox); + rightPanel.add(fetchVersionsButton); + + add(leftPanel, BorderLayout.CENTER); + add(rightPanel, BorderLayout.EAST); + + // Load saved servers + List savedServers = JSONManager.load(); + savedServers.forEach(serverListModel::addElement); + + // Button listeners + addButton.addActionListener(e -> addServer()); + saveButton.addActionListener(e -> saveServers()); + fetchVersionsButton.addActionListener(e -> fetchVersions()); + + setDefaultCloseOperation(EXIT_ON_CLOSE); + setSize(600, 400); + setLocationRelativeTo(null); + setVisible(true); + } + + private void addServer() { + String name = nameField.getText().trim(); + String ip = ipField.getText().trim(); + String portStr = portField.getText().trim(); + + if (name.isEmpty() || ip.isEmpty() || portStr.isEmpty()) { + JOptionPane.showMessageDialog(this, "Please fill all fields", "Input Error", JOptionPane.ERROR_MESSAGE); + return; + } + + int port; + try { + port = Integer.parseInt(portStr); + } catch (NumberFormatException ex) { + JOptionPane.showMessageDialog(this, "Invalid port number", "Input Error", JOptionPane.ERROR_MESSAGE); + return; + } + + ServerLocation server = new ServerLocation(name, ip, port); + serverListModel.addElement(server); + + nameField.setText(""); + ipField.setText(""); + portField.setText(""); + } + + private void saveServers() { + List servers = new ArrayList<>(); + for (int i = 0; i < serverListModel.size(); i++) { + servers.add(serverListModel.getElementAt(i)); + } + JSONManager.save(servers); + JOptionPane.showMessageDialog(this, "Servers saved!"); + } + + private void fetchVersions() { + System.out.println("Fetch versions button clicked"); + fetchVersionsButton.setEnabled(false); + versionsComboBox.removeAllItems(); + + SwingWorker, Void> worker = new SwingWorker, Void>() { + @Override + protected List doInBackground() throws Exception { + System.out.println("Starting background task to fetch versions"); + List versions = PaperAPIClient.fetchPaperVersions(); + System.out.println("Background task completed, fetched " + versions.size() + " versions"); + return versions; + } + + @Override + protected void done() { + try { + System.out.println("SwingWorker done() called"); + List versions = get(); + System.out.println("Retrieved " + versions.size() + " versions from SwingWorker"); + versions.forEach(versionsComboBox::addItem); + System.out.println("Added " + versions.size() + " items to combo box"); + } catch (InterruptedException | ExecutionException e) { + System.err.println("Error in SwingWorker done(): " + e.getMessage()); + e.printStackTrace(); + JOptionPane.showMessageDialog(ServerManagerUI.this, "Failed to fetch versions: " + e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE); + } finally { + fetchVersionsButton.setEnabled(true); + System.out.println("Fetch versions button re-enabled"); + } + } + }; + + worker.execute(); + System.out.println("SwingWorker executed"); + } + + public static void main(String[] args) { + SwingUtilities.invokeLater(ServerManagerUI::new); + } +} \ No newline at end of file diff --git a/src/test/java/mcmanage/AppTest.java b/src/test/java/mcmanage/AppTest.java new file mode 100644 index 0000000..f950d72 --- /dev/null +++ b/src/test/java/mcmanage/AppTest.java @@ -0,0 +1,38 @@ +package mcmanage; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; + +/** + * Unit test for simple App. + */ +public class AppTest + extends TestCase +{ + /** + * Create the test case + * + * @param testName name of the test case + */ + public AppTest( String testName ) + { + super( testName ); + } + + /** + * @return the suite of tests being tested + */ + public static Test suite() + { + return new TestSuite( AppTest.class ); + } + + /** + * Rigourous Test :-) + */ + public void testApp() + { + assertTrue( true ); + } +} diff --git a/steps.md b/steps.md new file mode 100644 index 0000000..8c28575 --- /dev/null +++ b/steps.md @@ -0,0 +1,272 @@ +## Step 1: Set up your project + +* Use Java 11 or higher (for built-in HttpClient) +* Add **Gson** to your build: + + * If using Maven or Gradle, add Gson as explained before. + * If plain, download gson JAR and add it to classpath. +* Create your package, e.g., `mcmanage`. + +--- + +## Step 2: Create ServerLocation class + +```java +package mcmanage; + +public class ServerLocation { + public String name; + public String ip; + public int port; + + public ServerLocation(String name, String ip, int port) { + this.name = name; + this.ip = ip; + this.port = port; + } + + @Override + public String toString() { + return name + " (" + ip + ":" + port + ")"; + } +} +``` + +--- + +## Step 3: Create JSON utility to load/save server locations + +```java +package mcmanage; + +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; + +import java.io.*; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.List; + +public class ServerLocationStorage { + + private static final String FILE = "servers.json"; + + public static List load() { + File file = new File(FILE); + if (!file.exists()) { + return new ArrayList<>(); + } + try (Reader reader = new FileReader(file)) { + Gson gson = new Gson(); + Type listType = new TypeToken>(){}.getType(); + return gson.fromJson(reader, listType); + } catch (IOException e) { + e.printStackTrace(); + return new ArrayList<>(); + } + } + + public static void save(List servers) { + try (Writer writer = new FileWriter(FILE)) { + Gson gson = new Gson(); + gson.toJson(servers, writer); + } catch (IOException e) { + e.printStackTrace(); + } + } +} +``` + +--- + +## Step 4: Create PaperAPI client to fetch versions + +```java +package mcmanage; + +import com.google.gson.*; +import java.io.IOException; +import java.net.URI; +import java.net.http.*; +import java.util.ArrayList; +import java.util.List; + +public class PaperAPIClient { + + private static final String API_URL = "https://api.papermc.io/v2/projects/paper"; + + public static List fetchPaperVersions() throws IOException, InterruptedException { + HttpClient client = HttpClient.newHttpClient(); + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(API_URL)) + .GET() + .build(); + + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); + + List versions = new ArrayList<>(); + + if (response.statusCode() == 200) { + JsonObject json = JsonParser.parseString(response.body()).getAsJsonObject(); + JsonArray versionsJson = json.getAsJsonArray("versions"); + + for (JsonElement v : versionsJson) { + versions.add(v.getAsString()); + } + } + return versions; + } +} +``` + +--- + +## Step 5: Build the Swing GUI + +```java +package mcmanage; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.*; +import java.util.List; +import java.util.concurrent.ExecutionException; + +public class ServerManagerUI extends JFrame { + + private DefaultListModel serverListModel = new DefaultListModel<>(); + private JList serverList = new JList<>(serverListModel); + private JTextField nameField = new JTextField(15); + private JTextField ipField = new JTextField(15); + private JTextField portField = new JTextField(5); + private JButton addButton = new JButton("Add Server"); + private JButton saveButton = new JButton("Save Servers"); + private JButton fetchVersionsButton = new JButton("Fetch Paper Versions"); + private JComboBox versionsComboBox = new JComboBox<>(); + + public ServerManagerUI() { + super("Minecraft Server Manager"); + + setLayout(new BorderLayout()); + + // Left panel - list of servers + JPanel leftPanel = new JPanel(new BorderLayout()); + leftPanel.add(new JLabel("Saved Servers:"), BorderLayout.NORTH); + leftPanel.add(new JScrollPane(serverList), BorderLayout.CENTER); + + // Right panel - input and buttons + JPanel rightPanel = new JPanel(); + rightPanel.setLayout(new BoxLayout(rightPanel, BoxLayout.Y_AXIS)); + rightPanel.add(new JLabel("Name:")); + rightPanel.add(nameField); + rightPanel.add(new JLabel("IP Address:")); + rightPanel.add(ipField); + rightPanel.add(new JLabel("Port:")); + rightPanel.add(portField); + + rightPanel.add(addButton); + rightPanel.add(saveButton); + rightPanel.add(new JLabel("Paper Versions:")); + rightPanel.add(versionsComboBox); + rightPanel.add(fetchVersionsButton); + + add(leftPanel, BorderLayout.CENTER); + add(rightPanel, BorderLayout.EAST); + + // Load saved servers + List savedServers = ServerLocationStorage.load(); + savedServers.forEach(serverListModel::addElement); + + // Button listeners + addButton.addActionListener(e -> addServer()); + saveButton.addActionListener(e -> saveServers()); + fetchVersionsButton.addActionListener(e -> fetchVersions()); + + setDefaultCloseOperation(EXIT_ON_CLOSE); + setSize(600, 400); + setLocationRelativeTo(null); + setVisible(true); + } + + private void addServer() { + String name = nameField.getText().trim(); + String ip = ipField.getText().trim(); + String portStr = portField.getText().trim(); + + if (name.isEmpty() || ip.isEmpty() || portStr.isEmpty()) { + JOptionPane.showMessageDialog(this, "Please fill all fields", "Input Error", JOptionPane.ERROR_MESSAGE); + return; + } + + int port; + try { + port = Integer.parseInt(portStr); + } catch (NumberFormatException ex) { + JOptionPane.showMessageDialog(this, "Invalid port number", "Input Error", JOptionPane.ERROR_MESSAGE); + return; + } + + ServerLocation server = new ServerLocation(name, ip, port); + serverListModel.addElement(server); + + nameField.setText(""); + ipField.setText(""); + portField.setText(""); + } + + private void saveServers() { + List servers = serverListModel.elements().asIterator().toList(); + ServerLocationStorage.save(servers); + JOptionPane.showMessageDialog(this, "Servers saved!"); + } + + private void fetchVersions() { + fetchVersionsButton.setEnabled(false); + versionsComboBox.removeAllItems(); + + SwingWorker, Void> worker = new SwingWorker<>() { + @Override + protected List doInBackground() throws Exception { + return PaperAPIClient.fetchPaperVersions(); + } + + @Override + protected void done() { + try { + List versions = get(); + versions.forEach(versionsComboBox::addItem); + } catch (InterruptedException | ExecutionException e) { + JOptionPane.showMessageDialog(ServerManagerUI.this, "Failed to fetch versions", "Error", JOptionPane.ERROR_MESSAGE); + } finally { + fetchVersionsButton.setEnabled(true); + } + } + }; + + worker.execute(); + } + + public static void main(String[] args) { + SwingUtilities.invokeLater(ServerManagerUI::new); + } +} +``` + +--- + +## Step 6: Run & test + +* Launch your `ServerManagerUI` main class. +* Add servers manually and save them. +* Click “Fetch Paper Versions” to load versions from Paper API. +* The versions appear in the combo box dynamically. + +--- + +## Summary + +* You have JSON save/load with Gson. +* You fetch Paper versions dynamically using Java 11 HttpClient. +* You use Swing for a simple GUI to manage your servers and versions. + +--- diff --git a/target/classes/mcmanage/EnhancedServerManagerUI$1.class b/target/classes/mcmanage/EnhancedServerManagerUI$1.class new file mode 100644 index 0000000..f985aa0 Binary files /dev/null and b/target/classes/mcmanage/EnhancedServerManagerUI$1.class differ diff --git a/target/classes/mcmanage/EnhancedServerManagerUI$2.class b/target/classes/mcmanage/EnhancedServerManagerUI$2.class new file mode 100644 index 0000000..f7f1c3c Binary files /dev/null and b/target/classes/mcmanage/EnhancedServerManagerUI$2.class differ diff --git a/target/classes/mcmanage/EnhancedServerManagerUI$3.class b/target/classes/mcmanage/EnhancedServerManagerUI$3.class new file mode 100644 index 0000000..cf3c833 Binary files /dev/null and b/target/classes/mcmanage/EnhancedServerManagerUI$3.class differ diff --git a/target/classes/mcmanage/EnhancedServerManagerUI$4.class b/target/classes/mcmanage/EnhancedServerManagerUI$4.class new file mode 100644 index 0000000..791facf Binary files /dev/null and b/target/classes/mcmanage/EnhancedServerManagerUI$4.class differ diff --git a/target/classes/mcmanage/EnhancedServerManagerUI.class b/target/classes/mcmanage/EnhancedServerManagerUI.class new file mode 100644 index 0000000..37f9585 Binary files /dev/null and b/target/classes/mcmanage/EnhancedServerManagerUI.class differ diff --git a/target/classes/mcmanage/JSONManager$1.class b/target/classes/mcmanage/JSONManager$1.class new file mode 100644 index 0000000..4d704ea Binary files /dev/null and b/target/classes/mcmanage/JSONManager$1.class differ diff --git a/target/classes/mcmanage/JSONManager$2.class b/target/classes/mcmanage/JSONManager$2.class new file mode 100644 index 0000000..6d59b27 Binary files /dev/null and b/target/classes/mcmanage/JSONManager$2.class differ diff --git a/target/classes/mcmanage/JSONManager.class b/target/classes/mcmanage/JSONManager.class new file mode 100644 index 0000000..f142d1d Binary files /dev/null and b/target/classes/mcmanage/JSONManager.class differ diff --git a/target/classes/mcmanage/Main.class b/target/classes/mcmanage/Main.class new file mode 100644 index 0000000..565746b Binary files /dev/null and b/target/classes/mcmanage/Main.class differ diff --git a/target/classes/mcmanage/PaperAPIClient.class b/target/classes/mcmanage/PaperAPIClient.class new file mode 100644 index 0000000..51fb50a Binary files /dev/null and b/target/classes/mcmanage/PaperAPIClient.class differ diff --git a/target/classes/mcmanage/ServerLocation.class b/target/classes/mcmanage/ServerLocation.class new file mode 100644 index 0000000..9ac8037 Binary files /dev/null and b/target/classes/mcmanage/ServerLocation.class differ diff --git a/target/classes/mcmanage/ServerManager$ServerInstance.class b/target/classes/mcmanage/ServerManager$ServerInstance.class new file mode 100644 index 0000000..874d1fa Binary files /dev/null and b/target/classes/mcmanage/ServerManager$ServerInstance.class differ diff --git a/target/classes/mcmanage/ServerManager.class b/target/classes/mcmanage/ServerManager.class new file mode 100644 index 0000000..6ee0169 Binary files /dev/null and b/target/classes/mcmanage/ServerManager.class differ diff --git a/target/classes/mcmanage/ServerManagerUI$1.class b/target/classes/mcmanage/ServerManagerUI$1.class new file mode 100644 index 0000000..9a1e670 Binary files /dev/null and b/target/classes/mcmanage/ServerManagerUI$1.class differ diff --git a/target/classes/mcmanage/ServerManagerUI.class b/target/classes/mcmanage/ServerManagerUI.class new file mode 100644 index 0000000..12ab830 Binary files /dev/null and b/target/classes/mcmanage/ServerManagerUI.class differ diff --git a/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst b/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst new file mode 100644 index 0000000..1014211 --- /dev/null +++ b/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst @@ -0,0 +1,15 @@ +mcmanage/ServerManager.class +mcmanage/JSONManager$1.class +mcmanage/EnhancedServerManagerUI$2.class +mcmanage/ServerManagerUI$1.class +mcmanage/EnhancedServerManagerUI$1.class +mcmanage/JSONManager.class +mcmanage/JSONManager$2.class +mcmanage/EnhancedServerManagerUI.class +mcmanage/PaperAPIClient.class +mcmanage/Main.class +mcmanage/EnhancedServerManagerUI$4.class +mcmanage/ServerLocation.class +mcmanage/ServerManagerUI.class +mcmanage/EnhancedServerManagerUI$3.class +mcmanage/ServerManager$ServerInstance.class diff --git a/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst b/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst new file mode 100644 index 0000000..0842c13 --- /dev/null +++ b/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst @@ -0,0 +1,7 @@ +/home/rattatwinko/Documents/gitproj/mvn_MCMANAGE/src/main/java/mcmanage/JSONManager.java +/home/rattatwinko/Documents/gitproj/mvn_MCMANAGE/src/main/java/mcmanage/ServerLocation.java +/home/rattatwinko/Documents/gitproj/mvn_MCMANAGE/src/main/java/mcmanage/ServerManager.java +/home/rattatwinko/Documents/gitproj/mvn_MCMANAGE/src/main/java/mcmanage/ServerManagerUI.java +/home/rattatwinko/Documents/gitproj/mvn_MCMANAGE/src/main/java/mcmanage/EnhancedServerManagerUI.java +/home/rattatwinko/Documents/gitproj/mvn_MCMANAGE/src/main/java/mcmanage/PaperAPIClient.java +/home/rattatwinko/Documents/gitproj/mvn_MCMANAGE/src/main/java/mcmanage/Main.java