diff --git a/Loader/src/main/java/de/com/baseband/launcher/Loader.java b/Loader/src/main/java/de/com/baseband/launcher/Loader.java index 470cd46..bec4b54 100644 --- a/Loader/src/main/java/de/com/baseband/launcher/Loader.java +++ b/Loader/src/main/java/de/com/baseband/launcher/Loader.java @@ -30,8 +30,10 @@ import oshi.hardware.Processor; import javax.swing.*; import java.awt.*; import java.io.*; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.Socket; +import java.net.URL; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Paths; @@ -42,6 +44,7 @@ import java.util.HashMap; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; import java.util.zip.ZipOutputStream; @Mod(modid = "baseband") @@ -85,6 +88,18 @@ public class Loader implements Util { preOptions = new TCN(); } preOptions.setIfAbsent("timeout", 5); + TCN branches; + try { + branches = JSON.read(new StreamReader(new URL("https://download.baseband.com.de/branches").openStream()).readAllAsString()); + } catch (Exception e) { + exit(); + return; + } + + if(!GitHash.GIT_HASH.equals("[dev]") && !GitHash.GIT_HASH.equals(branches.getSub("loader").getString("commit"))) { + splUpdate(); + return; + } TCN options = new ClientBoot("BaseBand", preOptions.getInteger("timeout") * 1000, preOptions) .label("Start Minecraft:") @@ -222,9 +237,18 @@ public class Loader implements Util { .option("Set timeout: 5s", x1 -> x1.data.set("timeout", 5)) .option("Set timeout: 10s", x1 -> x1.data.set("timeout", 10)) .option("Back", ClientBoot::back)) - .option("Set branch", x -> x.newScreen() - .option("release (Broadway)", x1 -> x1.back().data.set("branch", "release")) - .option("main (Iceland)", x1 -> x1.back().data.set("branch", "main"))) + .option("Set branch", x -> { + ClientBoot screen = x.newScreen(); + for (String entry : branches.map.keys()) { + if(entry.equals("loader")) + continue; + + screen.option(entry + " (" + branches.getSub(entry).getString("name") + ")", x1 -> x1.back().data.set("branch", entry)); + } + }) + .option("Server mode", x -> x.newScreen() + .option("SPL", x1 -> x1.back().data.set("server", "spl")) + .option("Java", x1 -> x1.back().data.set("server", "java"))) .option("Persist changes", x -> { try(FileOutputStream fos = new FileOutputStream("baseband_launch.json")) { fos.write(JSON.writeReadable(x.data).getBytes(StandardCharsets.UTF_8)); @@ -235,101 +259,145 @@ public class Loader implements Util { .spacer() .option("Exit Minecraft", x -> exit(0)) .show(); + options.setIfAbsent("server", "java"); options.setIfAbsent("branch", "release"); options.setIfAbsent("ip", "baseband.com.de"); options.setIfAbsent("disabled-modules", new TCNArray()); - try(Socket client = new Socket(options.getString("ip"), 40000)) { - client.setSoTimeout(5000); - client.getOutputStream().write(0); - TypedInputStream inputStream = new TypedInputStream(client.getInputStream()); - TypedOutputStream outputStream = new TypedOutputStream(client.getOutputStream()); + if(options.getString("server").equals("java")) { + try (Socket client = new Socket(options.getString("ip"), 40000)) { + client.setSoTimeout(5000); + client.getOutputStream().write(0); + TypedInputStream inputStream = new TypedInputStream(client.getInputStream()); + TypedOutputStream outputStream = new TypedOutputStream(client.getOutputStream()); - RSAKey rsaKey = new RSAKey(inputStream.readString()); //get publickey - Key key = new Key(); - outputStream.writeString(rsaKey.rsaEnc(key.toString().getBytes(StandardCharsets.ISO_8859_1))); - outputStream.writeString(key.encryptString(Tools.mapToString(getData(options).toMap()))); + RSAKey rsaKey = new RSAKey(inputStream.readString()); //get publickey + Key key = new Key(); + outputStream.writeString(rsaKey.rsaEnc(key.toString().getBytes(StandardCharsets.ISO_8859_1))); + outputStream.writeString(key.encryptString(Tools.mapToString(getData(options).toMap()))); - Response status = Response.values()[inputStream.readInt()]; + Response status = Response.values()[inputStream.readInt()]; - if(status == Response.OUTDATED) { - LOGGER.info("BaseBand is downloading a significant update."); - RawKey rk = new RawKey(key.toBytes()); - ZipOutputStream out = new ZipOutputStream(new FileOutputStream(HTTPUtils.decodeUTF8(Loader.class.getProtectionDomain().getCodeSource().getLocation().getFile()))); - int n = inputStream.readInt(); - for (int i = 0; i < n; i++) { - out.putNextEntry(new ZipEntry(rk.decryptString(inputStream.readString()))); - out.write(rk.decryptBytes(inputStream.readByteArray())); - out.closeEntry(); - } - out.close(); - LOGGER.info("BaseBand has downloaded a significant update. Minecraft will exit."); - JOptionPane.showMessageDialog(null, "BaseBand has downloaded a significant update. Please restart Minecraft."); - exit(0); - } else if(status == Response.OK) { - LOGGER.info(status.name); - try { - TCN clientData = TCN.readMap(Tools.stringToMap(key.decryptString(inputStream.readString()))); - - for (TLMap.Entry entry : options.map.entries()) { - if(entry.val instanceof TCNArray && clientData.get(entry.key) != null) { - clientData.getArray(entry.key).addAll((TCNArray) entry.val); - } - else { - clientData.setIfAbsent(entry.key, entry.val); - } - } - - HashMap data = new HashMap<>(); - SimpleLock downloadUpdated = new SimpleLock(); - SimpleLock continueDownload = new SimpleLock(); - AtomicInteger bytes = new AtomicInteger(); - new Thread(() -> { - long lastUpdate = System.currentTimeMillis(); - while (classLoader == null) { - downloadUpdated.waitHere(2000); - if(downloadUpdated.isLocked()) { - LOGGER.warn("No new BaseBand chunk has been downloaded in 2 seconds. Consider restarting your game."); - } - else if(System.currentTimeMillis() - lastUpdate >= 500) { - //LOGGER.info("Downloading at {}KB/s...", (int) ((bytes.get() / 1024f) * (System.currentTimeMillis() - lastUpdate) / 1000f)); - lastUpdate = System.currentTimeMillis(); - bytes.set(0); - } - downloadUpdated.lock(); - continueDownload.unlock(); - } - }, "Download guard").start(); + if (status == Response.OUTDATED) { + LOGGER.info("BaseBand is downloading a significant update."); RawKey rk = new RawKey(key.toBytes()); + ZipOutputStream out = new ZipOutputStream(new FileOutputStream(HTTPUtils.decodeUTF8(Loader.class.getProtectionDomain().getCodeSource().getLocation().getFile()))); int n = inputStream.readInt(); for (int i = 0; i < n; i++) { - String name = inputStream.readString(); - byte[] classBytes = inputStream.readByteArray(); - data.put(rk.decryptString(name), rk.decryptBytes(classBytes)); - bytes.getAndAdd(classBytes.length); - continueDownload.lock(); - downloadUpdated.unlock(); - continueDownload.waitHere(); + out.putNextEntry(new ZipEntry(rk.decryptString(inputStream.readString()))); + out.write(rk.decryptBytes(inputStream.readByteArray())); + out.closeEntry(); } - LOGGER.info("BaseBand downloaded: {} chunks.", data.size()); - LOGGER.info("Booting BaseBand {} @ {}", clientData.getString("build-name"), new String(data.get("commit")).trim()); - classLoader = new CustomClassLoader(data); - downloadUpdated.unlock(); - classLoader.inject(); - classLoader.informClient(clientData); - } catch (Exception e) { - LOGGER.fatal("BaseBand failed to (down)load."); - e.printStackTrace(); + out.close(); + LOGGER.info("BaseBand has downloaded a significant update. Minecraft will exit."); + JOptionPane.showMessageDialog(null, "BaseBand has downloaded a significant update. Please restart Minecraft."); + exit(0); + } else if (status == Response.OK) { + LOGGER.info(status.name); + try { + TCN clientData = TCN.readMap(Tools.stringToMap(key.decryptString(inputStream.readString()))); + + for (TLMap.Entry entry : options.map.entries()) { + if (entry.val instanceof TCNArray && clientData.get(entry.key) != null) { + clientData.getArray(entry.key).addAll((TCNArray) entry.val); + } else { + clientData.setIfAbsent(entry.key, entry.val); + } + } + + HashMap data = new HashMap<>(); + SimpleLock downloadUpdated = new SimpleLock(); + SimpleLock continueDownload = new SimpleLock(); + AtomicInteger bytes = new AtomicInteger(); + new Thread(() -> { + long lastUpdate = System.currentTimeMillis(); + while (classLoader == null) { + downloadUpdated.waitHere(2000); + if (downloadUpdated.isLocked()) { + LOGGER.warn("No new BaseBand chunk has been downloaded in 2 seconds. Consider restarting your game."); + } else if (System.currentTimeMillis() - lastUpdate >= 500) { + //LOGGER.info("Downloading at {}KB/s...", (int) ((bytes.get() / 1024f) * (System.currentTimeMillis() - lastUpdate) / 1000f)); + lastUpdate = System.currentTimeMillis(); + bytes.set(0); + } + downloadUpdated.lock(); + continueDownload.unlock(); + } + }, "Download guard").start(); + RawKey rk = new RawKey(key.toBytes()); + int n = inputStream.readInt(); + for (int i = 0; i < n; i++) { + String name = inputStream.readString(); + byte[] classBytes = inputStream.readByteArray(); + data.put(rk.decryptString(name), rk.decryptBytes(classBytes)); + bytes.getAndAdd(classBytes.length); + continueDownload.lock(); + downloadUpdated.unlock(); + continueDownload.waitHere(); + } + LOGGER.info("BaseBand downloaded: {} chunks.", data.size()); + LOGGER.info("Booting BaseBand {} @ {}", clientData.getString("build-name"), new String(data.get("commit")).trim()); + classLoader = new CustomClassLoader(data); + downloadUpdated.unlock(); + classLoader.inject(); + classLoader.informClient(clientData); + } catch (Exception e) { + LOGGER.fatal("BaseBand failed to (down)load."); + e.printStackTrace(); + } + } else { + LOGGER.error(status.name); + exit(); } - } else { - LOGGER.error(status.name); + } catch (Exception e) { + LOGGER.fatal("Failed to connect to Server-side."); + LOGGER.fatal(e.getMessage()); exit(); } - } catch (Exception e) { - LOGGER.fatal("Failed to connect to Server-side."); - LOGGER.fatal(e.getMessage()); - exit(); } + + if(options.getString("server").equals("spl")) { + if(options.getBoolean("redownload")) { + splUpdate(); + } + + options.set("build-name", branches.getSub(options.getString("branch")).getString("name")); + HashMap data = downloadFromSPL("https://download.baseband.com.de/download/client/" + options.getString("branch")); + LOGGER.info("BaseBand downloaded: {} chunks.", data.size()); + LOGGER.info("Booting BaseBand {} @ {}", options.getString("build-name"), new String(data.get("commit")).trim()); + classLoader = new CustomClassLoader(data); + classLoader.inject(); + classLoader.informClient(options); + } + } + + private static void splUpdate() { + LOGGER.info("BaseBand is downloading a significant update..."); + CustomClassLoader loaderReloader = new CustomClassLoader(downloadFromSPL("https://download.baseband.com.de/download/loader")); + loaderReloader.inject(); + LOGGER.info("BaseBand has downloaded a significant update. Applying..."); + try { + loaderReloader.loadClass(Tweaker.class.getName()).getMethod("load").invoke(null); + } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException | + ClassNotFoundException e) { + JOptionPane.showMessageDialog(null, "BaseBand has downloaded a significant update. Please restart Minecraft."); + exit(0); + } + } + + private static HashMap downloadFromSPL(String url) { + HashMap data = new HashMap<>(); + try (ZipInputStream jar = new ZipInputStream(new URL(url).openStream())) { + ZipEntry entry; + while ((entry = jar.getNextEntry()) != null) { + byte[] bytes = new StreamReader(jar).readAllAsBytes(); + data.put(entry.getName(), bytes); + jar.closeEntry(); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + return data; } public static void loaded(Class baseBandClass) { diff --git a/Loader/src/main/java/de/com/baseband/launcher/Tweaker.java b/Loader/src/main/java/de/com/baseband/launcher/Tweaker.java index f217ce5..cf33114 100644 --- a/Loader/src/main/java/de/com/baseband/launcher/Tweaker.java +++ b/Loader/src/main/java/de/com/baseband/launcher/Tweaker.java @@ -26,6 +26,9 @@ public class Tweaker implements ITweaker, IFMLLoadingPlugin { public static void loaded(Class baseBandClass) { Loader.loaded(baseBandClass); } + public static void load() { + Loader.run(); + } private final MixinTweaker wrapped; @@ -40,7 +43,7 @@ public class Tweaker implements ITweaker, IFMLLoadingPlugin { @Override public void injectIntoClassLoader(LaunchClassLoader launchClassLoader) { - Loader.run(); + load(); wrapped.injectIntoClassLoader(launchClassLoader); Mixins.addConfiguration("mixins.baseband.json");