diff --git a/LaunchServer/resources/launchserver/defaults/config.cfg b/LaunchServer/resources/launchserver/defaults/config.cfg index e7999a7..dd9fd33 100644 --- a/LaunchServer/resources/launchserver/defaults/config.cfg +++ b/LaunchServer/resources/launchserver/defaults/config.cfg @@ -24,3 +24,6 @@ # Launch4J EXE binary building launch4J: false; + +# Compress files when updating using Inflate algorithm +compress: true; diff --git a/LaunchServer/source/LaunchServer.java b/LaunchServer/source/LaunchServer.java index 528ce8d..247207b 100644 --- a/LaunchServer/source/LaunchServer.java +++ b/LaunchServer/source/LaunchServer.java @@ -416,12 +416,15 @@ public static final class Config extends ConfigObject { @LauncherAPI public final int port; + // Handlers & Providers @LauncherAPI public final AuthHandler authHandler; @LauncherAPI public final AuthProvider authProvider; @LauncherAPI public final TextureProvider textureProvider; - // EXE binary building + + // Misc options @LauncherAPI public final boolean launch4J; + @LauncherAPI public final boolean compress; private final StringConfigEntry address; private final String bindAddress; @@ -441,8 +444,9 @@ textureProvider = TextureProvider.newProvider(block.getEntryValue("textureProvider", StringConfigEntry.class), block.getEntry("textureProviderConfig", BlockConfigEntry.class)); - // Set launch4J config + // Set misc config launch4J = block.getEntryValue("launch4J", BooleanConfigEntry.class); + compress = block.getEntryValue("compress", BooleanConfigEntry.class); } @LauncherAPI diff --git a/LaunchServer/source/response/ResponseThread.java b/LaunchServer/source/response/ResponseThread.java index 032cd71..e48fa51 100644 --- a/LaunchServer/source/response/ResponseThread.java +++ b/LaunchServer/source/response/ResponseThread.java @@ -10,7 +10,6 @@ import launcher.helper.LogHelper; import launcher.helper.SecurityHelper; import launcher.helper.VerifyHelper; -import launcher.request.Request; import launcher.request.Request.Type; import launcher.request.RequestException; import launcher.serialize.HInput; @@ -42,13 +41,13 @@ @Override public void run() { - boolean cancelled = false; - Exception savedError = null; if (!server.serverSocketHandler.logConnections) { LogHelper.debug("Connection #%d from %s", id, IOHelper.getIP(socket.getRemoteSocketAddress())); } // Process connection + boolean cancelled = false; + Exception savedError = null; try (HInput input = new HInput(socket.getInputStream()); HOutput output = new HOutput(socket.getOutputStream())) { Type type = readHandshake(input, output); @@ -76,11 +75,16 @@ } private Type readHandshake(HInput input, HOutput output) throws IOException { + boolean legacy = false; + // Verify magic number int magicNumber = input.readInt(); if (magicNumber != Launcher.PROTOCOL_MAGIC) { - output.writeBoolean(false); - throw new IOException(String.format("#%d Protocol magic mismatch", id)); + if (magicNumber != 0x724724_00 + 16) { // 15.3- launcher protocol + output.writeBoolean(false); + throw new IOException(String.format("#%d Protocol magic mismatch", id)); + } + legacy = true; } // Verify key modulus @@ -92,6 +96,10 @@ // Read request type Type type = Type.read(input); + if (legacy) { + output.writeBoolean(false); + throw new IOException(String.format("#%d Not LAUNCHER request on legacy protocol", id)); + } if (!server.serverSocketHandler.onHandshake(id, type)) { output.writeBoolean(false); return null; diff --git a/LaunchServer/source/response/update/UpdateResponse.java b/LaunchServer/source/response/update/UpdateResponse.java index 7437284..6555b95 100644 --- a/LaunchServer/source/response/update/UpdateResponse.java +++ b/LaunchServer/source/response/update/UpdateResponse.java @@ -6,6 +6,7 @@ import java.nio.file.Path; import java.util.Deque; import java.util.LinkedList; +import java.util.zip.DeflaterOutputStream; import launcher.hasher.HashedDir; import launcher.hasher.HashedEntry; @@ -46,6 +47,7 @@ dirStack.add(hdir.object); // Perform update + HOutput fileOutput = server.config.compress ? new HOutput(new DeflaterOutputStream(output.stream, IOHelper.newDeflater(), IOHelper.BUFFER_SIZE, true)) : output; Action[] actionsSlice = new Action[UpdateRequest.MAX_QUEUE_SIZE]; loop: while (true) { @@ -84,13 +86,13 @@ // Resolve and write file Path file = dir.resolve(action.name); if (Files.size(file) != hFile.size()) { - output.writeBoolean(false); - output.flush(); + fileOutput.writeUnsignedByte(0x0); + fileOutput.flush(); throw new IOException("Unknown hashed file: " + action.name); } - output.writeBoolean(true); + fileOutput.writeUnsignedByte(0xFF); try (InputStream fileInput = IOHelper.newInput(file)) { - IOHelper.transfer(fileInput, output.stream); + IOHelper.transfer(fileInput, fileOutput.stream); } break; case CD_BACK: @@ -113,7 +115,7 @@ } // Flush all actions - output.flush(); + fileOutput.flush(); } // So we've updated :) diff --git a/Launcher/source/Launcher.java b/Launcher/source/Launcher.java index e5202c2..d804765 100644 --- a/Launcher/source/Launcher.java +++ b/Launcher/source/Launcher.java @@ -78,9 +78,9 @@ private static final AtomicReference CONFIG = new AtomicReference<>(); // Version info - @LauncherAPI public static final String VERSION = "15.3.1"; + @LauncherAPI public static final String VERSION = "15.4"; @LauncherAPI public static final String BUILD = readBuildNumber(); - @LauncherAPI public static final int PROTOCOL_MAGIC = 0x724724_16; + @LauncherAPI public static final int PROTOCOL_MAGIC = 0x724724_00 + 17; // Constants @LauncherAPI public static final String RUNTIME_DIR = "runtime"; diff --git a/Launcher/source/helper/IOHelper.java b/Launcher/source/helper/IOHelper.java index 1196938..a5e0fb6 100644 --- a/Launcher/source/helper/IOHelper.java +++ b/Launcher/source/helper/IOHelper.java @@ -49,6 +49,8 @@ import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.zip.Deflater; +import java.util.zip.Inflater; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import javax.imageio.ImageIO; @@ -261,6 +263,18 @@ } @LauncherAPI + public static Deflater newDeflater() { + Deflater deflater = new Deflater(Deflater.DEFAULT_COMPRESSION, false); + deflater.setStrategy(Deflater.DEFAULT_STRATEGY); + return deflater; + } + + @LauncherAPI + public static Inflater newInflater() { + return new Inflater(false); + } + + @LauncherAPI public static InputStream newInput(URL url) throws IOException { return newConnection(url).getInputStream(); } diff --git a/Launcher/source/request/update/UpdateRequest.java b/Launcher/source/request/update/UpdateRequest.java index 5d5f06a..87ba000 100644 --- a/Launcher/source/request/update/UpdateRequest.java +++ b/Launcher/source/request/update/UpdateRequest.java @@ -13,6 +13,7 @@ import java.util.Map.Entry; import java.util.Objects; import java.util.Queue; +import java.util.zip.InflaterInputStream; import launcher.Launcher.Config; import launcher.LauncherAPI; @@ -44,6 +45,7 @@ private volatile Callback stateCallback; // State + private boolean compress; private HashedDir localDir; private long totalDownloaded; private long totalSize; @@ -79,6 +81,8 @@ @Override protected SignedObjectHolder requestDo(HInput input, HOutput output) throws IOException, SignatureException { + compress = input.readBoolean(); + // Write update dir name output.writeString(dirName, 255); output.flush(); @@ -112,6 +116,7 @@ output.flush(); // Perform actions + HInput fileInput = compress ? new HInput(new InflaterInputStream(input.stream, IOHelper.newInflater(), IOHelper.BUFFER_SIZE)) : input; for (int i = 0; i < length; i++) { Action action = actionsSlice[i]; switch (action.type) { @@ -121,10 +126,10 @@ break; case GET: Path targetFile = currentDir.resolve(action.name); - if (!input.readBoolean()) { + if (fileInput.readUnsignedByte() != 0xFF) { throw new IOException("Serverside cached size mismath for file " + action.name); } - downloadFile(targetFile, (HashedFile) action.entry, input); + downloadFile(targetFile, (HashedFile) action.entry, fileInput); break; case CD_BACK: currentDir = currentDir.getParent();