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
This commit is contained in:
2020-09-22 15:12:07 +02:00
parent 262418ff7f
commit 2b6315a914
3 changed files with 111 additions and 96 deletions

View File

@ -4,6 +4,7 @@ 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 net.knarcraft.minecraftserverlauncher.utility.CommonFunctions;
import javax.naming.ConfigurationException;
import java.io.BufferedReader;
@ -15,8 +16,12 @@ import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
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.
@ -45,6 +50,7 @@ public class Server {
private BufferedWriter writer;
private BufferedReader reader;
private boolean started;
private ScheduledExecutorService consoleOutputExecutor;
/**
* 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>
*/
public BufferedReader getReader() {
private BufferedReader getReader() {
return this.reader;
}
@ -276,7 +282,8 @@ public class Server {
/**
* Checks whether this server is fully stopped
*/
public void stopped() {
private void stopped() {
consoleOutputExecutor.shutdown();
process = null;
writer = null;
reader = null;
@ -307,7 +314,7 @@ public class Server {
* @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) {
private boolean hasPlayer(String name) {
for (String player : this.playerList) {
if (player.equals(name)) {
return true;
@ -321,7 +328,7 @@ public class Server {
*
* @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);
Main.getController().getGUI().getServerControlTab().addPlayer(name);
}
@ -331,7 +338,7 @@ public class Server {
*
* @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));
Main.getController().getGUI().getServerControlTab().removePlayer(name);
}
@ -396,6 +403,79 @@ public class Server {
this.process = builder.start();
this.writer = new BufferedWriter(new OutputStreamWriter(this.process.getOutputStream()));
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;
}
/**