From 5f98c8ce805f03e167aef1187cfbfbbf8f3172e3 Mon Sep 17 00:00:00 2001 From: Melledy <121644117+Melledy@users.noreply.github.com> Date: Sat, 15 Nov 2025 04:29:55 -0800 Subject: [PATCH] Allow the server to send ClientDiff --- .gitignore | 1 + src/main/java/emu/nebula/Config.java | 1 + src/main/java/emu/nebula/GameConstants.java | 2 +- .../command/commands/ReloadCommand.java | 5 ++ .../java/emu/nebula/server/HttpServer.java | 46 ++++++++++++++++++- .../java/emu/nebula/server/PatchList.java | 39 ++++++++++++++++ .../server/routes/MetaServerlistHandler.java | 3 +- .../nebula/server/routes/MetaWinHandler.java | 32 +++++++------ 8 files changed, 111 insertions(+), 18 deletions(-) create mode 100644 src/main/java/emu/nebula/server/PatchList.java diff --git a/.gitignore b/.gitignore index ed6de74..149c888 100644 --- a/.gitignore +++ b/.gitignore @@ -71,6 +71,7 @@ tmp/ # Extra Nebula Handbook.txt config.json +patchlist.json *.mv *.exe *.p12 diff --git a/src/main/java/emu/nebula/Config.java b/src/main/java/emu/nebula/Config.java index e19c7af..9e14a5c 100644 --- a/src/main/java/emu/nebula/Config.java +++ b/src/main/java/emu/nebula/Config.java @@ -24,6 +24,7 @@ public class Config { public String resourceDir = "./resources"; public String dataDir = "./data"; + public String patchListPath = "./patchlist.json"; @Getter public static class DatabaseInfo { diff --git a/src/main/java/emu/nebula/GameConstants.java b/src/main/java/emu/nebula/GameConstants.java index 396c751..aada45d 100644 --- a/src/main/java/emu/nebula/GameConstants.java +++ b/src/main/java/emu/nebula/GameConstants.java @@ -3,7 +3,7 @@ package emu.nebula; import java.time.ZoneId; public class GameConstants { - public static final int DATA_VERSION = 40; + public static final int DATA_VERSION = 46; public static final String VERSION = "1.0.0." + DATA_VERSION; public static final ZoneId UTC_ZONE = ZoneId.of("UTC"); diff --git a/src/main/java/emu/nebula/command/commands/ReloadCommand.java b/src/main/java/emu/nebula/command/commands/ReloadCommand.java index 26bb3de..50f224f 100644 --- a/src/main/java/emu/nebula/command/commands/ReloadCommand.java +++ b/src/main/java/emu/nebula/command/commands/ReloadCommand.java @@ -11,6 +11,11 @@ public class ReloadCommand implements CommandHandler { @Override public void execute(CommandArgs args) { Nebula.loadConfig(); + + if (Nebula.getHttpServer() != null) { + Nebula.getHttpServer().loadPatchList(); + } + args.sendMessage("Reloaded the server config"); } diff --git a/src/main/java/emu/nebula/server/HttpServer.java b/src/main/java/emu/nebula/server/HttpServer.java index 079d3a7..dc51345 100644 --- a/src/main/java/emu/nebula/server/HttpServer.java +++ b/src/main/java/emu/nebula/server/HttpServer.java @@ -1,5 +1,8 @@ package emu.nebula.server; +import java.io.File; +import java.io.FileReader; + import org.eclipse.jetty.server.HttpConfiguration; import org.eclipse.jetty.server.HttpConnectionFactory; import org.eclipse.jetty.server.SecureRequestCustomizer; @@ -7,9 +10,12 @@ import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.util.ssl.SslContextFactory; import emu.nebula.Config.HttpServerConfig; +import emu.nebula.GameConstants; import emu.nebula.Nebula; import emu.nebula.Nebula.ServerType; +import emu.nebula.proto.Pb.ClientDiff; import emu.nebula.server.routes.*; +import emu.nebula.util.JsonUtils; import io.javalin.Javalin; import io.javalin.http.ContentType; import io.javalin.http.Context; @@ -21,10 +27,15 @@ public class HttpServer { private ServerType type; private boolean started; + // Cached client diff + private PatchList patchlist; + private byte[] diff; + public HttpServer(ServerType type) { this.app = Javalin.create(); this.type = type; + this.loadPatchList(); this.addRoutes(); } @@ -49,6 +60,39 @@ public class HttpServer { return sslContextFactory; } + // Patch list + + public long getDataVersion() { + return getPatchlist() != null ? getPatchlist().getVersion() : GameConstants.DATA_VERSION; + } + + public synchronized void loadPatchList() { + // Clear + this.patchlist = null; + this.diff = null; + + // Get file + File file = new File(Nebula.getConfig().getPatchListPath()); + + if (!file.exists()) { + this.diff = ClientDiff.newInstance().toByteArray(); + return; + } + + // Load + try (FileReader reader = new FileReader(file)) { + this.patchlist = JsonUtils.loadToClass(reader, PatchList.class); + this.diff = patchlist.toProto().toByteArray(); + } catch (Exception e) { + this.patchlist = null; + this.diff = ClientDiff.newInstance().toByteArray(); + } + + if (this.patchlist != null) { + Nebula.getLogger().info("Loaded patchlist version " + patchlist.getVersion()); + } + } + // Start server public void start() { @@ -107,7 +151,7 @@ public class HttpServer { // https://nova-static.stellasora.global/ getApp().get("/meta/serverlist.html", new MetaServerlistHandler(this)); - getApp().get("/meta/win.html", new MetaWinHandler()); + getApp().get("/meta/win.html", new MetaWinHandler(this)); } private void addGameServerRoutes() { diff --git a/src/main/java/emu/nebula/server/PatchList.java b/src/main/java/emu/nebula/server/PatchList.java new file mode 100644 index 0000000..fe3787a --- /dev/null +++ b/src/main/java/emu/nebula/server/PatchList.java @@ -0,0 +1,39 @@ +package emu.nebula.server; + +import java.util.List; + +import emu.nebula.proto.Pb.ClientDiff; +import emu.nebula.proto.Pb.FileDiff; +import lombok.Getter; + +@Getter +public class PatchList { + public long version; + public List files; + + @Getter + public static class PatchListFile { + public String name; + public String hash; + public long version; + public String additionalPath; + } + + // Proto + + public ClientDiff toProto() { + var proto = ClientDiff.newInstance(); + + for (var file : files) { + var diff = FileDiff.newInstance() + .setFileName(file.getName()) + .setHash(file.getHash()) + .setVersion(this.getVersion()) + .setAdditionalPath(file.getAdditionalPath()); + + proto.addDiff(diff); + } + + return proto; + } +} diff --git a/src/main/java/emu/nebula/server/routes/MetaServerlistHandler.java b/src/main/java/emu/nebula/server/routes/MetaServerlistHandler.java index 1669053..d46adbf 100644 --- a/src/main/java/emu/nebula/server/routes/MetaServerlistHandler.java +++ b/src/main/java/emu/nebula/server/routes/MetaServerlistHandler.java @@ -2,7 +2,6 @@ package emu.nebula.server.routes; import org.jetbrains.annotations.NotNull; -import emu.nebula.GameConstants; import emu.nebula.proto.Pb.ServerAgent; import emu.nebula.proto.Pb.ServerListMeta; import emu.nebula.server.HttpServer; @@ -24,7 +23,7 @@ public class MetaServerlistHandler implements Handler { // Create server list this.list = ServerListMeta.newInstance() - .setVersion(GameConstants.DATA_VERSION) + .setVersion(server.getDataVersion()) .setReportEndpoint(server.getServerConfig().getDisplayAddress() + "/report"); var agent = ServerAgent.newInstance() diff --git a/src/main/java/emu/nebula/server/routes/MetaWinHandler.java b/src/main/java/emu/nebula/server/routes/MetaWinHandler.java index 68c9b03..c42dc26 100644 --- a/src/main/java/emu/nebula/server/routes/MetaWinHandler.java +++ b/src/main/java/emu/nebula/server/routes/MetaWinHandler.java @@ -2,8 +2,9 @@ package emu.nebula.server.routes; import org.jetbrains.annotations.NotNull; -import emu.nebula.proto.Pb.ClientDiff; +import emu.nebula.server.HttpServer; import emu.nebula.util.AeadHelper; +import emu.nebula.util.Utils; import io.javalin.http.ContentType; import io.javalin.http.Context; import io.javalin.http.Handler; @@ -12,24 +13,27 @@ import lombok.Getter; @Getter(AccessLevel.PRIVATE) public class MetaWinHandler implements Handler { - private ClientDiff list; - private byte[] proto; - - public MetaWinHandler() { - // Create client diff - this.list = ClientDiff.newInstance(); - - // TODO load from json or something - - // Cache proto - this.proto = list.toByteArray(); + private HttpServer server; + + public MetaWinHandler(HttpServer server) { + this.server = server; } @Override public void handle(@NotNull Context ctx) throws Exception { - // Result + // Get diff + var diffBytes = this.getServer().getDiff(); + + // Sanity check + if (diffBytes == null) { + ctx.contentType(ContentType.APPLICATION_OCTET_STREAM); + ctx.result(Utils.EMPTY_BYTE_ARRAY); + return; + } + + // Encrypt patch list ctx.contentType(ContentType.APPLICATION_OCTET_STREAM); - ctx.result(AeadHelper.encryptCBC(this.getProto())); + ctx.result(AeadHelper.encryptCBC(diffBytes)); } }