Add SPLServer support to loader
All checks were successful
/ Build BaseBand Loader (push) Successful in 1m55s

This commit is contained in:
Daniella / Tove 2024-10-14 01:34:50 +02:00
parent be2750c4dd
commit c3f6505a8f
Signed by: TudbuT
GPG key ID: B3CF345217F202D3
2 changed files with 155 additions and 84 deletions

View file

@ -30,8 +30,10 @@ import oshi.hardware.Processor;
import javax.swing.*; import javax.swing.*;
import java.awt.*; import java.awt.*;
import java.io.*; import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.net.Socket; import java.net.Socket;
import java.net.URL;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Paths; import java.nio.file.Paths;
@ -42,6 +44,7 @@ import java.util.HashMap;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream; import java.util.zip.ZipOutputStream;
@Mod(modid = "baseband") @Mod(modid = "baseband")
@ -85,6 +88,18 @@ public class Loader implements Util {
preOptions = new TCN(); preOptions = new TCN();
} }
preOptions.setIfAbsent("timeout", 5); 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) TCN options = new ClientBoot("BaseBand", preOptions.getInteger("timeout") * 1000, preOptions)
.label("Start Minecraft:") .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: 5s", x1 -> x1.data.set("timeout", 5))
.option("Set timeout: 10s", x1 -> x1.data.set("timeout", 10)) .option("Set timeout: 10s", x1 -> x1.data.set("timeout", 10))
.option("Back", ClientBoot::back)) .option("Back", ClientBoot::back))
.option("Set branch", x -> x.newScreen() .option("Set branch", x -> {
.option("release (Broadway)", x1 -> x1.back().data.set("branch", "release")) ClientBoot screen = x.newScreen();
.option("main (Iceland)", x1 -> x1.back().data.set("branch", "main"))) 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 -> { .option("Persist changes", x -> {
try(FileOutputStream fos = new FileOutputStream("baseband_launch.json")) { try(FileOutputStream fos = new FileOutputStream("baseband_launch.json")) {
fos.write(JSON.writeReadable(x.data).getBytes(StandardCharsets.UTF_8)); fos.write(JSON.writeReadable(x.data).getBytes(StandardCharsets.UTF_8));
@ -235,101 +259,145 @@ public class Loader implements Util {
.spacer() .spacer()
.option("Exit Minecraft", x -> exit(0)) .option("Exit Minecraft", x -> exit(0))
.show(); .show();
options.setIfAbsent("server", "java");
options.setIfAbsent("branch", "release"); options.setIfAbsent("branch", "release");
options.setIfAbsent("ip", "baseband.com.de"); options.setIfAbsent("ip", "baseband.com.de");
options.setIfAbsent("disabled-modules", new TCNArray()); options.setIfAbsent("disabled-modules", new TCNArray());
try(Socket client = new Socket(options.getString("ip"), 40000)) { if(options.getString("server").equals("java")) {
client.setSoTimeout(5000); try (Socket client = new Socket(options.getString("ip"), 40000)) {
client.getOutputStream().write(0); client.setSoTimeout(5000);
TypedInputStream inputStream = new TypedInputStream(client.getInputStream()); client.getOutputStream().write(0);
TypedOutputStream outputStream = new TypedOutputStream(client.getOutputStream()); TypedInputStream inputStream = new TypedInputStream(client.getInputStream());
TypedOutputStream outputStream = new TypedOutputStream(client.getOutputStream());
RSAKey rsaKey = new RSAKey(inputStream.readString()); //get publickey RSAKey rsaKey = new RSAKey(inputStream.readString()); //get publickey
Key key = new Key(); Key key = new Key();
outputStream.writeString(rsaKey.rsaEnc(key.toString().getBytes(StandardCharsets.ISO_8859_1))); outputStream.writeString(rsaKey.rsaEnc(key.toString().getBytes(StandardCharsets.ISO_8859_1)));
outputStream.writeString(key.encryptString(Tools.mapToString(getData(options).toMap()))); 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) { if (status == Response.OUTDATED) {
LOGGER.info("BaseBand is downloading a significant update."); 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<String, Object> 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<String, byte[]> 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()); RawKey rk = new RawKey(key.toBytes());
ZipOutputStream out = new ZipOutputStream(new FileOutputStream(HTTPUtils.decodeUTF8(Loader.class.getProtectionDomain().getCodeSource().getLocation().getFile())));
int n = inputStream.readInt(); int n = inputStream.readInt();
for (int i = 0; i < n; i++) { for (int i = 0; i < n; i++) {
String name = inputStream.readString(); out.putNextEntry(new ZipEntry(rk.decryptString(inputStream.readString())));
byte[] classBytes = inputStream.readByteArray(); out.write(rk.decryptBytes(inputStream.readByteArray()));
data.put(rk.decryptString(name), rk.decryptBytes(classBytes)); out.closeEntry();
bytes.getAndAdd(classBytes.length);
continueDownload.lock();
downloadUpdated.unlock();
continueDownload.waitHere();
} }
LOGGER.info("BaseBand downloaded: {} chunks.", data.size()); out.close();
LOGGER.info("Booting BaseBand {} @ {}", clientData.getString("build-name"), new String(data.get("commit")).trim()); LOGGER.info("BaseBand has downloaded a significant update. Minecraft will exit.");
classLoader = new CustomClassLoader(data); JOptionPane.showMessageDialog(null, "BaseBand has downloaded a significant update. Please restart Minecraft.");
downloadUpdated.unlock(); exit(0);
classLoader.inject(); } else if (status == Response.OK) {
classLoader.informClient(clientData); LOGGER.info(status.name);
} catch (Exception e) { try {
LOGGER.fatal("BaseBand failed to (down)load."); TCN clientData = TCN.readMap(Tools.stringToMap(key.decryptString(inputStream.readString())));
e.printStackTrace();
for (TLMap.Entry<String, Object> 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<String, byte[]> 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 { } catch (Exception e) {
LOGGER.error(status.name); LOGGER.fatal("Failed to connect to Server-side.");
LOGGER.fatal(e.getMessage());
exit(); 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<String, byte[]> 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<String, byte[]> downloadFromSPL(String url) {
HashMap<String, byte[]> 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) { public static void loaded(Class<?> baseBandClass) {

View file

@ -26,6 +26,9 @@ public class Tweaker implements ITweaker, IFMLLoadingPlugin {
public static void loaded(Class<?> baseBandClass) { public static void loaded(Class<?> baseBandClass) {
Loader.loaded(baseBandClass); Loader.loaded(baseBandClass);
} }
public static void load() {
Loader.run();
}
private final MixinTweaker wrapped; private final MixinTweaker wrapped;
@ -40,7 +43,7 @@ public class Tweaker implements ITweaker, IFMLLoadingPlugin {
@Override @Override
public void injectIntoClassLoader(LaunchClassLoader launchClassLoader) { public void injectIntoClassLoader(LaunchClassLoader launchClassLoader) {
Loader.run(); load();
wrapped.injectIntoClassLoader(launchClassLoader); wrapped.injectIntoClassLoader(launchClassLoader);
Mixins.addConfiguration("mixins.baseband.json"); Mixins.addConfiguration("mixins.baseband.json");