diff --git a/.gitignore b/.gitignore index 72287a1..4f5d0e9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,7 @@ .idea *.class *.jar -Parabot Launcher.iml -.DS_Store \ No newline at end of file +*.iml +.DS_Store +out/ +target/ \ No newline at end of file diff --git a/pom.xml b/pom.xml index 8a89d84..9f7e0f9 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,94 @@ org.parabot launcher - 0.1 + 0.2.1 + jar + + 1.7 + + + + + + parabot-maven + Parabot its Maven Repository + http://maven.parabot.org/ + + + + + + org.parabot + internal-api + 1.51.1 + + + + com.jfoenix + jfoenix + 1.0.0 + + + junit + junit + 4.10 + test + + + + + Parabot-Launcher-V${version}${build.version} + + + src/main/resources + true + + + deploy + true + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 2.3.2 + + ${jdk.version} + ${jdk.version} + + + + + org.apache.maven.plugins + maven-assembly-plugin + 2.5.3 + + + jar-with-dependencies + + + + org.parabot.launcher.Core + + + ${project.build.directory}/final/ + false + + + + + make-assembly + package + + single + + + + + + \ No newline at end of file diff --git a/src/main/java/org/parabot/launcher/Controller.java b/src/main/java/org/parabot/launcher/Controller.java index a0d3f3b..7ec812c 100644 --- a/src/main/java/org/parabot/launcher/Controller.java +++ b/src/main/java/org/parabot/launcher/Controller.java @@ -1,63 +1,219 @@ package org.parabot.launcher; +import com.jfoenix.controls.JFXButton; +import com.jfoenix.controls.JFXTextField; +import com.jfoenix.controls.JFXToggleButton; +import javafx.application.Platform; +import javafx.event.ActionEvent; import javafx.fxml.FXML; -import javafx.scene.control.ComboBox; +import javafx.fxml.Initializable; import javafx.scene.control.Label; -import javafx.scene.control.PasswordField; -import javafx.scene.control.TextField; +import org.parabot.api.misc.JavaUtil; +import org.parabot.launcher.helpers.Launcher; +import org.parabot.launcher.helpers.SettingHelper; +import org.parabot.launcher.helpers.VersionHelper; +import org.parabot.launcher.io.CacheManager; +import org.parabot.launcher.io.Reader; +import org.parabot.launcher.models.Setting; -public class Controller { +import java.net.URL; +import java.util.HashMap; +import java.util.ResourceBundle; - public static boolean useServer; - public static boolean useLogin; +public class Controller implements Initializable, ControllerImpl { - public static String server; - public static String username; - public static String password; + @FXML // fx:id="clearCacheButton" + private JFXButton clearCacheButton; - @FXML - ComboBox serverComboBox; + @FXML // fx:id="loadLocalToggleButton" + private JFXToggleButton loadLocalToggleButton; - @FXML - TextField usernameTextField; + @FXML // fx:id="versionLabel" + private Label versionLabel; - @FXML - PasswordField passwordPassField; + @FXML // fx:id="startButton" + private JFXButton startButton; - @FXML - Label versionLabel,statusLabel,javaVersionLabel; + @FXML // fx:id="noVerifyToggleButton" + private JFXToggleButton noVerifyToggleButton; + + @FXML // fx:id="verboseToggleButton" + private JFXToggleButton verboseToggleButton; + @FXML // fx:id="debugToggleButton" + private JFXToggleButton debugToggleButton; + @FXML // fx:id="serverTextField" + private JFXTextField serverTextField; - public void clearCache(){ + @FXML // fx:id="javaVersionLabel" + private Label javaVersionLabel; + @FXML // fx:id="serverToggleButton" + private JFXToggleButton serverToggleButton; + + @FXML // fx:id="statusLabel" + private Label statusLabel; + + private HashMap defaultLabelValues; + + public Controller() { + defaultLabelValues = new HashMap<>(); } - public void clearWorkingDir(){ + @Override + public void initialize(URL location, ResourceBundle resources) { + //Set Status TextField + setLabel(statusLabel, "Starting up"); + + //Remove the ServerTextField on launch if needed. + handleServerTextField(); + //Set serverTextField text color to white + serverTextField.setStyle("-fx-text-fill: white;"); + + fillComponents(); + + setStatus("Ready"); } - public void startClient(){ + @Override + public void fillComponentsFromOtherThread() { + Platform.runLater(new Runnable() { + @Override + public void run() { + fillComponents(); + } + }); + } + @Override + public void setReady(final int sleep) { + new Thread() { + @Override + public void run() { + try { + Thread.sleep(sleep); + } catch (InterruptedException e) { + e.printStackTrace(); + } + setStatus("Ready", true); + startButton.setDisable(false); + } + }.start(); } - public void setServer(){ - if(useServer) { - server = serverComboBox.getSelectionModel().getSelectedItem().toString(); + @Override + public void disableStart(boolean otherThread) { + if (otherThread) { + Platform.runLater(new Runnable() { + @Override + public void run() { + startButton.setDisable(true); + } + }); + } else { + startButton.setDisable(true); } } - public void setUsername(){ - if(useLogin) { - username = usernameTextField.getText(); + private void fillComponents() { + //Set Java Version. + setLabel(javaVersionLabel, String.valueOf(JavaUtil.JAVA_VERSION)); + + Reader.parseConfiguration(); + for (Setting setting : SettingHelper.getSettings()) { + switch (setting.getSetting().toLowerCase()) { + case "noverify": + noVerifyToggleButton.setSelected(setting.isEnabled()); + break; + case "loadlocal": + loadLocalToggleButton.setSelected(setting.isEnabled()); + break; + case "verbose": + verboseToggleButton.setSelected(setting.isEnabled()); + break; + case "debug": + debugToggleButton.setSelected(setting.isEnabled()); + break; + } } + + setLabel(versionLabel, String.valueOf(VersionHelper.getCurrentVersion())); } - public void setPassword(){ - if(useLogin) { - password = passwordPassField.getText(); + @FXML + private void clearCache(ActionEvent event) { + setReady(1500); + setLabel(statusLabel, "Clearing cache"); + CacheManager.clearCache(); + setLabel(statusLabel, "Cache cleared"); + } + + @FXML + private void startClient(ActionEvent event) { + disableStart(false); + setStatus("Starting client"); + + for (Setting setting : SettingHelper.getSettings()) { + switch (setting.getSetting().toLowerCase()) { + case "noverify": + setting.setEnabled(noVerifyToggleButton.isSelected()); + break; + case "loadlocal": + setting.setEnabled(loadLocalToggleButton.isSelected()); + break; + case "verbose": + setting.setEnabled(verboseToggleButton.isSelected()); + break; + case "debug": + setting.setEnabled(debugToggleButton.isSelected()); + break; + } } + + new Launcher(this).start(); + } + + @FXML + private void getServerSelected() { + handleServerTextField(); + } + + private void handleServerTextField() { + if (!serverToggleButton.isSelected() && serverTextField.isVisible()) { + serverTextField.setVisible(false); + } else { + serverTextField.setVisible(true); + } + } + + private void setLabel(Label label, String append) { + if (!defaultLabelValues.containsKey(label.getId())) { + defaultLabelValues.put(label.getId(), label.getText()); + } + + label.setText(defaultLabelValues.get(label.getId()) + append); + } + + @Override + public void setStatus(String status) { + setLabel(statusLabel, status); } + @Override + public void setStatus(final String status, boolean otherThread) { + if (otherThread) { + Platform.runLater(new Runnable() { + @Override + public void run() { + setStatus(status); + } + }); + } else { + setStatus(status); + } + } } + diff --git a/src/main/java/org/parabot/launcher/ControllerImpl.java b/src/main/java/org/parabot/launcher/ControllerImpl.java new file mode 100644 index 0000000..f8b32bd --- /dev/null +++ b/src/main/java/org/parabot/launcher/ControllerImpl.java @@ -0,0 +1,16 @@ +package org.parabot.launcher; + +/** + * @author JKetelaar + */ +public interface ControllerImpl { + void setStatus(String status); + + void setStatus(String status, boolean otherThread); + + void fillComponentsFromOtherThread(); + + void setReady(int sleep); + + void disableStart(boolean otherThread); +} diff --git a/src/main/java/org/parabot/launcher/Core.java b/src/main/java/org/parabot/launcher/Core.java index 481ac37..2329ad1 100644 --- a/src/main/java/org/parabot/launcher/Core.java +++ b/src/main/java/org/parabot/launcher/Core.java @@ -2,29 +2,26 @@ import javafx.application.Application; import javafx.fxml.FXMLLoader; -import javafx.scene.Parent; import javafx.scene.Scene; +import javafx.scene.layout.AnchorPane; import javafx.stage.Stage; - -import java.net.URL; +import org.parabot.launcher.data.Configuration; /** - * @author JKetelaar + * @author JKetelaar, Fryslan */ public class Core extends Application { + public static void main(String[] args) { + launch(args); + } + @Override - public void start(Stage stage) throws Exception{ - //todo set the right path for fxml file, file on webserver is preferred @JKetelaar. - URL fxmlLocation = this.getClass().getResource("view.fxml"); - Parent root = FXMLLoader.load(fxmlLocation); - stage.setTitle("Parabot"); + public void start(Stage stage) throws Exception { + //noinspection RedundantCast + AnchorPane root = (AnchorPane) FXMLLoader.load(this.getClass().getResource("/storage/interface.fxml")); + stage.setTitle(Configuration.BOT_TITLE); stage.setScene(new Scene(root)); stage.show(); } - - - public static void main(String[] args) { - launch(args); - } } diff --git a/src/main/java/org/parabot/launcher/Terminal.java b/src/main/java/org/parabot/launcher/Terminal.java new file mode 100644 index 0000000..051fc42 --- /dev/null +++ b/src/main/java/org/parabot/launcher/Terminal.java @@ -0,0 +1,42 @@ +package org.parabot.launcher; + +import org.parabot.launcher.data.Configuration; +import org.parabot.launcher.helpers.SettingHelper; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collections; + +/** + * @author EmmaStone, JKetelaar + */ +public class Terminal { + + public void execute() throws IOException, InterruptedException { + ArrayList executions = new ArrayList<>(); + + executions.add(System.getProperty("java.home") + File.separator + "bin/java"); + executions.add("-jar"); + + Collections.addAll(executions, SettingHelper.createJavaCommandLine()); + + executions.add(Configuration.CLIENT_LOCATION); + + Collections.addAll(executions, SettingHelper.createApplicationCommandLine()); + + Process process = Runtime.getRuntime().exec(executions.toArray(new String[0])); + + process.waitFor(); + + InputStream inputStream = process.getInputStream(); + InputStream processErrorStream = process.getErrorStream(); + + byte b[] = new byte[inputStream.available()]; + inputStream.read(b, 0, b.length); + + byte c[] = new byte[processErrorStream.available()]; + processErrorStream.read(c, 0, c.length); + } +} diff --git a/src/main/java/org/parabot/launcher/data/Configuration.java b/src/main/java/org/parabot/launcher/data/Configuration.java new file mode 100644 index 0000000..6448032 --- /dev/null +++ b/src/main/java/org/parabot/launcher/data/Configuration.java @@ -0,0 +1,17 @@ +package org.parabot.launcher.data; + +import org.parabot.api.io.Directories; + +import java.io.File; + +/** + * @author EmmaStone + */ +public class Configuration extends org.parabot.api.Configuration { + + public static final String CONFIG_NAME = "launcher"; + public static final String LAUNCHER_CONFIG_LOCATION = Directories.getSettingsPath() + File.separator + Configuration.CONFIG_NAME + ".json"; + public static final String CLIENT_LOCATION = Directories.getCachePath() + File.separator + BOT_TITLE + ".jar"; + public static final String GET_BOT_VERSION = "http://bdn.parabot.org/api/v2/bot/version"; + public static final String GET_LATEST_BOT_VERSION = "http://v3.bdn.parabot.org/api/bot/list/client?limit=1"; +} diff --git a/src/main/java/org/parabot/launcher/helpers/Launcher.java b/src/main/java/org/parabot/launcher/helpers/Launcher.java new file mode 100644 index 0000000..8e82355 --- /dev/null +++ b/src/main/java/org/parabot/launcher/helpers/Launcher.java @@ -0,0 +1,74 @@ +package org.parabot.launcher.helpers; + +import org.json.simple.JSONObject; +import org.json.simple.parser.ParseException; +import org.parabot.api.io.WebUtil; +import org.parabot.api.misc.Version; +import org.parabot.launcher.ControllerImpl; +import org.parabot.launcher.Terminal; +import org.parabot.launcher.data.Configuration; +import org.parabot.launcher.io.Downloader; +import org.parabot.launcher.io.Writer; + +import java.io.File; +import java.io.IOException; + +/** + * @author JKetelaar + */ +public class Launcher extends Thread { + + private ControllerImpl controller; + + public Launcher(ControllerImpl controller) { + this.controller = controller; + } + + @Override + public void run() { + controller.setStatus("Checking if client is up to date", true); + if (!new File(Configuration.CLIENT_LOCATION).exists() || !VersionHelper.validVersion()) { + System.out.println(VersionHelper.validVersion()); + controller.setStatus("Updating client (25%)", true); + + try { + JSONObject version = (JSONObject) WebUtil.getJsonParser().parse(WebUtil.getReader(Configuration.GET_LATEST_BOT_VERSION)); + VersionHelper.setCurrentVersion(new Version((String) version.get("version"))); + } catch (IOException | ParseException e) { + e.printStackTrace(); + } + + controller.setStatus("Updating client (50%)", true); + + Thread downloadThread = new Thread(new DownloadExecutor()); + downloadThread.start(); + try { + downloadThread.join(); + } catch (InterruptedException e) { + controller.setStatus("Client failed to update", true); + e.printStackTrace(); + } + + controller.setStatus("Client updated", true); + } + + Writer.writeConfiguration(); + controller.fillComponentsFromOtherThread(); + controller.setStatus("Starting client", true); + + controller.setReady(3500); + + try { + new Terminal().execute(); + } catch (IOException | InterruptedException e) { + e.printStackTrace(); + } + } + + private class DownloadExecutor implements Runnable { + @Override + public void run() { + Downloader.downloadFile(Configuration.DOWNLOAD_BOT, Configuration.CLIENT_LOCATION); + } + } +} diff --git a/src/main/java/org/parabot/launcher/helpers/SettingHelper.java b/src/main/java/org/parabot/launcher/helpers/SettingHelper.java new file mode 100644 index 0000000..af64434 --- /dev/null +++ b/src/main/java/org/parabot/launcher/helpers/SettingHelper.java @@ -0,0 +1,76 @@ +package org.parabot.launcher.helpers; + +import org.json.simple.JSONObject; +import org.parabot.launcher.models.ServerSetting; +import org.parabot.launcher.models.Setting; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author JKetelaar + */ +public class SettingHelper { + + private static final List settings; + + static { + settings = new ArrayList<>(); + createSettings(); + } + + private static void createSettings() { + settings.add(new Setting("noverify", false)); + settings.add(new Setting("loadlocal")); + settings.add(new Setting("verbose")); + settings.add(new Setting("debug")); + settings.add(new ServerSetting()); + } + + public static List getSettings() { + return settings; + } + + public static Setting getSettingByCommand(String command) { + for (Setting setting : settings) { + if (setting.getSetting().equals(command)) { + return setting; + } + } + + return null; + } + + public static JSONObject createJSONObjects() { + JSONObject object = new JSONObject(); + for (Setting setting : settings) { + if (setting.isEnabled()) { + object.put(setting.getSetting(), setting.toJSON()); + } + } + + return object; + } + + public static String[] createApplicationCommandLine() { + ArrayList total = new ArrayList<>(); + for (Setting setting : settings) { + if (setting.isEnabled() && setting.isApplicationArgument()) { + total.add(setting.toCommand()); + } + } + + return total.toArray(new String[0]); + } + + public static String[] createJavaCommandLine() { + ArrayList total = new ArrayList<>(); + for (Setting setting : settings) { + if (setting.isEnabled() && !setting.isApplicationArgument()) { + total.add(setting.toCommand()); + } + } + + return total.toArray(new String[0]); + } +} diff --git a/src/main/java/org/parabot/launcher/helpers/VersionHelper.java b/src/main/java/org/parabot/launcher/helpers/VersionHelper.java new file mode 100644 index 0000000..b7141f3 --- /dev/null +++ b/src/main/java/org/parabot/launcher/helpers/VersionHelper.java @@ -0,0 +1,55 @@ +package org.parabot.launcher.helpers; + +import org.json.simple.JSONObject; +import org.json.simple.parser.ParseException; +import org.parabot.api.Configuration; +import org.parabot.api.io.Directories; +import org.parabot.api.io.WebUtil; +import org.parabot.api.misc.Version; + +import java.io.BufferedReader; +import java.io.IOException; + +/** + * @author EmmaStone + */ +public class VersionHelper { + + private static Version currentVersion; + + public static String getCurrentVersion() { + return currentVersion != null ? currentVersion.get() : ""; + } + + public static void setCurrentVersion(Version currentVersion) { + VersionHelper.currentVersion = currentVersion; + } + + public static boolean validVersion() { + String url = String.format(Configuration.COMPARE_VERSION_URL, "client", getCurrentVersion()); + + BufferedReader br = WebUtil.getReader(url); + try { + if (br != null) { + JSONObject object = (JSONObject) WebUtil.getJsonParser().parse(br); + boolean latest = (boolean) object.get("result"); + if (!latest) { + Directories.clearCache(); + } + return latest; + } + } catch (IOException | ParseException e) { + e.printStackTrace(); + } finally { + try { + if (br != null) { + br.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + + return true; + } +} diff --git a/src/main/java/org/parabot/launcher/io/CacheManager.java b/src/main/java/org/parabot/launcher/io/CacheManager.java new file mode 100644 index 0000000..5c6aa42 --- /dev/null +++ b/src/main/java/org/parabot/launcher/io/CacheManager.java @@ -0,0 +1,13 @@ +package org.parabot.launcher.io; + +import org.parabot.api.io.Directories; + +/** + * @author EmmaStone + */ +public class CacheManager { + + public static void clearCache() { + Directories.clearCache(); + } +} diff --git a/src/main/java/org/parabot/launcher/io/Downloader.java b/src/main/java/org/parabot/launcher/io/Downloader.java new file mode 100644 index 0000000..6cf22af --- /dev/null +++ b/src/main/java/org/parabot/launcher/io/Downloader.java @@ -0,0 +1,21 @@ +package org.parabot.launcher.io; + +import org.parabot.api.io.WebUtil; + +import java.io.File; +import java.net.MalformedURLException; +import java.net.URL; + +/** + * @author EmmaStone + */ +public class Downloader { + + public static void downloadFile(String url, String location) { + try { + WebUtil.downloadFile(new URL(url), new File(location), null); + } catch (MalformedURLException e) { + e.printStackTrace(); + } + } +} diff --git a/src/main/java/org/parabot/launcher/io/Reader.java b/src/main/java/org/parabot/launcher/io/Reader.java new file mode 100644 index 0000000..858e9df --- /dev/null +++ b/src/main/java/org/parabot/launcher/io/Reader.java @@ -0,0 +1,46 @@ +package org.parabot.launcher.io; + +import org.json.simple.JSONObject; +import org.json.simple.parser.ParseException; +import org.parabot.api.io.WebUtil; +import org.parabot.api.misc.Version; +import org.parabot.launcher.data.Configuration; +import org.parabot.launcher.helpers.SettingHelper; +import org.parabot.launcher.helpers.VersionHelper; +import org.parabot.launcher.models.Setting; + +import java.io.File; +import java.io.FileReader; +import java.io.IOException; + +/** + * @author JKetelaar + */ +public class Reader { + + public static void parseConfiguration() { + if (new File(Configuration.LAUNCHER_CONFIG_LOCATION).exists()) { + try { + Object object = WebUtil.getJsonParser().parse(new FileReader(Configuration.LAUNCHER_CONFIG_LOCATION)); + JSONObject jsonObject = (JSONObject) object; + + if (jsonObject.containsKey("version")) { + String version = (String) jsonObject.get("version"); + VersionHelper.setCurrentVersion(new Version(version)); + } + + for (Object keyObject : ((JSONObject) jsonObject.get("commands")).keySet()) { + String key = (String) keyObject; + JSONObject value = (JSONObject) ((JSONObject) jsonObject.get("commands")).get(key); + + Setting setting; + if ((setting = SettingHelper.getSettingByCommand(key)) != null) { + setting.setFromJSONObject(value); + } + } + } catch (IOException | ParseException e) { + e.printStackTrace(); + } + } + } +} diff --git a/src/main/java/org/parabot/launcher/io/Writer.java b/src/main/java/org/parabot/launcher/io/Writer.java new file mode 100644 index 0000000..efa2bab --- /dev/null +++ b/src/main/java/org/parabot/launcher/io/Writer.java @@ -0,0 +1,31 @@ +package org.parabot.launcher.io; + +import org.json.simple.JSONObject; +import org.parabot.launcher.data.Configuration; +import org.parabot.launcher.helpers.SettingHelper; +import org.parabot.launcher.helpers.VersionHelper; + +import java.io.FileWriter; +import java.io.IOException; + +/** + * @author JKetelaar + */ +public class Writer { + + public static void writeConfiguration() { + try { + JSONObject jsonObject = new JSONObject(); + jsonObject.put("commands", SettingHelper.createJSONObjects()); + jsonObject.put("version", VersionHelper.getCurrentVersion()); + + FileWriter fileWriter = new FileWriter(Configuration.LAUNCHER_CONFIG_LOCATION); + + fileWriter.write(jsonObject.toJSONString()); + fileWriter.flush(); + fileWriter.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/src/main/java/org/parabot/launcher/models/ServerSetting.java b/src/main/java/org/parabot/launcher/models/ServerSetting.java new file mode 100644 index 0000000..fe0e63d --- /dev/null +++ b/src/main/java/org/parabot/launcher/models/ServerSetting.java @@ -0,0 +1,42 @@ +package org.parabot.launcher.models; + +import org.json.simple.JSONObject; + +/** + * @author JKetelaar + */ +public class ServerSetting extends Setting { + + private String server; + + public ServerSetting() { + super("server"); + } + + public String getServer() { + return server; + } + + public void setServer(String server) { + this.server = server; + } + + @Override + public JSONObject toJSON() { + JSONObject object = super.toJSON(); + object.put("server", server); + + return object; + } + + @Override + public void setFromJSONObject(JSONObject object) { + super.setFromJSONObject(object); + this.server = (String) object.get("server"); + } + + @Override + public String toCommand() { + return super.toCommand() + " " + server; + } +} diff --git a/src/main/java/org/parabot/launcher/models/Setting.java b/src/main/java/org/parabot/launcher/models/Setting.java new file mode 100644 index 0000000..2ff363d --- /dev/null +++ b/src/main/java/org/parabot/launcher/models/Setting.java @@ -0,0 +1,70 @@ +package org.parabot.launcher.models; + +import org.json.simple.JSONObject; + +/** + * @author JKetelaar + */ +public class Setting { + + private final String setting; + private boolean enabled; + /** + * Defines if the setting should be put before or after the application + * if true: java -jar application.jar -setting + * if false: java -jar -setting application.jar + */ + private boolean applicationArgument = true; + + public Setting(String setting) { + this.setting = setting; + } + + public Setting(String setting, boolean applicationArgument) { + this.setting = setting; + this.applicationArgument = applicationArgument; + this.enabled = false; + } + + public Setting(String setting, boolean applicationArgument, boolean enabled) { + this.setting = setting; + this.applicationArgument = applicationArgument; + this.enabled = enabled; + } + + public Setting(boolean enabled, String setting) { + this.enabled = enabled; + this.setting = setting; + } + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public String getSetting() { + return setting; + } + + public JSONObject toJSON() { + JSONObject object = new JSONObject(); + object.put("enabled", enabled); + + return object; + } + + public void setFromJSONObject(JSONObject object) { + this.enabled = (boolean) object.get("enabled"); + } + + public String toCommand() { + return "-" + setting; + } + + public boolean isApplicationArgument() { + return applicationArgument; + } +} diff --git a/src/main/java/org/parabot/launcher/view.fxml b/src/main/java/org/parabot/launcher/view.fxml deleted file mode 100644 index 7ad5c70..0000000 --- a/src/main/java/org/parabot/launcher/view.fxml +++ /dev/null @@ -1,74 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -