50 Commits

Author SHA1 Message Date
44b9bcc960 Updates current version to 1.2.1
All checks were successful
KnarCraft/Minecraft-Server-Launcher/pipeline/head This commit looks good
2020-09-20 18:18:45 +02:00
9f2dda7c6b Updates Minecraft .jar version to 1.16.3 where applicable 2020-09-20 18:18:27 +02:00
6c0e352649 Fixes a bug caused by the version file not being properly closed 2020-09-20 17:36:10 +02:00
5058383f93 Updates the version of the software to beta
All checks were successful
KnarCraft/Minecraft-Server-Launcher/pipeline/head This commit looks good
2020-08-29 14:03:48 +02:00
39a8c14ece Updates the updater to use the new updater .jar 2020-08-29 14:03:26 +02:00
5b15fea7b3 Splits the GUI into more components to increase maintainability 2020-08-20 18:33:45 +02:00
52946c9aec Improves string storage and variable names in the main GUI. Also adds the fallback web browsing method
All checks were successful
KnarCraft/Minecraft-Server-Launcher/pipeline/head This commit looks good
2020-08-19 21:26:39 +02:00
83f8834530 Improves a lot of comments and variable names 2020-08-19 21:24:37 +02:00
d06cc66f31 Adds a WebServer class as a fallback when native browsing is unavailable 2020-08-19 21:23:10 +02:00
3d6476b1ef Makes Console extend KeyAdapter to remove empty methods. Also improves comments 2020-08-19 15:19:00 +02:00
070f87222b Removes a redundant semicolon from the custom server type 2020-08-19 14:58:27 +02:00
1300f6808b Updates spigot version to 1.16.2 and adds craftbukkit versions beyond 1.13.2 2020-08-19 14:40:53 +02:00
23161c1a8d Splits the Waterfall and Travertine server types by Minecraft version
All checks were successful
KnarCraft/Minecraft-Server-Launcher/pipeline/head This commit looks good
2020-08-19 13:06:27 +02:00
4f60ed5d10 Changes the order of server types to make more sense 2020-08-19 12:05:54 +02:00
2acc6f4d13 Adds the ability to download old bungee versions 2020-08-19 12:01:19 +02:00
2f8117ebfa Adds craftbukkit beta version 1.8 and 1.7 2020-08-19 11:43:36 +02:00
2008ec8fd1 Adds the SpongeForge server type 2020-08-19 11:24:05 +02:00
d06cf4114a Adds and improves comments for server types and profile 2020-08-19 10:39:24 +02:00
e71e95df7f Improves some comments and structure for the Controller 2020-08-17 23:09:37 +02:00
d60e16b4a5 Improves some comments and variable names for ServerTab 2020-08-17 22:55:09 +02:00
cc0a5a1659 Makes all folder selection use a common method 2020-08-17 22:19:30 +02:00
a3de8a9f6c Changes a lot of things to make everything cleaner. Closes #3
All checks were successful
KnarCraft/Minecraft-Server-Launcher/pipeline/head This commit looks good
Drops the idea of using serializable
Adds a new controller object which takes care of profiles and saving
Moves profile independent settings to its own file
Makes saving and loading from file a lot cleaner
Fixes the bug preventing lastly used profile from loading
Makes the profile object only do profile things
Moves gui initialization to the controller object
Updates vanilla version from 1.16.1 to 1.16.2
Moves backup to common functions
2020-08-17 21:41:38 +02:00
2ed6b8bedb Fixes the tray image not being loaded 2020-08-17 13:59:19 +02:00
ba9b192790 Fixes a NullPointerException thrown when the profiles file does not exist 2020-08-17 12:39:29 +02:00
a5db6b3a70 Prevents tests from being re-run during deployment stage
All checks were successful
KnarCraft/Minecraft-Server-Launcher/pipeline/head This commit looks good
2020-08-17 12:24:33 +02:00
1d49229b87 Simplifies building steps
All checks were successful
KnarCraft/Minecraft-Server-Launcher/pipeline/head This commit looks good
2020-08-17 12:10:42 +02:00
c721aef2a6 Tries a new fix to remove the pesky FileNotFoundException caused by the server version container
Some checks failed
KnarCraft/Minecraft-Server-Launcher/pipeline/head There was a failure building this commit
2020-08-17 11:26:45 +02:00
8e626d8097 Fixes a FileNotFoundException caused by the version file not existing
Some checks failed
KnarCraft/Minecraft-Server-Launcher/pipeline/head There was a failure building this commit
2020-08-15 20:55:29 +02:00
0bf355c4de This should fix all issues regarding the "latest" version for all server types
Some checks failed
KnarCraft/Minecraft-Server-Launcher/pipeline/head There was a failure building this commit
2020-08-15 20:40:46 +02:00
70d064e590 Makes snapshot a vanilla version and loads all jars from the jars folder
Some checks failed
KnarCraft/Minecraft-Server-Launcher/pipeline/head There was a failure building this commit
2020-08-13 03:04:02 +02:00
ab6453cdc3 Adds missing comments, simplifies proxy distinction, moves updating to own class and fixes formatting 2020-08-11 19:29:28 +02:00
094a1facb2 Adds support for the travertine and waterfall server proxies 2020-08-11 14:03:26 +02:00
a2d4d491ba Fixes some bugs preventing servers from starting
All checks were successful
KnarCraft/Minecraft-Server-Launcher/pipeline/head This commit looks good
2020-08-07 04:08:05 +02:00
b144bbb903 Fixes some messages and some jar downloads
All checks were successful
KnarCraft/Minecraft-Server-Launcher/pipeline/head This commit looks good
2020-08-07 02:59:09 +02:00
26cc5370e2 Does some much needed cleanup, adds the Paper server type and fixes broken downloads
Some checks failed
KnarCraft/Minecraft-Server-Launcher/pipeline/head There was a failure building this commit
2020-08-06 19:55:27 +02:00
9ef314b178 Updates available server versions
All checks were successful
KnarCraft/Minecraft-Server-Launcher/pipeline/head This commit looks good
2020-08-03 18:05:28 +02:00
f0ad156e16 Tweaks pom to create jar file with all dependencies
All checks were successful
KnarCraft/Minecraft-Server-Launcher/master This commit looks good
2020-02-13 23:09:03 +01:00
1e34571ca6 Fixes scope for pom depencency
All checks were successful
KnarCraft/Minecraft-Server-Launcher/master This commit looks good
2020-02-13 21:21:38 +01:00
040740db84 Adds comments and refactors several classes
All checks were successful
KnarCraft/Minecraft-Server-Launcher/master This commit looks good
Adds comments to Collection
Makes some variable and function names more descriptive
Adds some new methods for showing messsages and errors
Adds a lot of missing comments
Enhances some existing comments
2020-02-13 21:10:18 +01:00
c59cbcefbb Changes resource loading
Some checks failed
KnarCraft/Minecraft-Server-Launcher/master There was a failure building this commit
Loads configuration and image files from resources folder
Removes duplicate resources
2020-02-12 23:00:51 +01:00
f841d73e2d Adds missing pom
Some checks failed
KnarCraft/Minecraft-Server-Launcher/master There was a failure building this commit
2020-02-12 21:35:01 +01:00
194686b9d8 Makes the project into a Maven project
Some checks failed
KnarCraft/Minecraft-Server-Launcher/master There was a failure building this commit
Moves stuff around
Adds Jenkinsfile
Changes some package names
Replaces library with Maven dependency
2020-02-12 21:30:36 +01:00
4901ea0627 Merge remote-tracking branch 'origin/master' 2018-12-18 00:59:04 +01:00
cade97adbe Closes version file 2018-12-18 00:58:42 +01:00
6869743844 Update 'README.md' 2018-11-22 16:30:40 +01:00
998b901687 Merge remote-tracking branch 'origin/master' 2018-11-07 17:40:56 +01:00
21737b7549 Fixes downloading of jar files
Links to knarcraft.net updated.
Code for parsing the new minecraft vanilla json files added.
Adds auto updates
2018-11-07 17:40:39 +01:00
c88e90b5b5 Fixes jar downloading 2018-09-30 22:11:47 +02:00
a3a8e4f377 Fixes downloading of jar files
Links to knarcraft.net updated.
Code for parsing the new minecraft vanilla json files added.
2018-09-30 17:06:57 +02:00
5ac22babab Bugfixes and optimizations
Fixes a bug where the server version is not updated on load.
Optimized downloading of all jar files.
2018-03-29 16:09:08 +02:00
65 changed files with 4935 additions and 2995 deletions

1
.gitignore vendored
View File

@ -14,7 +14,6 @@
.mtj.tmp/
# Package Files #
*.jar
*.war
*.ear
*.zip

24
Jenkinsfile vendored Normal file
View File

@ -0,0 +1,24 @@
pipeline {
agent any
stages {
stage('Build') {
steps {
echo 'Building...'
sh 'mvn clean & mvn validate & mvn compile'
}
}
stage('Test') {
steps {
echo 'Testing...'
sh 'mvn test'
}
}
stage('Deploy') {
steps {
echo 'Deploying...'
sh 'mvn verify -Dmaven.test.skip=true'
archiveArtifacts artifacts: '**/target/*.jar', fingerprint: true
}
}
}
}

4
META-INF/MANIFEST.MF Normal file
View File

@ -0,0 +1,4 @@
Manifest-Version: 1.0
Class-Path: gson-2.8.5.jar
Main-Class: net.knarcraft.minecraftserverlauncher.Main

View File

@ -1,6 +1,6 @@
# Minecraft-Server-Launcher
I originally created this software in 2013 using AutoIt. Since I am now learning Java, I have recreated it in Java.
The original version can be found at: https://knarcraft.net/Downloads/Bungeeminecraftserverlauncher/
The original version can be found at: https://archive.knarcraft.net/Scripts/BungeeMinecraftServerLauncher/
Its goal is to do everything the original does, just better. The original is 1595 lines of code repetition and dirty code. I had no prior programming experience when I first started working on the original, which is why it became such a mess.
The code is now in a fully working state. It is still missing an auto update feature, but it can be used, and is fully functional.

View File

@ -1,5 +0,0 @@
setup=1. Create a new profile, or use the default._BREAK_2. Click on the add server button and insert the name of your server._BREAK_3. Navigate to the servers tab, and find your server._BREAK_4. Fill in all the settings with what you want, and remember to check Enabled._BREAK_5. Navigate back to the Control panel and click Start servers
runinbk=The program will run in the background. You don't need to have the gui open all the time._BREAK_When in background mode, you can left click on the tray icon to open the gui again. To exit the program, right click the tray icon and press exit.
delaystartup=This adds a delay (in seconds) between the start of each server._BREAK_This option should be used for heavy servers with many plugins, but may not be needed on a single server or servers using a few plugins.
downloadjars=This option will download all the .jar files available in the program._BREAK_Instead of downloading .jar files when you start servers, it will download all files you don't already have, on startup._BREAK_This will be faster and more reliable than usual._BREAK_You need to restart the software for this setting to take action.
about=This software was created to start and manage several servers simultaneously._BREAK_You no longer have to do the tedious work of manually downloading different .jar files every time you want to try something new.
1 setup=1. Create a new profile, or use the default._BREAK_2. Click on the add server button and insert the name of your server._BREAK_3. Navigate to the servers tab, and find your server._BREAK_4. Fill in all the settings with what you want, and remember to check Enabled._BREAK_5. Navigate back to the Control panel and click Start servers
2 runinbk=The program will run in the background. You don't need to have the gui open all the time._BREAK_When in background mode, you can left click on the tray icon to open the gui again. To exit the program, right click the tray icon and press exit.
3 delaystartup=This adds a delay (in seconds) between the start of each server._BREAK_This option should be used for heavy servers with many plugins, but may not be needed on a single server or servers using a few plugins.
4 downloadjars=This option will download all the .jar files available in the program._BREAK_Instead of downloading .jar files when you start servers, it will download all files you don't already have, on startup._BREAK_This will be faster and more reliable than usual._BREAK_You need to restart the software for this setting to take action.
5 about=This software was created to start and manage several servers simultaneously._BREAK_You no longer have to do the tedious work of manually downloading different .jar files every time you want to try something new.

View File

@ -1,8 +0,0 @@
Vanilla;Latest,1.12.2,1.11.2,1.10.2,1.9.4,1.8.9,1.7.10,1.6.4,1.5.2,1.4.7,1.3.2,1.2.5;https://launchermeta.mojang.com/mc/game/version_manifest.json;"release":";";https://s3.amazonaws.com/Minecraft.Download/versions/;/minecraft_server.
Snapshot;Latest;https://launchermeta.mojang.com/mc/game/version_manifest.json;"snapshot":";";https://s3.amazonaws.com/Minecraft.Download/versions/;/minecraft_server.
SpongeVanilla;1.12.2,1.11.2,1.10.2,1.8.9;https://dl-api.spongepowered.org/v1/org.spongepowered/spongevanilla/downloads?type=stable&minecraft=;"version":";",;https://repo.spongepowered.org/maven/org/spongepowered/spongevanilla/;/spongevanilla-
Bungee;Latest;https://ci.md-5.net/job/BungeeCord/lastSuccessfulBuild/artifact/bootstrap/target/;Artifacts of BungeeCord #; ;http://ci.md-5.net/job/BungeeCord/lastSuccessfulBuild/artifact/bootstrap/target/BungeeCord.jar;
Spigot;1.12.2,1.11.2,1.10.2,1.9.4,1.9.4,1.8.8,1.7.10,1.6.4,1.5.2,1.4.7;https://knarcraft.net/Api/Download/bungeeminecraftserverlauncher/jars/Spigot/
MCPCplus;1.6.4,1.6.2,1.5.2,1.4.7;https://knarcraft.net/Api/Download/bungeeminecraftserverlauncher/jars/MCPC+/
Craftbukkit;1.12.2,1.11.2,1.10.2,1.9.4,1.8.8,1.7.10,1.6.4,1.5.2,1.4.6,1.3.2,1.2.5,1.1,1.0;https://knarcraft.net/Api/Download/bungeeminecraftserverlauncher/jars/Bukkit/
Custom;;
Can't render this file because it contains an unexpected character in line 1 and column 156.

0
git Normal file
View File

135
pom.xml Normal file
View File

@ -0,0 +1,135 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>net.knarcraft.minecraftserverlauncher</groupId>
<artifactId>minecraft-server-launcher</artifactId>
<version>1.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>Minecraft Server Launcher</name>
<url>https://git.knarcraft.net/KnarCraft/Minecraft-Server-Launcher</url>
<inceptionYear>2013</inceptionYear>
<licenses>
<license>
<name>ALL RIGHTS RESERVED</name>
<url>https://git.knarcraft.net/KnarCraft/Minecraft-Server-Launcher/LICENCE</url>
</license>
</licenses>
<developers>
<developer>
<id>EpicKnarvik97</id>
<name>Kristian Knarvik</name>
<url>https://kristianknarvik.knarcraft.net</url>
<roles>
<role>leader</role>
<role>developer</role>
</roles>
<timezone>Europe/Oslo</timezone>
</developer>
</developers>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<build>
<plugins>
<plugin>
<!-- Build an executable JAR -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.2.0</version>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<classpathPrefix>lib/</classpathPrefix>
<mainClass>net.knarcraft.minecraftserverlauncher.Main</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<appendAssemblyId>true</appendAssemblyId>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<mainClass>net.knarcraft.minecraftserverlauncher.Main</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
<executions>
<execution>
<id>make-jar-with-dependencies</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>prepare-package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/lib</outputDirectory>
<overWriteReleases>true</overWriteReleases>
<overWriteSnapshots>true</overWriteSnapshots>
<overWriteIfNewer>true</overWriteIfNewer>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
<directory>${project.basedir}/target</directory>
<outputDirectory>${project.build.directory}/classes</outputDirectory>
<finalName>${project.artifactId}-${project.version}</finalName>
<testOutputDirectory>${project.build.directory}/test-classes</testOutputDirectory>
<sourceDirectory>${project.basedir}/src/main/java</sourceDirectory>
<testSourceDirectory>${project.basedir}/src/test/java</testSourceDirectory>
<resources>
<resource>
<directory>${project.basedir}/src/main/resources</directory>
</resource>
</resources>
<testResources>
<testResource>
<directory>${project.basedir}/src/test/resources</directory>
</testResource>
</testResources>
</build>
<dependencies>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.5</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -1,3 +0,0 @@
Manifest-Version: 1.0
Main-Class: net.knarcraft.serverlauncher.Main

View File

@ -0,0 +1,180 @@
package net.knarcraft.minecraftserverlauncher;
import net.knarcraft.minecraftserverlauncher.profile.Collection;
import net.knarcraft.minecraftserverlauncher.profile.Controller;
import net.knarcraft.minecraftserverlauncher.server.Server;
import net.knarcraft.minecraftserverlauncher.userinterface.ServerConsoles;
import net.knarcraft.minecraftserverlauncher.userinterface.ServerLauncherGUI;
import net.knarcraft.minecraftserverlauncher.utility.CommonFunctions;
import net.knarcraft.minecraftserverlauncher.utility.Updater;
import java.awt.*;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.URISyntaxException;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import static net.knarcraft.minecraftserverlauncher.utility.CommonFunctions.stringBetween;
//Java 8 required.
/**
* A software for managing Minecraft servers.
*
* @author Kristian Knarvik <kristian.knarvik@knett.no>
* @version 1.0.0
* @since 1.0.0
*/
public class Main {
private static String applicationWorkDirectory;
private static boolean serversAreRunning = false;
private static final String updateChannel = "beta";
private static final String updateURL = "https://api.knarcraft.net/minecraftserverlauncher";
private static ServerLauncherGUI gui;
private static final Controller controller = Controller.getInstance();
public static void main(String[] args) throws IOException {
Updater.checkForUpdate(updateURL, updateChannel);
try (PrintWriter file = new PrintWriter(Main.getApplicationWorkDirectory() + File.separator + "latestrun.log")) {
file.print("");
} catch (IOException e) {
e.printStackTrace();
}
EventQueue.invokeLater(() -> {
try {
new ServerConsoles();
controller.loadState();
gui = controller.getGUI();
ScheduledExecutorService exec = Executors.newSingleThreadScheduledExecutor();
exec.scheduleAtFixedRate(Main::updateConsoles, 10, 500, TimeUnit.MILLISECONDS);
} catch (Exception e) {
e.printStackTrace();
}
});
}
/**
* Gets the controller used by the software
*
* @return <p>The controller used by the software</p>
*/
public static Controller getController() {
return controller;
}
/**
* Retrieves the directory the .jar file is running from
*
* @return A string path
*/
public static String getApplicationWorkDirectory() {
if (applicationWorkDirectory == null) {
try {
applicationWorkDirectory = String.valueOf(new File(Main.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath()).getParentFile());
} catch (URISyntaxException e) {
e.printStackTrace();
System.exit(1);
}
}
return applicationWorkDirectory;
}
/**
* Reads from server processes, and writes the output to consoles.
*/
private static void updateConsoles() {
try {
for (Collection collection : controller.getCurrentProfile().getCollections()) {
Server server = collection.getServer();
if (server.isEnabled() && server.getProcess() != null) {
try {
String readText = CommonFunctions.readBufferedReader(server.getReader());
if (!readText.equals("")) {
collection.getServerConsole().output(readText);
updatePlayerList(readText, server);
}
} catch (IOException e) {
e.printStackTrace();
}
if (!server.getProcess().isAlive()) {
server.stopped();
}
}
}
boolean runningNew = serversRunning();
if (!runningNew && serversAreRunning) {
gui.updateGUIElementsWhenServersStartOrStop(false);
gui.setStatus("Servers are stopped");
} else if (runningNew && !serversAreRunning) {
gui.updateGUIElementsWhenServersStartOrStop(true);
}
serversAreRunning = runningNew;
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Goes through all servers and looks for any running servers.
*
* @return Is at least one server running?
*/
private static boolean serversRunning() {
int num = 0;
for (Collection collection : controller.getCurrentProfile().getCollections()) {
if (collection.getServer().isStarted() ||
(collection.getServer().getProcess() != null && collection.getServer().getProcess().isAlive())) {
num++;
}
}
return num > 0;
}
/**
* Looks for strings implying a player has joined or left, and updates the appropriate lists
*
* @param text <p>The text to search</p>
* @param server <p>The server which sent the text</p>
*/
private static void updatePlayerList(String text, Server server) {
if (!server.getType().isProxy()) {
String joinedPlayer = getPlayer(text, true);
String leftPlayer = getPlayer(text, false);
if (!joinedPlayer.equals("")) {
if (!server.hasPlayer(joinedPlayer)) {
server.addPlayer(joinedPlayer);
}
} else if (!leftPlayer.equals("")) {
if (server.hasPlayer(leftPlayer)) {
server.removePlayer(leftPlayer);
}
}
}
}
/**
* Searches a string for players joining or leaving
*
* @param text <p>The text string to search through</p>
* @param joined <p>Whether to search for a joining player</p>
* @return <p>The name of a player, or an empty string</p>
*/
private static String getPlayer(String text, boolean joined) {
String playerName;
if (joined) {
playerName = stringBetween(text, "[Server thread/INFO]: ", " joined the game");
if (playerName.equals("")) {
playerName = stringBetween(text, "UUID of player ", " is ");
}
} else {
playerName = stringBetween(text, "INFO]: ", " lost connection");
if (playerName.equals("")) {
playerName = stringBetween(text, "[Server thread/INFO]: ", " left the game");
}
}
return playerName;
}
}

View File

@ -0,0 +1,86 @@
package net.knarcraft.minecraftserverlauncher.profile;
import net.knarcraft.minecraftserverlauncher.server.Server;
import net.knarcraft.minecraftserverlauncher.userinterface.Console;
import net.knarcraft.minecraftserverlauncher.userinterface.ServerConsoles;
import net.knarcraft.minecraftserverlauncher.userinterface.ServerTab;
import javax.naming.ConfigurationException;
/**
* Acts as a wrapper for objects necessary for each server.
*
* @author Kristian Knarvik <kristian.knarvik@knett.no>
* @version 1.0.0
* @since 1.0.0
*/
public class Collection {
private final Server server;
private final ServerTab serverTab;
private final Console serverConsole;
private final String name;
/**
* Creates a new collection with the given name
*
* @param name <p>The name identifying the server, server tab, collection and server console</p>
*/
Collection(String name) throws ConfigurationException {
this.serverTab = new ServerTab(name);
this.server = new Server(name);
this.serverConsole = ServerConsoles.addConsoleTab(name);
this.name = name;
}
/**
* Creates a new collection with the given server
*
* @param server <p>The server used for as part of the collection</p>
* @throws ConfigurationException <p>If unable to configure the collection</p>
*/
Collection(Server server) throws ConfigurationException {
String serverName = server.getName();
this.serverTab = new ServerTab(serverName);
this.server = server;
this.serverConsole = ServerConsoles.addConsoleTab(serverName);
this.name = serverName;
this.serverTab.setData(server.getPath(), server.isEnabled(), server.getTypeName(), server.getServerVersion(),
server.getMaxRam());
}
/**
* Gets the name of the collection
*
* @return <p>Collection name</p>
*/
public String getName() {
return this.name;
}
/**
* Gets the server of the collection
*
* @return <p>Collection server</p>
*/
public Server getServer() {
return this.server;
}
/**
* Gets the server tab of the collection
*
* @return <p>Collection server tab</p>
*/
public ServerTab getServerTab() {
return this.serverTab;
}
/**
* Gets the server console of the collection
*
* @return <p>Collection server console</p>
*/
public Console getServerConsole() {
return this.serverConsole;
}
}

View File

@ -0,0 +1,313 @@
package net.knarcraft.minecraftserverlauncher.profile;
import net.knarcraft.minecraftserverlauncher.Main;
import net.knarcraft.minecraftserverlauncher.server.Server;
import net.knarcraft.minecraftserverlauncher.server.ServerTypeHandler;
import net.knarcraft.minecraftserverlauncher.userinterface.ServerLauncherGUI;
import net.knarcraft.minecraftserverlauncher.userinterface.ServerTab;
import net.knarcraft.minecraftserverlauncher.utility.CommonFunctions;
import net.knarcraft.minecraftserverlauncher.utility.JarDownloader;
import javax.naming.ConfigurationException;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
import java.util.concurrent.Executors;
/**
* This class handles profiles, GUI creation and session restoration
*/
public class Controller {
private final String workingDirectory = Main.getApplicationWorkDirectory() + File.separator + "files";
private final String profilesFile = workingDirectory + File.separator + "Profiles.txt";
private final String mainFile = workingDirectory + File.separator + "Config.txt";
private final String jarDirectory = workingDirectory + File.separator + "Jars" + File.separator;
private final List<Profile> profileList;
private ServerLauncherGUI serverLauncherGUI;
private Profile currentProfile;
private boolean downloadAllJars;
private static Controller controller;
/**
* Instantiates a new controller
*/
private Controller() {
this.profileList = new ArrayList<>();
}
/**
* Gets an instance of the controller
*
* @return <p>An instance of the controller</p>
*/
public static Controller getInstance() {
if (controller == null) {
controller = new Controller();
}
return controller;
}
/**
* Returns the GUI used by the software
*
* @return <p>The GUI used by the software</p>
*/
public ServerLauncherGUI getGUI() {
return this.serverLauncherGUI;
}
/**
* Gets whether to download all jar files
*
* @return <p>Whether to download all .jar files</p>
*/
public boolean getDownloadAllJars() {
return downloadAllJars;
}
/**
* Gets whether to run in background after closing
*
* @return <p>Whether to run in background after closing</p>
*/
public boolean getRunInBackground() {
return currentProfile.getRunInBackground();
}
/**
* Gets whether to delay server startup
*
* @return <p>Whether to delay server startup</p>
*/
public int getDelayStartup() {
return currentProfile.getDelayStartup();
}
@Override
public String toString() {
int guiWidth;
int guiHeight;
if (serverLauncherGUI == null) {
guiWidth = 440;
guiHeight = 170;
} else {
guiWidth = serverLauncherGUI.getSize().width;
guiHeight = serverLauncherGUI.getSize().height;
}
return String.format("%s;%d;%d;%b", currentProfile.getName(), guiWidth, guiHeight, downloadAllJars);
}
/**
* Gets a profile by its name
*
* @param name <p>The name of the profile to get</p>
* @return <p>The profile with the given name or null if not found</p>
*/
public Profile getProfileByName(String name) {
for (Profile profile : profileList) {
if (profile.getName().equals(name)) {
return profile;
}
}
return null;
}
/**
* Gets the names of all available profiles
*
* @return <p>A list of all available profiles</p>
*/
public List<String> getProfileNames() {
List<String> profileNames = new ArrayList<>();
profileList.forEach((profile) -> profileNames.add(profile.getName()));
return profileNames;
}
/**
* Adds a profile if the name is valid and unique
*
* @param name <p>The name of the new profile</p>
*/
public void addProfile(String name) {
if (!CommonFunctions.nameIsValid(name)) {
serverLauncherGUI.showError("Profile name cannot be blank and cannot contain special characters.");
return;
}
for (Profile profile : profileList) {
if (profile.getName().equals(name)) {
serverLauncherGUI.showError("There is already a profile with this name.");
return;
}
}
Profile newProfile = new Profile(name);
profileList.add(newProfile);
if (currentProfile == null) {
currentProfile = newProfile;
}
}
/**
* Removes a profile with the given name from the list of profiles, if such a profile exists
*
* @param name <p>The name of the profile to remove</p>
*/
public void removeProfile(String name) {
if (profileList.size() > 1) {
profileList.removeIf(profile -> profile.getName().equals(name));
} else {
serverLauncherGUI.showError("This software requires the existence of at least one profile.");
}
}
/**
* Gets the currently selected profile
*
* @return <p>The currently selected profile</p>
*/
public Profile getCurrentProfile() {
return this.currentProfile;
}
/**
* Loads data about the controller from a save string
*
* @param data <p>The save string to load</p>
* @return <p>The currently selected profile</p>
* @throws IOException <p>If unable to create the GUI</p>
*/
private String fromString(String data) throws IOException {
String[] dataList = data.split(";");
this.downloadAllJars = Boolean.parseBoolean(dataList[3]);
this.serverLauncherGUI = new ServerLauncherGUI(Integer.parseInt(dataList[1]), Integer.parseInt(dataList[2]));
return dataList[0];
}
/**
* Reads profiles and servers from a text file
*/
public void loadState() throws ConfigurationException, IOException {
//Loads data regarding this controller
String currentProfile = null;
if (new File(mainFile).exists()) {
String controllerData = CommonFunctions.readBufferedReader(new BufferedReader(new InputStreamReader(new FileInputStream(mainFile))));
currentProfile = this.fromString(controllerData);
} else {
this.serverLauncherGUI = new ServerLauncherGUI();
}
//Loads all saved profiles
loadProfiles(currentProfile);
//Makes the GUI show the loaded data
executeGUILoadingTasks();
}
/**
* Loads all saved profiles
*
* @param currentProfile <p>The profile saved as the current profile</p>
* @throws ConfigurationException <p>If unable to load a profile</p>
*/
private void loadProfiles(String currentProfile) throws ConfigurationException {
try (Scanner in = new Scanner(new File(profilesFile))) {
while (in.hasNextLine()) {
String profileData = in.nextLine();
Profile loadedProfile = Profile.fromString(profileData);
profileList.add(loadedProfile);
}
} catch (FileNotFoundException e) {
addProfile("Default");
}
this.currentProfile = getProfileByName(currentProfile);
if (this.currentProfile == null) {
this.currentProfile = this.profileList.get(0);
}
}
/**
* Updates the GUI as necessary to display loaded data
*/
private void executeGUILoadingTasks() {
this.serverLauncherGUI.updateProfiles();
this.serverLauncherGUI.updateWithSavedProfileData();
this.currentProfile.updateConsoles();
if (this.downloadAllJars) {
Executors.newSingleThreadExecutor().execute(() -> {
JarDownloader downloader = new JarDownloader(serverLauncherGUI, jarDirectory);
try {
downloader.downloadJars();
} catch (IOException e) {
e.printStackTrace();
}
});
}
if (this.currentProfile.getRunInBackground()) {
Executors.newSingleThreadExecutor().execute(Server::startServers);
this.serverLauncherGUI.hideToTray();
}
}
/**
* Sets the download all jars option
*
* @param downloadAllJars <p>Whether to download all jars</p>
*/
public void setDownloadAllJars(boolean downloadAllJars) {
this.downloadAllJars = downloadAllJars;
}
/**
* Sets the currently selected profile
*
* @param profileName <p>The name of the currently selected profile</p>
*/
public void setCurrentProfile(String profileName) {
this.currentProfile = getProfileByName(profileName);
}
/**
* Saves the state of the entire application to disk
*/
public void saveState() {
saveGUIStateToServer();
try {
CommonFunctions.createAllFolders();
PrintWriter mFile = new PrintWriter(mainFile);
mFile.println(this.toString());
mFile.close();
PrintWriter pFile = new PrintWriter(profilesFile);
for (Profile profile : profileList) {
pFile.println(profile.toString());
}
pFile.close();
} catch (FileNotFoundException e) {
serverLauncherGUI.showError("Unable to save data.");
e.printStackTrace();
}
}
/**
* Updates the server object with the current state of the GUI server tab
*/
private void saveGUIStateToServer() {
for (Collection collection : this.currentProfile.getCollections()) {
Server server = collection.getServer();
ServerTab serverTab = collection.getServerTab();
server.setPath(serverTab.getPath());
server.setMaxRam(serverTab.getMaxRam());
try {
server.setType(ServerTypeHandler.getByName(serverTab.getType()));
} catch (ConfigurationException e) {
e.printStackTrace();
}
try {
server.setServerVersion(serverTab.getVersion());
} catch (IllegalArgumentException e) {
serverLauncherGUI.showError("Invalid server version for " + server.getName());
}
server.setEnabled(serverTab.isEnabled());
}
}
}

View File

@ -0,0 +1,251 @@
package net.knarcraft.minecraftserverlauncher.profile;
import net.knarcraft.minecraftserverlauncher.Main;
import net.knarcraft.minecraftserverlauncher.server.Server;
import net.knarcraft.minecraftserverlauncher.userinterface.ServerConsoles;
import net.knarcraft.minecraftserverlauncher.userinterface.ServerLauncherGUI;
import net.knarcraft.minecraftserverlauncher.utility.CommonFunctions;
import javax.naming.ConfigurationException;
import javax.swing.*;
import java.io.*;
import java.util.ArrayList;
/**
* Keeps track of a set of servers and some user settings
*
* @author Kristian Knarvik <kristian.knarvik@knett.no>
* @version 1.0.0
* @since 1.0.0
*/
public class Profile {
private static final ServerLauncherGUI serverLauncherGui = Main.getController().getGUI();
private final ArrayList<Collection> collections;
private final String name;
private boolean runInBackground;
private int delayStartup;
/**
* Instantiates a new default profile
*
* @param name <p>The name of the profile</p>
*/
public Profile(String name) {
this.collections = new ArrayList<>();
this.name = name;
this.runInBackground = false;
this.delayStartup = 0;
}
/**
* Instantiates a new profile
*
* @param name <p>The name of the profile</p>
* @param runInBackground <p>Whether to run the software in the background the next time it starts</p>
* @param delayStartup <p>Whether to delay the startup of servers</p>
*/
private Profile(String name, boolean runInBackground, int delayStartup) {
this.collections = new ArrayList<>();
this.name = name;
this.runInBackground = runInBackground;
this.delayStartup = delayStartup;
}
/**
* Parses a profile, and creates a profile with the data.
*
* @param profileData <p>The data of the new profile</p>
* @return <p>The new profile</p>
*/
private static Profile parseProfile(String[] profileData) {
return new Profile(profileData[0], Boolean.parseBoolean(profileData[1]), Integer.parseInt(profileData[2]));
}
/**
* Gets whether the software should keep running in the background
*
* @return <p>Whether the software should keep running in the background</p>
*/
public boolean getRunInBackground() {
return this.runInBackground;
}
/**
* Sets whether the software should keep running in the background
*
* @param value <p>Whether the software should keep running in the background</p>
*/
public void setRunInBackground(boolean value) {
this.runInBackground = value;
}
/**
* Gets the number of seconds to delay startup
*
* @return <p>The number of seconds to delay startup</p>
*/
public int getDelayStartup() {
return this.delayStartup;
}
/**
* Sets the amount of time to delay startup
*
* @param value <p>The number of seconds to delay startup</p>
*/
public void setDelayStartup(int value) {
if (value >= 0) {
this.delayStartup = value;
}
}
/**
* Gets all collections stored as part of this profile
*
* @return <p>All collections stored by this profile</p>
*/
public ArrayList<Collection> getCollections() {
return this.collections;
}
/**
* Gets the name of this profile
*
* @return <p>The name of this profile</p>
*/
public String getName() {
return this.name;
}
/**
* Gets a collection given its name
*
* @param name <p>The name of the collection to get</p>
* @return <p>A collection or null if no collection exists with the given name</p>
*/
public Collection getCollection(String name) {
for (Collection collection : this.collections) {
if (collection.getName().equals(name)) {
return collection;
}
}
return null;
}
/**
* Adds a collection to the profile if the name is valid
*
* @param name <p>The name of the collection and its elements</p>
*/
public void addCollection(String name) throws ConfigurationException {
if (name == null) { //If a user cancels or crosses out window
return;
}
if (getCollection(name) == null && !name.equals("All") && CommonFunctions.nameIsValid(name)) {
collections.add(new Collection(name));
} else {
serverLauncherGui.showError("A server name must my unique and not empty or \"All\"." +
"It can't contain any of the characters \"!\", \"?\" or \";\".");
}
}
/**
* Removes the collection with the given name
*
* @param name <p>The name of the collection to remove</p>
*/
public void removeCollection(String name) {
for (int i = 0; i < collections.size(); i++) {
if (collections.get(i).getName().equals(name)) {
this.collections.remove(i);
serverLauncherGui.updateWithSavedProfileData();
break;
}
}
}
/**
* Updates console tabs with the current servers
*/
public void updateConsoles() {
JTabbedPane consolesTab = ServerConsoles.getTabbedPane();
consolesTab.removeAll();
for (Collection collection : collections) {
consolesTab.add(collection.getName(), collection.getServerConsole().getPanel());
}
}
/**
* Sends a command to a server, or all servers
*
* @param serverName <p>The target server</p>
* @param command <p>The command to send</p>
*/
public void sendCommand(String serverName, String command) {
if (serverName.equals("All")) {
for (Collection collection : this.collections) {
try {
collection.getServer().sendCommand(command);
} catch (IOException e) {
serverLauncherGui.showError("Server " + collection.getName() + " caused an exception.");
}
}
} else {
Collection collection = getCollection(serverName);
if (collection != null) {
Server target = collection.getServer();
try {
target.sendCommand(command);
} catch (IOException e) {
serverLauncherGui.showError("Server " + target.getName() + " caused an exception.");
}
} else {
serverLauncherGui.showError("Server " + serverName + " is invalid.");
}
}
}
@Override
public String toString() {
StringBuilder saveString = new StringBuilder(String.format(
"%s;%b;%d?",
this.name,
this.runInBackground,
this.delayStartup)
);
for (Collection collection : this.collections) {
saveString.append(collection.getServer().toString());
}
saveString = new StringBuilder(saveString.substring(0, saveString.length() - 1));
return saveString.toString();
}
/**
* Gets a profile given a saved string
*
* @param profileString <p>The string containing all profile data</p>
* @return <p>A profile with the given data</p>
* @throws ConfigurationException <p>If unable to load one of the profile's servers</p>
*/
public static Profile fromString(String profileString) throws ConfigurationException {
Profile profile;
if (profileString.contains("?")) {
String[] data = profileString.split("\\?");
String[] profileData = data[0].split(";", -1);
profile = parseProfile(profileData);
if (data[1].contains("!")) {
String[] servers = data[1].split("!", -1);
for (String server : servers) {
profile.collections.add(new Collection(Server.fromString(server)));
}
} else {
profile.collections.add(new Collection(Server.fromString(data[1])));
}
} else {
profile = parseProfile(profileString.split(";", -1));
}
return profile;
}
}

View File

@ -0,0 +1,483 @@
package net.knarcraft.minecraftserverlauncher.server;
import net.knarcraft.minecraftserverlauncher.Main;
import net.knarcraft.minecraftserverlauncher.profile.Collection;
import net.knarcraft.minecraftserverlauncher.profile.Controller;
import net.knarcraft.minecraftserverlauncher.server.servertypes.ServerType;
import javax.naming.ConfigurationException;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* Contains all necessary information to create, runServer and manage a Minecraft server.
*
* @author Kristian Knarvik <kristian.knarvik@knett.no>
* @version 1.0.0
* @since 1.0.0
*/
public class Server {
/**
* Available ram sizes. For ServerLauncherGUI combo
*/
private static final String[] ramList = {"512M", "1G", "2G", "3G", "4G", "5G", "6G", "7G", "8G", "9G", "10G",
"11G", "12G", "13G", "14G", "15G", "16G"};
private static final String jarDirectory = Main.getApplicationWorkDirectory() + File.separator + "files" +
File.separator + "Jars" + File.separator;
private final String name;
private final ArrayList<String> playerList;
private String path;
private boolean enabled;
private ServerType type;
private String serverVersion;
private String maxRam;
private Process process;
private BufferedWriter writer;
private BufferedReader reader;
private boolean started;
/**
* Initializes a new server with default values
*
* @param name <p>The name of the server</p>
*/
public Server(String name) {
this.name = name;
this.path = "";
this.enabled = false;
this.playerList = new ArrayList<>();
this.type = null;
this.serverVersion = "";
this.maxRam = ramList[0];
this.process = null;
this.writer = null;
this.reader = null;
}
/**
* Initializes a server with the given values
*
* @param name <p>The name of the server</p>
* @param path <p>The file path of the folder containing the server files</p>
* @param enabled <p>Whether the server is enabled to start the next time servers are started</p>
* @param typeName <p>The name of the server type currently in use on the server</p>
* @param serverVersion <p>The currently selected server version for the given server type</p>
* @param maxRam <p>The maximum amount of ram the server is allowed to use</p>
*/
public Server(String name, String path, boolean enabled, String typeName, String serverVersion, String maxRam) throws ConfigurationException {
this.name = name;
this.path = path;
this.enabled = enabled;
this.type = ServerTypeHandler.getByName(typeName);
this.serverVersion = serverVersion;
this.maxRam = maxRam;
this.playerList = new ArrayList<>();
}
/**
* Gets the list of available RAM choices allowed
*
* @return <p>All available RAM choices</p>
*/
public static String[] getRamList() {
return ramList;
}
/**
* Gets the buffered reader used to read from this server
*
* @return <p>The buffered reader used to read from this server</p>
*/
public BufferedReader getReader() {
return this.reader;
}
/**
* Tries to stop all enabled servers
*
* @throws IOException <p>If a writer's process is already closed but not null</p>
*/
public static void stop() throws IOException {
for (Collection collection : Main.getController().getCurrentProfile().getCollections()) {
Server server = collection.getServer();
if (server.writer != null) {
if (server.type.isProxy()) {
server.writer.write("end\n");
} else {
server.writer.write("stop\n");
}
server.writer.flush();
server.writer = null;
server.started = false;
}
}
}
/**
* Runs all enabled servers with their settings
*/
public static void startServers() {
Controller controller = Main.getController();
controller.getGUI().setStatus("Starting servers");
for (Collection collection : controller.getCurrentProfile().getCollections()) {
if (!collection.getServer().runServer()) {
controller.getGUI().setStatus("An error occurred. Start aborted");
try {
Server.stop();
} catch (IOException e) {
e.printStackTrace();
}
controller.getGUI().updateGUIElementsWhenServersStartOrStop(false);
return;
}
}
}
/**
* Gets a server object from a server save string
*
* @param saveString <p>The string containing necessary data regarding the server</p>
* @return <p>A server in the same state it was saved in</p>
*/
public static Server fromString(String saveString) throws ConfigurationException {
String[] data = saveString.split(";");
return new Server(data[0], data[1], Boolean.parseBoolean(data[2]), data[3], data[4], data[5]);
}
/**
* Gets the name of the server
*
* @return <p>The name of the server</p>
*/
public String getName() {
return this.name;
}
/**
* Whether the server has been started
*
* @return <p>True if the server has been started. False otherwise</p>
*/
public boolean isStarted() {
return started;
}
/**
* Gets the name of the server type used by this server
*
* @return <p>The name of the server type used by this server</p>
*/
public String getTypeName() {
return this.type.getName();
}
/**
* Gets the server type used by this server
*
* @return <p>The server type used by this server</p>
*/
public ServerType getType() {
return this.type;
}
/**
* Gets the version used given server type used
*
* @return <p>The server version given server type</p>
*/
public String getServerVersion() {
return this.serverVersion;
}
/**
* Sets the server's server version to a valid version, or ignores the request
*
* @param serverVersion <p>The new server version</p>
*/
public void setServerVersion(String serverVersion) throws IllegalArgumentException {
if (this.type.getName().equals("Custom")) {
this.serverVersion = serverVersion;
} else {
String[] versions = this.type.getVersions();
for (String version : versions) {
if (version.equals(serverVersion)) {
this.serverVersion = serverVersion;
return;
}
}
throw new IllegalArgumentException("Invalid server version.");
}
}
/**
* Gets the path for this server's files
*
* @return <p>The path of this server's files</p>
*/
public String getPath() {
return this.path;
}
/**
* Sets the path of this server's files
*
* @param path <p>The new path of the server's files</p>
*/
public void setPath(String path) {
this.path = path;
}
/**
* Gets the process of the server
*
* @return <p>The server process</p>
*/
public Process getProcess() {
return this.process;
}
/**
* Gets the maximum amount of ram usable by this server
*
* @return <p>The maximum amount of ram this server can use</p>
*/
public String getMaxRam() {
return this.maxRam;
}
/**
* Sets the max ram to be used by the server
*
* @param ram <p>The new maximum ram amount</p>
*/
public void setMaxRam(String ram) {
this.maxRam = ram;
}
/**
* Gets a list of all players connected to this server
*
* @return <p>A list of all players connected to the server</p>
*/
public List<String> getPlayers() {
return this.playerList;
}
/**
* Checks whether this server is fully stopped
*/
public void stopped() {
process = null;
writer = null;
reader = null;
started = false;
}
/**
* Checks whether this server is currently enabled
*
* @return <p>True if the server is currently enabled</p>
*/
public boolean isEnabled() {
return this.enabled;
}
/**
* Sets whether this server is currently enabled
*
* @param value <p>Whether the server is currently enabled</p>
*/
public void setEnabled(boolean value) {
this.enabled = value;
}
/**
* Checks whether this server has a given player
*
* @param name <p>The name of the player to check</p>
* @return <p>True if the player is connected</p>
*/
public boolean hasPlayer(String name) {
for (String player : this.playerList) {
if (player.equals(name)) {
return true;
}
}
return false;
}
/**
* Adds a player to the GUI and this server's player list
*
* @param name <p>The name of the player to add</p>
*/
public void addPlayer(String name) {
this.playerList.add(name);
Main.getController().getGUI().getServerControlTab().addPlayer(name);
}
/**
* Removes a player with the selected name from the player list
*
* @param name <p>The name of the player to remove</p>
*/
public void removePlayer(String name) {
playerList.removeIf(player -> player.equals(name));
Main.getController().getGUI().getServerControlTab().removePlayer(name);
}
/**
* Sets the server type to be used by the server
*
* @param type <p>The new server type to be used by the server</p>
*/
public void setType(ServerType type) {
this.type = type;
}
/**
* Runs a Minecraft server
*
* @return <p>True if nothing went wrong</p>
*/
private boolean runServer() {
//Ignore a disabled server
if (!this.enabled) {
this.started = false;
return true;
}
//Tries to do necessary pre-start work
if (!initializeJarDownload() || !delayStartup()) {
this.started = false;
return false;
}
//Starts the server if possible
try {
startServerProcess();
Main.getController().getGUI().setStatus("Servers are running");
this.started = true;
return true;
} catch (IOException e) {
Main.getController().getGUI().setStatus("Could not start server");
this.started = false;
return false;
}
}
/**
* Starts the process running this server
*
* @throws IOException <p>If the process cannot be started</p>
*/
private void startServerProcess() throws IOException {
ProcessBuilder builder;
String serverPath;
//Decide the path of the .jar file to be executed
if (type.getName().equals("Custom")) {
serverPath = this.path + File.separator + serverVersion;
} else {
serverPath = jarDirectory + this.type.getName() + serverVersion + ".jar";
}
builder = new ProcessBuilder("java", "-Xmx" + this.maxRam, "-Xms512M",
"-Djline.terminal=jline.UnsupportedTerminal", "-Dcom.mojang.eula.agree=true", "-jar", serverPath,
"nogui");
builder.directory(new File(this.path));
builder.redirectErrorStream(true);
this.process = builder.start();
this.writer = new BufferedWriter(new OutputStreamWriter(this.process.getOutputStream()));
this.reader = new BufferedReader(new InputStreamReader(this.process.getInputStream()));
}
/**
* Delays the server's startup for the given amount of time
*
* @return <p>True if the delay was successful</p>
*/
private boolean delayStartup() {
try {
Main.getController().getGUI().setStatus("Delaying startup");
TimeUnit.SECONDS.sleep(Main.getController().getCurrentProfile().getDelayStartup());
return true;
} catch (InterruptedException e) {
e.printStackTrace();
this.started = false;
return false;
}
}
/**
* Starts downloading the necessary .jar file
*
* @return <p>True if nothing went wrong</p>
*/
private boolean initializeJarDownload() {
Controller controller = Main.getController();
if (!controller.getDownloadAllJars()) {
try {
controller.getGUI().setStatus("Downloading jar...");
this.downloadJar();
controller.getGUI().setStatus("File downloaded");
} catch (IOException e) {
controller.getGUI().setStatus("Error: Jar file not found");
e.printStackTrace();
this.started = false;
return false;
}
}
return true;
}
/**
* Downloads necessary .jar file for the server
*
* @throws FileNotFoundException <p>If the file was not found and could not be acquired</p>
*/
private void downloadJar() throws IOException {
String path;
if (this.type.getName().equals("Custom")) {
path = this.path + File.separator + this.serverVersion;
} else {
path = jarDirectory;
}
File file = new File(path);
if (!(file.isFile() || type.downloadJar(path, this.serverVersion))) {
throw new FileNotFoundException("Jar file could not be downloaded.");
}
}
/**
* Sends a command to this server through its writer
*
* @param command <p>Command to send to the server</p>
* @throws IOException <p>If write fails</p>
*/
public void sendCommand(String command) throws IOException {
if (this.process != null && this.writer != null) {
this.writer.write(command + "\n");
this.writer.flush();
}
}
@Override
public String toString() {
return String.format(
"%s;%s;%b;%s;%s;%s;!",
this.getName(),
this.getPath(),
this.isEnabled(),
this.getTypeName(),
this.getServerVersion(),
this.getMaxRam()
);
}
}

View File

@ -0,0 +1,151 @@
package net.knarcraft.minecraftserverlauncher.server;
import net.knarcraft.minecraftserverlauncher.server.servertypes.*;
import javax.naming.ConfigurationException;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Scanner;
import static net.knarcraft.minecraftserverlauncher.utility.CommonFunctions.getResourceAsScanner;
/**
* A class which keeps track of available server types
*/
public class ServerTypeHandler {
private static final ArrayList<ServerType> serverTypes = new ArrayList<>();
/**
* Gets a list of all server types' names
*
* @return <p>A list containing the names of all server types</p>
*/
public static String[] getTypeNames() throws ConfigurationException {
if (serverTypes.isEmpty()) {
loadServerTypes();
}
ArrayList<ServerType> types = getServerTypes();
String[] serverTypeNames = new String[types.size()];
for (int i = 0; i < types.size(); i++) {
serverTypeNames[i] = types.get(i).getName();
}
return serverTypeNames;
}
/**
* Gets all instantiated server types
*
* @return <p>A list of server types</p>
*/
public static ArrayList<ServerType> getServerTypes() throws ConfigurationException {
if (serverTypes.isEmpty()) {
loadServerTypes();
}
return serverTypes;
}
/**
* Gets a server type by the given name
*
* @param name <p>Then name of the server type</p>
* @return <p>A AbstractServerType</p>
*/
public static ServerType getByName(String name) throws ConfigurationException {
if (serverTypes.isEmpty()) {
loadServerTypes();
}
for (ServerType serverType : serverTypes) {
if (serverType.getName().equals(name)) {
return serverType;
}
}
return null;
}
/**
* Reads valid server types and version from a file, and creates their objects
*
* @throws ConfigurationException <p>If anything goes wrong</p>
*/
private static void loadServerTypes() throws ConfigurationException {
Scanner file;
try {
file = getResourceAsScanner("servertypes.csv");
} catch (FileNotFoundException e) {
throw new ConfigurationException("Server type configuration file is missing.");
}
while (file.hasNextLine()) {
//Splits the next file line into arguments
String[] serverTypeInfo = file.nextLine().split(";", -1);
//Gets list of server versions from file line
String[] serverVersions;
if (serverTypeInfo[1].contains(",")) {
serverVersions = serverTypeInfo[1].split(",", -1);
} else {
serverVersions = new String[]{serverTypeInfo[1]};
}
addServerType(serverTypeInfo, serverVersions);
}
}
/**
* Adds a new server type
*
* @param serverTypeInfo <p>A list containing necessary information for initializing the server type</p>
* @param serverVersions <p>A list of all server versions for the server type</p>
*/
private static void addServerType(String[] serverTypeInfo, String[] serverVersions) {
ServerType newType;
switch (serverTypeInfo[0]) {
case "Craftbukkit":
newType = new CraftBukkit("Bukkit", false, serverVersions, serverTypeInfo[2],
serverTypeInfo[3]);
break;
case "Spigot":
newType = new Spigot("Spigot", false, serverVersions, serverTypeInfo[2],
serverTypeInfo[3]);
break;
case "Vanilla":
newType = new Vanilla("Vanilla", false, serverVersions, serverTypeInfo[2],
serverTypeInfo[3]);
break;
case "MCPCplus":
newType = new MCPCPlus("MCPCplus", false, serverVersions, serverTypeInfo[2],
serverTypeInfo[3]);
break;
case "Paper":
newType = new Paper("Paper", false, serverVersions, serverTypeInfo[2],
serverTypeInfo[3]);
break;
case "Bungee":
newType = new BungeeCord("Bungee", true, serverVersions, serverTypeInfo[2],
serverTypeInfo[3], serverTypeInfo[4], serverTypeInfo[5], serverTypeInfo[6], serverTypeInfo[7]);
break;
case "Travertine":
newType = new Travertine("Travertine", true, serverVersions, serverTypeInfo[2],
serverTypeInfo[3], serverTypeInfo[4], serverTypeInfo[5]);
break;
case "Waterfall":
newType = new Waterfall("Waterfall", true, serverVersions, serverTypeInfo[2],
serverTypeInfo[3], serverTypeInfo[4], serverTypeInfo[5]);
break;
case "SpongeVanilla":
newType = new SpongeVanilla("SpongeVanilla", false, serverVersions,
serverTypeInfo[2], serverTypeInfo[3], serverTypeInfo[4], serverTypeInfo[5], serverTypeInfo[6]);
break;
case "SpongeForge":
newType = new SpongeForge("SpongeForge", false, serverVersions,
serverTypeInfo[2], serverTypeInfo[3], serverTypeInfo[4], serverTypeInfo[5], serverTypeInfo[6]);
break;
case "Custom":
newType = new Custom("Custom");
break;
default:
throw new IllegalArgumentException("Unknown server type defined in config file.");
}
serverTypes.add(newType);
}
}

View File

@ -0,0 +1,327 @@
package net.knarcraft.minecraftserverlauncher.server;
import net.knarcraft.minecraftserverlauncher.Main;
import net.knarcraft.minecraftserverlauncher.utility.CommonFunctions;
import java.io.*;
import java.util.HashMap;
import java.util.Map;
/**
* This class acts as a container for all "latest" server versions
*/
public class ServerVersionContainer {
private static ServerVersionContainer serverVersionContainer;
private final String versionFile = Main.getApplicationWorkDirectory() + File.separator + "files" + File.separator + "versions.csv";
private String vanillaVersion;
private String snapshotVersion;
private String bungeeVersion;
private Map<String, String> waterfallVersions;
private Map<String, String> travertineVersions;
private Map<String, String> spongeVanillaVersions;
private Map<String, String> spongeForgeVersions;
/**
* Initializes a new server version container
*/
private ServerVersionContainer() {
this.waterfallVersions = new HashMap<>();
this.travertineVersions = new HashMap<>();
this.spongeVanillaVersions = new HashMap<>();
this.spongeForgeVersions = new HashMap<>();
loadState();
}
/**
* Gives a server version container instance
*
* @return <p>A server version container instance</p>
*/
public static ServerVersionContainer getInstance() {
if (serverVersionContainer == null) {
serverVersionContainer = new ServerVersionContainer();
}
return serverVersionContainer;
}
/**
* Resets the state of the server version container
*/
void reset() {
this.vanillaVersion = null;
this.snapshotVersion = null;
this.bungeeVersion = null;
this.waterfallVersions = new HashMap<>();
this.travertineVersions = new HashMap<>();
this.spongeVanillaVersions = new HashMap<>();
this.spongeForgeVersions = new HashMap<>();
}
@Override
public String toString() {
return "vanillaVersion;" + vanillaVersion + "\n" +
"snapshotVersion;" + snapshotVersion + "\n" +
"bungeeVersion;" + bungeeVersion + "\n" +
"waterfallVersions;" + mapToString(waterfallVersions) + "\n" +
"travertineVersions;" + mapToString(travertineVersions) + "\n" +
"spongeVanillaVersions;" + mapToString(spongeVanillaVersions) + "\n" +
"spongeForgeVersions;" + mapToString(spongeForgeVersions);
}
/**
* Formats a map to a string given a map
*
* @param targetMap <p>The map to format</p>
* @return <p>A string representing the map</p>
*/
private String mapToString(Map<?, ?> targetMap) {
StringBuilder stringBuilder = new StringBuilder();
int mapSize = targetMap.keySet().size();
int mapIndex = 0;
for (Object key : targetMap.keySet()) {
stringBuilder.append(key).append("!").append(targetMap.get(key));
if (mapIndex < mapSize) {
stringBuilder.append(",");
}
mapIndex++;
}
return stringBuilder.toString();
}
/**
* Tries to save the state of this server version container
*/
void saveState() {
File saveFile = new File(versionFile);
PrintWriter file;
try {
if (!saveFile.exists()) {
if (!saveFile.getParentFile().exists() && !saveFile.getParentFile().mkdirs()) {
throw new FileNotFoundException("Unable to create folder for version file");
}
if (!saveFile.createNewFile()) {
throw new FileNotFoundException("Unable to create version file");
}
}
file = new PrintWriter(versionFile);
file.println(this.toString());
file.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* Loads the object state from the save file
*/
private void loadState() {
if (!new File(versionFile).exists()) {
return;
}
try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(versionFile)))) {
String currentData = CommonFunctions.readBufferedReader(reader);
for (String line : currentData.split("\n")) {
parseSaveLine(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* Parses one line in the version save file and updates the appropriate variable
*
* @param line <p>The line from the save file to parse</p>
*/
private void parseSaveLine(String line) {
String[] lineData = line.split(";");
if (lineData.length != 2) {
return;
}
String variableKey = lineData[0];
String variableValue = lineData[1];
switch (variableKey) {
case "vanillaVersion":
vanillaVersion = variableValue;
break;
case "snapshotVersion":
snapshotVersion = variableValue;
break;
case "bungeeVersion":
bungeeVersion = variableValue;
break;
case "waterfallVersions":
parseVersionsToMap(waterfallVersions, variableValue);
break;
case "travertineVersions":
parseVersionsToMap(travertineVersions, variableValue);
break;
case "spongeVanillaVersions":
parseVersionsToMap(spongeVanillaVersions, variableValue);
break;
case "spongeForgeVersions":
parseVersionsToMap(spongeForgeVersions, variableValue);
break;
default:
throw new IllegalArgumentException("Invalid key encountered in the server version file.");
}
}
/**
* Reads versions from a text string and updates the version map
*
* @param targetMap <p>The map to update</p>
* @param data <p>The data string to parse</p>
*/
private void parseVersionsToMap(Map<String,String> targetMap, String data) {
String[] versions = data.split(",");
for (String version : versions) {
String[] versionData = version.split("!");
targetMap.put(versionData[0], versionData[1]);
}
}
/**
* Gets the current vanilla version
*
* @return <p>The current vanilla version</p>
*/
public String getVanillaVersion() {
return this.vanillaVersion;
}
/**
* Sets the vanilla server version
*
* @param newVersion <p>The new vanilla server version</p>
*/
public void setVanillaVersion(String newVersion) {
this.vanillaVersion = newVersion;
saveState();
}
/**
* Gets the current snapshot version
*
* @return <p>The current snapshot version</p>
*/
public String getSnapshotVersion() {
return this.snapshotVersion;
}
/**
* Sets the snapshot server version
*
* @param newVersion <p>The new snapshot server version</p>
*/
public void setSnapshotVersion(String newVersion) {
this.snapshotVersion = newVersion;
saveState();
}
/**
* Gets the current bungee version
*
* @return <p>The current bungee version</p>
*/
public String getBungeeVersion() {
return this.bungeeVersion;
}
/**
* Sets the bungee server version
*
* @param newVersion <p>The new bungee server version</p>
*/
public void setBungeeVersion(String newVersion) {
this.bungeeVersion = newVersion;
saveState();
}
/**
* Gets a specific waterfall version
*
* @param versionKey <p>The version to check current version of</p>
* @return <p>The current waterfall version</p>
*/
public String getWaterfallVersion(String versionKey) {
return this.waterfallVersions.get(versionKey);
}
/**
* Sets the current version for a given waterfall version
*
* @param mapKey <p>The version key to set version for</p>
* @param newValue <p>The new current version</p>
*/
public void setWaterfallVersion(String mapKey, String newValue) {
this.waterfallVersions.put(mapKey, newValue);
saveState();
}
/**
* Gets a specific travertine version
*
* @param versionKey <p>The version to check current version of</p>
* @return <p>The current travertine version</p>
*/
public String getTravertineVersion(String versionKey) {
return this.travertineVersions.get(versionKey);
}
/**
* Sets the current version for a given travertine version
*
* @param mapKey <p>The version key to set version for</p>
* @param newValue <p>The new current version</p>
*/
public void setTravertineVersion(String mapKey, String newValue) {
this.travertineVersions.put(mapKey, newValue);
saveState();
}
/**
* Gets a specific sponge vanilla version
*
* @param versionKey <p>The version to check current version of</p>
* @return <p>The current sponge vanilla version</p>
*/
public String getSpongeVanillaVersion(String versionKey) {
return spongeVanillaVersions.get(versionKey);
}
/**
* Sets the current version for a given sponge vanilla version
*
* @param mapKey <p>The version key to set version for</p>
* @param newValue <p>The new current version</p>
*/
public void setSpongeVanillaVersion(String mapKey, String newValue) {
spongeVanillaVersions.put(mapKey, newValue);
saveState();
}
/**
* Gets a specific sponge forge version
*
* @param versionKey <p>The version to check current version of</p>
* @return <p>The current sponge forge version</p>
*/
public String getSpongeForgeVersion(String versionKey) {
return spongeForgeVersions.get(versionKey);
}
/**
* Sets the current version for a given sponge forge version
*
* @param mapKey <p>The version key to set version for</p>
* @param newValue <p>The new current version</p>
*/
public void setSpongeForgeVersion(String mapKey, String newValue) {
spongeForgeVersions.put(mapKey, newValue);
saveState();
}
}

View File

@ -0,0 +1,46 @@
package net.knarcraft.minecraftserverlauncher.server.servertypes;
/**
* Contains the bare minimum to be a functional server type.
*
* @author Kristian Knarvik <kristian.knarvik@knett.no>
* @version 1.0.0
* @since 1.0.0
*/
public abstract class AbstractServerType implements ServerType {
final String downloadURL;
private final String typeName;
private final boolean isProxy;
private final String[] versions;
/**
* Instantiates a new server type
*
* @param typeName <p>The typeName of the server type</p>
* @param isProxy <p>Whether this server type is a proxy server</p>
* @param versions <p>A list of one or more server versions for the type</p>
* @param downloadURL <p>The URL used for downloading .jar files</p>
*/
AbstractServerType(String typeName, boolean isProxy, String[] versions, String downloadURL) {
this.typeName = typeName;
this.isProxy = isProxy;
this.versions = versions;
this.downloadURL = downloadURL;
}
@Override
public String getName() {
return this.typeName;
}
@Override
public boolean isProxy() {
return this.isProxy;
}
@Override
public String[] getVersions() {
return this.versions;
}
}

View File

@ -0,0 +1,82 @@
package net.knarcraft.minecraftserverlauncher.server.servertypes;
import net.knarcraft.minecraftserverlauncher.server.ServerVersionContainer;
import java.io.File;
import java.io.IOException;
import java.nio.file.Paths;
import static net.knarcraft.minecraftserverlauncher.utility.CommonFunctions.downloadFile;
import static net.knarcraft.minecraftserverlauncher.utility.CommonFunctions.readFile;
import static net.knarcraft.minecraftserverlauncher.utility.CommonFunctions.stringBetween;
/**
* This class represents the BungeeCord proxy server type
*/
public class BungeeCord extends AbstractServerType {
private final String versionURL;
private final String srcStart;
private final String srcEnd;
private final String oldVersionsURL;
private final String oldVersionsPrefix;
/**
* Instantiates a new BungeeCord server type
*
* @param typeName <p>The name of the server type</p>
* @param isProxy <p>Whether this server type is a proxy server</p>
* @param versions <p>The available versions for the server type</p>
* @param versionURL <p>The URL used to finding the newest version</p>
* @param srcStart <p>The string after which the version id starts</p>
* @param srcEnd <p>The string marking the end of the version id</p>
* @param downloadURL <p>The URL used for downloading the latest version</p>
*/
public BungeeCord(String typeName, boolean isProxy, String[] versions, String versionURL, String srcStart,
String srcEnd, String downloadURL, String oldVersionsURL, String oldVersionsPrefix) {
super(typeName, isProxy, versions, downloadURL);
this.versionURL = versionURL;
this.srcStart = srcStart;
this.srcEnd = srcEnd;
this.oldVersionsURL = oldVersionsURL;
this.oldVersionsPrefix = oldVersionsPrefix;
}
@Override
public boolean downloadJar(String folder, String version) throws IOException {
String targetFile = this.getName() + version + ".jar";
File filePath = new File(folder + targetFile);
if (version.equals("Latest")) {
return downloadLatestJar(filePath);
} else {
String file = oldVersionsPrefix + version + ".jar";
return filePath.isFile() || downloadFile(oldVersionsURL + file, Paths.get(filePath.toURI()));
}
}
/**
* Downloads the latest BungeeCord .jar file
*
* @param filePath <p>The file path to download the new jar to</p>
* @return <p>True if the jar exists or was successfully downloaded</p>
* @throws IOException <p>If unable to download the new jar</p>
*/
private boolean downloadLatestJar(File filePath) throws IOException {
ServerVersionContainer versionContainer = ServerVersionContainer.getInstance();
String newestVersion = stringBetween(readFile(versionURL), srcStart, srcEnd);
String oldVersion = versionContainer.getBungeeVersion();
//The file is already the newest version
if (filePath.isFile() && newestVersion.equals(oldVersion)) {
return true;
}
//The new jar file could not be downloaded
if (!downloadFile(downloadURL, Paths.get(filePath.toURI()))) {
return false;
}
versionContainer.setBungeeVersion(newestVersion);
return true;
}
}

View File

@ -0,0 +1,37 @@
package net.knarcraft.minecraftserverlauncher.server.servertypes;
import java.io.File;
import java.nio.file.Paths;
import static net.knarcraft.minecraftserverlauncher.utility.CommonFunctions.downloadFile;
/**
* This class represents the CraftBukkit Minecraft server type
*/
public class CraftBukkit extends AbstractServerType {
private final String downloadURLPart;
/**
* Instantiates a new CraftBukkit server type
*
* @param typeName <p>The name of the server type</p>
* @param isProxy <p>Whether this server type is a proxy server</p>
* @param versions <p>A list of one or more server versions for the type</p>
* @param downloadURL <p>The URL used for downloading .jar files</p>
* @param downloadURLPart <p>A string used after the download url as an additional part of the URL</p>
*/
public CraftBukkit(String typeName, boolean isProxy, String[] versions, String downloadURL, String downloadURLPart) {
super(typeName, isProxy, versions, downloadURL);
this.downloadURLPart = downloadURLPart;
}
@Override
public boolean downloadJar(String folder, String version) {
String targetFile = this.getName() + version + ".jar";
String file = downloadURLPart + version + ".jar";
File filePath = new File(folder + targetFile);
return filePath.isFile() || downloadFile(downloadURL + file, Paths.get(filePath.toURI()));
}
}

View File

@ -0,0 +1,20 @@
package net.knarcraft.minecraftserverlauncher.server.servertypes;
import java.io.File;
public class Custom extends AbstractServerType {
/**
* Instantiates a new custom server type
*
* @param typeName <p>The name of the server type</p>
*/
public Custom(String typeName) {
super(typeName, false, new String[]{}, "");
}
@Override
public boolean downloadJar(String folder, String version) {
File filePath = new File(folder + version);
return filePath.isFile();
}
}

View File

@ -0,0 +1,21 @@
package net.knarcraft.minecraftserverlauncher.server.servertypes;
/**
* This class represents the MCPC+ Minecraft server type
*/
public class MCPCPlus extends CraftBukkit {
/**
* Instantiates a new MCPCplus server type
*
* @param typeName <p>The name of the server type</p>
* @param isProxy <p>Whether this server type is a proxy server</p>
* @param versions <p>A list of one or more server versions for the type</p>
* @param downloadURL <p>The URL used for downloading .jar files</p>
* @param downloadURLPart <p>A string used after the download url as an additional part of the URL</p>
*/
public MCPCPlus(String typeName, boolean isProxy, String[] versions, String downloadURL, String downloadURLPart) {
super(typeName, isProxy, versions, downloadURL, downloadURLPart);
}
}

View File

@ -0,0 +1,21 @@
package net.knarcraft.minecraftserverlauncher.server.servertypes;
/**
* This class represents the Paper Minecraft server type
*/
public class Paper extends Spigot {
/**
* Instantiates a new Paper server type
*
* @param typeName <p>The name of the server type</p>
* @param isProxy <p>Whether this server type is a proxy server</p>
* @param versions <p>A list of one or more server versions for the type</p>
* @param downloadURL <p>The URL used for downloading .jar files</p>
* @param downloadURLPart <p>A string used after the download url as an additional part of the URL</p>
*/
public Paper(String typeName, boolean isProxy, String[] versions, String downloadURL, String downloadURLPart) {
super(typeName, isProxy, versions, downloadURL, downloadURLPart);
}
}

View File

@ -0,0 +1,39 @@
package net.knarcraft.minecraftserverlauncher.server.servertypes;
import java.io.IOException;
/**
* Describes a server type
*/
public interface ServerType {
/**
* Gets whether this server type is a proxy server
*
* @return <p>True if this server type is a proxy server</p>
*/
boolean isProxy();
/**
* Gets the name of the server type
*
* @return <p>Server type name</p>
*/
String getName();
/**
* Gets a list of versions available for the server type
*
* @return <p>A list of server versions</p>
*/
String[] getVersions();
/**
* Downloads a .jar file for this server type
*
* @param folder <p>The folder to save the downloaded file to</p>
* @param version <p>The server type version to use</p>
* @return <p>True if the file exists or was downloaded</p>
*/
boolean downloadJar(String folder, String version) throws IOException;
}

View File

@ -0,0 +1,21 @@
package net.knarcraft.minecraftserverlauncher.server.servertypes;
/**
* This class represents the CraftBukkit Minecraft server type
*/
public class Spigot extends CraftBukkit {
/**
* Instantiates a new Spigot server type
*
* @param typeName <p>The name of the server type</p>
* @param isProxy <p>Whether this server type is a proxy server</p>
* @param versions <p>A list of one or more server versions for the type</p>
* @param downloadURL <p>The URL used for downloading .jar files</p>
* @param downloadURLPart <p>A string used after the download url as an additional part of the URL</p>
*/
public Spigot(String typeName, boolean isProxy, String[] versions, String downloadURL, String downloadURLPart) {
super(typeName, isProxy, versions, downloadURL, downloadURLPart);
}
}

View File

@ -0,0 +1,26 @@
package net.knarcraft.minecraftserverlauncher.server.servertypes;
/**
* This class represents the SpongeForge Minecraft server type
*/
public class SpongeForge extends SpongeVanilla {
/**
* Instantiates a new SpongeForge server type
*
* @param typeName <p>The name of this server type</p>
* @param isProxy <p>Whether this server type is a proxy server</p>
* @param versions <p>Available versions for this server type</p>
* @param versionURL <p>The URL used for downloading the version document</p>
* @param srcStart <p>The string to search for to determine newest version</p>
* @param srcEnd <p>The string marking the end of the newest version statement</p>
* @param downloadURL <p>The URL used for downloading the new file</p>
* @param downloadURLPart <p>A string used after the download url as an additional part of the URL</p>
*/
public SpongeForge(String typeName, boolean isProxy, String[] versions, String versionURL, String srcStart, String srcEnd, String downloadURL, String downloadURLPart) {
super(typeName, isProxy, versions, versionURL, srcStart, srcEnd, downloadURL, downloadURLPart);
this.oldVersionFunction = serverVersionContainer::getSpongeForgeVersion;
this.versionUpdateFunction = serverVersionContainer::setSpongeForgeVersion;
}
}

View File

@ -0,0 +1,73 @@
package net.knarcraft.minecraftserverlauncher.server.servertypes;
import net.knarcraft.minecraftserverlauncher.server.ServerVersionContainer;
import net.knarcraft.minecraftserverlauncher.utility.CommonFunctions;
import java.io.File;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.function.BiConsumer;
import java.util.function.Function;
import static net.knarcraft.minecraftserverlauncher.utility.CommonFunctions.downloadFile;
/**
* This class represents the SpongeVanilla Minecraft server type
*/
public class SpongeVanilla extends AbstractServerType {
private final String versionURL;
private final String srcStart;
private final String srcEnd;
private final String downloadURLPart;
Function<String,String> oldVersionFunction;
BiConsumer<String,String> versionUpdateFunction;
final ServerVersionContainer serverVersionContainer;
/**
* Instantiates a new SpongeVanilla server type
*
* @param typeName <p>The name of this server type</p>
* @param isProxy <p>Whether this server type is a proxy server</p>
* @param versions <p>Available versions for this server type</p>
* @param versionURL <p>The URL used for downloading the version document</p>
* @param srcStart <p>The string to search for to determine newest version</p>
* @param srcEnd <p>The string marking the end of the newest version statement</p>
* @param downloadURL <p>The URL used for downloading the new file</p>
* @param downloadURLPart <p>A string used after the download url as an additional part of the URL</p>
*/
public SpongeVanilla(String typeName, boolean isProxy, String[] versions, String versionURL, String srcStart, String srcEnd,
String downloadURL, String downloadURLPart) {
super(typeName, isProxy, versions, downloadURL);
this.serverVersionContainer = ServerVersionContainer.getInstance();
this.versionURL = versionURL;
this.srcStart = srcStart;
this.srcEnd = srcEnd;
this.downloadURLPart = downloadURLPart;
this.oldVersionFunction = serverVersionContainer::getSpongeVanillaVersion;
this.versionUpdateFunction = serverVersionContainer::setSpongeVanillaVersion;
}
@Override
public boolean downloadJar(String folder, String version) throws IOException {
String file = this.getName() + version + ".jar";
File filePath = new File(folder + file);
String versionText = CommonFunctions.readFile(versionURL + version);
String newestVersion = CommonFunctions.stringBetween(versionText, srcStart, srcEnd);
String jarURL = downloadURL + newestVersion + downloadURLPart + newestVersion + ".jar";
String oldVersion = this.oldVersionFunction.apply(version);
//The file is already the newest version
if (filePath.isFile() && newestVersion.equals(oldVersion)) {
return true;
}
//The new jar file could not be downloaded
if (!downloadFile(jarURL, Paths.get(filePath.toURI()))) {
return false;
}
versionUpdateFunction.accept(version, newestVersion);
return true;
}
}

View File

@ -0,0 +1,26 @@
package net.knarcraft.minecraftserverlauncher.server.servertypes;
/**
* This class represents the Travertine proxy server type
*/
public class Travertine extends Waterfall {
/**
* Instantiates a new Travertine server type
*
* @param typeName <p>The name of the server type</p>
* @param isProxy <p>Whether this server type is a proxy server</p>
* @param versions <p>The available versions for the server type</p>
* @param versionURL <p>The URL used to finding the newest version</p>
* @param srcStart <p>The string after which the version id starts</p>
* @param srcEnd <p>The string marking the end of the version id</p>
* @param downloadURL <p>The URL used for downloading the latest version</p>
*/
public Travertine(String typeName, boolean isProxy, String[] versions, String versionURL, String srcStart,
String srcEnd, String downloadURL) {
super(typeName, isProxy, versions, versionURL, srcStart, srcEnd, downloadURL);
this.oldVersionFunction = serverVersionContainer::getTravertineVersion;
this.versionUpdateFunction = serverVersionContainer::setTravertineVersion;
}
}

View File

@ -0,0 +1,135 @@
package net.knarcraft.minecraftserverlauncher.server.servertypes;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import net.knarcraft.minecraftserverlauncher.server.ServerVersionContainer;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.file.Paths;
import static net.knarcraft.minecraftserverlauncher.utility.CommonFunctions.downloadFile;
import static net.knarcraft.minecraftserverlauncher.utility.CommonFunctions.readFile;
/**
* This class represents the regular vanilla Minecraft server type
*/
public class Vanilla extends AbstractServerType {
private final String versionURL;
/**
* Instantiates a vanilla server type
*
* @param typeName <p>The name of this server type to display</p>
* @param isProxy <p>Whether this server type is a proxy server</p>
* @param versions <p>Available versions for this server type</p>
* @param versionURL <p>The URL used for downloading the version document</p>
* @param downloadURL <p>The URL used for downloading the new file</p>
*/
public Vanilla(String typeName, boolean isProxy, String[] versions, String versionURL, String downloadURL) {
super(typeName, isProxy, versions, downloadURL);
this.versionURL = versionURL;
}
@Override
public boolean downloadJar(String folder, String version) throws IOException {
ServerVersionContainer versionContainer = ServerVersionContainer.getInstance();
String file = this.getName() + version + ".jar";
File filePath = new File(folder + file);
if (version.equals("Latest") || version.equals("Snapshot")) {
String releaseType = version.equals("Latest") ? "release" : "snapshot";
String lastVersion = version.equals("Latest") ? versionContainer.getVanillaVersion() :
versionContainer.getSnapshotVersion();
return downloadLatestJar(filePath, releaseType, lastVersion, versionContainer);
} else {
String downloadURL = getVanillaDownloadURL(getServerFileVersionURL(version));
return filePath.isFile() || downloadFile(downloadURL, Paths.get(filePath.toURI()));
}
}
/**
* Downloads the latest .jar file found if necessary
*
* @param filePath <p>The path of the jar file to download</p>
* @param releaseType <p>The release type used for downloading</p>
* @param lastVersion <p>The last server version found</p>
* @param versionContainer <p>The version container to use</p>
* @return <p>True if the jar exists and is the latest version or was downloaded</p>
* @throws IOException <p>If the .jar cannot be downloaded</p>
*/
private boolean downloadLatestJar(File filePath, String releaseType, String lastVersion,
ServerVersionContainer versionContainer) throws IOException {
String[] latestData = getLatestFile(releaseType);
String latest = latestData[0];
String jarFile = latestData[1];
//The file is already the newest version
if (filePath.isFile() && latest.equals(lastVersion)) {
return true;
}
//The new jar file could not be downloaded
if (!downloadFile(jarFile, Paths.get(filePath.toURI()))) {
return false;
}
if (releaseType.equals("release")) {
versionContainer.setVanillaVersion(latest);
} else {
versionContainer.setSnapshotVersion(latest);
}
return true;
}
/**
* Gets the URL to the .jar file for the newest version
*
* @param releaseType <p>The type of release to read latest version from</p>
* @return <p>An array containing the latest version and a link to its file</p>
* @throws IOException <p>If the remote resource cannot be readFromServer</p>
*/
private String[] getLatestFile(String releaseType) throws IOException {
String versionText = readFile(versionURL);
JsonObject jsonObject = new JsonParser().parse(versionText).getAsJsonObject();
String latest = jsonObject.getAsJsonObject("latest").get(releaseType).getAsString();
String versionURL = getServerFileVersionURL(latest);
String jarURL = getVanillaDownloadURL(versionURL);
return new String[]{latest, jarURL};
}
/**
* Gets the URL necessary for downloading a given minecraft .jar file
*
* @param versionURL <p>The URL to the version document describing the .jar file</p>
* @return <p>The URL necessary do download the .jar file</p>
* @throws IOException <p>If the remote resource cannot be readFromServer</p>
*/
private String getVanillaDownloadURL(String versionURL) throws IOException {
String versionText = readFile(versionURL);
JsonObject jsonObject = new JsonParser().parse(versionText).getAsJsonObject();
return jsonObject.getAsJsonObject("downloads").getAsJsonObject("server").get("url").getAsString();
}
/**
* Gets the version URL from the Minecraft vanilla version document
*
* @param targetVersion <p>The version to download</p>
* @return <p>The URL to the file</p>
* @throws IOException <p>If the file cannot be downloaded</p>
*/
private String getServerFileVersionURL(String targetVersion) throws IOException {
String versionText = readFile(versionURL);
JsonObject jsonObject = new JsonParser().parse(versionText).getAsJsonObject();
JsonArray availableVersions = jsonObject.getAsJsonArray("versions");
for (JsonElement availableVersion : availableVersions) {
JsonObject versionObject = availableVersion.getAsJsonObject();
if (!versionObject.get("id").getAsString().equals(targetVersion)) {
continue;
}
return versionObject.get("url").getAsString();
}
throw new FileNotFoundException("Unable to find the requested Minecraft vanilla .jar file.");
}
}

View File

@ -0,0 +1,67 @@
package net.knarcraft.minecraftserverlauncher.server.servertypes;
import net.knarcraft.minecraftserverlauncher.server.ServerVersionContainer;
import java.io.File;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.function.BiConsumer;
import java.util.function.Function;
import static net.knarcraft.minecraftserverlauncher.utility.CommonFunctions.downloadFile;
import static net.knarcraft.minecraftserverlauncher.utility.CommonFunctions.readFile;
import static net.knarcraft.minecraftserverlauncher.utility.CommonFunctions.stringBetween;
/**
* This class represents the Travertine proxy server type
*/
public class Waterfall extends AbstractServerType {
private final String srcStart;
private final String srcEnd;
private final String versionURL;
Function<String, String> oldVersionFunction;
BiConsumer<String, String> versionUpdateFunction;
final ServerVersionContainer serverVersionContainer;
/**
* Instantiates a new Waterfall server type
*
* @param typeName <p>The name of the server type</p>
* @param isProxy <p>Whether this server type is a proxy server</p>
* @param versions <p>The available versions for the server type</p>
* @param versionURL <p>The URL used to finding the newest version</p>
* @param srcStart <p>The string after which the version id starts</p>
* @param srcEnd <p>The string marking the end of the version id</p>
* @param downloadURL <p>The URL used for downloading the latest version</p>
*/
public Waterfall(String typeName, boolean isProxy, String[] versions, String versionURL, String srcStart,
String srcEnd, String downloadURL) {
super(typeName, isProxy, versions, downloadURL);
this.serverVersionContainer = ServerVersionContainer.getInstance();
this.srcStart = srcStart;
this.srcEnd = srcEnd;
this.versionURL = versionURL;
this.oldVersionFunction = serverVersionContainer::getWaterfallVersion;
this.versionUpdateFunction = serverVersionContainer::setWaterfallVersion;
}
@Override
public boolean downloadJar(String folder, String version) throws IOException {
String file = this.getName() + version + ".jar";
File filePath = new File(folder + file);
String newestVersion = stringBetween(readFile(versionURL + version), srcStart, srcEnd);
String fullURL = downloadURL + version + "/" + newestVersion + "/download";
String oldVersion = oldVersionFunction.apply(version);
//The file is already the newest version
if (filePath.isFile() && newestVersion.equals(oldVersion)) {
return true;
}
//The new jar file could not be downloaded
if (!downloadFile(fullURL, Paths.get(filePath.toURI()))) {
return false;
}
versionUpdateFunction.accept(version, newestVersion);
return true;
}
}

View File

@ -0,0 +1,126 @@
package net.knarcraft.minecraftserverlauncher.userinterface;
import net.knarcraft.minecraftserverlauncher.Main;
import javax.swing.*;
import javax.swing.text.DefaultCaret;
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import static javax.swing.text.DefaultCaret.ALWAYS_UPDATE;
/**
* Acts as a single writable/readable tab
*
* <p>Has a box for user input, and a textArea for server output.</p>
*
* @author Kristian Knarvik <kristian.knarvik@knett.no>
* @version 1.0.0
* @since 1.0.0
*/
public class Console extends KeyAdapter implements ActionListener, KeyListener {
private final JTextField textInput;
private final JTextArea textOutput;
private final String name;
private final JPanel panel;
private final ArrayList<String> commands = new ArrayList<>();
private int commandIndex;
/**
* Instantiates a new console
*
* @param tab <p>The tabbed pane used for displaying the console</p>
* @param name <p>The name of the console tab</p>
*/
Console(JTabbedPane tab, String name) {
this.name = name;
panel = new JPanel();
tab.addTab(name, null, panel, null);
panel.setLayout(new BorderLayout(0, 0));
textInput = new JTextField();
panel.add(textInput, BorderLayout.SOUTH);
textInput.setColumns(10);
textInput.addActionListener(this);
textOutput = new JTextArea();
JScrollPane scroll = new JScrollPane(textOutput);
panel.add(scroll, BorderLayout.CENTER);
textOutput.setEditable(false);
DefaultCaret caret = (DefaultCaret) textOutput.getCaret();
caret.setUpdatePolicy(ALWAYS_UPDATE);
textOutput.setLineWrap(true);
textInput.addKeyListener(this);
}
/**
* Gets the JPanel this console is drawn on
*
* @return <p>The JPanel this console is drawn on</p>
*/
public JPanel getPanel() {
return this.panel;
}
/**
* Prints a string to the console's output
*
* @param text <p>The string to print</p>
*/
public void output(String text) {
this.textOutput.setText(this.textOutput.getText() + "\n" + text);
}
@Override
public void actionPerformed(ActionEvent e) {
//Sends the command from the input to the server with the same name.
if (e.getSource() == textInput) {
java.lang.String text = textInput.getText();
Main.getController().getCurrentProfile().sendCommand(this.name, text);
commands.add(text);
if (commands.size() > 25) {
commands.remove(0);
}
commandIndex = commands.size();
textInput.setText("");
}
}
@Override
public void keyPressed(KeyEvent e) {
//Cycles through previously used commands
if (e.getKeyCode() == KeyEvent.VK_UP) {
showPreviousCommand();
} else if (e.getKeyCode() == KeyEvent.VK_DOWN) {
showNextCommand();
}
}
/**
* Shows the previously executed command in the input field
*
* <p>Shows the previously executed command if a command was just executed.
* Shows the command executed earlier if already showing a previously executed command.</p>
*/
private void showPreviousCommand() {
if (commands.size() > 0 && commandIndex > 0) {
textInput.setText(commands.get(--commandIndex));
}
}
/**
* Shows the next previously executed command or clears the input field
*
* <p>Shows the next previously executed command if such a command exists.
* Clears the input field if no next used command exists.</p>
*/
private void showNextCommand() {
if (commands.size() > 0) {
if (commandIndex == commands.size() - 1) {
commandIndex++;
textInput.setText("");
} else if (commandIndex >= 0 && commandIndex <= commands.size() - 1) {
textInput.setText(commands.get(++commandIndex));
}
}
}
}

View File

@ -0,0 +1,55 @@
package net.knarcraft.minecraftserverlauncher.userinterface;
import java.io.File;
/**
* Describes a generic GUI
*/
public interface GUI {
/**
* Displays a message to the user in the GUI message field
*
* @param message <p>The message contents</p>
*/
void setStatus(String message);
/**
* Displays an error to the user as an independent box
*
* @param title <p>The title of the error message</p>
* @param message <p>The error message contents</p>
*/
void showError(String title, String message);
/**
* Displays an error to the user as an independent box
*
* @param message <p>The error message contents</p>
*/
void showError(String message);
/**
* Displays a message to the user as an independent box
*
* @param title <p>The title of the message</p>
* @param message <p>The message contents</p>
*/
void showMessage(String title, String message);
/**
* Displays a message to the user as an independent box
*
* @param message <p>The message contents</p>
*/
void showMessage(String message);
/**
* Asks the user for a directory as a file object
*
* @param prompt <p>The prompt to show the user</p>
* @return <p>The directory given by the user</p>
*/
File askForDirectory(String prompt);
}

View File

@ -0,0 +1,59 @@
package net.knarcraft.minecraftserverlauncher.userinterface;
import javax.swing.*;
/**
* This class handles displaying messages to the user
*/
public abstract class MessageHandler implements GUI {
private final boolean silent;
/***
* Initializes a new message handler
*
* @param silent <p>Whether to print to cli instead of showing a GUI element</p>
*/
public MessageHandler(boolean silent) {
this.silent = silent;
}
@Override
public void showError(String title, String message) {
if (silent) {
System.out.println(message);
} else {
showJOptionPane(title, message, JOptionPane.ERROR_MESSAGE);
}
}
@Override
public void showError(String message) {
showError("Error", message);
}
@Override
public void showMessage(String title, String message) {
if (silent) {
System.out.println(message);
} else {
showJOptionPane(title, message, JOptionPane.INFORMATION_MESSAGE);
}
}
@Override
public void showMessage(String message) {
showMessage("Info", message);
}
/**
* Shows a JOptionPane
*
* @param title <p>The title of the pane</p>
* @param message <p>The message of the pane</p>
* @param paneType <p>The type of the pane</p>
*/
private void showJOptionPane(String title, String message, int paneType) {
JOptionPane.showMessageDialog(null, message, title, paneType);
}
}

View File

@ -0,0 +1,56 @@
package net.knarcraft.minecraftserverlauncher.userinterface;
import javax.swing.*;
import java.awt.*;
/**
* This class keeps track of all consoles
*
* <p>A parent window for server consoles. Should be toggled with the "View server consoles" button.
* Keeps track of all consoles.</p>
*
* @author Kristian Knarvik <kristian.knarvik@knett.no>
* @version 1.0.0
* @since 1.0.0
*/
public class ServerConsoles {
private static JFrame frame;
private static JTabbedPane consolesTabbedPane;
/**
* Initializes the server consoles frame
*/
public ServerConsoles() {
frame = new JFrame();
frame.setBounds(100, 100, 450, 300);
frame.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
consolesTabbedPane = new JTabbedPane(JTabbedPane.TOP);
frame.getContentPane().add(consolesTabbedPane, BorderLayout.CENTER);
}
/**
* Adds a new console tab
*
* @param name <p>The name of the consoles tab</p>
* @return <p>A new console element with the new tabbed pane</p>
*/
public static Console addConsoleTab(String name) {
return new Console(consolesTabbedPane, name);
}
/**
* Sets the server consoles frame as visible
*/
public static void setAsVisible() {
frame.setVisible(true);
}
/**
* Returns the tabbed pane containing the server consoles
*
* @return <p>The tabbed pane containing the server consoles</p>
*/
public static JTabbedPane getTabbedPane() {
return consolesTabbedPane;
}
}

View File

@ -0,0 +1,273 @@
package net.knarcraft.minecraftserverlauncher.userinterface;
import net.knarcraft.minecraftserverlauncher.profile.Collection;
import net.knarcraft.minecraftserverlauncher.profile.Controller;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
/**
* This class takes care of all server controls on the server control tab
*/
public class ServerControlTab implements ActionListener {
private JComboBox<String> targetServerCombo;
private JComboBox<String> targetPlayerCombo;
private JButton kickButton;
private JButton banButton;
private JButton opButton;
private JButton deopButton;
private JButton customCommandButton;
private JButton saveServerButton;
private JButton reloadButton;
private JButton showConsolesButton;
private JTextField customCommandTextField;
private Controller controller = Controller.getInstance();
private final ArrayList<String> globalPlayers;
/**
* Instantiates a new server control tab
*
* @param mainFrame <p>The main frame of the GUI</p>
* @param controlServers <p>The JPanel to attach the server controls to</p>
*/
public ServerControlTab(JFrame mainFrame, JPanel controlServers) {
this.globalPlayers = new ArrayList<>();
initialize(mainFrame, controlServers);
}
/**
* Initializes GUI elements for the server control tab
*
* @param mainFrame <p>The main frame of the GUI</p>
* @param controlServers <p>The JPanel to attach the server controls to</p>
*/
private void initialize(JFrame mainFrame, JPanel controlServers) {
SpringLayout springLayout = new SpringLayout();
controlServers.setLayout(springLayout);
targetServerCombo = new JComboBox<>();
springLayout.putConstraint(SpringLayout.NORTH, targetServerCombo, 10, SpringLayout.NORTH, controlServers);
controlServers.add(targetServerCombo);
targetServerCombo.addActionListener(this);
targetPlayerCombo = new JComboBox<>();
springLayout.putConstraint(SpringLayout.NORTH, targetPlayerCombo, 6, SpringLayout.SOUTH, targetServerCombo);
targetPlayerCombo.setEditable(true);
controlServers.add(targetPlayerCombo);
kickButton = new JButton("Kick");
springLayout.putConstraint(SpringLayout.NORTH, kickButton, 10, SpringLayout.NORTH, controlServers);
springLayout.putConstraint(SpringLayout.WEST, kickButton, 6, SpringLayout.EAST, targetServerCombo);
springLayout.putConstraint(SpringLayout.EAST, kickButton, 104, SpringLayout.WEST, kickButton);
springLayout.putConstraint(SpringLayout.SOUTH, targetServerCombo, 0, SpringLayout.SOUTH, kickButton);
controlServers.add(kickButton);
kickButton.addActionListener(this);
banButton = new JButton("Ban");
springLayout.putConstraint(SpringLayout.NORTH, banButton, 6, SpringLayout.SOUTH, kickButton);
springLayout.putConstraint(SpringLayout.WEST, banButton, 6, SpringLayout.EAST, targetPlayerCombo);
springLayout.putConstraint(SpringLayout.EAST, banButton, 104, SpringLayout.WEST, banButton);
springLayout.putConstraint(SpringLayout.SOUTH, targetPlayerCombo, 0, SpringLayout.SOUTH, banButton);
controlServers.add(banButton);
banButton.addActionListener(this);
opButton = new JButton("OP");
springLayout.putConstraint(SpringLayout.NORTH, opButton, 10, SpringLayout.NORTH, controlServers);
springLayout.putConstraint(SpringLayout.WEST, opButton, 6, SpringLayout.EAST, kickButton);
springLayout.putConstraint(SpringLayout.EAST, opButton, -10, SpringLayout.EAST, controlServers);
controlServers.add(opButton);
opButton.addActionListener(this);
deopButton = new JButton("DEOP");
springLayout.putConstraint(SpringLayout.WEST, deopButton, 6, SpringLayout.EAST, banButton);
springLayout.putConstraint(SpringLayout.NORTH, deopButton, 5, SpringLayout.SOUTH, opButton);
springLayout.putConstraint(SpringLayout.EAST, deopButton, -10, SpringLayout.EAST, controlServers);
controlServers.add(deopButton);
deopButton.addActionListener(this);
JLabel lblTargetServer = new JLabel("Target server");
springLayout.putConstraint(SpringLayout.WEST, targetServerCombo, 6, SpringLayout.EAST, lblTargetServer);
springLayout.putConstraint(SpringLayout.EAST, targetServerCombo, 121, SpringLayout.EAST, lblTargetServer);
springLayout.putConstraint(SpringLayout.NORTH, lblTargetServer, 10, SpringLayout.NORTH, controlServers);
springLayout.putConstraint(SpringLayout.SOUTH, lblTargetServer, 0, SpringLayout.SOUTH, targetServerCombo);
springLayout.putConstraint(SpringLayout.WEST, lblTargetServer, 10, SpringLayout.WEST, controlServers);
controlServers.add(lblTargetServer);
JLabel lblTargetPlayer = new JLabel("Target player");
springLayout.putConstraint(SpringLayout.WEST, targetPlayerCombo, 7, SpringLayout.EAST, lblTargetPlayer);
springLayout.putConstraint(SpringLayout.EAST, targetPlayerCombo, 122, SpringLayout.EAST, lblTargetPlayer);
springLayout.putConstraint(SpringLayout.NORTH, lblTargetPlayer, 6, SpringLayout.SOUTH, lblTargetServer);
springLayout.putConstraint(SpringLayout.SOUTH, lblTargetPlayer, 0, SpringLayout.SOUTH, targetPlayerCombo);
springLayout.putConstraint(SpringLayout.WEST, lblTargetPlayer, 0, SpringLayout.WEST, lblTargetServer);
controlServers.add(lblTargetPlayer);
customCommandButton = new JButton("Custom command");
springLayout.putConstraint(SpringLayout.WEST, customCommandButton, 250, SpringLayout.WEST, controlServers);
springLayout.putConstraint(SpringLayout.EAST, customCommandButton, 0, SpringLayout.EAST, opButton);
controlServers.add(customCommandButton);
customCommandButton.addActionListener(this);
mainFrame.getRootPane().setDefaultButton(customCommandButton);
customCommandTextField = new JTextField();
springLayout.putConstraint(SpringLayout.WEST, customCommandTextField, 10, SpringLayout.WEST, controlServers);
springLayout.putConstraint(SpringLayout.EAST, customCommandTextField, -6, SpringLayout.WEST, customCommandButton);
springLayout.putConstraint(SpringLayout.NORTH, customCommandButton, 0, SpringLayout.NORTH, customCommandTextField);
springLayout.putConstraint(SpringLayout.SOUTH, customCommandTextField, 0, SpringLayout.SOUTH, customCommandButton);
controlServers.add(customCommandTextField);
customCommandTextField.setColumns(10);
saveServerButton = new JButton("Save server");
springLayout.putConstraint(SpringLayout.NORTH, customCommandTextField, 6, SpringLayout.SOUTH, saveServerButton);
springLayout.putConstraint(SpringLayout.NORTH, saveServerButton, 6, SpringLayout.SOUTH, banButton);
springLayout.putConstraint(SpringLayout.WEST, saveServerButton, 0, SpringLayout.WEST, kickButton);
springLayout.putConstraint(SpringLayout.EAST, saveServerButton, 104, SpringLayout.WEST, kickButton);
springLayout.putConstraint(SpringLayout.EAST, saveServerButton, 104, SpringLayout.WEST, kickButton);
controlServers.add(saveServerButton);
saveServerButton.addActionListener(this);
reloadButton = new JButton("Reload");
springLayout.putConstraint(SpringLayout.NORTH, reloadButton, 6, SpringLayout.SOUTH, deopButton);
springLayout.putConstraint(SpringLayout.WEST, reloadButton, 0, SpringLayout.WEST, deopButton);
springLayout.putConstraint(SpringLayout.EAST, reloadButton, 0, SpringLayout.EAST, opButton);
controlServers.add(reloadButton);
reloadButton.addActionListener(this);
showConsolesButton = new JButton("View server consoles");
springLayout.putConstraint(SpringLayout.NORTH, showConsolesButton, 0, SpringLayout.NORTH, saveServerButton);
springLayout.putConstraint(SpringLayout.WEST, showConsolesButton, 0, SpringLayout.WEST, lblTargetServer);
springLayout.putConstraint(SpringLayout.EAST, showConsolesButton, 0, SpringLayout.EAST, targetServerCombo);
controlServers.add(showConsolesButton);
showConsolesButton.addActionListener(this);
}
/**
* Updates available servers according to existing collections
*/
public void update() {
this.targetServerCombo.removeAllItems();
this.targetServerCombo.addItem("All");
for (Collection collection : Controller.getInstance().getCurrentProfile().getCollections()) {
this.targetServerCombo.addItem(collection.getName());
}
}
@Override
public void actionPerformed(ActionEvent actionEvent) {
//Registers actions on the server control tab
handleServerControlTabButtons(actionEvent.getSource());
}
/**
* Handles buttons and combos on the server control tab
*
* @param actionSource <p>The object being interacted with</p>
*/
private void handleServerControlTabButtons(Object actionSource) {
String selectedServerValue = null;
String selectedPlayerValue = null;
Object selectedServer = targetServerCombo.getSelectedItem();
if (selectedServer != null) {
selectedServerValue = selectedServer.toString();
}
Object selectedPlayer = targetPlayerCombo.getSelectedItem();
if (selectedPlayer != null) {
selectedPlayerValue = selectedPlayer.toString();
}
//Register actions on all commands executed on a specific player
handlePlayerCommands(actionSource, selectedServerValue, selectedPlayerValue);
//Registers actions on all commands executed on a specific server
handleServerCommands(actionSource, selectedServerValue);
if (actionSource == showConsolesButton) {
ServerConsoles.setAsVisible();
} else if (actionSource == targetServerCombo) {
updatePlayers();
}
}
/**
* Updates the list of players currently online on the selected server
*/
private void updatePlayers() {
String selectedServerValue;
Object selectedServer = targetServerCombo.getSelectedItem();
if (selectedServer != null) {
targetPlayerCombo.removeAllItems();
selectedServerValue = selectedServer.toString();
if (selectedServerValue.equals("All")) {
for (String player : this.globalPlayers) {
targetPlayerCombo.addItem(player);
}
} else {
for (String player : controller.getCurrentProfile().getCollection(selectedServerValue).getServer().getPlayers()) {
targetPlayerCombo.addItem(player);
}
}
}
}
/**
* Adds a player to the global player list, and updates the players combo
*
* @param name <p>The name of the player to add</p>
*/
public void addPlayer(String name) {
this.globalPlayers.add(name);
this.updatePlayers();
}
/**
* Removes a player from the global list of players
*
* @param name <p>The name of the player to remove</p>
*/
public void removePlayer(String name) {
globalPlayers.removeIf(playerName -> playerName.equals(name));
this.updatePlayers();
}
/**
* Handles command buttons acting on a specific server
*
* @param actionSource <p>The object being interacted with</p>
* @param selectedServerValue <p>The server currently selected</p>
*/
private void handleServerCommands(Object actionSource, String selectedServerValue) {
if (selectedServerValue == null) {
return;
}
if (actionSource == customCommandButton) {
controller.getCurrentProfile().sendCommand(selectedServerValue, customCommandTextField.getText());
customCommandTextField.setText("");
} else if (actionSource == saveServerButton) {
controller.getCurrentProfile().sendCommand(selectedServerValue, "save-all");
} else if (actionSource == reloadButton) {
controller.getCurrentProfile().sendCommand(selectedServerValue, "reload");
}
}
/**
* Handles command buttons which act on a player
*
* @param actionSource <p>The clicked object</p>
* @param selectedServerValue <p>The server currently selected</p>
* @param selectedPlayerValue <p>The player currently selected</p>
*/
private void handlePlayerCommands(Object actionSource, String selectedServerValue, String selectedPlayerValue) {
if (selectedServerValue == null || selectedPlayerValue == null) {
return;
}
if (actionSource == kickButton) {
controller.getCurrentProfile().sendCommand(selectedServerValue, "kick " + selectedPlayerValue);
} else if (actionSource == banButton) {
controller.getCurrentProfile().sendCommand(selectedServerValue, "ban " + selectedPlayerValue);
} else if (actionSource == opButton) {
controller.getCurrentProfile().sendCommand(selectedServerValue, "op " + selectedPlayerValue);
} else if (actionSource == deopButton) {
controller.getCurrentProfile().sendCommand(selectedServerValue, "deop " + selectedPlayerValue);
}
}
}

View File

@ -0,0 +1,430 @@
package net.knarcraft.minecraftserverlauncher.userinterface;
import net.knarcraft.minecraftserverlauncher.Main;
import net.knarcraft.minecraftserverlauncher.profile.Collection;
import net.knarcraft.minecraftserverlauncher.profile.Controller;
import net.knarcraft.minecraftserverlauncher.server.Server;
import net.knarcraft.minecraftserverlauncher.utility.CommonFunctions;
import javax.imageio.ImageIO;
import javax.naming.ConfigurationException;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.*;
import java.util.*;
import java.util.concurrent.Executors;
import static java.awt.Frame.NORMAL;
import static javax.swing.WindowConstants.DO_NOTHING_ON_CLOSE;
import static net.knarcraft.minecraftserverlauncher.utility.CommonFunctions.getResourceAsScanner;
import static net.knarcraft.minecraftserverlauncher.utility.CommonFunctions.getResourceAsStream;
/**
* Generates a ServerLauncherGUI.
*
* @author Kristian Knarvik <kristian.knarvik@knett.no>
* @version 1.0.0
* @since 1.0.0
*/
public class ServerLauncherGUI extends MessageHandler implements ActionListener, GUI {
private final JLabel lblStatuslabel = new JLabel("Servers are stopped");
private Controller controller;
private Map<String, String> textStrings;
private Tray applicationTray;
private JFrame frame;
private JTabbedPane tabbedPane;
private JTabbedPane serversPane;
private ServerControlTab serverControlTab;
private ServerLauncherMenu serverLauncherMenu;
//Basic controls
private JButton startServerButton;
private JButton stopServerButton;
private JButton addServerButton;
private JButton backupButton;
private JButton addProfileButton;
private JButton deleteProfileButton;
private JComboBox<String> profiles;
/**
* Creates the application window
*/
public ServerLauncherGUI() throws IOException {
super(false);
initialize(440, 170);
loadMessages();
this.controller = Main.getController();
}
/**
* Creates the application window with a preferred width and height
*
* @param width <p>The preferred width</p>
* @param height <p>The preferred height</p>
*/
public ServerLauncherGUI(int width, int height) throws IOException {
super(false);
initialize(width, height);
loadMessages();
this.controller = Main.getController();
}
/**
* Gets the pane used for server configurations
*
* @return <p>The pane used for server configurations</p>
*/
public JTabbedPane getPane() {
return this.serversPane;
}
@Override
public void setStatus(String text) {
this.lblStatuslabel.setText(text);
try (PrintWriter file = new PrintWriter(new FileWriter(Main.getApplicationWorkDirectory() +
File.separator + "latestrun.log", true))) {
file.println(text);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public File askForDirectory(String prompt) {
JFileChooser chooser = new JFileChooser();
chooser.setCurrentDirectory(new java.io.File("."));
chooser.setDialogTitle(prompt);
chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
chooser.setAcceptAllFileFilterUsed(false);
if (chooser.showOpenDialog(null) != JFileChooser.APPROVE_OPTION) {
setStatus("Choice aborted by user");
return null;
}
return chooser.getSelectedFile();
}
/**
* Updates the profiles combo
*/
public void updateProfiles() {
String selectedProfile = Main.getController().getCurrentProfile().getName();
this.profiles.removeAllItems();
for (String profile : Main.getController().getProfileNames()) {
this.profiles.addItem(profile);
}
this.profiles.setSelectedItem(selectedProfile);
}
/**
* Gets the server control tab used by this GUI
*
* @return <p>The server control tab used by this GUI</p>
*/
public ServerControlTab getServerControlTab() {
return this.serverControlTab;
}
/**
* Gets the size of the main JFrame
*
* @return <p>The Dimension of the main JFrame</p>
*/
public Dimension getSize() {
return frame.getContentPane().getSize();
}
/**
* Updates ServerLauncherGUI according to current profile settings
*/
public void updateWithSavedProfileData() {
Controller controller = Main.getController();
serversPane.removeAll();
serverLauncherMenu.update();
serverControlTab.update();
for (Collection collection : controller.getCurrentProfile().getCollections()) {
serversPane.addTab(collection.getName(), collection.getServerTab().getPanel());
}
}
/**
* Initializes the server launcher GUI
*
* @param width <p>The width of the GUI</p>
* @param height <p>The height of the GUI</p>
* @throws IOException <p>If unable to load the GUI icon</p>
*/
private void initialize(int width, int height) throws IOException {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException |
UnsupportedLookAndFeelException |
InstantiationException |
IllegalAccessException e
) {
e.printStackTrace();
}
frame = new JFrame("Minecraft server launcher");
frame.setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
frame.getContentPane().setPreferredSize(new Dimension(width, height));
ImageIcon img = new ImageIcon(ImageIO.read(getResourceAsStream("GUIIcon.png")));
frame.setIconImage(img.getImage());
JMenuBar menuBar = new JMenuBar();
frame.setJMenuBar(menuBar);
this.serverLauncherMenu = new ServerLauncherMenu(menuBar, this);
tabbedPane = new JTabbedPane(JTabbedPane.TOP);
frame.getContentPane().add(tabbedPane);
JPanel panelBasic = new JPanel();
tabbedPane.addTab("Control panel", null, panelBasic, null);
SpringLayout sl_panel = new SpringLayout();
panelBasic.setLayout(sl_panel);
JLabel lblBasicControls = new JLabel("Basic controls");
sl_panel.putConstraint(SpringLayout.NORTH, lblBasicControls, 10, SpringLayout.NORTH, panelBasic);
panelBasic.add(lblBasicControls);
startServerButton = new JButton("Start servers");
sl_panel.putConstraint(SpringLayout.WEST, lblBasicControls, 0, SpringLayout.WEST, startServerButton);
sl_panel.putConstraint(SpringLayout.NORTH, startServerButton, 6, SpringLayout.SOUTH, lblBasicControls);
sl_panel.putConstraint(SpringLayout.WEST, startServerButton, 10, SpringLayout.WEST, panelBasic);
panelBasic.add(startServerButton);
startServerButton.addActionListener(this);
stopServerButton = new JButton("Stop servers");
sl_panel.putConstraint(SpringLayout.NORTH, stopServerButton, 0, SpringLayout.NORTH, startServerButton);
sl_panel.putConstraint(SpringLayout.WEST, stopServerButton, 6, SpringLayout.EAST, startServerButton);
panelBasic.add(stopServerButton);
stopServerButton.addActionListener(this);
JLabel lblProfile = new JLabel("Profile");
sl_panel.putConstraint(SpringLayout.NORTH, lblProfile, 6, SpringLayout.SOUTH, startServerButton);
sl_panel.putConstraint(SpringLayout.WEST, lblProfile, 10, SpringLayout.WEST, panelBasic);
panelBasic.add(lblProfile);
addProfileButton = new JButton("+");
sl_panel.putConstraint(SpringLayout.NORTH, addProfileButton, 6, SpringLayout.SOUTH, lblProfile);
sl_panel.putConstraint(SpringLayout.WEST, addProfileButton, 10, SpringLayout.WEST, panelBasic);
panelBasic.add(addProfileButton);
addProfileButton.addActionListener(this);
deleteProfileButton = new JButton("-");
sl_panel.putConstraint(SpringLayout.NORTH, deleteProfileButton, 0, SpringLayout.NORTH, addProfileButton);
sl_panel.putConstraint(SpringLayout.WEST, deleteProfileButton, 6, SpringLayout.EAST, addProfileButton);
panelBasic.add(deleteProfileButton);
deleteProfileButton.addActionListener(this);
profiles = new JComboBox<>();
sl_panel.putConstraint(SpringLayout.NORTH, profiles, 0, SpringLayout.NORTH, addProfileButton);
sl_panel.putConstraint(SpringLayout.WEST, profiles, 6, SpringLayout.EAST, deleteProfileButton);
sl_panel.putConstraint(SpringLayout.EAST, profiles, 124, SpringLayout.EAST, deleteProfileButton);
panelBasic.add(profiles);
profiles.addActionListener(this);
sl_panel.putConstraint(SpringLayout.NORTH, lblStatuslabel, 6, SpringLayout.SOUTH, addProfileButton);
sl_panel.putConstraint(SpringLayout.SOUTH, lblStatuslabel, -10, SpringLayout.SOUTH, panelBasic);
sl_panel.putConstraint(SpringLayout.WEST, lblStatuslabel, 10, SpringLayout.WEST, panelBasic);
sl_panel.putConstraint(SpringLayout.EAST, lblStatuslabel, -10, SpringLayout.EAST, panelBasic);
panelBasic.add(lblStatuslabel);
addServerButton = new JButton("Add server");
sl_panel.putConstraint(SpringLayout.NORTH, addServerButton, 0, SpringLayout.NORTH, startServerButton);
sl_panel.putConstraint(SpringLayout.WEST, addServerButton, 6, SpringLayout.EAST, stopServerButton);
panelBasic.add(addServerButton);
addServerButton.addActionListener(this);
backupButton = new JButton("Backup");
sl_panel.putConstraint(SpringLayout.NORTH, backupButton, 0, SpringLayout.NORTH, startServerButton);
sl_panel.putConstraint(SpringLayout.WEST, backupButton, 6, SpringLayout.EAST, addServerButton);
panelBasic.add(backupButton);
backupButton.addActionListener(this);
JPanel controlServers = new JPanel();
tabbedPane.addTab("Control servers", null, controlServers, null);
serverControlTab = new ServerControlTab(frame, controlServers);
JPanel panel_2 = new JPanel();
tabbedPane.addTab("Servers", null, panel_2, null);
SpringLayout sl_panel_2 = new SpringLayout();
panel_2.setLayout(sl_panel_2);
JTabbedPane tabbedPane_1 = new JTabbedPane(JTabbedPane.TOP);
sl_panel_2.putConstraint(SpringLayout.NORTH, tabbedPane_1, 0, SpringLayout.NORTH, panel_2);
sl_panel_2.putConstraint(SpringLayout.WEST, tabbedPane_1, 0, SpringLayout.WEST, panel_2);
sl_panel_2.putConstraint(SpringLayout.SOUTH, tabbedPane_1, 0, SpringLayout.SOUTH, panel_2);
sl_panel_2.putConstraint(SpringLayout.EAST, tabbedPane_1, 0, SpringLayout.EAST, panel_2);
panel_2.add(tabbedPane_1);
this.serversPane = tabbedPane_1;
tabbedPane_1.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
frame.validate();
frame.pack();
frame.setVisible(true);
applicationTray = new Tray(frame, this);
updateGUIElementsWhenServersStartOrStop(false);
}
/**
* Hides the GUI to the system tray
*/
public void hideToTray() {
applicationTray.hideToTray();
}
/**
* Gets the currently selected profile
*
* @return <p>The currently selected profile or null</p>
*/
public String getSelectedProfile() {
Object selectedProfile = profiles.getSelectedItem();
if (selectedProfile != null) {
return selectedProfile.toString();
} else {
return null;
}
}
@Override
public void actionPerformed(ActionEvent e) {
Object actionSource = e.getSource();
//Registers actions on the main tab
handleMainTabButtons(actionSource);
}
/**
* Handles buttons and the combo on the main tab
*
* @param actionSource <p>The object being interacted with</p>
*/
private void handleMainTabButtons(Object actionSource) {
if (actionSource == startServerButton) {
controller.saveState();
Executors.newSingleThreadExecutor().execute(Server::startServers);
} else if (actionSource == stopServerButton) {
stopServers();
} else if (actionSource == addServerButton) {
addServer();
} else if (actionSource == backupButton) {
CommonFunctions.backup(this);
} else if (actionSource == addProfileButton) {
controller.addProfile(JOptionPane.showInputDialog("Profile name: "));
updateProfiles();
} else if (actionSource == deleteProfileButton) {
deleteProfile();
} else if (actionSource == profiles) {
changeProfile();
}
}
/**
* Deletes the selected profile
*/
private void deleteProfile() {
Object selected = profiles.getSelectedItem();
if (selected != null) {
controller.removeProfile(selected.toString());
updateProfiles();
}
}
/**
* Adds a new server with a server tab
*/
private void addServer() {
String serverName = JOptionPane.showInputDialog("Name of server: ");
try {
controller.getCurrentProfile().addCollection(serverName);
} catch (ConfigurationException e1) {
e1.printStackTrace();
}
this.updateWithSavedProfileData();
controller.getCurrentProfile().updateConsoles();
}
/**
* Updates the ServerLauncherGUI components to block a user from doing illegal actions
*
* @param running <p>Whether the servers are currently running</p>
*/
public void updateGUIElementsWhenServersStartOrStop(boolean running) {
boolean stopped = !running; //Most gui is only enabled when the server is stopped rather than running.
profiles.setEnabled(stopped);
addProfileButton.setEnabled(stopped);
deleteProfileButton.setEnabled(stopped);
startServerButton.setEnabled(stopped);
addServerButton.setEnabled(stopped);
tabbedPane.setEnabledAt(2, stopped);
stopServerButton.setEnabled(running);
}
/**
* Saves the previous profile and loads data from the new profile
*/
private void changeProfile() {
controller.saveState();
Object current = this.profiles.getSelectedItem();
if (current != null) {
controller.setCurrentProfile(current.toString());
}
this.updateWithSavedProfileData();
controller.getCurrentProfile().updateConsoles();
}
/**
* Stops all servers
*/
public void stopServers() {
try {
setStatus("Servers are stopping...");
Server.stop();
} catch (IOException e1) {
showError("Could not stop server.");
e1.printStackTrace();
}
}
/**
* Gets a specific message from its key
*
* @param stringKey <p>The key for the string to get</p>
* @return <p>The corresponding string</p>
*/
public String getMessage(String stringKey) {
return textStrings.get(stringKey);
}
/**
* Loads popup messages from a text file
*/
private void loadMessages() throws FileNotFoundException {
textStrings = new HashMap<>();
Scanner file = getResourceAsScanner("menumsg.csv");
while (file.hasNextLine()) {
String nextLine = file.nextLine();
String[] line = nextLine.split("=");
String content = line[1].replaceAll("_BREAK_", System.getProperty("line.separator"));
textStrings.put(line[0], content);
}
}
}

View File

@ -0,0 +1,195 @@
package net.knarcraft.minecraftserverlauncher.userinterface;
import net.knarcraft.minecraftserverlauncher.profile.Controller;
import net.knarcraft.minecraftserverlauncher.profile.Profile;
import net.knarcraft.minecraftserverlauncher.utility.CommonFunctions;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Objects;
/**
* This class takes care of the GUI's top menu
*/
public class ServerLauncherMenu implements ActionListener {
private JMenuBar menuBar;
//Options
private JCheckBoxMenuItem runInBackgroundCheckBoxMenuItem;
private JCheckBoxMenuItem delayStartupCheckBoxMenuItem;
private JCheckBoxMenuItem downloadJarsCheckBoxMenuItem;
//Help
private JMenuItem errorsMenuItem;
private JMenuItem setupMenuItem;
private JMenuItem manualUpdateMenuItem;
//Info/options
private JMenuItem runInBackgroundMenuItem;
private JMenuItem delayStartupMenuItem;
private JMenuItem downloadJarsMenuItem;
//Info/about
private JMenuItem aboutMenuItem;
private JMenuItem storyMenuItem;
private Controller controller;
private ServerLauncherGUI serverLauncherGUI;
/**
* Initializes a new server launcher menu
*
* @param menuBar <p>The menu bar to attach items to</p>
* @param serverLauncherGUI <p>The server launcher GUI to use</p>
*/
public ServerLauncherMenu(JMenuBar menuBar, ServerLauncherGUI serverLauncherGUI) {
this.controller = Controller.getInstance();
this.menuBar = menuBar;
this.serverLauncherGUI = serverLauncherGUI;
initialize();
}
/**
* Initializes all GUI elements
*/
public void initialize() {
JMenu mnOptions = new JMenu("Options");
menuBar.add(mnOptions);
runInBackgroundCheckBoxMenuItem = new JCheckBoxMenuItem("Run in background on exit");
mnOptions.add(runInBackgroundCheckBoxMenuItem);
runInBackgroundCheckBoxMenuItem.addActionListener(this);
delayStartupCheckBoxMenuItem = new JCheckBoxMenuItem("Delay Startup");
mnOptions.add(delayStartupCheckBoxMenuItem);
delayStartupCheckBoxMenuItem.addActionListener(this);
downloadJarsCheckBoxMenuItem = new JCheckBoxMenuItem("Download jars");
mnOptions.add(downloadJarsCheckBoxMenuItem);
downloadJarsCheckBoxMenuItem.addActionListener(this);
JMenu mnHelp = new JMenu("Help");
menuBar.add(mnHelp);
errorsMenuItem = new JMenuItem("Errors");
mnHelp.add(errorsMenuItem);
errorsMenuItem.addActionListener(this);
setupMenuItem = new JMenuItem("Setup");
mnHelp.add(setupMenuItem);
setupMenuItem.addActionListener(this);
manualUpdateMenuItem = new JMenuItem("Manual update");
mnHelp.add(manualUpdateMenuItem);
manualUpdateMenuItem.addActionListener(this);
JMenu mnInfo = new JMenu("Info");
menuBar.add(mnInfo);
JMenu mnOptionsInfo = new JMenu("Options");
mnInfo.add(mnOptionsInfo);
runInBackgroundMenuItem = new JMenuItem("Run in background on exit");
mnOptionsInfo.add(runInBackgroundMenuItem);
runInBackgroundMenuItem.addActionListener(this);
delayStartupMenuItem = new JMenuItem("Delay Startup");
mnOptionsInfo.add(delayStartupMenuItem);
delayStartupMenuItem.addActionListener(this);
downloadJarsMenuItem = new JMenuItem("Download jars");
mnOptionsInfo.add(downloadJarsMenuItem);
downloadJarsMenuItem.addActionListener(this);
JMenu mnAbout = new JMenu("About");
mnInfo.add(mnAbout);
aboutMenuItem = new JMenuItem("About");
mnAbout.add(aboutMenuItem);
aboutMenuItem.addActionListener(this);
storyMenuItem = new JMenuItem("Story");
mnAbout.add(storyMenuItem);
storyMenuItem.addActionListener(this);
}
public void update() {
runInBackgroundCheckBoxMenuItem.setState(controller.getRunInBackground());
delayStartupCheckBoxMenuItem.setState(controller.getDelayStartup() > 0);
downloadJarsCheckBoxMenuItem.setState(controller.getDownloadAllJars());
}
@Override
public void actionPerformed(ActionEvent actionEvent) {
Object actionSource = actionEvent.getSource();
if (actionSource == runInBackgroundCheckBoxMenuItem) {
background();
} else if (actionSource == delayStartupCheckBoxMenuItem) {
delay();
} else if (actionSource == downloadJarsCheckBoxMenuItem) {
downloadJars();
} else if (actionSource == errorsMenuItem) {
CommonFunctions.goToURL(serverLauncherGUI.getMessage("infoURL"));
} else if (actionSource == setupMenuItem) {
serverLauncherGUI.showMessage("Setup", serverLauncherGUI.getMessage("setupText"));
} else if (actionSource == manualUpdateMenuItem) {
CommonFunctions.goToURL(serverLauncherGUI.getMessage("manualUpdateURL"));
} else if (actionSource == runInBackgroundMenuItem) {
serverLauncherGUI.showMessage("Run in background", serverLauncherGUI.getMessage("runInBackgroundText"));
} else if (actionSource == delayStartupMenuItem) {
serverLauncherGUI.showMessage("Delay startup", serverLauncherGUI.getMessage("delayStartupText"));
} else if (actionSource == downloadJarsMenuItem) {
serverLauncherGUI.showMessage("Download jars", serverLauncherGUI.getMessage("downloadJarsText"));
} else if (actionSource == aboutMenuItem) {
serverLauncherGUI.showMessage("About", serverLauncherGUI.getMessage("aboutText"));
} else if (actionSource == storyMenuItem) {
CommonFunctions.goToURL(serverLauncherGUI.getMessage("storyURL"));
}
}
/**
* Asks the user for a delay if checked, and sets the value to the current profile
*/
private void delay() {
String selectedProfile = serverLauncherGUI.getSelectedProfile();
if (selectedProfile != null) {
Profile profile = controller.getProfileByName(selectedProfile);
if (delayStartupCheckBoxMenuItem.isSelected()) {
String response = JOptionPane.showInputDialog("Seconds to delay: ");
if (response == null) {
delayStartupCheckBoxMenuItem.setState(false);
return;
}
int parsed = Integer.parseInt(response);
Objects.requireNonNull(profile).setDelayStartup(parsed);
} else {
Objects.requireNonNull(profile).setDelayStartup(0);
}
} else {
serverLauncherGUI.showError("No profile selected");
}
}
/**
* Saves the runInBackground setting to the current profile
*/
private void background() {
String selectedProfile = serverLauncherGUI.getSelectedProfile();
if (selectedProfile != null) {
Profile profile = controller.getProfileByName(selectedProfile);
Objects.requireNonNull(profile).setRunInBackground(runInBackgroundCheckBoxMenuItem.isSelected());
} else {
serverLauncherGUI.showError("No profile selected");
}
}
/**
* Saves the downloadJars setting to the current profile
*/
private void downloadJars() {
String selectedProfile = serverLauncherGUI.getSelectedProfile();
if (selectedProfile != null) {
controller.setDownloadAllJars(downloadJarsCheckBoxMenuItem.isSelected());
} else {
serverLauncherGUI.showError("No profile selected");
}
}
}

View File

@ -0,0 +1,274 @@
package net.knarcraft.minecraftserverlauncher.userinterface;
import net.knarcraft.minecraftserverlauncher.Main;
import net.knarcraft.minecraftserverlauncher.server.Server;
import net.knarcraft.minecraftserverlauncher.server.ServerTypeHandler;
import net.knarcraft.minecraftserverlauncher.server.servertypes.ServerType;
import javax.naming.ConfigurationException;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
/**
* Represents a visual server tab used to configure a server
*
* @author Kristian Knarvik <kristian.knarvik@knett.no>
* @version 1.0.0
* @since 1.0.0
*/
public class ServerTab implements ActionListener {
private final JComboBox<String> serverTypes;
private final JComboBox<String> serverVersions;
private final JComboBox<String> maxRam;
private final JCheckBox enabledCheckbox;
private final JButton removeServerButton;
private final JButton browseButton;
private final JTextField directory;
private final JPanel panel;
private final String name;
/**
* Initializes a new server tab with the given name
*
* @param name <p>The name of the new server tab</p>
* @throws ConfigurationException <p>If unable to create the new tab</p>
*/
public ServerTab(String name) throws ConfigurationException {
this.name = name;
panel = new JPanel();
Main.getController().getGUI().getPane().addTab(name, null, panel, null);
SpringLayout sl_panel_3 = new SpringLayout();
panel.setLayout(sl_panel_3);
JLabel lblServerType = new JLabel("Server type");
sl_panel_3.putConstraint(SpringLayout.WEST, lblServerType, 10, SpringLayout.WEST, panel);
panel.add(lblServerType);
JLabel lblServerVersion = new JLabel("Server version");
sl_panel_3.putConstraint(SpringLayout.NORTH, lblServerVersion, 6, SpringLayout.SOUTH, lblServerType);
sl_panel_3.putConstraint(SpringLayout.WEST, lblServerVersion, 10, SpringLayout.WEST, panel);
sl_panel_3.putConstraint(SpringLayout.SOUTH, lblServerVersion, 26, SpringLayout.SOUTH, lblServerType);
panel.add(lblServerVersion);
serverTypes = new JComboBox<>(ServerTypeHandler.getTypeNames());
sl_panel_3.putConstraint(SpringLayout.NORTH, serverTypes, 10, SpringLayout.NORTH, panel);
sl_panel_3.putConstraint(SpringLayout.WEST, serverTypes, 5, SpringLayout.EAST, lblServerVersion);
sl_panel_3.putConstraint(SpringLayout.EAST, serverTypes, 154, SpringLayout.WEST, serverTypes);
sl_panel_3.putConstraint(SpringLayout.NORTH, lblServerType, 0, SpringLayout.NORTH, serverTypes);
sl_panel_3.putConstraint(SpringLayout.SOUTH, lblServerType, 0, SpringLayout.SOUTH, serverTypes);
panel.add(serverTypes);
serverTypes.addActionListener(this);
serverVersions = new JComboBox<>(ServerTypeHandler.getServerTypes().get(0).getVersions());
sl_panel_3.putConstraint(SpringLayout.NORTH, serverVersions, 6, SpringLayout.SOUTH, serverTypes);
sl_panel_3.putConstraint(SpringLayout.EAST, serverVersions, 0, SpringLayout.EAST, serverTypes);
sl_panel_3.putConstraint(SpringLayout.WEST, serverVersions, 6, SpringLayout.EAST, lblServerVersion);
sl_panel_3.putConstraint(SpringLayout.NORTH, lblServerVersion, 0, SpringLayout.NORTH, serverVersions);
sl_panel_3.putConstraint(SpringLayout.SOUTH, lblServerVersion, 0, SpringLayout.SOUTH, serverVersions);
panel.add(serverVersions);
serverVersions.addActionListener(this);
JLabel lblMaxRam = new JLabel("Max ram");
sl_panel_3.putConstraint(SpringLayout.NORTH, lblMaxRam, 0, SpringLayout.NORTH, serverTypes);
sl_panel_3.putConstraint(SpringLayout.SOUTH, lblMaxRam, 0, SpringLayout.SOUTH, serverTypes);
sl_panel_3.putConstraint(SpringLayout.WEST, lblMaxRam, 6, SpringLayout.EAST, serverTypes);
panel.add(lblMaxRam);
maxRam = new JComboBox<>(Server.getRamList());
sl_panel_3.putConstraint(SpringLayout.NORTH, maxRam, 0, SpringLayout.NORTH, serverTypes);
sl_panel_3.putConstraint(SpringLayout.WEST, maxRam, 6, SpringLayout.EAST, lblMaxRam);
sl_panel_3.putConstraint(SpringLayout.EAST, maxRam, -10, SpringLayout.EAST, panel);
panel.add(maxRam);
maxRam.addActionListener(this);
enabledCheckbox = new JCheckBox("Enabled");
sl_panel_3.putConstraint(SpringLayout.WEST, enabledCheckbox, 10, SpringLayout.WEST, panel);
panel.add(enabledCheckbox);
enabledCheckbox.addActionListener(this);
removeServerButton = new JButton("Remove server");
sl_panel_3.putConstraint(SpringLayout.NORTH, removeServerButton, 0, SpringLayout.NORTH, serverVersions);
sl_panel_3.putConstraint(SpringLayout.SOUTH, removeServerButton, 0, SpringLayout.SOUTH, serverVersions);
sl_panel_3.putConstraint(SpringLayout.WEST, removeServerButton, 6, SpringLayout.EAST, serverVersions);
sl_panel_3.putConstraint(SpringLayout.EAST, removeServerButton, -10, SpringLayout.EAST, panel);
panel.add(removeServerButton);
removeServerButton.addActionListener(this);
JLabel lblDirectory = new JLabel("Directory");
sl_panel_3.putConstraint(SpringLayout.WEST, lblDirectory, 6, SpringLayout.EAST, enabledCheckbox);
panel.add(lblDirectory);
directory = new JTextField();
sl_panel_3.putConstraint(SpringLayout.WEST, directory, 6, SpringLayout.EAST, lblDirectory);
sl_panel_3.putConstraint(SpringLayout.SOUTH, lblDirectory, 0, SpringLayout.SOUTH, directory);
sl_panel_3.putConstraint(SpringLayout.NORTH, lblDirectory, 0, SpringLayout.NORTH, directory);
sl_panel_3.putConstraint(SpringLayout.NORTH, enabledCheckbox, 0, SpringLayout.NORTH, directory);
sl_panel_3.putConstraint(SpringLayout.SOUTH, enabledCheckbox, 0, SpringLayout.SOUTH, directory);
panel.add(directory);
directory.setColumns(10);
directory.addActionListener(this);
browseButton = new JButton("Browse");
sl_panel_3.putConstraint(SpringLayout.EAST, directory, -6, SpringLayout.WEST, browseButton);
sl_panel_3.putConstraint(SpringLayout.NORTH, browseButton, 3, SpringLayout.SOUTH, removeServerButton);
sl_panel_3.putConstraint(SpringLayout.EAST, browseButton, -10, SpringLayout.EAST, panel);
sl_panel_3.putConstraint(SpringLayout.SOUTH, directory, 0, SpringLayout.SOUTH, browseButton);
sl_panel_3.putConstraint(SpringLayout.NORTH, directory, 0, SpringLayout.NORTH, browseButton);
panel.add(browseButton);
browseButton.addActionListener(this);
}
/**
* Updates the server tab components according to the received parameters
*
* @param path <p>The new path of the server tab</p>
* @param isEnabled <p>Whether to mark the server as enabled</p>
* @param typeName <p>The name of the selected server type</p>
* @param serverVersion <p>The version of the server</p>
* @param maxRam <p>The maximum usable ram for the server</p>
*/
public void setData(String path, boolean isEnabled, String typeName, String serverVersion, String maxRam) throws ConfigurationException {
this.directory.setText(path);
this.enabledCheckbox.setSelected(isEnabled);
this.serverTypes.setSelectedItem(typeName);
this.updateServerVersion();
this.serverVersions.setSelectedItem(serverVersion);
this.maxRam.setSelectedItem(maxRam);
}
/**
* Gets the JPanel containing the server tab's elements
*
* @return <p>The JPanel containing the server tab's elements</p>
*/
public JPanel getPanel() {
return this.panel;
}
/**
* Gets the file path selected by the user
*
* @return <p>The file path selected by the user</p>
*/
public String getPath() {
return this.directory.getText();
}
/**
* Gets the maximum RAM selected by the user
*
* @return <p>The maximum RAM selected by the user</p>
*/
public String getMaxRam() {
Object selected = this.maxRam.getSelectedItem();
if (selected != null) {
return selected.toString();
} else {
return "512M";
}
}
/**
* Gets the server type selected by the user
*
* @return <p>The server type selected by the user</p>
*/
public String getType() {
Object selected = this.serverTypes.getSelectedItem();
if (selected != null) {
return selected.toString();
} else {
return "Vanilla";
}
}
/**
* Gets the server version selected by the user
*
* @return <p>The server version selected by the user</p>
*/
public String getVersion() {
Object selected = this.serverVersions.getSelectedItem();
if (selected != null) {
return selected.toString();
} else {
return "Latest";
}
}
/**
* Checks whether this server is enabled
*
* @return <p>True if this server is enabled</p>
*/
public boolean isEnabled() {
return this.enabledCheckbox.isSelected();
}
@Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == removeServerButton) {
remove();
} else if (e.getSource() == browseButton) {
browse();
} else if (e.getSource() == serverTypes) {
try {
updateServerVersion();
} catch (ConfigurationException e1) {
e1.printStackTrace();
}
}
}
/**
* Removes the collection containing this ServerTab, and updates everything necessary
*/
private void remove() {
Main.getController().getCurrentProfile().removeCollection(this.name);
Main.getController().getGUI().updateWithSavedProfileData();
Main.getController().getCurrentProfile().updateConsoles();
}
/**
* Asks the user for server location and updates the GUI if given a valid value
*/
private void browse() {
File chosenFolder = Main.getController().getGUI().askForDirectory("Server folder");
if (chosenFolder != null) {
directory.setText(chosenFolder.toString());
}
}
/**
* Updates server version choices according to the server type
*/
private void updateServerVersion() throws ConfigurationException {
serverVersions.removeAllItems();
String selectedServerTypes = null;
Object selectedType = serverTypes.getSelectedItem();
if (selectedType != null) {
selectedServerTypes = selectedType.toString();
}
if (selectedServerTypes == null) {
return;
}
if (selectedServerTypes.equals("Custom")) {
serverVersions.setEditable(true);
} else {
serverVersions.setEditable(false);
ServerType current = null;
for (ServerType servertype : ServerTypeHandler.getServerTypes()) {
if (servertype.getName().equals(selectedServerTypes)) {
current = servertype;
}
}
if (current != null) {
for (String version : current.getVersions()) {
serverVersions.addItem(version);
}
}
}
}
}

View File

@ -0,0 +1,132 @@
package net.knarcraft.minecraftserverlauncher.userinterface;
import net.knarcraft.minecraftserverlauncher.Main;
import net.knarcraft.minecraftserverlauncher.profile.Controller;
import net.knarcraft.minecraftserverlauncher.utility.CommonFunctions;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.io.IOException;
import java.io.InputStream;
import static java.awt.Frame.NORMAL;
/**
* This class adds a tray to the GUI
*/
public class Tray {
private SystemTray tray;
private TrayIcon trayIcon;
private Controller controller;
private ServerLauncherGUI serverLauncherGUI;
private JFrame mainFrame;
/**
* Instantiates a new tray
*
* @param mainFrame <p>The main frame of the GUI</p>
* @param serverLauncherGUI <p>The server launcher GUI to use</p>
*/
public Tray(JFrame mainFrame, ServerLauncherGUI serverLauncherGUI) {
this.controller = Main.getController();
this.mainFrame = mainFrame;
this.serverLauncherGUI = serverLauncherGUI;
initializeTray();
}
/**
* Prepares the system tray if available
*/
private void initializeTray() {
if (SystemTray.isSupported()) {
tray = SystemTray.getSystemTray();
InputStream imageStream = CommonFunctions.getResourceAsStream("GUIIcon.png");
Image trayImage;
try {
trayImage = ImageIO.read(imageStream);
} catch (IOException e) {
trayImage = Toolkit.getDefaultToolkit().getImage("resources/GUIIcon.png");
e.printStackTrace();
}
PopupMenu popup = new PopupMenu();
trayIcon = new TrayIcon(trayImage, "Minecraft Server Launcher", popup);
trayIcon.setImageAutoSize(true);
ActionListener exitListener = e -> {
serverLauncherGUI.stopServers();
controller.saveState();
System.exit(0);
};
MenuItem restoreItem = new MenuItem("Restore");
popup.add(restoreItem);
restoreItem.addActionListener(e -> {
mainFrame.setExtendedState(NORMAL);
tray.remove(trayIcon);
mainFrame.setVisible(true);
});
MenuItem exitItem = new MenuItem("Exit");
exitItem.addActionListener(exitListener);
popup.add(exitItem);
mainFrame.addWindowStateListener(e -> {
if (e.getNewState() == NORMAL) {
tray.remove(trayIcon);
mainFrame.setVisible(true);
}
});
mainFrame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
if (controller.getRunInBackground()) {
try {
tray.add(trayIcon);
mainFrame.setVisible(false);
} catch (AWTException e1) {
e1.printStackTrace();
}
} else {
serverLauncherGUI.stopServers();
controller.saveState();
System.exit(0);
}
}
});
trayIcon.addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
if (e.getClickCount() >= 1 && e.getButton() == MouseEvent.BUTTON1) {
mainFrame.setExtendedState(NORMAL);
tray.remove(trayIcon);
mainFrame.setVisible(true);
}
}
});
} else {
mainFrame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
controller.saveState();
serverLauncherGUI.stopServers();
System.exit(0);
}
});
}
}
/**
* Hides the gui to the tray
*/
public void hideToTray() {
mainFrame.setVisible(false);
try {
tray.add(trayIcon);
} catch (AWTException e) {
e.printStackTrace();
}
}
}

View File

@ -0,0 +1,55 @@
package net.knarcraft.minecraftserverlauncher.userinterface;
import javax.swing.*;
import javax.swing.event.HyperlinkEvent;
import java.io.IOException;
/**
* This class allows for displaying web pages
*/
public class WebBrowser {
private static JFrame browserFrame;
private static JTextPane editorPane;
private WebBrowser() {}
/**
* Instantiates a new web browser
*/
private static void instantiate() {
editorPane = new JTextPane();
editorPane.setEditable(false);
browserFrame = new JFrame("Web Browser");
JScrollPane scrollPane = new JScrollPane(editorPane);
browserFrame.add(scrollPane);
browserFrame.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
browserFrame.setBounds(100, 100, 800, 600);
}
/**
* Displays the web page with the given URL
*
* @param url <p>The URL to open</p>
*/
public static void displayPage(String url) {
if (browserFrame == null) {
instantiate();
}
browserFrame.setVisible(true);
try {
browserFrame.setTitle("Browsing: " + url);
editorPane.setPage(url);
editorPane.setContentType("text/html");
editorPane.addHyperlinkListener(hyperlinkEvent -> {
if (hyperlinkEvent.getEventType().equals(HyperlinkEvent.EventType.ACTIVATED)) {
displayPage(hyperlinkEvent.getURL().toString()); }
});
} catch (IOException e) {
editorPane.setContentType("text/html");
editorPane.setText("<html>Could not load</html>");
}
}
}

View File

@ -0,0 +1,245 @@
package net.knarcraft.minecraftserverlauncher.utility;
import net.knarcraft.minecraftserverlauncher.Main;
import net.knarcraft.minecraftserverlauncher.profile.Collection;
import net.knarcraft.minecraftserverlauncher.server.Server;
import net.knarcraft.minecraftserverlauncher.userinterface.GUI;
import net.knarcraft.minecraftserverlauncher.userinterface.WebBrowser;
import java.io.*;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.Scanner;
/**
* A holding class for methods shared between classes.
*
* @author Kristian Knarvik <kristian.knarvik@knett.no>
* @version 1.0.0
* @since 1.0.0
*/
public final class CommonFunctions {
private static final String filesDirectory = Main.getApplicationWorkDirectory() + File.separator + "files";
/**
* Creates all folders necessary for tests and normal operation
*
* @throws FileNotFoundException <p>If unable to create a folder</p>
*/
public static void createAllFolders() throws FileNotFoundException {
createFolder(new File(filesDirectory));
createFolder(new File(filesDirectory + File.separator + "Jars"));
createFolder(new File(filesDirectory + File.separator + "testjars"));
}
/**
* Creates a given folder
*
* @param folder <p>The folder to create</p>
* @throws FileNotFoundException <p>If unable to create the folder</p>
*/
private static void createFolder(File folder) throws FileNotFoundException {
if (!folder.exists()) {
if (!folder.mkdirs()) {
throw new FileNotFoundException("Cannot create necessary directory.");
}
}
}
/**
* Gets a resource as an InputStream
*
* @param resourceName <p>The name of the resource you want to readFromServer</p>
* @return <p>An input stream which can be used to access the resource</p>
*/
public static InputStream getResourceAsStream(String resourceName) {
ClassLoader classloader = Thread.currentThread().getContextClassLoader();
return classloader.getResourceAsStream(resourceName);
}
/**
* Gets a resource as a Scanner
*
* @param resourceName <p>The name of the resource you want to readFromServer</p>
* @return <p>A scanner which can be used to readFromServer contents of the resource</p>
* @throws FileNotFoundException <p>If the resource is not found</p>
*/
public static Scanner getResourceAsScanner(String resourceName) throws FileNotFoundException {
InputStream is = getResourceAsStream(resourceName);
if (is == null) {
throw new FileNotFoundException("The resource was not found.");
}
return new Scanner(is);
}
/**
* Finds a substring between two substrings in a string
*
* @param string <p>The string containing the substrings</p>
* @param start <p>The substring before the wanted substring</p>
* @param end <p>The substring after the wanted substring</p>
* @return <p>The wanted substring</p>
*/
public static String stringBetween(String string, String start, String end) {
int startPos = string.indexOf(start) + start.length();
if (!string.contains(start) || string.indexOf(end, startPos) < startPos) {
return "";
}
return string.substring(startPos, string.indexOf(end, startPos));
}
/**
* Reads a file from a website
*
* <p>This is used to find the newest version of jars and the software.</p>
*
* @param path <p>The full url of the file to readFromServer</p>
* @return <p>True if successful. False otherwise</p>
*/
public static String readFile(String path) throws IOException {
URL url = new URL(path);
return new Scanner(url.openStream()).useDelimiter("\\Z").next();
}
/**
* Downloads a file from a website and replaces the target file
*
* @param path <p>The full url of the file to download</p>
* @param outfile <p>The file to save to</p>
* @return <p>True if successful. False otherwise</p>
*/
public static boolean downloadFile(String path, Path outfile) {
try {
URL url = new URL(path);
InputStream in = url.openStream();
Files.copy(in, outfile, StandardCopyOption.REPLACE_EXISTING);
return true;
} catch (IOException e) {
return false;
}
}
/**
* Recursively copies a folder to another location
*
* @param source <p>The folder to copy</p>
* @param destination <p>Target destination</p>
* @throws IOException <p>If we can't start a file stream</p>
*/
private static void copyFolder(GUI serverLauncherGui, File source, File destination) throws IOException {
if (!source.isDirectory()) {
copyFile(serverLauncherGui, source, destination);
} else {
serverLauncherGui.setStatus("Copying directory " + source);
if (!destination.exists() && !destination.mkdir()) {
return;
}
String[] files = source.list();
if (files != null) {
for (String file : files) {
File srcFile = new File(source, file);
File destinationFile = new File(destination, file);
copyFolder(serverLauncherGui, srcFile, destinationFile);
}
}
serverLauncherGui.setStatus("Copied directory " + source);
}
}
/**
* Copies a file from one location to another
*
* @param serverLauncherGui <p>The serverLauncherGui to use for alerting the user</p>
* @param source <p>The file to copy</p>
* @param destination <p>The location of the copied file</p>
* @throws IOException <p>If reading or writing fails</p>
*/
private static void copyFile(GUI serverLauncherGui, File source, File destination) throws IOException {
serverLauncherGui.setStatus("Copying file " + source + "...");
InputStream in = new FileInputStream(source);
OutputStream out = new FileOutputStream(destination);
byte[] buffer = new byte[1024];
int length;
while ((length = in.read(buffer)) > 0) {
out.write(buffer, 0, length);
}
in.close();
out.close();
serverLauncherGui.setStatus("Copied file " + source);
}
/**
* Opens an url in the user's default application.
*
* @param url <p>The URL to open</p>
*/
public static void goToURL(String url) {
java.awt.Desktop desktop = java.awt.Desktop.getDesktop();
try {
desktop.browse(new URI(url));
} catch (URISyntaxException | IOException | UnsupportedOperationException e1) {
WebBrowser.displayPage(url);
}
}
/**
* Reads all lines from a buffered reader
*
* @param reader <p>The buffered reader to read from</p>
* @return <p>All lines currently readable from the reader, split by the \n character</p>
* @throws IOException <p>If unable to read from the buffered reader</p>
*/
public static String readBufferedReader(BufferedReader reader) throws IOException {
String line;
StringBuilder text = new StringBuilder();
while (reader.ready() && (line = reader.readLine()) != null) {
text.append(line).append("\n");
}
reader.close();
return text.toString().trim();
}
/**
* Copies all server directories to a folder specified by the user.
*/
public static void backup(GUI gui) {
File path = gui.askForDirectory("Backup folder");
for (Collection collection : Main.getController().getCurrentProfile().getCollections()) {
//Ignore disabled and invalid servers
if (collection.getServer().getPath().equals("") || !collection.getServer().isEnabled()) {
continue;
}
//Decide sub-folders
Server targetServer = collection.getServer();
String name = targetServer.getName();
File srcFolder = new File(targetServer.getPath());
File destinationFolder = new File(path, name);
//Create child folders
if (!destinationFolder.exists() && !destinationFolder.mkdirs()) {
throw new IllegalArgumentException("Unable to create necessary sub-folder in the backup folder");
}
//Backup
try {
CommonFunctions.copyFolder(gui, srcFolder, destinationFolder);
} catch (IOException e) {
e.printStackTrace();
}
}
gui.setStatus("Backup finished");
}
/**
* Validates that a name is not empty and does not contain invalid characters
*
* @param name <p>The name to check</p>
* @return <p>True if the name is valid</p>
*/
public static boolean nameIsValid(String name) {
return !name.equals("") && name.matches("[^!?;,]+");
}
}

View File

@ -0,0 +1,71 @@
package net.knarcraft.minecraftserverlauncher.utility;
import net.knarcraft.minecraftserverlauncher.server.ServerTypeHandler;
import net.knarcraft.minecraftserverlauncher.server.servertypes.ServerType;
import net.knarcraft.minecraftserverlauncher.userinterface.GUI;
import javax.naming.ConfigurationException;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
/**
* This class handles all downloading of .jar files
*/
public final class JarDownloader {
private final String jarDirectory;
private final GUI gui;
/**
* Initializes a jar downloader
*
* @param gui <p>The GUI to use for displaying messages</p>
* @param jarDirectory <p>The directory to download jar files to</p>
*/
public JarDownloader(GUI gui, String jarDirectory) {
this.gui = gui;
this.jarDirectory = jarDirectory;
}
/**
* Downloads all jars to the program directory.
*
* @throws IOException On version file failure or folder creation failure
*/
public void downloadJars() throws IOException {
if (!new File(jarDirectory).exists() && !new File(jarDirectory).mkdirs()) {
gui.showError("Could not create the Jars folder. Please run the program with admin permissions, or move it to " +
"a writable directory.");
throw new FileNotFoundException("Unable to create jars folder");
}
try {
gui.setStatus("Downloading all jars...");
downloadAll();
gui.setStatus("Finished downloading jars");
} catch (FileNotFoundException | ConfigurationException e) {
gui.setStatus(e.getMessage());
throw new FileNotFoundException("One or more downloads failed: " + e.getMessage());
}
}
/**
* Downloads jar files for all possible server versions
*
* @throws IOException <p>If a jar fails to download</p>
*/
private void downloadAll() throws IOException, ConfigurationException {
for (ServerType type : ServerTypeHandler.getServerTypes()) {
if (type.getName().equals("Custom")) {
continue;
}
for (String version : type.getVersions()) {
gui.setStatus("Downloading " + type.getName() + version + "...");
boolean success = type.downloadJar(jarDirectory, version);
if (!success) {
throw new FileNotFoundException("Unable to download the jar file for " + type.getName() + version);
}
}
}
}
}

View File

@ -0,0 +1,92 @@
package net.knarcraft.minecraftserverlauncher.utility;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import net.knarcraft.minecraftserverlauncher.Main;
import net.knarcraft.minecraftserverlauncher.userinterface.GUI;
import javax.swing.*;
import java.io.*;
import java.nio.file.Paths;
import java.util.Scanner;
import static net.knarcraft.minecraftserverlauncher.utility.CommonFunctions.downloadFile;
import static net.knarcraft.minecraftserverlauncher.utility.CommonFunctions.getResourceAsScanner;
import static net.knarcraft.minecraftserverlauncher.utility.CommonFunctions.readFile;
/**
* A utility used for updating the software
*/
public final class Updater {
private static final String updaterFile = Main.getApplicationWorkDirectory() + File.separator + "Updater.jar";
private static final String updaterURL = "https://jenkins.knarcraft.net/job/KnarCraft/job/Jar-Updater/job/master/" +
"lastSuccessfulBuild/artifact/target/jar-updater-1.0-SNAPSHOT.jar";
private static final String targetFile = "minecraft-server-launcher.jar";
/**
* Checks if a newer version is available
*
* @param updateURL <p>The URL used for checking for updates</p>
* @param updateChannel <p>The release channel to use</p>
* @throws IOException <p>If the update data cannot be read</p>
*/
public static void checkForUpdate(String updateURL, String updateChannel) throws IOException {
Scanner file = getResourceAsScanner("currentversion.csv");
if (!file.hasNextLine()) {
throw new FileNotFoundException("File currentversion.csv is invalid");
}
String oldType = file.nextLine();
if (!file.hasNextLine()) {
throw new FileNotFoundException("File currentversion.csv is invalid");
}
String oldVer = file.nextLine();
file.close();
String data = readFile(updateURL);
JsonObject jsonObject = new JsonParser().parse(data).getAsJsonObject();
String latest = jsonObject.getAsJsonObject("latest").get(updateChannel).getAsString();
if (!oldType.equals(updateChannel) || !oldVer.equals(latest)) {
JsonArray versionList = jsonObject.getAsJsonArray("versions");
String url = "";
for (JsonElement elem : versionList) {
JsonObject obj = elem.getAsJsonObject();
String ver = obj.get("id").getAsString();
String type = obj.get("type").getAsString();
if (ver.equals(latest) && type.equals(updateChannel)) {
url = obj.get("url").getAsString();
break;
}
}
if (!new File(updaterFile).exists()) {
downloadFile(updaterURL, Paths.get(updaterFile));
}
int answer = JOptionPane.showConfirmDialog(null,
"An update is available. Do you want to update?", "Update available",
JOptionPane.YES_NO_OPTION
);
if (answer == JOptionPane.YES_NO_OPTION) {
runUpdater(url);
}
}
}
/**
* Updates the software
*
* @param url <p>The URL of the new file to download</p>
* @throws IOException <p>If unable to run the updater</p>
*/
private static void runUpdater(String url) throws IOException {
ProcessBuilder builder;
builder = new ProcessBuilder("java", "-jar", "Updater.jar", url, "yes", targetFile, "5", "nogui");
builder.directory(new File(Main.getApplicationWorkDirectory()));
builder.redirectErrorStream(true);
builder.start();
System.exit(1);
}
}

View File

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@ -0,0 +1,2 @@
beta
1.2.1
1 beta
2 1.2.1

View File

@ -0,0 +1,8 @@
setupText=1. Create a new profile, or use the default._BREAK_2. Click on the add server button and insert the name of your server._BREAK_3. Navigate to the servers tab, and find your server._BREAK_4. Fill in all the settings with what you want, and remember to check Enabled._BREAK_5. Navigate back to the Control panel and click Start servers
runInBackgroundText=The program will run in the background. You don't need to have the gui open all the time._BREAK_When in background mode, you can left click on the tray icon to open the gui again. To exit the program, right click the tray icon and press exit.
delayStartupText=This adds a delay (in seconds) between the start of each server._BREAK_This option should be used for heavy servers with many plugins, but may not be needed on a single server or servers using a few plugins.
downloadJarsText=This option will download all the .jar files available in the program._BREAK_Instead of downloading .jar files when you start servers, it will download all files you don't already have, on startup._BREAK_This will be faster and more reliable than usual._BREAK_You need to restart the software for this setting to take action.
aboutText=This software was created to start and manage several servers simultaneously._BREAK_You no longer have to do the tedious work of manually downloading different .jar files every time you want to try something new.
infoURL=https://archive.knarcraft.net/Scripts/BungeeMinecraftServerLauncherInfo/
manualUpdateURL=https://git.knarcraft.net/KnarCraft/Minecraft-Server-Launcher/releases
storyURL=https://archive.knarcraft.net/Scripts/BungeeMinecraftServerLauncherStory/
1 setupText=1. Create a new profile, or use the default._BREAK_2. Click on the add server button and insert the name of your server._BREAK_3. Navigate to the servers tab, and find your server._BREAK_4. Fill in all the settings with what you want, and remember to check Enabled._BREAK_5. Navigate back to the Control panel and click Start servers
2 runInBackgroundText=The program will run in the background. You don't need to have the gui open all the time._BREAK_When in background mode, you can left click on the tray icon to open the gui again. To exit the program, right click the tray icon and press exit.
3 delayStartupText=This adds a delay (in seconds) between the start of each server._BREAK_This option should be used for heavy servers with many plugins, but may not be needed on a single server or servers using a few plugins.
4 downloadJarsText=This option will download all the .jar files available in the program._BREAK_Instead of downloading .jar files when you start servers, it will download all files you don't already have, on startup._BREAK_This will be faster and more reliable than usual._BREAK_You need to restart the software for this setting to take action.
5 aboutText=This software was created to start and manage several servers simultaneously._BREAK_You no longer have to do the tedious work of manually downloading different .jar files every time you want to try something new.
6 infoURL=https://archive.knarcraft.net/Scripts/BungeeMinecraftServerLauncherInfo/
7 manualUpdateURL=https://git.knarcraft.net/KnarCraft/Minecraft-Server-Launcher/releases
8 storyURL=https://archive.knarcraft.net/Scripts/BungeeMinecraftServerLauncherStory/

View File

@ -0,0 +1,11 @@
Vanilla;Latest,Snapshot,1.16.3,1.15.2,1.14.4,1.13.2,1.12.2,1.11.2,1.10.2,1.9.4,1.8.9,1.7.10,1.6.4,1.5.2,1.4.7,1.3.2,1.2.5;https://launchermeta.mojang.com/mc/game/version_manifest.json;"release":";";https://s3.amazonaws.com/Minecraft.Download/versions/
Spigot;1.16.3,1.16.1,1.15.2,1.14.4,1.13.2,1.12.2,1.11.2,1.10.2,1.9.4,1.9.4,1.8.8,1.7.10,1.6.4-R2.1,1.5.2-R1.1,1.4.7-R1.1;https://static.knarcraft.net/archive/downloads/minecraftserverjars/Spigot/;spigot-
Paper;1.16.3,1.15.2,1.14.4,1.13.2,1.12.2,1.11.2,1.10.2,1.9.4,1.8.8;https://static.knarcraft.net/archive/downloads/minecraftserverjars/Paper/;Paper-
SpongeVanilla;1.12.2,1.11.2,1.10.2,1.8.9;https://dl-api.spongepowered.org/v1/org.spongepowered/spongevanilla/downloads?type=stable&minecraft=;"version":";",;https://repo.spongepowered.org/maven/org/spongepowered/spongevanilla/;/spongevanilla-
Craftbukkit;1.16.3,1.15.2,1.14.4,1.13.2,1.12.2,1.11.2,1.10.2,1.9.4,1.8.8,1.7.10-R0.1,1.6.4-R2.0,1.5.2-R1.0,1.4.6-R0.3,1.3.2-R3.0,1.2.5-R2.0,1.1-R6,1.0.1-R1,b1.8.1,b1.7.3;https://static.knarcraft.net/archive/downloads/minecraftserverjars/Bukkit/;craftbukkit-
SpongeForge;1.12.2,1.11.2,1.10.2;https://dl-api.spongepowered.org/v1/org.spongepowered/spongeforge/downloads?type=stable&minecraft=;"version":";",;https://repo.spongepowered.org/maven/org/spongepowered/spongeforge/;/spongeforge-
MCPCplus;1.6.4,1.6.2,1.5.2,1.4.7;https://static.knarcraft.net/archive/downloads/minecraftserverjars/MCPC+/;mcpcplus
Bungee;Latest,1.7.10,1.6.4,1.5.2,1.4.7;https://ci.md-5.net/job/BungeeCord/lastSuccessfulBuild/artifact/bootstrap/target/;Artifacts of BungeeCord #; ;http://ci.md-5.net/job/BungeeCord/lastSuccessfulBuild/artifact/bootstrap/target/BungeeCord.jar;https://static.knarcraft.net/archive/downloads/minecraftserverjars/BungeeCord/;BungeeCord-
Waterfall;1.16,1.15,1.14,1.13,1.12,1.11;https://papermc.io/api/v1/waterfall/;"latest":";";https://papermc.io/api/v1/waterfall/
Travertine;1.16,1.15,1.14,1.13,1.12;https://papermc.io/api/v1/travertine/;"latest":";";https://papermc.io/api/v1/travertine/
Custom;
Can't render this file because it contains an unexpected character in line 1 and column 193.

View File

@ -1,168 +0,0 @@
package net.knarcraft.serverlauncher;
import net.knarcraft.serverlauncher.profile.Collection;
import net.knarcraft.serverlauncher.profile.Profile;
import net.knarcraft.serverlauncher.server.Server;
import net.knarcraft.serverlauncher.server.ServerType;
import net.knarcraft.serverlauncher.userinterface.ServerConsoles;
import javax.naming.ConfigurationException;
import java.awt.*;
import java.io.*;
import java.net.URISyntaxException;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import static net.knarcraft.serverlauncher.Shared.stringBetween;
//Java 8 required.
/**
* A software for managing Minecraft servers.
*
* @author Kristian Knarvik <kristian.knarvik@knett.no>
* @version 1.0.0
* @since 1.0.0
*/
public class Main {
@SuppressWarnings("CanBeFinal")
private static String appDir;
private static boolean running = false;
static {
try {
appDir = String.valueOf(new File(Main.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath()).getParentFile());
} catch (URISyntaxException e) {
e.printStackTrace();
System.exit(1);
}
try (PrintWriter file = new PrintWriter(Main.getAppDir() + File.separator + "latestrun.log")) {
file.print("");
} catch (IOException e ) {
e.printStackTrace();
}
}
public static void main(String[] args) {
EventQueue.invokeLater(() -> {
try {
setup();
new ServerConsoles();
Profile.load();
ScheduledExecutorService exec = Executors.newSingleThreadScheduledExecutor();
exec.scheduleAtFixedRate(Main::updateConsoles, 10, 500, TimeUnit.MILLISECONDS);
} catch (Exception e) {
e.printStackTrace();
}
});
}
private static void setup() {
try {
ServerType.loadServerTypes();
} catch (ConfigurationException e) {
e.printStackTrace();
System.exit(1);
}
}
public static String getAppDir() {
return appDir;
}
/**
* Reads from server processes, and writes the output to consoles.
*/
private static void updateConsoles() {
try {
for (Collection collection : Profile.getCurrent().getCollections()) {
Server server = collection.getServer();
if (server.isEnabled() && server.getProcess() != null) {
try {
String readText = server.read();
if (!readText.equals("")) {
collection.getServerConsole().output(readText);
updatePlayerList(readText, server);
}
} catch (IOException e) {
e.printStackTrace();
}
if (!server.getProcess().isAlive()) {
server.stopped();
}
}
}
boolean runningNew = serversRunning();
if (!runningNew && running) {
Profile.getGUI().updateRunning(false);
Profile.getGUI().setStatus("Servers are stopped");
} else if (runningNew && !running) {
Profile.getGUI().updateRunning(true);
}
running = runningNew;
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Goes through all servers and looks for any running servers.
*
* @return Is at least one server running?
*/
private static boolean serversRunning() {
int num = 0;
for (Collection collection : Profile.getCurrent().getCollections()) {
if (collection.getServer().isStarted() || (collection.getServer().getProcess() != null && collection.getServer().getProcess().isAlive())) {
num++;
}
}
return num > 0;
}
/**
* Looks for strings implying a player has joined or left, and updates the appropriate lists.
*
* @param text The text to search.
* @param server The server which sent the text.
*/
private static void updatePlayerList(String text, Server server) {
if (!server.getTypeName().equals("Bungee")) {
String joinedPlayer = getPlayer(text, true);
String leftPlayer = getPlayer(text, false);
if (!joinedPlayer.equals("")) {
if (!server.hasPlayer(joinedPlayer)) {
server.addPlayer(joinedPlayer);
}
} else if (!leftPlayer.equals("")) {
if (server.hasPlayer(leftPlayer)) {
server.removePlayer(leftPlayer);
}
}
}
}
/**
* Searches a string for players joining or leaving.
*
* @param text The string to search
* @param joined Are we searching for a joining player or not
* @return The name of a player or an empty string
*/
private static String getPlayer(String text, boolean joined) {
String playerName;
if (joined) {
playerName = stringBetween(text, "[Server thread/INFO]: ", " joined the game");
if (playerName.equals("")) {
playerName = stringBetween(text, "UUID of player ", " is ");
}
} else {
playerName = stringBetween(text, "INFO]: ", " lost connection");
if (playerName.equals("")) {
playerName = stringBetween(text, "[Server thread/INFO]: ", " left the game");
}
}
return playerName;
}
}

View File

@ -1,64 +0,0 @@
package net.knarcraft.serverlauncher;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.Scanner;
/**
* A holding class for methods shared between classes.
*
* @author Kristian Knarvik <kristian.knarvik@knett.no>
* @version 1.0.0
* @since 1.0.0
*/
public class Shared {
/**
* Finds a substring between two substrings in a string.
*
* @param string The string containing the substrings
* @param start The substring before the wanted substring
* @param end The substring after the wanted substring
* @return The wanted substring.
*/
public static String stringBetween(String string, String start, String end) {
int startPos = string.indexOf(start) + start.length();
if (!string.contains(start) || string.indexOf(end, startPos) < startPos) {
return "";
}
return string.substring(startPos, string.indexOf(end, startPos));
}
/**
* Reads a file from a website.
* This is used to find the newest version of jars and the software.
*
* @param path The full url of the file to read
* @return True if successful. False otherwise
*/
public static String readFile(String path) throws IOException {
URL url = new URL(path);
return new Scanner(url.openStream()).useDelimiter("\\Z").next();
}
/**
* Downloads a file from a website.
*
* @param path The full url of the file to download.
* @param outfile The file to save to
* @return True if successful. False otherwise
*/
public static boolean downloadFile(String path, Path outfile) {
try {
URL url = new URL(path);
InputStream in = url.openStream();
Files.copy(in, outfile, StandardCopyOption.REPLACE_EXISTING);
return true;
} catch (IOException e) {
return false;
}
}
}

View File

@ -1,72 +0,0 @@
package net.knarcraft.serverlauncher.profile;
import net.knarcraft.serverlauncher.server.Server;
import net.knarcraft.serverlauncher.userinterface.ServerConsoles;
import net.knarcraft.serverlauncher.userinterface.ServerTab;
import net.knarcraft.serverlauncher.userinterface.Console;
/**
* Acts as a wrapper for objects necessary for each server.
*
* @author Kristian Knarvik <kristian.knarvik@knett.no>
* @version 1.0.0
* @since 1.0.0
*/
public class Collection {
private final Server server;
private final ServerTab serverTab;
private final Console serverConsole;
private final String name;
Collection(String name) {
this.serverTab = new ServerTab(name);
this.server = new Server(name);
this.serverConsole = ServerConsoles.addTab(name);
this.name = name;
}
Collection(String name,
String path,
boolean enabled,
String typeName,
String serverVersion,
String maxRam,
String vanillaVersion,
String snapshotVersion,
String spongeVanillaVersion,
String bungeeVersion
) {
this.serverTab = new ServerTab(name);
this.server = new Server(
name,
path,
enabled,
typeName,
serverVersion,
maxRam,
vanillaVersion,
snapshotVersion,
spongeVanillaVersion,
bungeeVersion
);
this.serverConsole = ServerConsoles.addTab(name);
this.name = name;
this.serverTab.setData(path, enabled, typeName, serverVersion, maxRam);
}
public String getName() {
return this.name;
}
public Server getServer() {
return this.server;
}
public ServerTab getServerTab() {
return this.serverTab;
}
public Console getServerConsole() {
return this.serverConsole;
}
}

View File

@ -1,681 +0,0 @@
package net.knarcraft.serverlauncher.profile;
import net.knarcraft.serverlauncher.server.AdvancedServerType;
import net.knarcraft.serverlauncher.server.Server;
import net.knarcraft.serverlauncher.server.ServerType;
import net.knarcraft.serverlauncher.userinterface.GUI;
import net.knarcraft.serverlauncher.userinterface.ServerConsoles;
import net.knarcraft.serverlauncher.userinterface.ServerTab;
import net.knarcraft.serverlauncher.Main;
import javax.swing.*;
import java.io.*;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Objects;
import java.util.Scanner;
import java.util.concurrent.Executors;
import static net.knarcraft.serverlauncher.Shared.downloadFile;
import static net.knarcraft.serverlauncher.Shared.readFile;
import static net.knarcraft.serverlauncher.Shared.stringBetween;
/**
* Contains all user settings, and a list of servers.
*
* @author Kristian Knarvik <kristian.knarvik@knett.no>
* @version 1.0.0
* @since 1.0.0
*/
public class Profile {
private static final ArrayList<Profile> profiles = new ArrayList<>();
private static Profile current;
private static GUI gui;
private static final String profilesDir = Main.getAppDir() + File.separator + "files";
private static final String profilesFile = Main.getAppDir() + File.separator + "files" + File.separator + "Profiles.txt";
private static final String jarDir = Main.getAppDir() + File.separator + "files" + File.separator + "Jars" + File.separator;
private final ArrayList<Collection> collections;
private final String name;
private boolean runInBackground;
private int delayStartup;
private boolean downloadJars;
private static String vanillaVersion;
private static String snapshotVersion;
private static String bungeeVersion;
private Profile(String name) {
this.collections = new ArrayList<>();
this.name = name;
this.runInBackground = false;
this.delayStartup = 0;
this.downloadJars = false;
profiles.add(this);
if (current == null) {
current = this;
}
}
private Profile(String name, boolean runInBackground, int delayStartup, boolean downloadJars) {
this.collections = new ArrayList<>();
this.name = name;
this.runInBackground = runInBackground;
this.delayStartup = delayStartup;
this.downloadJars = downloadJars;
profiles.add(this);
if (current == null) {
current = this;
}
}
public static GUI getGUI() {
return gui;
}
public boolean getRunInBackground() {
return this.runInBackground;
}
public int getDelayStartup() {
return this.delayStartup;
}
public boolean getDownloadJars() {
return this.downloadJars;
}
public static Profile getCurrent() {
return current;
}
public ArrayList<Collection> getCollections() {
return this.collections;
}
public String getName() {
return this.name;
}
/**
* Gets a Collection object by name.
*
* @param name The name of the collection.
* @return A collection object.
*/
public Collection getCollection(String name) {
for (Collection collection : this.collections) {
if (collection.getName().equals(name)) {
return collection;
}
}
return null;
}
public static Profile getProfile(String name) {
for (Profile profile : profiles) {
if (profile.name.equals(name)) {
return profile;
}
}
return null;
}
public static ArrayList<Profile> getProfiles() {
return profiles;
}
public void setRunInBackground(boolean value) {
this.runInBackground = value;
}
public void setDelayStartup(int value) {
if (value >= 0) {
this.delayStartup = value;
}
}
public void setDownloadJars(boolean value) {
this.downloadJars = value;
}
/**
* Set the current profile to the profile with a certain name.
*
* @param name The name of the profile
*/
public static void setCurrent(String name) {
for (Profile profile : profiles) {
if (profile.name.equals(name)) {
current = profile;
break;
}
}
}
/**
* Adds a collection to the profile if the name is valid.
*
* @param name The name of the collection and its elements.
*/
public void addCollection(String name) {
if (name == null) { //If a user cancels or crosses out window
return;
}
if (getCollection(name) == null &&
!name.equals("") &&
!name.equals("All") &&
!name.contains("!") &&
!name.contains("?") &&
!name.contains(";")
) {
collections.add(new Collection(name));
} else {
JOptionPane.showMessageDialog(
null,
"A server name must my unique and not empty or \"All\"." +
"It can't contain any of the characters \"!\", \"?\" or \";\".",
"Error",
JOptionPane.ERROR_MESSAGE
);
}
}
/**
* Adds a profile if the name is valid and unique.
*
* @param name The name of the new profile.
*/
public static void addProfile(String name) {
if (name == null) { //If a user cancels or crosses out window
return;
}
if (name.equals("") && !name.contains("!") && !name.contains("?") && !name.contains(";")) {
JOptionPane.showMessageDialog(
null,
"Profile name can't be blank.",
"Error",
JOptionPane.ERROR_MESSAGE
);
return;
}
for (Profile profile : profiles) {
if (profile.name.equals(name)) {
JOptionPane.showMessageDialog(
null,
"There is already a profile with this name.",
"Error",
JOptionPane.ERROR_MESSAGE
);
return;
}
}
new Profile(name);
}
public void removeCollection(String name) {
for (int i = 0; i < collections.size(); i++) {
if (collections.get(i).getName().equals(name)) {
this.collections.remove(i);
gui.update();
break;
}
}
}
public static void removeProfile(String name) {
if (profiles.size() > 1) {
for (int i = 0; i < profiles.size(); i++) {
if (profiles.get(i).name.equals((name))) {
profiles.remove(i);
}
}
}
}
public void updateConsoles() {
JTabbedPane consolesTab = ServerConsoles.getTab();
consolesTab.removeAll();
for (Collection collection : collections) {
consolesTab.add(collection.getName(), collection.getServerConsole().getPanel());
}
}
/**
* Sends a command to a server, or all servers
*
* @param serverName The target server
* @param command The command to send.
*/
public void sendCommand(String serverName, String command) {
if (serverName.equals("All")) {
for (Collection collection : this.collections) {
try {
collection.getServer().sendCommand(command);
} catch (IOException e) {
JOptionPane.showMessageDialog(
null,
"Server " + collection.getName() + " caused an exception.",
"Error",
JOptionPane.ERROR_MESSAGE
);
}
}
} else {
Collection collection = getCollection(serverName);
if (collection != null) {
Server target = collection.getServer();
try {
target.sendCommand(command);
} catch (IOException e) {
JOptionPane.showMessageDialog(
null,
"Server " + target.getName() + " caused an exception.",
"Error",
JOptionPane.ERROR_MESSAGE
);
}
} else {
JOptionPane.showMessageDialog(
null,
"Server " + serverName + " is invalid.",
"Error", JOptionPane.ERROR_MESSAGE
);
}
}
}
/**
* Reads all server tabs, and saves it to the variables of the corresponding servers.
* Saves all profiles and servers to a text file.
*/
public void save() throws FileNotFoundException {
for (Collection collection : this.collections) {
Server server = collection.getServer();
ServerTab serverTab = collection.getServerTab();
server.setPath(serverTab.getPath());
server.setMaxRam(serverTab.getMaxRam());
server.setType(ServerType.getByName(serverTab.getType()));
try {
server.setServerVersion(serverTab.getVersion());
} catch (IllegalArgumentException e) {
JOptionPane.showMessageDialog(
null,
"Invalid server version for " + server.getName(),
"Error",
JOptionPane.ERROR_MESSAGE
);
}
server.toggle(serverTab.enabled());
}
if (!new File(profilesDir).exists() && !new File(profilesDir).mkdirs()) {
JOptionPane.showMessageDialog(
null,
"Unable to create the folder " + profilesDir,
"Error",
JOptionPane.ERROR_MESSAGE
);
throw new FileNotFoundException("Unable to create the profiles folder: " + profilesDir);
}
try (PrintWriter file = new PrintWriter(profilesFile)) {
file.println(String.format(
"%s;%s;%s;%s;%d;%d",
current.name,
vanillaVersion,
snapshotVersion,
bungeeVersion,
gui.getSize().width,
gui.getSize().height
));
file.close();
for (Profile profile : profiles) {
StringBuilder saveString = new StringBuilder(String.format(
"%s;%b;%d;%b?",
profile.name,
profile.runInBackground,
profile.delayStartup,
profile.downloadJars)
);
for (Collection collection : profile.collections) {
Server server = collection.getServer();
saveString.append(String.format(
"%s;%s;%b;%s;%s;%s;%s;%s;%s;%s!",
server.getName(),
server.getPath(),
server.isEnabled(),
server.getTypeName(),
server.getServerVersion(),
server.getMaxRam(),
server.getVanillaVersion(),
server.getSnapshotVersion(),
server.getSpongeVanillaVersion(),
server.getBungeeVersion()
)
);
}
saveString = new StringBuilder(saveString.substring(0, saveString.length() - 1));
try (PrintWriter fileAppend = new PrintWriter(new FileWriter(
profilesFile,
true
))) {
fileAppend.println(saveString);
} catch (IOException e) {
if (gui != null) {
JOptionPane.showMessageDialog(
null,
"Unable to save to file. Try running the software as an administrator.",
"Error",
JOptionPane.ERROR_MESSAGE
);
throw new FileNotFoundException("Unable to save to the profiles file.");
}
}
}
} catch (IOException e) {
if (gui != null) {
JOptionPane.showMessageDialog(
null,
"Unable to save to file. Try running the software as an administrator.",
"Error",
JOptionPane.ERROR_MESSAGE
);
}
throw new FileNotFoundException("Unable to create the profiles file");
}
}
/**
* Reads profiles and servers from a text file.
*/
public static void load() {
try (Scanner in = new Scanner(new File(profilesFile))) {
try {
String[] staticData = in.nextLine().split(";", -1);
String profileName = staticData[0];
vanillaVersion = staticData[1];
snapshotVersion = staticData[2];
bungeeVersion = staticData[3];
int guiWidth = Integer.parseInt(staticData[4]);
int guiHeight = Integer.parseInt(staticData[5]);
gui = new GUI(guiWidth, guiHeight);
while (in.hasNextLine()) {
String line = in.nextLine();
if (line.contains("?")) {
String[] data = line.split("\\?");
String[] profileData = data[0].split(";", -1);
Profile profile = parseProfile(profileData);
if (data[1].contains("!")) {
String[] servers = data[1].split("!", -1);
for (String server : servers) {
String[] serverData = server.split(";", -1);
parseServer(profile, serverData);
}
} else {
String[] serverData = data[1].split(";", -1);
parseServer(profile, serverData);
}
} else {
String[] profileData = line.split(";", -1);
parseProfile(profileData);
}
}
current = getProfile(profileName);
} catch (ArrayIndexOutOfBoundsException | NumberFormatException e) {
e.printStackTrace();
JOptionPane.showMessageDialog(
null,
"Invalid Profile.txt file. Profiles could not be loaded. If this error persists, please manually delete the file.",
"Error",
JOptionPane.ERROR_MESSAGE
);
System.exit(1);
}
if (profiles.size() == 0) {
addProfile("Default");
}
} catch (FileNotFoundException e) {
JOptionPane.showMessageDialog(
null,
"A profiles file was not found. Default profile was created.",
"Info",
JOptionPane.INFORMATION_MESSAGE
);
gui = new GUI();
addProfile("Default");
}
gui.update();
gui.updateProfiles();
current.updateConsoles();
if (current.downloadJars) {
Executors.newSingleThreadExecutor().execute(() -> {
try {
downloadJars();
} catch (IOException e) {
e.printStackTrace();
}
});
}
if (current.runInBackground) {
Executors.newSingleThreadExecutor().execute(Server::startServers);
gui.hide();
}
}
private static Profile parseProfile(String[] profileData) {
return new Profile(
profileData[0],
Boolean.parseBoolean(profileData[1]),
Integer.parseInt(profileData[2]),
Boolean.parseBoolean(profileData[3])
);
}
private static void parseServer(Profile profile, String[] serverData) {
profile.collections.add(new Collection(
serverData[0],
serverData[1],
Boolean.parseBoolean(serverData[2]),
serverData[3],
serverData[4],
serverData[5],
serverData[6],
serverData[7],
serverData[8],
serverData[9])
);
}
public static void downloadJars() throws IOException {
if (!new File(jarDir).exists() && !new File(jarDir).mkdirs()) {
JOptionPane.showMessageDialog(
null,
"Could not create the Jars folder. Please run the program with admin permissions, or move it to a writable directory.",
"Error",
JOptionPane.ERROR_MESSAGE
);
throw new FileNotFoundException("Unable to create jars folder");
}
downloadSimple("Spigot");
downloadSimple("Craftbukkit");
downloadSimple("MCPCplus");
downloadMixed("Vanilla");
downloadMixed("Snapshot");
downloadSponge();
downloadBungee();
gui.setStatus("Finished downloading jars");
}
private static void downloadSimple(String typeName) throws FileNotFoundException {
ServerType type = ServerType.getByName(typeName);
String url = Objects.requireNonNull(type).getDownloadURL();
String name = type.getName();
Boolean success;
for (String version : type.getVersions()) {
File file = new File(jarDir + type.getName() + version + ".jar");
if (!file.isFile()) {
Path filePath = Paths.get(jarDir + type.getName() + version + ".jar");
if (gui != null) {
gui.setStatus("Downloading: " + name + version + ".jar");
}
success = downloadFile(url + name + version + ".jar", filePath);
if (!success) {
if (gui != null) {
gui.setStatus("Error downloading: " + name + version + ".jar");
}
throw new FileNotFoundException("Error downloading: " + name + version + ".jar");
}
}
}
}
private static void downloadMixed(String typeName) throws IOException {
AdvancedServerType type = (AdvancedServerType) ServerType.getByName(typeName);
String url = Objects.requireNonNull(type).getDownloadURL();
String name = type.getName();
String versionText;
String newestVersion;
Boolean success;
for (String version : type.getVersions()) {
File file = new File(jarDir + type.getName() + version + ".jar");
Path filePath = Paths.get(jarDir + type.getName() + version + ".jar");
if (gui != null) {
gui.setStatus("Downloading: " + name + version + ".jar");
}
if (version.equals("Latest")) {
try {
versionText = readFile(type.getVersionURL());
} catch (IOException e) {
throw new IOException("Error reading: " + type.getVersionURL());
}
newestVersion = stringBetween(versionText, type.getSrcStart(), type.getSrcEnd());
if (!file.isFile() || !newestVersion.equals(getVersion(name))) {
success = downloadFile(
url + newestVersion + type.getDownloadURLPart() + newestVersion + ".jar",
filePath
);
setVersion(name, newestVersion);
if (!success) {
if (gui != null) {
gui.setStatus("Error downloading: " + name + version + ".jar");
}
throw new FileNotFoundException("Error downloading: " + name + version + ".jar");
}
}
} else {
if (!file.isFile()) {
success = downloadFile(url + version + type.getDownloadURLPart() + version + ".jar", filePath);
if (!success) {
if (gui != null) {
gui.setStatus("Error downloading: " + name + version + ".jar");
}
throw new FileNotFoundException("Error downloading: " + name + version + ".jar");
}
}
}
}
}
private static void downloadSponge() throws IOException {
AdvancedServerType type = (AdvancedServerType) ServerType.getByName("SpongeVanilla");
String url = Objects.requireNonNull(type).getDownloadURL();
String name = type.getName();
String versionText;
String newestVersion;
Boolean success;
for (String version : type.getVersions()) {
File file = new File(jarDir + name + version + ".jar");
Path filePath = Paths.get(jarDir + name + version + ".jar");
if (gui != null) {
gui.setStatus("Downloading: " + name + version + ".jar");
}
try {
versionText = readFile(type.getVersionURL() + version);
} catch (IOException e) {
throw new IOException("Error reading: " + type.getVersionURL());
}
newestVersion = stringBetween(versionText, type.getSrcStart(), type.getSrcEnd());
if (!file.isFile()) {
success = downloadFile(
url + newestVersion + type.getDownloadURLPart() + newestVersion + ".jar",
filePath
);
if (!success) {
if (gui != null) {
gui.setStatus("Error downloading: " + name + version + ".jar");
}
throw new FileNotFoundException("Error downloading: " + name + version + ".jar");
}
}
}
}
private static void downloadBungee() throws IOException {
AdvancedServerType type = (AdvancedServerType) ServerType.getByName("Bungee");
String url = Objects.requireNonNull(type).getDownloadURL();
String name = type.getName();
String versionText;
String newestVersion;
Boolean success;
File file = new File(jarDir + type.getName() + ".jar");
Path filePath = Paths.get(jarDir + type.getName() + ".jar");
if (gui != null) {
gui.setStatus("Downloading: " + name + ".jar");
}
try {
versionText = readFile(type.getVersionURL());
} catch (IOException e) {
throw new IOException("Error reading: " + type.getVersionURL());
}
newestVersion = stringBetween(versionText, type.getSrcStart(), type.getSrcEnd());
if (!file.isFile() || !newestVersion.equals(getVersion(name))) {
success = downloadFile(url, filePath);
setVersion(name, newestVersion);
if (!success) {
if (gui != null) {
gui.setStatus("Error downloading: " + name + ".jar");
}
throw new FileNotFoundException("Error downloading: " + name + ".jar");
}
}
}
/**
* Returns the current version of a type
*
* @param type The version type
* @return The version string
*/
private static String getVersion(String type) {
switch (type) {
case "Vanilla":
return vanillaVersion;
case "Snapshot":
return snapshotVersion;
case "Bungee":
return bungeeVersion;
default:
return "";
}
}
/**
* Sets a server type's last downloaded version.
*
* @param type The version type
* @param version The version string
*/
private static void setVersion(String type, String version) {
if (!type.equals("")) {
switch (type) {
case "Vanilla":
vanillaVersion = version;
break;
case "Snapshot":
snapshotVersion = version;
break;
case "Bungee":
bungeeVersion = version;
}
}
}
}

View File

@ -1,39 +0,0 @@
package net.knarcraft.serverlauncher.server;
/**
* A more advanced servertype for particularly tricky jar downloads.
*
* @author Kristian Knarvik <kristian.knarvik@knett.no>
* @version 1.0.0
* @since 1.0.0
*/
public class AdvancedServerType extends ServerType {
private final String versionURL;
private final String downloadURLPart;
private final String srcStart;
private final String srcEnd;
AdvancedServerType(String name, String[] versions, String versionURL, String srcStart, String srcEnd, String downloadURL, String downloadURLPart) {
super(name, versions, downloadURL);
this.srcStart = srcStart;
this.srcEnd = srcEnd;
this.versionURL = versionURL;
this.downloadURLPart = downloadURLPart;
}
public String getVersionURL() {
return this.versionURL;
}
public String getDownloadURLPart() {
return this.downloadURLPart;
}
public String getSrcStart() {
return this.srcStart;
}
public String getSrcEnd() {
return this.srcEnd;
}
}

View File

@ -1,499 +0,0 @@
package net.knarcraft.serverlauncher.server;
import net.knarcraft.serverlauncher.Main;
import net.knarcraft.serverlauncher.profile.Collection;
import net.knarcraft.serverlauncher.profile.Profile;
import java.io.*;
import java.nio.file.*;
import java.util.ArrayList;
import java.util.concurrent.TimeUnit;
import static net.knarcraft.serverlauncher.Shared.downloadFile;
import static net.knarcraft.serverlauncher.Shared.readFile;
import static net.knarcraft.serverlauncher.Shared.stringBetween;
/**
* Contains all necessary information to create, run and manage a Minecraft server.
*
* @author Kristian Knarvik <kristian.knarvik@knett.no>
* @version 1.0.0
* @since 1.0.0
*/
public class Server {
/** Available ram sizes. For GUI dropdown */
private static final String[] ramList = {
"512M", "1G", "2G", "3G", "4G", "5G", "6G", "7G", "8G", "9G", "10G","11G", "12G", "13G", "14G", "15G", "16G"
};
private static final String jarDir = Main.getAppDir() + File.separator + "files" + File.separator + "Jars" + File.separator;
private final String name;
private String path;
private boolean enabled;
private final ArrayList<String> playerList;
private ServerType type;
private String serverVersion;
private String maxRam;
private Process process;
private BufferedWriter writer;
private BufferedReader reader;
private String vanillaVersion;
private String snapshotVersion;
private String spongeVanillaVersion;
private String bungeeVersion;
private boolean started;
public Server(String name) {
this.name = name;
this.path = "";
this.enabled = false;
this.playerList = new ArrayList<>();
this.type = null;
this.serverVersion = "";
this.maxRam = ramList[0];
this.process = null;
this.writer = null;
this.reader = null;
this.vanillaVersion = "";
this.snapshotVersion = "";
this.spongeVanillaVersion = "";
this.bungeeVersion = "";
}
public Server(
String name,
String path,
boolean enabled,
String typeName,
String serverVersion,
String maxRam,
String vanillaVersion,
String snapshotVersion,
String spongeVanillaVersion,
String bungeeVersion
) {
this.name = name;
this.path = path;
this.enabled = enabled;
this.type = ServerType.getByName(typeName);
this.serverVersion = serverVersion;
this.maxRam = maxRam;
this.vanillaVersion = vanillaVersion;
this.snapshotVersion = snapshotVersion;
this.spongeVanillaVersion = spongeVanillaVersion;
this.bungeeVersion = bungeeVersion;
this.playerList = new ArrayList<>();
}
public String getName() {
return this.name;
}
public boolean isStarted() {
return started;
}
public String getTypeName() {
return this.type.getName();
}
public String getServerVersion() {
return this.serverVersion;
}
public String getPath() {
return this.path;
}
public Process getProcess() {
return this.process;
}
public String getMaxRam() {
return this.maxRam;
}
public static String[] getRamList() {
return ramList;
}
public String getVanillaVersion() {
return this.vanillaVersion;
}
public String getSnapshotVersion() {
return this.snapshotVersion;
}
public String getSpongeVanillaVersion() {
return this.spongeVanillaVersion;
}
public String getBungeeVersion() {
return this.bungeeVersion;
}
public ArrayList<String> getPlayers() {
return this.playerList;
}
public void stopped() {
process = null;
writer = null;
reader = null;
started = false;
}
/**
* @return A representation of the name of a jarfile.
*/
private String getType() {
if (this.type.getName().equals("Custom")) {
return this.serverVersion;
} else {
return this.type.getName() + this.serverVersion + ".jar";
}
}
public boolean isEnabled() {
return this.enabled;
}
public void toggle(boolean value) {
this.enabled = value;
}
public boolean hasPlayer(String name) {
for (String player : this.playerList) {
if (player.equals(name)) {
return true;
}
}
return false;
}
public void addPlayer(String name) {
this.playerList.add(name);
Profile.getGUI().addPlayer(name);
}
/**
* Removes a player with the selected name from the playerlist.
*
* @param name The name of the player to remove
*/
public void removePlayer(String name) {
for (int i = 0; i < playerList.size(); i++) {
if (name.equals(playerList.get(i))) {
playerList.remove(i);
}
}
Profile.getGUI().removePlayer(name);
}
public void setPath(String path) {
this.path = path;
}
public void setType(ServerType type) {
this.type = type;
}
public void setMaxRam(String ram) {
this.maxRam = ram;
}
/**
* Sets the server's server version to a valid version, or ignores the request.
*
* @param serverVersion Version number.
*/
public void setServerVersion(String serverVersion) throws IllegalArgumentException {
if (this.type.getName().equals("Custom")) {
this.serverVersion = serverVersion;
} else {
String[] versions = this.type.getVersions();
for (String version : versions) {
if (version.equals(serverVersion)) {
this.serverVersion = serverVersion;
return;
}
}
throw new IllegalArgumentException("Invalid server version.");
}
}
/**
* Tries to stop all enabled servers.
*
* @throws IOException If a writer's process is already closed but not null.
*/
public static void stop() throws IOException {
for (Collection collection : Profile.getCurrent().getCollections()) {
Server server = collection.getServer();
if (server.writer != null) {
if (server.type.getName().equals("Bungee")) {
server.writer.write("end\n");
} else {
server.writer.write("stop\n");
}
server.writer.flush();
server.writer = null;
server.started = false;
}
}
}
/**
* Runs all enabled servers with their settings.
*/
public static void startServers() {
Profile.getGUI().setStatus("Starting servers");
for (Collection collection : Profile.getCurrent().getCollections()) {
if (!collection.getServer().run()) {
Profile.getGUI().setStatus("An error occurred. Start aborted");
try {
Server.stop();
} catch (IOException e) {
e.printStackTrace();
}
Profile.getGUI().updateRunning(false);
return;
}
}
}
/**
* Runs the Minecraft server.
*/
private boolean run() {
if (this.enabled) {
this.started = true;
if (!Profile.getCurrent().getDownloadJars()) {
try {
Profile.getGUI().setStatus("Downloading jar...");
this.downloadJar();
Profile.getGUI().setStatus("File downloaded");
} catch (FileNotFoundException e) {
Profile.getGUI().setStatus("Error: Jar file not found");
e.printStackTrace();
this.started = false;
return false;
}
}
try {
Profile.getGUI().setStatus("Delaying startup");
TimeUnit.SECONDS.sleep(Profile.getCurrent().getDelayStartup());
} catch (InterruptedException e) {
e.printStackTrace();
this.started = false;
return false;
}
try {
ProcessBuilder builder;
String serverPath;
if (Profile.getCurrent().getDownloadJars() && !type.getName().equals("Custom")) {
serverPath = jarDir + this.getType();
} else {
serverPath = this.path + File.separator + this.getType();
}
builder = new ProcessBuilder(
"java",
"-Xmx" + this.maxRam,
"-Xms512M",
"-Djline.terminal=jline.UnsupportedTerminal",
"-Dcom.mojang.eula.agree=true",
"-jar",
serverPath,
"nogui"
);
builder.directory(new File(this.path));
builder.redirectErrorStream(true);
this.process = builder.start();
this.writer = new BufferedWriter(new OutputStreamWriter(this.process.getOutputStream()));
this.reader = new BufferedReader (new InputStreamReader(this.process.getInputStream()));
Profile.getGUI().setStatus("Servers are running");
this.started = true;
return true;
} catch (IOException e) {
Profile.getGUI().setStatus("Could not start server");
this.started = false;
return false;
}
} else {
this.started = false;
return true;
}
}
/**
* Reads all available output from the server process.
*
* @return The server output
* @throws IOException If reading from the reader fails
*/
public String read() throws IOException {
String line;
StringBuilder text = new StringBuilder();
while (reader.ready() && (line = reader.readLine()) != null) {
text.append(line).append("\n");
}
return text.toString().trim();
}
/**
* Downloads necessary .jar file for the server.
* This is unfortunately hardcoded since there is no golden standard, and we only host some jars ourselves.
*
* @throws FileNotFoundException if the file was not found and could not be acquired.
*/
private void downloadJar() throws FileNotFoundException {
AdvancedServerType type;
File file = new File(this.path + File.separator + this.getType());
Path filePath = Paths.get(this.path + File.separator + this.getType());
String versionText;
String newestVersion;
String url = this.type.getDownloadURL();
String name = this.type.getName();
String ver = this.serverVersion;
boolean success;
switch (this.type.getName()) {
case "Custom":
if (!file.isFile()) {
throw new FileNotFoundException("Specified custom jar was not found.");
}
break;
case "Spigot":
case "Craftbukkit":
case "MCPCplus":
if (!file.isFile()) {
success = downloadFile(url + name + ver + ".jar", filePath);
if (!success) {
throw new FileNotFoundException("Jar file could not be downloaded.");
}
}
break;
case "Vanilla":
case "Snapshot":
type = (AdvancedServerType) this.type;
if (this.serverVersion.equals("Latest")) {
try {
versionText = readFile(type.getVersionURL());
} catch (IOException e) {
throw new FileNotFoundException("Version file could not be downloaded.");
}
newestVersion = stringBetween(versionText, type.getSrcStart(), type.getSrcEnd());
if (!file.isFile() || !newestVersion.equals(this.getVersion(name))) {
success = downloadFile(
url + newestVersion + type.getDownloadURLPart() + newestVersion + ".jar",
filePath
);
this.setVersion(name, newestVersion);
if (!success) {
throw new FileNotFoundException("Jar file could not be downloaded.");
}
}
} else {
if (!file.isFile()) {
success = downloadFile(url + ver + type.getDownloadURLPart() + ver + ".jar", filePath);
if (!success) {
throw new FileNotFoundException("Jar file could not be downloaded.");
}
}
}
break;
case "SpongeVanilla":
type = (AdvancedServerType) this.type;
try {
versionText = readFile(type.getVersionURL() + this.serverVersion);
} catch (IOException e) {
throw new FileNotFoundException("Version file could not be downloaded.");
}
newestVersion = stringBetween(versionText, type.getSrcStart(), type.getSrcEnd());
if (!file.isFile() || !newestVersion.equals(this.getVersion(name))) {
success = downloadFile(
url + newestVersion + type.getDownloadURLPart() + newestVersion + ".jar",
filePath
);
this.setVersion(name, newestVersion);
if (!success) {
throw new FileNotFoundException("Jar file could not be downloaded.");
}
}
break;
case "Bungee":
type = (AdvancedServerType) this.type;
try {
versionText = readFile(type.getVersionURL());
} catch (IOException e) {
throw new FileNotFoundException("Version file could not be downloaded.");
}
newestVersion = stringBetween(versionText, type.getSrcStart(), type.getSrcEnd());
if (!file.isFile() || !newestVersion.equals(this.getVersion(name))) {
success = downloadFile(url, filePath);
this.setVersion(name, newestVersion);
if (!success) {
throw new FileNotFoundException("Jar file could not be downloaded.");
}
}
}
}
/**
* Returns the current version of a type
*
* @param type The version type
* @return The version string
*/
private String getVersion(String type) {
switch (type) {
case "Vanilla":
return this.vanillaVersion;
case "Snapshot":
return this.snapshotVersion;
case "SpongeVanilla":
return this.spongeVanillaVersion;
case "Bungee":
return this.bungeeVersion;
default:
return "";
}
}
/**
* Sets a server type's last downloaded version.
*
* @param type The version type
* @param version The version string
*/
private void setVersion(String type, String version) {
if (!type.equals("")) {
switch (type) {
case "Vanilla":
this.vanillaVersion = version;
break;
case "Snapshot":
this.snapshotVersion = version;
break;
case "SpongeVanilla":
this.spongeVanillaVersion = version;
break;
case "Bungee":
this.bungeeVersion = version;
}
}
}
/**
* Sends a command to this server through its writer.
*
* @param command Command to send to the server
* @throws IOException If write fails
*/
public void sendCommand(String command) throws IOException {
if (this.process != null && this.writer != null) {
this.writer.write(command + "\n");
this.writer.flush();
}
}
}

View File

@ -1,112 +0,0 @@
package net.knarcraft.serverlauncher.server;
import javax.naming.ConfigurationException;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Scanner;
/**
* Contains the bare minimum to be a functional server type.
*
* @author Kristian Knarvik <kristian.knarvik@knett.no>
* @version 1.0.0
* @since 1.0.0
*/
public class ServerType {
private final String name;
private final String[] versions;
private final String downloadURL;
private static final ArrayList<ServerType> serverTypes = new ArrayList<>();
ServerType(String name, String[] versions, String downloadURL) {
this.name = name;
this.versions = versions;
this.downloadURL = downloadURL;
serverTypes.add(this);
}
public String getName() {
return this.name;
}
public String[] getVersions() {
return this.versions;
}
public String getDownloadURL() {
return this.downloadURL;
}
public static ArrayList<ServerType> getServerTypes() {
return serverTypes;
}
/**
* Gets a list of all server types' names.
*
* @return A list of strings
*/
public static String[] getTypeNames() {
ArrayList<ServerType> types = ServerType.getServerTypes();
String[] serverTypes = new String[types.size()];
for (int i = 0; i < types.size(); i++) {
serverTypes[i] = types.get(i).getName();
}
return serverTypes;
}
/**
* Gets a server type by name.
*
* @param name Then name of the server type
* @return A ServerType
*/
public static ServerType getByName(String name) {
for (ServerType serverType : serverTypes) {
if (serverType.getName().equals(name)) {
return serverType;
}
}
return null;
}
/**
* Reads valid server types and version from a file, and creates their objects.
*
* @throws ConfigurationException if anything goes wrong.
*/
public static void loadServerTypes() throws ConfigurationException {
if (serverTypes.isEmpty()) {
Scanner file;
try {
file = new Scanner(new File("config/servertypes.csv"));
} catch (FileNotFoundException e) {
file = new Scanner(ServerType.class.getResourceAsStream("/config/servertypes.csv"));
}
while (file.hasNextLine()) {
String[] str = file.nextLine().split(";", -1);
int len = str.length;
String[] ver;
if (str[1].contains(",")) {
ver = str[1].split(",", -1);
} else {
ver = new String[]{str[1]};
}
switch (len) {
case 7:
new AdvancedServerType(str[0], ver, str[2], str[3], str[4], str[5], str[6]);
break;
case 3:
new ServerType(str[0], ver, str[2]);
break;
default:
throw new ConfigurationException("Error: Configuration file invalid.");
}
}
} else {
throw new ConfigurationException("Error: Configuration already loaded.");
}
}
}

View File

@ -1,103 +0,0 @@
package net.knarcraft.serverlauncher.userinterface;
import net.knarcraft.serverlauncher.profile.Profile;
import javax.swing.*;
import javax.swing.text.DefaultCaret;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.ArrayList;
import static javax.swing.text.DefaultCaret.ALWAYS_UPDATE;
/**
* Acts as a single writable/readable tab
* Has a box for user input, and a textArea for server output.
*
* @author Kristian Knarvik <kristian.knarvik@knett.no>
* @version 1.0.0
* @since 1.0.0
*/
public class Console implements ActionListener, KeyListener {
private final JTextField textInput;
private final JTextArea textOutput;
private final String name;
private final JPanel panel;
private final ArrayList<String> commands = new ArrayList<>();
private int commandIndex;
Console(JTabbedPane tab, String name) {
this.name = name;
panel = new JPanel();
tab.addTab(name, null, panel, null);
panel.setLayout(new BorderLayout(0, 0));
textInput = new JTextField();
panel.add(textInput, BorderLayout.SOUTH);
textInput.setColumns(10);
textInput.addActionListener(this);
textOutput = new JTextArea();
JScrollPane scroll = new JScrollPane(textOutput);
panel.add(scroll, BorderLayout.CENTER);
textOutput.setEditable(false);
DefaultCaret caret = (DefaultCaret) textOutput.getCaret();
caret.setUpdatePolicy(ALWAYS_UPDATE);
textOutput.setLineWrap(true);
textInput.addKeyListener(this);
}
public JPanel getPanel() {
return this.panel;
}
/**
* Prints a string to the textArea.
*
* @param text The text to print
*/
public void output(String text) {
this.textOutput.setText(this.textOutput.getText() + "\n" + text);
}
@Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == textInput) { //Sends the command from the input to the server with the same name.
java.lang.String text = textInput.getText();
Profile.getCurrent().sendCommand(this.name, text);
commands.add(text);
if (commands.size() > 25) {
commands.remove(0);
}
commandIndex = commands.size();
textInput.setText("");
}
}
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_UP) {
if (commands.size() > 0 && commandIndex > 0) {
textInput.setText(commands.get(--commandIndex));
}
} else if (e.getKeyCode() == KeyEvent.VK_DOWN) {
if (commands.size() > 0) {
if (commandIndex == commands.size() - 1) {
commandIndex++;
textInput.setText("");
} else if (commandIndex >= 0 && commandIndex <= commands.size() - 1) {
textInput.setText(commands.get(++commandIndex));
}
}
}
}
@Override
public void keyReleased(KeyEvent e) {
}
@Override
public void keyTyped(KeyEvent e) {
}
}

View File

@ -1,908 +0,0 @@
package net.knarcraft.serverlauncher.userinterface;
import net.knarcraft.serverlauncher.Main;
import net.knarcraft.serverlauncher.profile.Collection;
import net.knarcraft.serverlauncher.server.Server;
import net.knarcraft.serverlauncher.profile.Profile;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Objects;
import java.util.Scanner;
import java.util.concurrent.Executors;
import static java.awt.Frame.NORMAL;
import static javax.swing.WindowConstants.DO_NOTHING_ON_CLOSE;
/**
* Generates a GUI.
*
* @author Kristian Knarvik <kristian.knarvik@knett.no>
* @version 1.0.0
* @since 1.0.0
*/
public class GUI implements ActionListener {
private JFrame frame;
private JTabbedPane tabbedPane;
private JTabbedPane serversPane;
//Menu
private JCheckBoxMenuItem chckbxmntmRunInBackground, chckbxmntmDelayStartup, chckbxmntmDownloadJars; //Options
private JMenuItem mntmErrors, mntmSetup, mntmManualUpdate; //Help
private JMenuItem mntmRunInBackground, mntmDelayStartup, mntmDownloadJars; //Info/options
private JMenuItem mntmAbout, mntmStory; //Info/about
//Basic controls
private JButton btnStartServer, btnStopServer, addServer, backup, addProfile, delProfile;
private JComboBox<String> profiles;
private final JLabel lblStatuslabel = new JLabel("Servers are stopped");
//Server controls
private JComboBox<String> targetServer;
private JComboBox<String> targetPlayer;
private JButton btnKick, btnBan, btnOp, btnDeop, btnCustomCommand, btnSaveserver, btnReload, btnServerConsoles;
private JTextField customCommand;
//Text
private String setupText;
private String runInBackgroundText;
private String delayStartupText;
private String downloadJarsText;
private String aboutText;
private final ArrayList<String> globalPlayers;
private SystemTray tray;
private TrayIcon trayIcon;
/**
* Create the application window.
*/
public GUI() {
initialize(440, 170);
loadMessages();
this.globalPlayers = new ArrayList<>();
}
/**
* Creates the application window with a preferred width and height.
*
* @param width The preferred width
* @param height The preferred height
*/
public GUI(int width, int height) {
initialize(width, height);
loadMessages();
this.globalPlayers = new ArrayList<>();
}
/**
* Gets the pane used for server configurations
*
* @return A JTabbedPane
*/
public JTabbedPane getPane() {
return this.serversPane;
}
/**
* Sets the text of the status label.
*
* @param text The new text
*/
public void setStatus(String text) {
this.lblStatuslabel.setText(text);
try (PrintWriter file = new PrintWriter(new FileWriter(Main.getAppDir() + File.separator + "latestrun.log", true))) {
file.println(text);
} catch (IOException e ) {
e.printStackTrace();
}
}
/**
* Adds a player to the global playerlist, and updates the players combo.
*
* @param name The name of the player to add
*/
public void addPlayer(String name) {
this.globalPlayers.add(name);
this.updatePlayers();
}
/**
* Removes a player from the global list of players.
*
* @param name The name of the player to remove.
*/
public void removePlayer(String name) {
for (int i = 0; i < this.globalPlayers.size(); i++) {
if (this.globalPlayers.get(i).equals(name)) {
this.globalPlayers.remove(i);
}
}
this.updatePlayers();
}
/**
* Updates the profiles combo.
*/
public void updateProfiles() {
this.profiles.removeAllItems();
for (Profile profile : Profile.getProfiles()) {
this.profiles.addItem(profile.getName());
}
}
/**
* Gets the size of the main JFrame
*
* @return The Dimension of the frame
*/
public Dimension getSize() {
return frame.getContentPane().getSize();
}
/**
* Updates GUI according to current profile settings.
*/
public void update() {
serversPane.removeAll();
for (Collection collection : Profile.getCurrent().getCollections()) {
serversPane.addTab(collection.getName(), collection.getServerTab().getPanel());
}
chckbxmntmRunInBackground.setState(Profile.getCurrent().getRunInBackground());
chckbxmntmDelayStartup.setState(Profile.getCurrent().getDelayStartup() > 0);
chckbxmntmDownloadJars.setState(Profile.getCurrent().getDownloadJars());
this.targetServer.removeAllItems();
this.targetServer.addItem("All");
for (Collection collection : Profile.getCurrent().getCollections()) {
this.targetServer.addItem(collection.getName());
}
}
/**
* Creates the GUI,
*/
private void initialize(int width, int height) {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException |
UnsupportedLookAndFeelException |
InstantiationException |
IllegalAccessException e
) {
e.printStackTrace();
}
frame = new JFrame("Minecraft server launcher");
frame.setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
frame.getContentPane().setPreferredSize(new Dimension(width, height));
ImageIcon img;
try {
img = new ImageIcon(ImageIO.read(GUI.class.getResourceAsStream("/files/GUIIcon.png")));
} catch (IOException | IllegalArgumentException e) {
img = new ImageIcon("files/GUIIcon.png");
}
frame.setIconImage(img.getImage());
JMenuBar menuBar = new JMenuBar();
frame.setJMenuBar(menuBar);
JMenu mnOptions = new JMenu("Options");
menuBar.add(mnOptions);
chckbxmntmRunInBackground = new JCheckBoxMenuItem("Run in background on exit");
mnOptions.add(chckbxmntmRunInBackground);
chckbxmntmRunInBackground.addActionListener(this);
chckbxmntmDelayStartup = new JCheckBoxMenuItem("Delay Startup");
mnOptions.add(chckbxmntmDelayStartup);
chckbxmntmDelayStartup.addActionListener(this);
chckbxmntmDownloadJars = new JCheckBoxMenuItem("Download jars");
mnOptions.add(chckbxmntmDownloadJars);
chckbxmntmDownloadJars.addActionListener(this);
JMenu mnHelp = new JMenu("Help");
menuBar.add(mnHelp);
mntmErrors = new JMenuItem("Errors");
mnHelp.add(mntmErrors);
mntmErrors.addActionListener(this);
mntmSetup = new JMenuItem("Setup");
mnHelp.add(mntmSetup);
mntmSetup.addActionListener(this);
mntmManualUpdate = new JMenuItem("Manual update");
mnHelp.add(mntmManualUpdate);
mntmManualUpdate.addActionListener(this);
JMenu mnInfo = new JMenu("Info");
menuBar.add(mnInfo);
JMenu mnOptionsInfo = new JMenu("Options");
mnInfo.add(mnOptionsInfo);
mntmRunInBackground = new JMenuItem("Run in background on exit");
mnOptionsInfo.add(mntmRunInBackground);
mntmRunInBackground.addActionListener(this);
mntmDelayStartup = new JMenuItem("Delay Startup");
mnOptionsInfo.add(mntmDelayStartup);
mntmDelayStartup.addActionListener(this);
mntmDownloadJars = new JMenuItem("Download jars");
mnOptionsInfo.add(mntmDownloadJars);
mntmDownloadJars.addActionListener(this);
JMenu mnAbout = new JMenu("About");
mnInfo.add(mnAbout);
mntmAbout = new JMenuItem("About");
mnAbout.add(mntmAbout);
mntmAbout.addActionListener(this);
mntmStory = new JMenuItem("Story");
mnAbout.add(mntmStory);
mntmStory.addActionListener(this);
tabbedPane = new JTabbedPane(JTabbedPane.TOP);
frame.getContentPane().add(tabbedPane);
JPanel panelBasic = new JPanel();
tabbedPane.addTab("Control panel", null, panelBasic, null);
SpringLayout sl_panel = new SpringLayout();
panelBasic.setLayout(sl_panel);
JLabel lblBasicControls = new JLabel("Basic controls");
sl_panel.putConstraint(SpringLayout.NORTH, lblBasicControls, 10, SpringLayout.NORTH, panelBasic);
panelBasic.add(lblBasicControls);
btnStartServer = new JButton("Start servers");
sl_panel.putConstraint(SpringLayout.WEST, lblBasicControls, 0, SpringLayout.WEST, btnStartServer);
sl_panel.putConstraint(SpringLayout.NORTH, btnStartServer, 6, SpringLayout.SOUTH, lblBasicControls);
sl_panel.putConstraint(SpringLayout.WEST, btnStartServer, 10, SpringLayout.WEST, panelBasic);
panelBasic.add(btnStartServer);
btnStartServer.addActionListener(this);
btnStopServer = new JButton("Stop servers");
sl_panel.putConstraint(SpringLayout.NORTH, btnStopServer, 0, SpringLayout.NORTH, btnStartServer);
sl_panel.putConstraint(SpringLayout.WEST, btnStopServer, 6, SpringLayout.EAST, btnStartServer);
panelBasic.add(btnStopServer);
btnStopServer.addActionListener(this);
JLabel lblProfile = new JLabel("Profile");
sl_panel.putConstraint(SpringLayout.NORTH, lblProfile, 6, SpringLayout.SOUTH, btnStartServer);
sl_panel.putConstraint(SpringLayout.WEST, lblProfile, 10, SpringLayout.WEST, panelBasic);
panelBasic.add(lblProfile);
addProfile = new JButton("+");
sl_panel.putConstraint(SpringLayout.NORTH, addProfile, 6, SpringLayout.SOUTH, lblProfile);
sl_panel.putConstraint(SpringLayout.WEST, addProfile, 10, SpringLayout.WEST, panelBasic);
panelBasic.add(addProfile);
addProfile.addActionListener(this);
delProfile = new JButton("-");
sl_panel.putConstraint(SpringLayout.NORTH, delProfile, 0, SpringLayout.NORTH, addProfile);
sl_panel.putConstraint(SpringLayout.WEST, delProfile, 6, SpringLayout.EAST, addProfile);
panelBasic.add(delProfile);
delProfile.addActionListener(this);
profiles = new JComboBox<>();
sl_panel.putConstraint(SpringLayout.NORTH, profiles, 0, SpringLayout.NORTH, addProfile);
sl_panel.putConstraint(SpringLayout.WEST, profiles, 6, SpringLayout.EAST, delProfile);
sl_panel.putConstraint(SpringLayout.EAST, profiles, 124, SpringLayout.EAST, delProfile);
panelBasic.add(profiles);
profiles.addActionListener(this);
sl_panel.putConstraint(SpringLayout.NORTH, lblStatuslabel, 6, SpringLayout.SOUTH, addProfile);
sl_panel.putConstraint(SpringLayout.SOUTH, lblStatuslabel, -10, SpringLayout.SOUTH, panelBasic);
sl_panel.putConstraint(SpringLayout.WEST, lblStatuslabel, 10, SpringLayout.WEST, panelBasic);
sl_panel.putConstraint(SpringLayout.EAST, lblStatuslabel, -10, SpringLayout.EAST, panelBasic);
panelBasic.add(lblStatuslabel);
addServer = new JButton("Add server");
sl_panel.putConstraint(SpringLayout.NORTH, addServer, 0, SpringLayout.NORTH, btnStartServer);
sl_panel.putConstraint(SpringLayout.WEST, addServer, 6, SpringLayout.EAST, btnStopServer);
panelBasic.add(addServer);
addServer.addActionListener(this);
backup = new JButton("Backup");
sl_panel.putConstraint(SpringLayout.NORTH, backup, 0, SpringLayout.NORTH, btnStartServer);
sl_panel.putConstraint(SpringLayout.WEST, backup, 6, SpringLayout.EAST, addServer);
panelBasic.add(backup);
backup.addActionListener(this);
JPanel controlServers = new JPanel();
tabbedPane.addTab("Control servers", null, controlServers, null);
SpringLayout sl_panel_1 = new SpringLayout();
controlServers.setLayout(sl_panel_1);
targetServer = new JComboBox<>();
sl_panel_1.putConstraint(SpringLayout.NORTH, targetServer, 10, SpringLayout.NORTH, controlServers);
controlServers.add(targetServer);
targetServer.addActionListener(this);
targetPlayer = new JComboBox<>();
sl_panel_1.putConstraint(SpringLayout.NORTH, targetPlayer, 6, SpringLayout.SOUTH, targetServer);
targetPlayer.setEditable(true);
controlServers.add(targetPlayer);
btnKick = new JButton("Kick");
sl_panel_1.putConstraint(SpringLayout.NORTH, btnKick, 10, SpringLayout.NORTH, controlServers);
sl_panel_1.putConstraint(SpringLayout.WEST, btnKick, 6, SpringLayout.EAST, targetServer);
sl_panel_1.putConstraint(SpringLayout.EAST, btnKick, 104, SpringLayout.WEST, btnKick);
sl_panel_1.putConstraint(SpringLayout.SOUTH, targetServer, 0, SpringLayout.SOUTH, btnKick);
controlServers.add(btnKick);
btnKick.addActionListener(this);
btnBan = new JButton("Ban");
sl_panel_1.putConstraint(SpringLayout.NORTH, btnBan, 6, SpringLayout.SOUTH, btnKick);
sl_panel_1.putConstraint(SpringLayout.WEST, btnBan, 6, SpringLayout.EAST, targetPlayer);
sl_panel_1.putConstraint(SpringLayout.EAST, btnBan, 104, SpringLayout.WEST, btnBan);
sl_panel_1.putConstraint(SpringLayout.SOUTH, targetPlayer, 0, SpringLayout.SOUTH, btnBan);
controlServers.add(btnBan);
btnBan.addActionListener(this);
btnOp = new JButton("OP");
sl_panel_1.putConstraint(SpringLayout.NORTH, btnOp, 10, SpringLayout.NORTH, controlServers);
sl_panel_1.putConstraint(SpringLayout.WEST, btnOp, 6, SpringLayout.EAST, btnKick);
sl_panel_1.putConstraint(SpringLayout.EAST, btnOp, -10, SpringLayout.EAST, controlServers);
controlServers.add(btnOp);
btnOp.addActionListener(this);
btnDeop = new JButton("DEOP");
sl_panel_1.putConstraint(SpringLayout.WEST, btnDeop, 6, SpringLayout.EAST, btnBan);
sl_panel_1.putConstraint(SpringLayout.NORTH, btnDeop, 5, SpringLayout.SOUTH, btnOp);
sl_panel_1.putConstraint(SpringLayout.EAST, btnDeop, -10, SpringLayout.EAST, controlServers);
controlServers.add(btnDeop);
btnDeop.addActionListener(this);
JLabel lblTargetServer = new JLabel("Target server");
sl_panel_1.putConstraint(SpringLayout.WEST, targetServer, 6, SpringLayout.EAST, lblTargetServer);
sl_panel_1.putConstraint(SpringLayout.EAST, targetServer, 121, SpringLayout.EAST, lblTargetServer);
sl_panel_1.putConstraint(SpringLayout.NORTH, lblTargetServer, 10, SpringLayout.NORTH, controlServers);
sl_panel_1.putConstraint(SpringLayout.SOUTH, lblTargetServer, 0, SpringLayout.SOUTH, targetServer);
sl_panel_1.putConstraint(SpringLayout.WEST, lblTargetServer, 10, SpringLayout.WEST, controlServers);
controlServers.add(lblTargetServer);
JLabel lblTargetPlayer = new JLabel("Target player");
sl_panel_1.putConstraint(SpringLayout.WEST, targetPlayer, 7, SpringLayout.EAST, lblTargetPlayer);
sl_panel_1.putConstraint(SpringLayout.EAST, targetPlayer, 122, SpringLayout.EAST, lblTargetPlayer);
sl_panel_1.putConstraint(SpringLayout.NORTH, lblTargetPlayer, 6, SpringLayout.SOUTH, lblTargetServer);
sl_panel_1.putConstraint(SpringLayout.SOUTH, lblTargetPlayer, 0, SpringLayout.SOUTH, targetPlayer);
sl_panel_1.putConstraint(SpringLayout.WEST, lblTargetPlayer, 0, SpringLayout.WEST, lblTargetServer);
controlServers.add(lblTargetPlayer);
btnCustomCommand = new JButton("Custom command");
sl_panel_1.putConstraint(SpringLayout.WEST, btnCustomCommand, 250, SpringLayout.WEST, controlServers);
sl_panel_1.putConstraint(SpringLayout.EAST, btnCustomCommand, 0, SpringLayout.EAST, btnOp);
controlServers.add(btnCustomCommand);
btnCustomCommand.addActionListener(this);
frame.getRootPane().setDefaultButton(btnCustomCommand);
customCommand = new JTextField();
sl_panel_1.putConstraint(SpringLayout.WEST, customCommand, 10, SpringLayout.WEST, controlServers);
sl_panel_1.putConstraint(SpringLayout.EAST, customCommand, -6, SpringLayout.WEST, btnCustomCommand);
sl_panel_1.putConstraint(SpringLayout.NORTH, btnCustomCommand, 0, SpringLayout.NORTH, customCommand);
sl_panel_1.putConstraint(SpringLayout.SOUTH, customCommand, 0, SpringLayout.SOUTH, btnCustomCommand);
controlServers.add(customCommand);
customCommand.setColumns(10);
btnSaveserver = new JButton("Save server");
sl_panel_1.putConstraint(SpringLayout.NORTH, customCommand, 6, SpringLayout.SOUTH, btnSaveserver);
sl_panel_1.putConstraint(SpringLayout.NORTH, btnSaveserver, 6, SpringLayout.SOUTH, btnBan);
sl_panel_1.putConstraint(SpringLayout.WEST, btnSaveserver, 0, SpringLayout.WEST, btnKick);
sl_panel_1.putConstraint(SpringLayout.EAST, btnSaveserver, 104, SpringLayout.WEST, btnKick);
sl_panel_1.putConstraint(SpringLayout.EAST, btnSaveserver, 104, SpringLayout.WEST, btnKick);
controlServers.add(btnSaveserver);
btnSaveserver.addActionListener(this);
btnReload = new JButton("Reload");
sl_panel_1.putConstraint(SpringLayout.NORTH, btnReload, 6, SpringLayout.SOUTH, btnDeop);
sl_panel_1.putConstraint(SpringLayout.WEST, btnReload, 0, SpringLayout.WEST, btnDeop);
sl_panel_1.putConstraint(SpringLayout.EAST, btnReload, 0, SpringLayout.EAST, btnOp);
controlServers.add(btnReload);
btnReload.addActionListener(this);
btnServerConsoles = new JButton("View server consoles");
sl_panel_1.putConstraint(SpringLayout.NORTH, btnServerConsoles, 0, SpringLayout.NORTH, btnSaveserver);
sl_panel_1.putConstraint(SpringLayout.WEST, btnServerConsoles, 0, SpringLayout.WEST, lblTargetServer);
sl_panel_1.putConstraint(SpringLayout.EAST, btnServerConsoles, 0, SpringLayout.EAST, targetServer);
controlServers.add(btnServerConsoles);
btnServerConsoles.addActionListener(this);
JPanel panel_2 = new JPanel();
tabbedPane.addTab("Servers", null, panel_2, null);
SpringLayout sl_panel_2 = new SpringLayout();
panel_2.setLayout(sl_panel_2);
JTabbedPane tabbedPane_1 = new JTabbedPane(JTabbedPane.TOP);
sl_panel_2.putConstraint(SpringLayout.NORTH, tabbedPane_1, 0, SpringLayout.NORTH, panel_2);
sl_panel_2.putConstraint(SpringLayout.WEST, tabbedPane_1, 0, SpringLayout.WEST, panel_2);
sl_panel_2.putConstraint(SpringLayout.SOUTH, tabbedPane_1, 0, SpringLayout.SOUTH, panel_2);
sl_panel_2.putConstraint(SpringLayout.EAST, tabbedPane_1, 0, SpringLayout.EAST, panel_2);
panel_2.add(tabbedPane_1);
this.serversPane = tabbedPane_1;
tabbedPane_1.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
frame.validate();
frame.pack();
frame.setVisible(true);
tray();
updateRunning(false);
}
/**
* Prepares the system tray if available.
*/
private void tray() {
if (SystemTray.isSupported()) {
tray = SystemTray.getSystemTray();
Image trayImage = Toolkit.getDefaultToolkit().getImage("files/GUIIcon.png");
PopupMenu popup = new PopupMenu();
trayIcon = new TrayIcon(trayImage, "Minecraft Server Launcher", popup);
trayIcon.setImageAutoSize(true);
ActionListener exitListener= e -> {
stop();
try {
Profile.getCurrent().save();
} catch (FileNotFoundException e1) {
e1.printStackTrace();
}
System.exit(0);
};
MenuItem restoreItem = new MenuItem("Restore");
popup.add(restoreItem);
restoreItem.addActionListener(e -> {
frame.setExtendedState(NORMAL);
tray.remove(trayIcon);
frame.setVisible(true);
});
MenuItem exitItem = new MenuItem("Exit");
exitItem.addActionListener(exitListener);
popup.add(exitItem);
frame.addWindowStateListener(e -> {
if (e.getNewState() == NORMAL) {
tray.remove(trayIcon);
frame.setVisible(true);
}
});
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
if (Profile.getCurrent().getRunInBackground() && SystemTray.isSupported()) {
try {
tray.add(trayIcon);
frame.setVisible(false);
} catch (AWTException e1) {
e1.printStackTrace();
}
} else {
stop();
try {
Profile.getCurrent().save();
} catch (FileNotFoundException e1) {
e1.printStackTrace();
}
System.exit(0);
}
}
});
trayIcon.addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
if(e.getClickCount() >= 1 && e.getButton() == MouseEvent.BUTTON1){
frame.setExtendedState(NORMAL);
tray.remove(trayIcon);
frame.setVisible(true);
}
}
});
} else {
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
try {
Profile.getCurrent().save();
} catch (FileNotFoundException e1) {
e1.printStackTrace();
}
stop();
System.exit(0);
}
});
}
}
/**
* Hides the gui to the tray,
*/
public void hide() {
frame.setVisible(false);
try {
tray.add(trayIcon);
} catch (AWTException e) {
e.printStackTrace();
}
}
@Override
public void actionPerformed(ActionEvent e) {
String selectedServerValue = null, selectedPlayerValue = null;
Object selectedServer = targetServer.getSelectedItem();
if (selectedServer != null) {
selectedServerValue = selectedServer.toString();
}
Object selectedPlayer = targetPlayer.getSelectedItem();
if (selectedPlayer != null) {
selectedPlayerValue = selectedPlayer.toString();
}
if (e.getSource() == chckbxmntmRunInBackground) {
background();
} else if (e.getSource() == chckbxmntmDelayStartup) {
delay();
} else if (e.getSource() == chckbxmntmDownloadJars) {
downloadJars();
} else if (e.getSource() == mntmErrors) {
goToURL("https://knarcraft.net/Bungeeminecraftserverlauncher/Info/");
} else if (e.getSource() == mntmSetup) {
JOptionPane.showMessageDialog(
null,
setupText,
"Setup",
JOptionPane.INFORMATION_MESSAGE
);
} else if (e.getSource() == mntmManualUpdate) {
goToURL("https://knarcraft.net/Downloads/Bungeeminecraftserverlauncher/");
} else if (e.getSource() == mntmRunInBackground) {
JOptionPane.showMessageDialog(
null,
runInBackgroundText,
"Run in background",
JOptionPane.INFORMATION_MESSAGE
);
} else if (e.getSource() == mntmDelayStartup) {
JOptionPane.showMessageDialog(
null,
delayStartupText,
"Delay startup",
JOptionPane.INFORMATION_MESSAGE
);
} else if (e.getSource() == mntmDownloadJars) {
JOptionPane.showMessageDialog(
null,
downloadJarsText,
"Download jars",
JOptionPane.INFORMATION_MESSAGE
);
} else if (e.getSource() == mntmAbout) {
JOptionPane.showMessageDialog(
null,
aboutText,
"About",
JOptionPane.INFORMATION_MESSAGE
);
} else if (e.getSource() == mntmStory) {
goToURL("https://knarcraft.net/Bungeeminecraftserverlauncher/Story/");
} else if (e.getSource() == btnStartServer) {
try {
Profile.getCurrent().save();
} catch (FileNotFoundException e1) {
e1.printStackTrace();
}
Executors.newSingleThreadExecutor().execute(Server::startServers);
} else if (e.getSource() == btnStopServer) {
stop();
} else if (e.getSource() == addServer) {
String serverName = JOptionPane.showInputDialog("Name of server: ");
Profile.getCurrent().addCollection(serverName);
this.update();
Profile.getCurrent().updateConsoles();
} else if (e.getSource() == backup) {
backup();
} else if (e.getSource() == addProfile) {
Profile.addProfile(JOptionPane.showInputDialog("Profile name: "));
updateProfiles();
} else if (e.getSource() == delProfile) {
Object selected = profiles.getSelectedItem();
if (selected != null) {
Profile.removeProfile(selected.toString());
updateProfiles();
}
} else if (e.getSource() == profiles) {
try {
changeProfile();
} catch (FileNotFoundException e1) {
e1.printStackTrace();
}
} else if (e.getSource() == btnKick) {
if (selectedServerValue != null && selectedPlayerValue != null) {
Profile.getCurrent().sendCommand(selectedServerValue, "kick " + selectedPlayerValue);
}
} else if (e.getSource() == btnBan) {
if (selectedServerValue != null && selectedPlayerValue != null) {
Profile.getCurrent().sendCommand(selectedServerValue, "ban " + selectedPlayerValue);
}
} else if (e.getSource() == btnOp) {
if (selectedServerValue != null && selectedPlayerValue != null) {
Profile.getCurrent().sendCommand(selectedServerValue, "op " + selectedPlayerValue);
}
} else if (e.getSource() == btnDeop) {
if (selectedServerValue != null && selectedPlayerValue != null) {
Profile.getCurrent().sendCommand(selectedServerValue, "deop " + selectedPlayerValue);
}
} else if (e.getSource() == btnCustomCommand) {
if (selectedServerValue != null) {
Profile.getCurrent().sendCommand(selectedServerValue, customCommand.getText());
customCommand.setText("");
}
} else if (e.getSource() == btnSaveserver) {
if (selectedServerValue != null) {
Profile.getCurrent().sendCommand(selectedServerValue, "save-all");
}
} else if (e.getSource() == btnReload) {
if (selectedServerValue != null) {
Profile.getCurrent().sendCommand(selectedServerValue, "reload");
}
} else if (e.getSource() == btnServerConsoles) {
ServerConsoles.show();
} else if (e.getSource() == targetServer) {
updatePlayers();
}
}
/**
* Updates the GUI components to block a user from doing illegal actions.
*
* @param running Are the servers currently running?
*/
public void updateRunning(boolean running) {
boolean stopped = !running; //Most gui is only enabled when the server is stopped rather than running.
profiles.setEnabled(stopped);
addProfile.setEnabled(stopped);
delProfile.setEnabled(stopped);
btnStartServer.setEnabled(stopped);
addServer.setEnabled(stopped);
tabbedPane.setEnabledAt(2, stopped);
btnStopServer.setEnabled(running);
}
/**
* Saves the previous profile and loads data from the new profile.
*/
private void changeProfile() throws FileNotFoundException {
Profile.getCurrent().save();
Object current = this.profiles.getSelectedItem();
if (current != null) {
Profile.setCurrent(current.toString());
}
this.update();
Profile.getCurrent().updateConsoles();
}
/**
* Stops all servers
*/
private void stop() {
try {
setStatus("Servers are stopping...");
Server.stop();
} catch (IOException e1) {
JOptionPane.showMessageDialog(
null,
"Could not stop server.",
"Error",
JOptionPane.ERROR_MESSAGE
);
e1.printStackTrace();
}
}
/**
* Asks the user for a delay if checked, and sets the value to the current profile.
*/
private void delay() {
Object selected = profiles.getSelectedItem();
if (selected != null) {
Profile profile = Profile.getProfile(selected.toString());
if (chckbxmntmDelayStartup.isSelected()) {
Objects.requireNonNull(profile).setDelayStartup(
Integer.parseInt(JOptionPane.showInputDialog("Seconds to delay: "))
);
} else {
Objects.requireNonNull(profile).setDelayStartup(0);
}
} else {
JOptionPane.showMessageDialog(
null,
"No profile selected",
"Error",
JOptionPane.ERROR_MESSAGE
);
}
}
/**
* Saves the runInBackground setting to the current profile.
*/
private void background() {
Object selected = profiles.getSelectedItem();
if (selected != null) {
Profile profile = Profile.getProfile(selected.toString());
Objects.requireNonNull(profile).setRunInBackground(chckbxmntmRunInBackground.isSelected());
} else {
JOptionPane.showMessageDialog(
null,
"No profile selected",
"Error",
JOptionPane.ERROR_MESSAGE
);
}
}
/**
* Saves the downloadJars setting to the current profile.
*/
private void downloadJars() {
Object selected = profiles.getSelectedItem();
if (selected != null) {
Profile profile = Profile.getProfile(selected.toString());
Objects.requireNonNull(profile).setDownloadJars(chckbxmntmDownloadJars.isSelected());
} else {
JOptionPane.showMessageDialog(
null,
"No profile selected",
"Error",
JOptionPane.ERROR_MESSAGE
);
}
}
/**
* Copies all server directories to a folder specified by the user.
*/
private void backup() {
JFileChooser chooser = new JFileChooser();
chooser.setCurrentDirectory(new java.io.File("."));
chooser.setDialogTitle("Backup folder");
chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
chooser.setAcceptAllFileFilterUsed(false);
if (chooser.showOpenDialog(null) == JFileChooser.APPROVE_OPTION) {
File path = chooser.getSelectedFile();
for (Collection collection : Profile.getCurrent().getCollections()) {
if (!collection.getServer().getPath().equals("") && collection.getServer().isEnabled()) {
String name = collection.getServer().getName();
File srcFolder = new File(collection.getServer().getPath());
File destFolder = new File(path, name);
if (!destFolder.exists()) {
if (destFolder.mkdirs()) {
try {
copyFolder(srcFolder, destFolder);
} catch (IOException e) {
e.printStackTrace();
return;
}
} else {
return;
}
}
}
}
}
this.setStatus("Backup finished");
}
/**
* Updates the list of players currently online on the selected server,
*/
private void updatePlayers() {
String selectedServerValue;
Object selectedServer = targetServer.getSelectedItem();
if (selectedServer != null) {
targetPlayer.removeAllItems();
selectedServerValue = selectedServer.toString();
if (selectedServerValue.equals("All")) {
for (String player : this.globalPlayers) {
targetPlayer.addItem(player);
}
} else {
for (String player : Profile.getCurrent().getCollection(selectedServerValue).getServer().getPlayers()) {
targetPlayer.addItem(player);
}
}
}
}
/**
* Opens an url in the user's default application.
*
* @param url URL to open
*/
private void goToURL(String url) {
java.awt.Desktop desktop = java.awt.Desktop.getDesktop();
try {
desktop.browse(new URI(url));
} catch (URISyntaxException | IOException e1) {
e1.printStackTrace();
}
}
/**
* Loads popup messages from a text file.
*/
private void loadMessages() {
Scanner file;
try {
file = new Scanner(new File("config/menumsg.csv"));
} catch (FileNotFoundException e) {
file = new Scanner(GUI.class.getResourceAsStream("/config/menumsg.csv"));
}
while (file.hasNextLine()) {
String[] line = file.nextLine().split("=");
String content = line[1].replaceAll("_BREAK_", System.getProperty("line.separator"));
switch (line[0]) {
case "setup":
setupText = content;
break;
case "runinbk":
runInBackgroundText = content;
break;
case "delaystartup":
delayStartupText = content;
break;
case "downloadjars":
downloadJarsText = content;
break;
case "about":
aboutText = content;
}
}
}
/**
* Recursivly copies a folder to another location
*
* @param src The folder to copy
* @param dest Target destination
* @throws IOException If we can't start a file stream
*/
private void copyFolder(File src, File dest) throws IOException{
if (!src.isDirectory()) {
InputStream in = new FileInputStream(src);
OutputStream out = new FileOutputStream(dest);
byte[] buffer = new byte[1024];
int length;
while ((length = in.read(buffer)) > 0){
out.write(buffer, 0, length);
}
in.close();
out.close();
this.setStatus("Copied file " + src);
} else {
if(!dest.exists()){
if (dest.mkdir()) {
this.setStatus("Copied directory " + src);
} else {
return;
}
}
String files[] = src.list();
if (files != null) {
for (String file : files) {
File srcFile = new File(src, file);
File destFile = new File(dest, file);
copyFolder(srcFile, destFile);
}
}
}
}
}

View File

@ -1,39 +0,0 @@
package net.knarcraft.serverlauncher.userinterface;
import javax.swing.JFrame;
import javax.swing.JTabbedPane;
import java.awt.BorderLayout;
/**
* A parent window for server consoles.
* Should be toggled with the "View server consoles" button.
* Keeps track of all consoles.
*
* @author Kristian Knarvik <kristian.knarvik@knett.no>
* @version 1.0.0
* @since 1.0.0
*/
public class ServerConsoles {
private static JFrame frame;
private static JTabbedPane consolesTab;
public ServerConsoles() {
frame = new JFrame();
frame.setBounds(100, 100, 450, 300);
frame.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
consolesTab = new JTabbedPane(JTabbedPane.TOP);
frame.getContentPane().add(consolesTab, BorderLayout.CENTER);
}
public static Console addTab(String name) {
return new Console(consolesTab, name);
}
public static void show() {
frame.setVisible(true);
}
public static JTabbedPane getTab() {
return consolesTab;
}
}

View File

@ -1,243 +0,0 @@
package net.knarcraft.serverlauncher.userinterface;
import net.knarcraft.serverlauncher.profile.Profile;
import net.knarcraft.serverlauncher.server.Server;
import net.knarcraft.serverlauncher.server.ServerType;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
/**
* Contains all buttons for configuring a server.
* Does some visual stuff by itself, but otherwise reads user inputs.
*
* @author Kristian Knarvik <kristian.knarvik@knett.no>
* @version 1.0.0
* @since 1.0.0
*/
public class ServerTab implements ActionListener {
private final JComboBox<String> serverTypes, serverVersions, maxRam;
private final JCheckBox chckbxEnabled;
private final JButton btnRemoveServer, btnBrowse;
private final JTextField directory;
private final JPanel panel;
private final String name;
/**
* Updates the server tab components according to the received parameters.
*
* @param path The new path
* @param enabled The enabled status of the server
* @param typeName The name of the selected server type
* @param serverVersion The version of the server
* @param maxRam The max usable ram for the server
*/
public void setData(String path, boolean enabled, String typeName, String serverVersion, String maxRam) {
this.directory.setText(path);
this.chckbxEnabled.setSelected(enabled);
this.serverTypes.setSelectedItem(typeName);
this.serverTypes();
this.serverTypes.setSelectedItem(serverVersion);
this.maxRam.setSelectedItem(maxRam);
}
public ServerTab(String name) {
this.name = name;
panel = new JPanel();
Profile.getGUI().getPane().addTab(name, null, panel, null);
SpringLayout sl_panel_3 = new SpringLayout();
panel.setLayout(sl_panel_3);
JLabel lblServerType = new JLabel("Server type");
sl_panel_3.putConstraint(SpringLayout.WEST, lblServerType, 10, SpringLayout.WEST, panel);
panel.add(lblServerType);
JLabel lblServerVersion = new JLabel("Server version");
sl_panel_3.putConstraint(SpringLayout.NORTH, lblServerVersion, 6, SpringLayout.SOUTH, lblServerType);
sl_panel_3.putConstraint(SpringLayout.WEST, lblServerVersion, 10, SpringLayout.WEST, panel);
sl_panel_3.putConstraint(SpringLayout.SOUTH, lblServerVersion, 26, SpringLayout.SOUTH, lblServerType);
panel.add(lblServerVersion);
serverTypes = new JComboBox<>(ServerType.getTypeNames());
sl_panel_3.putConstraint(SpringLayout.NORTH, serverTypes, 10, SpringLayout.NORTH, panel);
sl_panel_3.putConstraint(SpringLayout.WEST, serverTypes, 5, SpringLayout.EAST, lblServerVersion);
sl_panel_3.putConstraint(SpringLayout.EAST, serverTypes, 154, SpringLayout.WEST, serverTypes);
sl_panel_3.putConstraint(SpringLayout.NORTH, lblServerType, 0, SpringLayout.NORTH, serverTypes);
sl_panel_3.putConstraint(SpringLayout.SOUTH, lblServerType, 0, SpringLayout.SOUTH, serverTypes);
panel.add(serverTypes);
serverTypes.addActionListener(this);
serverVersions = new JComboBox<>(ServerType.getServerTypes().get(0).getVersions());
sl_panel_3.putConstraint(SpringLayout.NORTH, serverVersions, 6, SpringLayout.SOUTH, serverTypes);
sl_panel_3.putConstraint(SpringLayout.EAST, serverVersions, 0, SpringLayout.EAST, serverTypes);
sl_panel_3.putConstraint(SpringLayout.WEST, serverVersions, 6, SpringLayout.EAST, lblServerVersion);
sl_panel_3.putConstraint(SpringLayout.NORTH, lblServerVersion, 0, SpringLayout.NORTH, serverVersions);
sl_panel_3.putConstraint(SpringLayout.SOUTH, lblServerVersion, 0, SpringLayout.SOUTH, serverVersions);
panel.add(serverVersions);
serverVersions.addActionListener(this);
JLabel lblMaxRam = new JLabel("Max ram");
sl_panel_3.putConstraint(SpringLayout.NORTH, lblMaxRam, 0, SpringLayout.NORTH, serverTypes);
sl_panel_3.putConstraint(SpringLayout.SOUTH, lblMaxRam, 0, SpringLayout.SOUTH, serverTypes);
sl_panel_3.putConstraint(SpringLayout.WEST, lblMaxRam, 6, SpringLayout.EAST, serverTypes);
panel.add(lblMaxRam);
maxRam = new JComboBox<>(Server.getRamList());
sl_panel_3.putConstraint(SpringLayout.NORTH, maxRam, 0, SpringLayout.NORTH, serverTypes);
sl_panel_3.putConstraint(SpringLayout.WEST, maxRam, 6, SpringLayout.EAST, lblMaxRam);
sl_panel_3.putConstraint(SpringLayout.EAST, maxRam, -10, SpringLayout.EAST, panel);
panel.add(maxRam);
maxRam.addActionListener(this);
chckbxEnabled = new JCheckBox("Enabled");
sl_panel_3.putConstraint(SpringLayout.WEST, chckbxEnabled, 10, SpringLayout.WEST, panel);
panel.add(chckbxEnabled);
chckbxEnabled.addActionListener(this);
btnRemoveServer = new JButton("Remove server");
sl_panel_3.putConstraint(SpringLayout.NORTH, btnRemoveServer, 0, SpringLayout.NORTH, serverVersions);
sl_panel_3.putConstraint(SpringLayout.SOUTH, btnRemoveServer, 0, SpringLayout.SOUTH, serverVersions);
sl_panel_3.putConstraint(SpringLayout.WEST, btnRemoveServer, 6, SpringLayout.EAST, serverVersions);
sl_panel_3.putConstraint(SpringLayout.EAST, btnRemoveServer, -10, SpringLayout.EAST, panel);
panel.add(btnRemoveServer);
btnRemoveServer.addActionListener(this);
JLabel lblDirectory = new JLabel("Directory");
sl_panel_3.putConstraint(SpringLayout.WEST, lblDirectory, 6, SpringLayout.EAST, chckbxEnabled);
panel.add(lblDirectory);
directory = new JTextField();
sl_panel_3.putConstraint(SpringLayout.WEST, directory, 6, SpringLayout.EAST, lblDirectory);
sl_panel_3.putConstraint(SpringLayout.SOUTH, lblDirectory, 0, SpringLayout.SOUTH, directory);
sl_panel_3.putConstraint(SpringLayout.NORTH, lblDirectory, 0, SpringLayout.NORTH, directory);
sl_panel_3.putConstraint(SpringLayout.NORTH, chckbxEnabled, 0, SpringLayout.NORTH, directory);
sl_panel_3.putConstraint(SpringLayout.SOUTH, chckbxEnabled, 0, SpringLayout.SOUTH, directory);
panel.add(directory);
directory.setColumns(10);
directory.addActionListener(this);
btnBrowse = new JButton("Browse");
sl_panel_3.putConstraint(SpringLayout.EAST, directory, -6, SpringLayout.WEST, btnBrowse);
sl_panel_3.putConstraint(SpringLayout.NORTH, btnBrowse, 3, SpringLayout.SOUTH, btnRemoveServer);
sl_panel_3.putConstraint(SpringLayout.EAST, btnBrowse, -10, SpringLayout.EAST, panel);
sl_panel_3.putConstraint(SpringLayout.SOUTH, directory, 0, SpringLayout.SOUTH, btnBrowse);
sl_panel_3.putConstraint(SpringLayout.NORTH, directory, 0, SpringLayout.NORTH, btnBrowse);
panel.add(btnBrowse);
btnBrowse.addActionListener(this);
}
public JPanel getPanel() {
return this.panel;
}
public String getPath() {
return this.directory.getText();
}
public String getMaxRam() {
Object selected = this.maxRam.getSelectedItem();
if (selected != null) {
return selected.toString();
} else {
return "512M";
}
}
public String getType() {
Object selected = this.serverTypes.getSelectedItem();
if (selected != null) {
return selected.toString();
} else {
return "Vanilla";
}
}
/**
* Gets the selected version from the serverVersion combo
*
* @return The combo value, or defaults to "Latest" on null
*/
public String getVersion() {
Object selected = this.serverVersions.getSelectedItem();
if (selected != null) {
return selected.toString();
} else {
return "Latest";
}
}
/**
* Checks if the server is enabled
*
* @return True if the checkbox is checked. False otherwise.
*/
public boolean enabled() {
return this.chckbxEnabled.isSelected();
}
@Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == btnRemoveServer) {
remove();
} else if (e.getSource() == btnBrowse) {
browse();
} else if (e.getSource() == serverTypes) {
serverTypes();
}
}
/**
* Removes the collection containing this ServerTab, and updates everything necessary.
*/
private void remove() {
Profile.getCurrent().removeCollection(this.name);
Profile.getGUI().update();
Profile.getCurrent().updateConsoles();
}
/**
* Asks the user for the server folder.
* Sets the directory input's value if successful.
*/
private void browse() {
JFileChooser chooser = new JFileChooser();
chooser.setCurrentDirectory(new java.io.File("/"));
chooser.setDialogTitle("Server folder");
chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
chooser.setAcceptAllFileFilterUsed(false);
if (chooser.showOpenDialog(null) == JFileChooser.APPROVE_OPTION) {
directory.setText(chooser.getSelectedFile().toString());
}
}
/**
* Updates the versions combo according to the value of the server type combo.
*/
private void serverTypes() {
serverVersions.removeAllItems();
String selectedserverTypes = null;
Object selectedType = serverTypes.getSelectedItem();
if (selectedType != null) {
selectedserverTypes = selectedType.toString();
}
if (selectedserverTypes != null) {
if (selectedserverTypes.equals("Custom")) {
serverVersions.setEditable(true);
} else {
serverVersions.setEditable(false);
ServerType current = null;
for (ServerType servertype : ServerType.getServerTypes()) {
if (servertype.getName().equals(selectedserverTypes)) {
current = servertype;
}
}
if (current != null) {
for (String version : current.getVersions()) {
serverVersions.addItem(version);
}
}
}
}
}
}

View File

@ -0,0 +1,23 @@
package net.knarcraft.minecraftserverlauncher.server;
import org.junit.Test;
import javax.naming.ConfigurationException;
import static junit.framework.TestCase.assertEquals;
import static junit.framework.TestCase.assertFalse;
public class ServerTest {
@Test
public void fromStringTest() throws ConfigurationException {
Server server = Server.fromString("asd;/home/;false;Bukkit;1.10.2;4G;");
assertEquals("asd", server.getName());
assertEquals("/home/", server.getPath());
assertFalse(server.isEnabled());
assertEquals("Bukkit", server.getTypeName());
assertEquals("1.10.2", server.getServerVersion());
assertEquals("4G", server.getMaxRam());
}
}

View File

@ -0,0 +1,38 @@
package net.knarcraft.minecraftserverlauncher.server;
import net.knarcraft.minecraftserverlauncher.server.servertypes.ServerType;
import org.junit.Test;
import javax.naming.ConfigurationException;
import java.util.Arrays;
import static junit.framework.TestCase.assertEquals;
public class ServerTypeHandlerTest {
@Test
public void getTypeNamesTest() throws ConfigurationException {
String[] serverTypeNames = ServerTypeHandler.getTypeNames();
String[] typeNames = new String[serverTypeNames.length];
int index = 0;
for (ServerType serverType : ServerTypeHandler.getServerTypes()) {
typeNames[index++] = serverType.getName();
}
assertEquals(Arrays.asList(typeNames), Arrays.asList(serverTypeNames));
}
@Test
public void getTypeFromNameTest() throws ConfigurationException {
ServerType targetType = null;
for (ServerType serverType : ServerTypeHandler.getServerTypes()) {
if (serverType.getName().equals("Vanilla")) {
targetType = serverType;
}
}
ServerType typeFromName = ServerTypeHandler.getByName("Vanilla");
assertEquals(targetType, typeFromName);
}
}

View File

@ -0,0 +1,50 @@
package net.knarcraft.minecraftserverlauncher.server;
import net.knarcraft.minecraftserverlauncher.Main;
import net.knarcraft.minecraftserverlauncher.utility.CommonFunctions;
import org.junit.Before;
import org.junit.Test;
import java.io.*;
import static junit.framework.TestCase.assertEquals;
import static net.knarcraft.minecraftserverlauncher.utility.CommonFunctions.createAllFolders;
public class ServerVersionContainerTest {
private final String filesDirectory = Main.getApplicationWorkDirectory() + File.separator + "files";
private final String versionFile = filesDirectory + File.separator + "versions.csv";
private ServerVersionContainer serverVersionContainer;
@Before
public void setUp() {
try {
createAllFolders();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
File versionFileFile = new File(versionFile);
if (versionFileFile.exists() && !versionFileFile.delete()) {
throw new IllegalArgumentException("Unable to remove old version file.");
}
serverVersionContainer = ServerVersionContainer.getInstance();
}
@Test
public void toStringTest() {
serverVersionContainer.reset();
System.out.println(serverVersionContainer.toString());
assertEquals("vanillaVersion;null\nsnapshotVersion;null\nbungeeVersion;null\nwaterfallVersions;\n" +
"travertineVersions;\nspongeVanillaVersions;\nspongeForgeVersions;",
serverVersionContainer.toString());
}
@Test
public void saveStateTest() throws IOException {
serverVersionContainer.saveState();
BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(versionFile)));
String savedData = CommonFunctions.readBufferedReader(reader);
assertEquals(serverVersionContainer.toString(), savedData);
}
}

View File

@ -0,0 +1,23 @@
package net.knarcraft.minecraftserverlauncher.userinterface;
import java.io.File;
public class FakeGUI extends MessageHandler implements GUI {
/***
* Initializes a new fake gui
*/
public FakeGUI() {
super(true);
}
@Override
public void setStatus(String message) {
System.out.println(message);
}
@Override
public File askForDirectory(String prompt) {
return null;
}
}

View File

@ -0,0 +1,28 @@
package net.knarcraft.minecraftserverlauncher.utility;
import net.knarcraft.minecraftserverlauncher.Main;
import org.junit.Test;
import static net.knarcraft.minecraftserverlauncher.utility.CommonFunctions.stringBetween;
import static org.junit.Assert.assertEquals;
public class CommonFunctionsTest {
@Test
public void saveProfileTest() {
Main.getController().addProfile("Test");
Main.getController().saveState();
}
@Test
public void stringBetweenTest() {
String substring = stringBetween("fish'nchips", "f", "'");
assertEquals("ish", substring);
substring = stringBetween("something", "whale", "fish");
assertEquals("", substring);
substring = stringBetween("something", "so", "fish");
assertEquals("", substring);
substring = stringBetween("something", "asd", "ing");
assertEquals("", substring);
}
}

View File

@ -0,0 +1,120 @@
package net.knarcraft.minecraftserverlauncher.utility;
import net.knarcraft.minecraftserverlauncher.Main;
import net.knarcraft.minecraftserverlauncher.server.ServerTypeHandler;
import net.knarcraft.minecraftserverlauncher.server.servertypes.ServerType;
import net.knarcraft.minecraftserverlauncher.userinterface.FakeGUI;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import javax.naming.ConfigurationException;
import java.io.File;
import java.io.IOException;
import static junit.framework.TestCase.assertNotNull;
import static junit.framework.TestCase.assertTrue;
public class JarDownloaderTest {
private static String targetDirectory;
@BeforeClass
public static void setUp() {
targetDirectory = Main.getApplicationWorkDirectory() + File.separator + "files" + File.separator +
"testjars" + File.separator;
removeDownloadedFiles();
}
@AfterClass
public static void cleanUp() {
removeDownloadedFiles();
}
@Test
public void downloadJarsTest() throws IOException {
JarDownloader downloader = new JarDownloader(new FakeGUI(), targetDirectory);
downloader.downloadJars();
}
@Test
public void spongeVanillaDownloadTest() throws IOException, ConfigurationException {
singleDownloadTest(ServerTypeHandler.getByName("SpongeVanilla"));
}
@Test
public void spongeForgeDownloadTest() throws IOException, ConfigurationException {
singleDownloadTest(ServerTypeHandler.getByName("SpongeForge"));
}
@Test
public void spigotDownloadTest() throws ConfigurationException, IOException {
singleDownloadTest(ServerTypeHandler.getByName("Spigot"));
}
@Test
public void mcpcPlusDownloadTest() throws ConfigurationException, IOException {
singleDownloadTest(ServerTypeHandler.getByName("MCPCplus"));
}
@Test
public void craftbukkitDownloadTest() throws ConfigurationException, IOException {
singleDownloadTest(ServerTypeHandler.getByName("Bukkit"));
}
@Test
public void paperDownloadTest() throws ConfigurationException, IOException {
singleDownloadTest(ServerTypeHandler.getByName("Paper"));
}
@Test
public void bungeeDownloadTest() throws ConfigurationException, IOException {
singleDownloadTest(ServerTypeHandler.getByName("Bungee"));
}
@Test
public void waterfallDownloadTest() throws ConfigurationException, IOException {
singleDownloadTest(ServerTypeHandler.getByName("Waterfall"));
}
@Test
public void travertineDownloadTest() throws ConfigurationException, IOException {
singleDownloadTest(ServerTypeHandler.getByName("Travertine"));
}
@Test
public void vanillaDownloadTest() throws ConfigurationException, IOException {
singleDownloadTest(ServerTypeHandler.getByName("Vanilla"));
}
/**
* Downloads all .jar files for a single server type and asserts it works
*
* @param serverType <p>The server type to test</p>
* @throws IOException <p>If unable to download any of the .jar files</p>
*/
private void singleDownloadTest(ServerType serverType) throws IOException {
assertNotNull(serverType);
for (String serverVersion : serverType.getVersions()) {
System.out.println("Downloading " + serverType.getName() + serverVersion + ".jar");
serverType.downloadJar(targetDirectory, serverVersion);
assertTrue(new File(targetDirectory + serverType.getName() + serverVersion + ".jar").exists());
}
}
/**
* Removes downloaded test jars
*/
private static void removeDownloadedFiles() {
File target = new File(targetDirectory);
if (!target.exists() && !target.mkdirs()) {
throw new IllegalArgumentException("Unable to create the test files directory");
}
File[] oldFiles = target.listFiles();
if (oldFiles == null) {
throw new IllegalArgumentException("Unable to list files in jar test directory");
}
for (File file : oldFiles) {
assertTrue(file.delete());
}
}
}

View File

@ -1,15 +0,0 @@
import net.knarcraft.serverlauncher.profile.Profile;
import net.knarcraft.serverlauncher.server.ServerType;
import org.junit.Test;
import javax.naming.ConfigurationException;
import java.io.IOException;
public class DownloadTests {
@Test
public void downloadJarsTest() throws IOException, ConfigurationException {
// Will currently always fail since knarcraft.net is down.
ServerType.loadServerTypes();
Profile.downloadJars();
}
}

View File

@ -1,34 +0,0 @@
import net.knarcraft.serverlauncher.profile.Profile;
import net.knarcraft.serverlauncher.server.ServerType;
import org.junit.Test;
import javax.naming.ConfigurationException;
import java.io.FileNotFoundException;
import static net.knarcraft.serverlauncher.Shared.stringBetween;
import static org.junit.Assert.assertEquals;
public class Tests {
@Test
public void loadServerVersions() throws ConfigurationException { //Make sure the server versions file has correct syntax
ServerType.loadServerTypes();
}
@Test
public void saveProfile() throws FileNotFoundException { //Make sure we can write profiles to disk
Profile.addProfile("Test");
Profile.getCurrent().save();
}
@Test
public void stringBetweenTest() { //Make sure stringBetween is not creating exceptions
String substring = stringBetween("fish'nchips", "f", "'");
assertEquals("ish", substring);
substring = stringBetween("something", "whale", "fish");
assertEquals("", substring);
substring = stringBetween("something", "so", "fish");
assertEquals("", substring);
substring = stringBetween("something", "asd", "ing");
assertEquals("", substring);
}
}