8 Commits

Author SHA1 Message Date
70f8b87eef Updates software version to 1.2.3
All checks were successful
KnarCraft/Minecraft-Server-Launcher/pipeline/head This commit looks good
2020-09-22 15:15:05 +02:00
a9332c0a6c Removes Spigot 1.16.1 as it is deemed unnecessary 2020-09-22 15:14:07 +02:00
2b6315a914 Rewrites console output. Closes #4
Makes the servers be responsible for their own consoles
Starts reading from console once a server starts, and stops once the server stops
Makes reading from a buffered reader read character by character rather than line by line to fix reading from streams
Makes sure each server's console is read on different threads to prevent lockup
Prevents possible console bugs caused by only reading servers already enabled on startup
2020-09-22 15:12:07 +02:00
262418ff7f Updates software version to 1.2.2
All checks were successful
KnarCraft/Minecraft-Server-Launcher/pipeline/head This commit looks good
2020-09-20 20:39:13 +02:00
31d167a8a4 Closes a buffered reader in saveStateTest properly and removes a .close causing server consoles not to display text 2020-09-20 20:38:21 +02:00
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
7 changed files with 121 additions and 107 deletions

View File

@ -2,10 +2,8 @@ package net.knarcraft.minecraftserverlauncher;
import net.knarcraft.minecraftserverlauncher.profile.Collection; import net.knarcraft.minecraftserverlauncher.profile.Collection;
import net.knarcraft.minecraftserverlauncher.profile.Controller; import net.knarcraft.minecraftserverlauncher.profile.Controller;
import net.knarcraft.minecraftserverlauncher.server.Server;
import net.knarcraft.minecraftserverlauncher.userinterface.ServerConsoles; import net.knarcraft.minecraftserverlauncher.userinterface.ServerConsoles;
import net.knarcraft.minecraftserverlauncher.userinterface.ServerLauncherGUI; import net.knarcraft.minecraftserverlauncher.userinterface.ServerLauncherGUI;
import net.knarcraft.minecraftserverlauncher.utility.CommonFunctions;
import net.knarcraft.minecraftserverlauncher.utility.Updater; import net.knarcraft.minecraftserverlauncher.utility.Updater;
import java.awt.*; import java.awt.*;
@ -16,8 +14,6 @@ import java.net.URISyntaxException;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import static net.knarcraft.minecraftserverlauncher.utility.CommonFunctions.stringBetween;
//Java 8 required. //Java 8 required.
/** /**
@ -29,12 +25,12 @@ import static net.knarcraft.minecraftserverlauncher.utility.CommonFunctions.stri
*/ */
public class Main { public class Main {
private static String applicationWorkDirectory;
private static boolean serversAreRunning = false;
private static final String updateChannel = "beta"; private static final String updateChannel = "beta";
private static final String updateURL = "https://api.knarcraft.net/minecraftserverlauncher"; private static final String updateURL = "https://api.knarcraft.net/minecraftserverlauncher";
private static ServerLauncherGUI gui;
private static final Controller controller = Controller.getInstance(); private static final Controller controller = Controller.getInstance();
private static String applicationWorkDirectory;
private static boolean serversAreRunning = false;
private static ServerLauncherGUI gui;
public static void main(String[] args) throws IOException { public static void main(String[] args) throws IOException {
Updater.checkForUpdate(updateURL, updateChannel); Updater.checkForUpdate(updateURL, updateChannel);
@ -49,7 +45,7 @@ public class Main {
controller.loadState(); controller.loadState();
gui = controller.getGUI(); gui = controller.getGUI();
ScheduledExecutorService exec = Executors.newSingleThreadScheduledExecutor(); ScheduledExecutorService exec = Executors.newSingleThreadScheduledExecutor();
exec.scheduleAtFixedRate(Main::updateConsoles, 10, 500, TimeUnit.MILLISECONDS); exec.scheduleAtFixedRate(Main::updateServersRunningState, 10, 500, TimeUnit.MILLISECONDS);
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
} }
@ -83,98 +79,34 @@ public class Main {
} }
/** /**
* Reads from server processes, and writes the output to consoles. * Updates the software state if the servers' running state has changed
*/ */
private static void updateConsoles() { private static void updateServersRunningState() {
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(); boolean runningNew = serversRunning();
if (!runningNew && serversAreRunning) { if (serversAreRunning && !runningNew) {
//Servers stopped running
gui.updateGUIElementsWhenServersStartOrStop(false); gui.updateGUIElementsWhenServersStartOrStop(false);
gui.setStatus("Servers are stopped"); gui.setStatus("Servers are stopped");
} else if (runningNew && !serversAreRunning) { } else if (!serversAreRunning && runningNew) {
//Servers started running
gui.updateGUIElementsWhenServersStartOrStop(true); gui.updateGUIElementsWhenServersStartOrStop(true);
} }
serversAreRunning = runningNew; serversAreRunning = runningNew;
} catch (Exception e) {
e.printStackTrace();
}
} }
/** /**
* Goes through all servers and looks for any running servers. * Goes through all servers and looks for any running servers
* *
* @return Is at least one server running? * @return <p>Whether at least one server is running</p>
*/ */
private static boolean serversRunning() { private static boolean serversRunning() {
int num = 0; int serversRunning = 0;
for (Collection collection : controller.getCurrentProfile().getCollections()) { for (Collection collection : controller.getCurrentProfile().getCollections()) {
if (collection.getServer().isStarted() || if (collection.getServer().isStarted() ||
(collection.getServer().getProcess() != null && collection.getServer().getProcess().isAlive())) { (collection.getServer().getProcess() != null && collection.getServer().getProcess().isAlive())) {
num++; serversRunning++;
} }
} }
return num > 0; return serversRunning > 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

@ -4,6 +4,7 @@ import net.knarcraft.minecraftserverlauncher.Main;
import net.knarcraft.minecraftserverlauncher.profile.Collection; import net.knarcraft.minecraftserverlauncher.profile.Collection;
import net.knarcraft.minecraftserverlauncher.profile.Controller; import net.knarcraft.minecraftserverlauncher.profile.Controller;
import net.knarcraft.minecraftserverlauncher.server.servertypes.ServerType; import net.knarcraft.minecraftserverlauncher.server.servertypes.ServerType;
import net.knarcraft.minecraftserverlauncher.utility.CommonFunctions;
import javax.naming.ConfigurationException; import javax.naming.ConfigurationException;
import java.io.BufferedReader; import java.io.BufferedReader;
@ -15,8 +16,12 @@ import java.io.InputStreamReader;
import java.io.OutputStreamWriter; import java.io.OutputStreamWriter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import static net.knarcraft.minecraftserverlauncher.utility.CommonFunctions.stringBetween;
/** /**
* Contains all necessary information to create, runServer and manage a Minecraft server. * Contains all necessary information to create, runServer and manage a Minecraft server.
@ -45,6 +50,7 @@ public class Server {
private BufferedWriter writer; private BufferedWriter writer;
private BufferedReader reader; private BufferedReader reader;
private boolean started; private boolean started;
private ScheduledExecutorService consoleOutputExecutor;
/** /**
* Initializes a new server with default values * Initializes a new server with default values
@ -98,7 +104,7 @@ public class Server {
* *
* @return <p>The buffered reader used to read from this server</p> * @return <p>The buffered reader used to read from this server</p>
*/ */
public BufferedReader getReader() { private BufferedReader getReader() {
return this.reader; return this.reader;
} }
@ -276,7 +282,8 @@ public class Server {
/** /**
* Checks whether this server is fully stopped * Checks whether this server is fully stopped
*/ */
public void stopped() { private void stopped() {
consoleOutputExecutor.shutdown();
process = null; process = null;
writer = null; writer = null;
reader = null; reader = null;
@ -307,7 +314,7 @@ public class Server {
* @param name <p>The name of the player to check</p> * @param name <p>The name of the player to check</p>
* @return <p>True if the player is connected</p> * @return <p>True if the player is connected</p>
*/ */
public boolean hasPlayer(String name) { private boolean hasPlayer(String name) {
for (String player : this.playerList) { for (String player : this.playerList) {
if (player.equals(name)) { if (player.equals(name)) {
return true; return true;
@ -321,7 +328,7 @@ public class Server {
* *
* @param name <p>The name of the player to add</p> * @param name <p>The name of the player to add</p>
*/ */
public void addPlayer(String name) { private void addPlayer(String name) {
this.playerList.add(name); this.playerList.add(name);
Main.getController().getGUI().getServerControlTab().addPlayer(name); Main.getController().getGUI().getServerControlTab().addPlayer(name);
} }
@ -331,7 +338,7 @@ public class Server {
* *
* @param name <p>The name of the player to remove</p> * @param name <p>The name of the player to remove</p>
*/ */
public void removePlayer(String name) { private void removePlayer(String name) {
playerList.removeIf(player -> player.equals(name)); playerList.removeIf(player -> player.equals(name));
Main.getController().getGUI().getServerControlTab().removePlayer(name); Main.getController().getGUI().getServerControlTab().removePlayer(name);
} }
@ -396,6 +403,79 @@ public class Server {
this.process = builder.start(); this.process = builder.start();
this.writer = new BufferedWriter(new OutputStreamWriter(this.process.getOutputStream())); this.writer = new BufferedWriter(new OutputStreamWriter(this.process.getOutputStream()));
this.reader = new BufferedReader(new InputStreamReader(this.process.getInputStream())); this.reader = new BufferedReader(new InputStreamReader(this.process.getInputStream()));
//Start the process for reading from the server's console
consoleOutputExecutor = Executors.newSingleThreadScheduledExecutor();
consoleOutputExecutor.scheduleAtFixedRate(() -> {
try {
updateConsole();
} catch (IOException e) {
e.printStackTrace();
}
}, 10, 500, TimeUnit.MILLISECONDS);
}
/**
* Updates a single console with process output
*
* @throws IOException <p>If unable to read from the server's buffered reader</p>
*/
private void updateConsole() throws IOException {
String readText = CommonFunctions.readBufferedReader(getReader());
if (!readText.equals("")) {
Main.getController().getCurrentProfile().getCollection(getName()).getServerConsole().output(readText);
updatePlayerList(readText);
}
if (!getProcess().isAlive()) {
stopped();
}
}
/**
* Looks for strings implying a player has joined or left, and updates the appropriate lists
*
* @param text <p>The text to search</p>
*/
private void updatePlayerList(String text) {
if (!getType().isProxy()) {
String joinedPlayer = getPlayer(text, true);
String leftPlayer = getPlayer(text, false);
if (!joinedPlayer.equals("")) {
if (!hasPlayer(joinedPlayer)) {
addPlayer(joinedPlayer);
}
} else if (!leftPlayer.equals("")) {
if (hasPlayer(leftPlayer)) {
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 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");
}
if (playerName.equals("")) {
playerName = stringBetween(text, "INFO]: ", " left the game");
}
}
return playerName;
} }
/** /**

View File

@ -48,7 +48,7 @@ public class ServerVersionContainer {
/** /**
* Resets the state of the server version container * Resets the state of the server version container
*/ */
public void reset() { void reset() {
this.vanillaVersion = null; this.vanillaVersion = null;
this.snapshotVersion = null; this.snapshotVersion = null;
this.bungeeVersion = null; this.bungeeVersion = null;
@ -92,7 +92,7 @@ public class ServerVersionContainer {
/** /**
* Tries to save the state of this server version container * Tries to save the state of this server version container
*/ */
public void saveState() { void saveState() {
File saveFile = new File(versionFile); File saveFile = new File(versionFile);
PrintWriter file; PrintWriter file;
try { try {
@ -116,12 +116,10 @@ public class ServerVersionContainer {
* Loads the object state from the save file * Loads the object state from the save file
*/ */
private void loadState() { private void loadState() {
BufferedReader reader;
if (!new File(versionFile).exists()) { if (!new File(versionFile).exists()) {
return; return;
} }
try { try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(versionFile)))) {
reader = new BufferedReader(new InputStreamReader(new FileInputStream(versionFile)));
String currentData = CommonFunctions.readBufferedReader(reader); String currentData = CommonFunctions.readBufferedReader(reader);
for (String line : currentData.split("\n")) { for (String line : currentData.split("\n")) {
parseSaveLine(line); parseSaveLine(line);

View File

@ -131,7 +131,7 @@ public final class CommonFunctions {
* @param destination <p>Target destination</p> * @param destination <p>Target destination</p>
* @throws IOException <p>If we can't start a file stream</p> * @throws IOException <p>If we can't start a file stream</p>
*/ */
public static void copyFolder(GUI serverLauncherGui, File source, File destination) throws IOException { private static void copyFolder(GUI serverLauncherGui, File source, File destination) throws IOException {
if (!source.isDirectory()) { if (!source.isDirectory()) {
copyFile(serverLauncherGui, source, destination); copyFile(serverLauncherGui, source, destination);
} else { } else {
@ -195,10 +195,13 @@ public final class CommonFunctions {
* @throws IOException <p>If unable to read from the buffered reader</p> * @throws IOException <p>If unable to read from the buffered reader</p>
*/ */
public static String readBufferedReader(BufferedReader reader) throws IOException { public static String readBufferedReader(BufferedReader reader) throws IOException {
String line; //String line;
StringBuilder text = new StringBuilder(); StringBuilder text = new StringBuilder();
while (reader.ready() && (line = reader.readLine()) != null) { char[] readCharacters = new char[1000];
text.append(line).append("\n"); while (reader.ready()) {
reader.read(readCharacters);
text.append(readCharacters);
readCharacters = new char[1000];
} }
return text.toString().trim(); return text.toString().trim();
} }

View File

@ -1,2 +1,2 @@
beta beta
1.2.0 1.2.3
1 beta
2 1.2.0 1.2.3

View File

@ -1,8 +1,8 @@
Vanilla;Latest,Snapshot,1.16.2,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/ 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.2,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- Spigot;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.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.1,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- 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- 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.2,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.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- 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- 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 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- 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-

Can't render this file because it contains an unexpected character in line 1 and column 193.

View File

@ -44,6 +44,7 @@ public class ServerVersionContainerTest {
serverVersionContainer.saveState(); serverVersionContainer.saveState();
BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(versionFile))); BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(versionFile)));
String savedData = CommonFunctions.readBufferedReader(reader); String savedData = CommonFunctions.readBufferedReader(reader);
reader.close();
assertEquals(serverVersionContainer.toString(), savedData); assertEquals(serverVersionContainer.toString(), savedData);
} }