Links to knarcraft.net updated. Code for parsing the new minecraft vanilla json files added.
635 lines
23 KiB
Java
635 lines
23 KiB
Java
package net.knarcraft.serverlauncher.profile;
|
|
|
|
import com.google.gson.JsonElement;
|
|
import com.google.gson.JsonObject;
|
|
import com.google.gson.JsonParser;
|
|
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
|
|
);
|
|
} else {
|
|
System.out.println("Unable to save to file. Try running the software as an administrator.");
|
|
}
|
|
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();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Parses a profile, and creates a profile with the data.
|
|
*
|
|
* @param profileData The data of the new profile
|
|
* @return The new profile
|
|
*/
|
|
private static Profile parseProfile(String[] profileData) {
|
|
return new Profile(
|
|
profileData[0],
|
|
Boolean.parseBoolean(profileData[1]),
|
|
Integer.parseInt(profileData[2]),
|
|
Boolean.parseBoolean(profileData[3])
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Parses a server, and creates a new collection.
|
|
*
|
|
* @param profile The profile which to add the collection
|
|
* @param serverData The data to parse
|
|
*/
|
|
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])
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Downloads all jars to the program directory.
|
|
*
|
|
* @throws IOException On version file failure or folder creation failure
|
|
*/
|
|
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");
|
|
}
|
|
try {
|
|
downloadAll();
|
|
printToGui("Finished downloading jars");
|
|
} catch (FileNotFoundException e) {
|
|
printToGui("One or more downloads failed: " + e.getMessage());
|
|
}
|
|
}
|
|
|
|
private static void printToGui(String str) {
|
|
if (gui != null) {
|
|
gui.setStatus(str);
|
|
} else {
|
|
System.out.println(str);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Downloads jar files for all possible server versions.
|
|
*
|
|
* @throws IOException If a jar fails to download.
|
|
*/
|
|
private static void downloadAll() throws IOException {
|
|
for (ServerType type : ServerType.getServerTypes()) {
|
|
String url = Objects.requireNonNull(type).getDownloadURL(), name = type.getName(), newestVersion;
|
|
AdvancedServerType advType = type instanceof AdvancedServerType ? (AdvancedServerType) type : null;
|
|
for (String version : type.getVersions()) {
|
|
Boolean success;
|
|
printToGui("Downloading: " + name + version + ".jar");
|
|
File file = new File(jarDir + type.getName() + version + ".jar");
|
|
Path filePath = Paths.get(jarDir + type.getName() + version + ".jar");
|
|
switch (type.getName()) {
|
|
case "Vanilla":
|
|
case "Snapshot":
|
|
if (version.equals("Latest")) {
|
|
String versionText = readFile(Objects.requireNonNull(advType).getVersionURL());
|
|
JsonObject jsonObject = new JsonParser().parse(versionText).getAsJsonObject();
|
|
String latest = jsonObject.getAsJsonObject("latest").get("release").getAsString();
|
|
JsonElement ver = jsonObject.getAsJsonArray("versions").get(0);
|
|
String versionFile = ver.getAsJsonObject().get("url").getAsString();
|
|
|
|
versionText = readFile(versionFile);
|
|
jsonObject = new JsonParser().parse(versionText).getAsJsonObject();
|
|
String jarFile = jsonObject.getAsJsonObject("downloads").getAsJsonObject("server").get("url").getAsString();
|
|
|
|
setVersion(name, latest);
|
|
success = (file.isFile() && latest.equals(getVersion(name))) || downloadFile(jarFile, filePath);
|
|
} else {
|
|
success = file.isFile() || downloadFile(url + version + Objects.requireNonNull(advType).getDownloadURLPart() + version + ".jar", filePath);
|
|
}
|
|
break;
|
|
case "Spigot":
|
|
case "Craftbukkit":
|
|
case "MCPCplus":
|
|
success = file.isFile() || downloadFile(url + name + version + ".jar", filePath);
|
|
break;
|
|
case "SpongeVanilla":
|
|
newestVersion = stringBetween(readFile(Objects.requireNonNull(advType).getVersionURL() + version), advType.getSrcStart(), advType.getSrcEnd());
|
|
success = file.isFile() || downloadFile(url + newestVersion + advType.getDownloadURLPart() + newestVersion + ".jar", filePath);
|
|
break;
|
|
case "Bungee":
|
|
newestVersion = stringBetween(readFile(Objects.requireNonNull(advType).getVersionURL()), advType.getSrcStart(), advType.getSrcEnd());
|
|
setVersion(name, newestVersion);
|
|
success = (file.isFile() && newestVersion.equals(getVersion(name))) || downloadFile(url, filePath);
|
|
break;
|
|
default:
|
|
success = true;
|
|
}
|
|
if (!success) {
|
|
printToGui("Error downloading: " + name + version + ".jar");
|
|
throw new FileNotFoundException("Error downloading: " + name + version + ".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;
|
|
}
|
|
}
|
|
}
|
|
}
|