diff --git a/.gitignore b/.gitignore index 5ffcfbf..9feece6 100644 --- a/.gitignore +++ b/.gitignore @@ -37,4 +37,8 @@ bin/ .vscode/ ### Mac OS ### -.DS_Store \ No newline at end of file +.DS_Store + + +### cache ### +cache/ \ No newline at end of file diff --git a/build.gradle b/build.gradle index e4e81f1..51bdf5e 100644 --- a/build.gradle +++ b/build.gradle @@ -7,7 +7,7 @@ sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 group = 'com.nexia' -version = '1.0.1' +version = '1.0.2' repositories { mavenCentral() diff --git a/src/main/java/com/nexia/installer/InstallerGUI.java b/src/main/java/com/nexia/installer/InstallerGUI.java index f723353..cbb1d5c 100644 --- a/src/main/java/com/nexia/installer/InstallerGUI.java +++ b/src/main/java/com/nexia/installer/InstallerGUI.java @@ -1,6 +1,7 @@ package com.nexia.installer; import com.nexia.installer.util.InstallerHelper; +import com.nexia.installer.util.fabric.FabricInstallerHelper; import javax.swing.*; import java.awt.*; @@ -11,13 +12,18 @@ public class InstallerGUI extends JFrame { public static InstallerGUI instance; + public JPanel vanilla; + + public JPanel fabric; + + public JTabbedPane pane; + public InstallerGUI() { - InstallerHelper helper = new InstallerHelper(); - JPanel panel = helper.setPanel(this); + this.vanilla = new InstallerHelper().setPanel(this); + this.fabric = new FabricInstallerHelper().setPanel(this); setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); - add(panel); instance = this; } @@ -43,6 +49,15 @@ public static void load() throws UnsupportedLookAndFeelException, ClassNotFoundE } InstallerGUI gui = new InstallerGUI(); + + gui.pane = new JTabbedPane(JTabbedPane.TOP); + + gui.pane.addTab(Main.BUNDLE.getString("installer.tab.vanilla"), gui.vanilla); + gui.pane.addTab(Main.BUNDLE.getString("installer.tab.fabric"), gui.fabric); + + gui.setContentPane(gui.pane); + + gui.updateSize(true); gui.setTitle(Main.BUNDLE.getString("installer.title")); gui.setIconImage(Main.icon); @@ -50,7 +65,7 @@ public static void load() throws UnsupportedLookAndFeelException, ClassNotFoundE gui.setVisible(true); } - public void updateSize(boolean updateMinimum) { + private void updateSize(boolean updateMinimum) { if (updateMinimum) setMinimumSize(null); setPreferredSize(null); pack(); diff --git a/src/main/java/com/nexia/installer/Main.java b/src/main/java/com/nexia/installer/Main.java index d28abf0..570505a 100644 --- a/src/main/java/com/nexia/installer/Main.java +++ b/src/main/java/com/nexia/installer/Main.java @@ -36,14 +36,13 @@ public ResourceBundle newBundle(String baseName, Locale locale, String format, C public static void main(String[] args) throws UnsupportedLookAndFeelException, ClassNotFoundException, InstantiationException, IllegalAccessException { + os = OS.LINUX; + if(System.getProperty("os.name").startsWith("Windows")){ os = OS.WINDOWS; System.setProperty("javax.net.ssl.trustStoreType", "WINDOWS-ROOT"); } - if(System.getProperty("os.name").startsWith("lin")) - os = OS.LINUX; - if(System.getProperty("os.name").startsWith("mac")) os = OS.MAC; diff --git a/src/main/java/com/nexia/installer/util/HttpAPI.java b/src/main/java/com/nexia/installer/util/HttpAPI.java new file mode 100644 index 0000000..1dcbe83 --- /dev/null +++ b/src/main/java/com/nexia/installer/util/HttpAPI.java @@ -0,0 +1,33 @@ +package com.nexia.installer.util; + +import java.io.*; +import java.net.HttpURLConnection; +import java.net.URL; + +public class HttpAPI { + + public static String userAgent = "Mozilla/5.0 (compatible; combat-test-installer; +https://github.com/nexia-cts/combat-test-installer)"; + + public static String get(String url) { + try { + URL apiUrl = new URL(url); + + HttpURLConnection connection = (HttpURLConnection) apiUrl.openConnection(); + + connection.setRequestProperty("User-Agent", userAgent); + connection.setRequestMethod("GET"); + + BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream())); + String line; + StringBuilder response = new StringBuilder(); + while ((line = reader.readLine()) != null) { + response.append(line); + } + + reader.close(); + connection.disconnect(); + + return response.toString(); + } catch (Exception ignored) { return null; } + } +} \ No newline at end of file diff --git a/src/main/java/com/nexia/installer/util/InstallerHelper.java b/src/main/java/com/nexia/installer/util/InstallerHelper.java index b62237b..189589e 100644 --- a/src/main/java/com/nexia/installer/util/InstallerHelper.java +++ b/src/main/java/com/nexia/installer/util/InstallerHelper.java @@ -55,7 +55,7 @@ public JPanel setPanel(InstallerGUI gui) { buttonInstall.addActionListener(e -> { buttonInstall.setEnabled(false); try { - install(); + launch(); } catch (IOException ex) { throw new RuntimeException(ex); } @@ -107,7 +107,7 @@ public void addRow(Container parent, GridBagConstraints c, boolean last, String c.gridx = 0; } - private void install() throws IOException { + public void launch() throws IOException { String stringGameVersion = (String) gameVersionComboBox.getSelectedItem(); VersionHandler.GameVersion gameVersion = VersionHandler.identifyGameVersion(stringGameVersion); diff --git a/src/main/java/com/nexia/installer/util/InstallerUtils.java b/src/main/java/com/nexia/installer/util/InstallerUtils.java index 1cd2f81..708b061 100644 --- a/src/main/java/com/nexia/installer/util/InstallerUtils.java +++ b/src/main/java/com/nexia/installer/util/InstallerUtils.java @@ -1,14 +1,12 @@ package com.nexia.installer.util; +import com.nexia.installer.InstallerGUI; import com.nexia.installer.Main; import com.nexia.installer.game.VersionHandler; import javax.swing.*; -import java.awt.*; import java.io.File; -import java.io.IOException; import java.net.URI; -import java.net.URISyntaxException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -115,7 +113,7 @@ public static void install(Path mcDir, VersionHandler.GameVersion gameVersion) { }).start(); } - private static ProfileInstaller.LauncherType showLauncherTypeSelection() { + public static ProfileInstaller.LauncherType showLauncherTypeSelection() { Object[] options = { Main.BUNDLE.getString("installer.prompt.launcher.type.xbox"), Main.BUNDLE.getString("installer.prompt.launcher.type.win32")}; int result = JOptionPane.showOptionDialog(null, @@ -135,7 +133,7 @@ private static ProfileInstaller.LauncherType showLauncherTypeSelection() { return result == JOptionPane.YES_OPTION ? ProfileInstaller.LauncherType.MICROSOFT_STORE : ProfileInstaller.LauncherType.WIN32; } - private static void showDone(VersionHandler.GameVersion gameVersion) throws URISyntaxException, IOException { + private static void showDone(VersionHandler.GameVersion gameVersion) { Object[] options = {"OK", "Install Fabric"}; int result = JOptionPane.showOptionDialog(null, MessageFormat.format(Main.BUNDLE.getString("installer.prompt.install.done"), gameVersion.getVersion()), @@ -147,6 +145,6 @@ private static void showDone(VersionHandler.GameVersion gameVersion) throws URIS options[0] ); - if(result == JOptionPane.NO_OPTION && Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) Desktop.getDesktop().browse(new URI("https://github.com/rizecookey/fabric-installer/releases")); + if(result == JOptionPane.NO_OPTION) InstallerGUI.instance.pane.setSelectedComponent(InstallerGUI.instance.fabric); } } diff --git a/src/main/java/com/nexia/installer/util/ProfileInstaller.java b/src/main/java/com/nexia/installer/util/ProfileInstaller.java index 0b6292b..b648f17 100644 --- a/src/main/java/com/nexia/installer/util/ProfileInstaller.java +++ b/src/main/java/com/nexia/installer/util/ProfileInstaller.java @@ -81,13 +81,17 @@ private static Json createProfile(String name) { } public enum LauncherType { - WIN32("launcher_profiles.json"), - MICROSOFT_STORE("launcher_profiles_microsoft_store.json"); + WIN32("win32", "launcher_profiles.json"), + MICROSOFT_STORE("microsoft_store", "launcher_profiles_microsoft_store.json"); public final String profileJsonName; - LauncherType(String profileJsonName) { + public final String name; + + LauncherType(String name, String profileJsonName) { + this.name = name; this.profileJsonName = profileJsonName; } + } } diff --git a/src/main/java/com/nexia/installer/util/Utils.java b/src/main/java/com/nexia/installer/util/Utils.java index 26016c7..c1abf14 100644 --- a/src/main/java/com/nexia/installer/util/Utils.java +++ b/src/main/java/com/nexia/installer/util/Utils.java @@ -25,7 +25,7 @@ public class Utils { public static void extractZip(Path file, Path path) throws IOException { ZipInputStream zipIn = new ZipInputStream(Files.newInputStream(Paths.get(file.toString()))); ZipEntry entry = zipIn.getNextEntry(); - String filePath = ""; + String filePath; // iterates over entries in the zip file while (entry != null) { filePath = path + File.separator + entry.getName(); @@ -136,9 +136,7 @@ public static String getProfileIcon() { return "data:image/png;base64," + Base64.getEncoder().encodeToString(Arrays.copyOf(ret, offset)); } catch (IOException e) { - e.printStackTrace(); + return "furnace"; // Fallback to furnace icon if we cant load Nexia icon. } - - return "furnace"; // Fallback to furnace icon if we cant load CTS icon. } } diff --git a/src/main/java/com/nexia/installer/util/fabric/FabricInstallerHelper.java b/src/main/java/com/nexia/installer/util/fabric/FabricInstallerHelper.java new file mode 100644 index 0000000..07334fb --- /dev/null +++ b/src/main/java/com/nexia/installer/util/fabric/FabricInstallerHelper.java @@ -0,0 +1,225 @@ +package com.nexia.installer.util.fabric; + +import com.nexia.installer.InstallerGUI; +import com.nexia.installer.Main; +import com.nexia.installer.util.*; +import mjson.Json; + +import javax.swing.*; +import javax.swing.border.EmptyBorder; +import java.awt.*; +import java.io.BufferedInputStream; +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.text.MessageFormat; +import java.util.List; + +public class FabricInstallerHelper extends InstallerHelper { + public static JButton buttonInstall; + + public static JButton buttonFabric; + public JComboBox gameVersionComboBox; + public JTextField installLocation; + public JButton selectFolderButton; + + public static JCheckBox createProfile; + + @Override + public JPanel setPanel(InstallerGUI gui) { + JPanel panel = new JPanel(new GridBagLayout()); + panel.setBorder(new EmptyBorder(4, 4, 4, 4)); + + GridBagConstraints c = new GridBagConstraints(); + c.insets = new Insets(6, 4, 6, 4); + c.gridx = c.gridy = 0; + + addRow(panel, c, "installer.prompt.game.version", + gameVersionComboBox = new JComboBox<>(), + createSpacer() + ); + + for(FabricVersionHandler.GameVersion version : FabricVersionHandler.versions) { + gameVersionComboBox.addItem(version.getVersion()); + } + + addRow(panel, c, "installer.prompt.select.location", + installLocation = new JTextField(20), + selectFolderButton = new JButton()); + selectFolderButton.setText("..."); + selectFolderButton.setPreferredSize(new Dimension(installLocation.getPreferredSize().height, installLocation.getPreferredSize().height)); + selectFolderButton.addActionListener(e -> InstallerGUI.selectInstallLocation(() -> installLocation.getText(), s -> installLocation.setText(s))); + + addRow(panel, c, null, + createProfile = new JCheckBox(Main.BUNDLE.getString("installer.option.create.profile"), true)); + + installLocation.setText(InstallerUtils.findDefaultInstallDir().toString()); + + addLastRow(panel, c, + buttonInstall = new JButton(Main.BUNDLE.getString("installer.button.install"))); + buttonInstall.addActionListener(e -> { + buttonInstall.setEnabled(false); + try { + launch(); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + }); + + addLastRow(panel, c, + buttonFabric = new JButton(Main.BUNDLE.getString("installer.button.fabric"))); + buttonFabric.addActionListener(e -> { + try { + Process process = Runtime.getRuntime().exec("java -jar cache/" + getJarFile().getName()); + while(process.isAlive()) { + buttonFabric.setEnabled(false); + } + buttonFabric.setEnabled(true); + } catch (IOException ex) { + buttonFabric.setEnabled(false); + } + }); + + return panel; + } + + @Override + public void launch() throws IOException { + + String stringGameVersion = (String) gameVersionComboBox.getSelectedItem(); + FabricVersionHandler.GameVersion gameVersion = FabricVersionHandler.identifyGameVersion(stringGameVersion); + if(gameVersion == null) return; + + + Path mcPath = Paths.get(installLocation.getText()); + + if (!Files.exists(mcPath)) { + throw new RuntimeException(Main.BUNDLE.getString("installer.exception.no.launcher.directory")); + } + + System.out.println("Installing Fabric " + gameVersion.getVersion() + " (" + gameVersion.getCodeName() + ")"); + String[] cmd2 = new String[]{"java", "-jar", "cache/" + getJarFile().getName(), "client", "-dir" + "\"" + mcPath.toAbsolutePath() + "\"", "-mcversion", gameVersion.codeName}; + + + try { + Process process = Runtime.getRuntime().exec(cmd2); + + BufferedInputStream successBufferedInputStream = new BufferedInputStream(process.getInputStream()); + BufferedInputStream errorBufferedInputStream = new BufferedInputStream(process.getErrorStream()); + synchronized (process) { + process.waitFor(); + } + + boolean hasError = false; + + if (errorBufferedInputStream.available() != 0) { + errorBufferedInputStream.close(); + hasError = true; + } + + if (process.exitValue() != 0) hasError = true; + if (successBufferedInputStream.available() == 0) hasError = true; + + if(hasError) { + this.error(); + } else { + this.showDone(gameVersion); + } + + } catch (Exception ignored) { + this.error(); + } + + buttonInstall.setEnabled(true); + } + + + private File getJarFile() throws IOException { + Path currentDir = new File("").toPath(); + Path cacheDir = currentDir.resolve("cache"); + + String fileName = "fabric-installer-" + getFabricVersion() + ".jar"; + URL url = new URL("https://github.com/rizecookey/fabric-installer/releases/latest/download/" + fileName); + + if(!Files.exists(cacheDir)) { + Files.createDirectories(cacheDir); + Utils.downloadFile(url, cacheDir.resolve(fileName)); + return new File(cacheDir.toFile(), fileName); + } + + if(cacheDir.toFile().listFiles().length == 0) { + Files.delete(cacheDir); + return getJarFile(); + } + + for(File file : cacheDir.toFile().listFiles()) { + if(file.getName().equals(fileName)) { + return file; + } else { + file.delete(); + Utils.downloadFile(url, cacheDir.resolve(fileName)); + return new File(cacheDir.toFile(), fileName); + } + } + + return null; + } + + private String getFabricVersion() { + + String version = "0.11.1"; + + try { + String response = HttpAPI.get("https://api.github.com/repos/rizecookey/fabric-installer/releases/latest"); + Json jsonObject = Json.read(response); + Json jsonVersion = jsonObject.at("tag_name"); + + if(jsonVersion == null || !jsonVersion.isString()) return version; + + return jsonVersion.asString(); + } catch (Exception ignored) { return version; } + } + + private void showDone(FabricVersionHandler.GameVersion gameVersion) { + Object[] options = {"OK", "Install Vanilla"}; + int result = JOptionPane.showOptionDialog(null, + MessageFormat.format(Main.BUNDLE.getString("installer.prompt.install.done.fabric"), gameVersion.getVersion()), + Main.BUNDLE.getString("installer.title"), + JOptionPane.YES_NO_OPTION, + JOptionPane.INFORMATION_MESSAGE, + null, + options, + options[0] + ); + + if(result == JOptionPane.NO_OPTION) InstallerGUI.instance.pane.setSelectedComponent(InstallerGUI.instance.vanilla); + } + + private void error() { + Object[] options = {"OK", "Cancel"}; + int result = JOptionPane.showOptionDialog(null, + MessageFormat.format(Main.BUNDLE.getString("installer.prompt.install.error"), ""), + Main.BUNDLE.getString("installer.title"), + JOptionPane.OK_CANCEL_OPTION, + JOptionPane.ERROR_MESSAGE, + null, + options, + options[0] + ); + + if(result == JOptionPane.OK_OPTION) { + try { + InstallerGUI.instance.dispose(); + Main.main(new String[]{}); + } catch (Exception ignored) { + System.exit(0); + } + } + + buttonInstall.setEnabled(true); + } +} + diff --git a/src/main/java/com/nexia/installer/util/fabric/FabricVersionHandler.java b/src/main/java/com/nexia/installer/util/fabric/FabricVersionHandler.java new file mode 100644 index 0000000..dd13766 --- /dev/null +++ b/src/main/java/com/nexia/installer/util/fabric/FabricVersionHandler.java @@ -0,0 +1,42 @@ +package com.nexia.installer.util.fabric; + +import java.util.ArrayList; +import java.util.List; + +public class FabricVersionHandler { + + public static List versions = new ArrayList<>(); + + public static GameVersion CombatTest8c = new GameVersion("Combat Test 8c", "1.16_combat-6"); + public static GameVersion CombatTest7c = new GameVersion("Combat Test 7c", "1.16_combat-3"); + public static GameVersion CombatTest1 = new GameVersion("1.14.3 - Combat Test", "1.14_combat-212796"); + + public static class GameVersion { + String version; + String codeName; + + public GameVersion(String version, String codeName) { + this.version = version; + this.codeName = codeName; + + FabricVersionHandler.versions.add(this); + } + + public String getVersion() { + return version; + } + + public String getCodeName() { + return codeName; + } + + } + + public static GameVersion identifyGameVersion(String version) { + if(version.trim().isEmpty()) return null; + for(GameVersion gameVersion : FabricVersionHandler.versions) { + if(version.equalsIgnoreCase(gameVersion.getCodeName()) || version.equalsIgnoreCase(gameVersion.getVersion())) return gameVersion; + } + return null; + } +} diff --git a/src/main/resources/lang/installer.properties b/src/main/resources/lang/installer.properties index f427043..700ca29 100644 --- a/src/main/resources/lang/installer.properties +++ b/src/main/resources/lang/installer.properties @@ -2,9 +2,16 @@ installer.prompt.game.version=Minecraft Version: installer.prompt.select.location=Select Install Location: installer.button.install=Install installer.prompt.install.done={0} has been installed, if you want to install fabric then click on the "Install Fabric" button. +installer.prompt.install.done.fabric={0} has been installed, if you want to install vanilla then click on the "Install Vanilla" button. installer.exception.no.launcher.directory=No launcher directory found! installer.prompt.launcher.type.body=The Combat Test Installer has detected 2 different installations of the Minecraft Launcher, which launcher do you wish to install the Combat Tests to?\n\n- Select Microsoft Store if you are playing Minecraft through the Xbox App or the Windows Store.\n- Select Standalone if you downloaded the Minecraft launcher directly from the Minecraft.net website.\n\nIf you are unsure try the Microsoft Store option first, you can always re-run the installer. installer.prompt.launcher.type.xbox=Microsoft Store / Xbox installer.prompt.launcher.type.win32=Standalone (Win32) installer.title=Combat Test Installer installer.option.create.profile=Create profile + +installer.prompt.install.error=An error has occurred, click "OK" to restart the program or click "Cancel" to continue. + +installer.button.fabric=Open the Fabric Installer +installer.tab.vanilla=Vanilla +installer.tab.fabric=Fabric