From aef28e9fe9cf3b5fdc47066579e38e1fc3e58019 Mon Sep 17 00:00:00 2001 From: Adrian Bergqvist Date: Sat, 2 Jul 2022 05:20:46 +0200 Subject: [PATCH] Use fingerprints to minimize transfers --- .../org/adde0109/ambassador/Ambassador.java | 4 +- .../ambassador/Forge/ForgeHandshakeUtils.java | 125 ++++++++++++------ .../Forge/ForgeServerConnection.java | 18 +-- 3 files changed, 92 insertions(+), 55 deletions(-) diff --git a/src/main/java/org/adde0109/ambassador/Ambassador.java b/src/main/java/org/adde0109/ambassador/Ambassador.java index f5bafc4..182ab60 100644 --- a/src/main/java/org/adde0109/ambassador/Ambassador.java +++ b/src/main/java/org/adde0109/ambassador/Ambassador.java @@ -15,6 +15,7 @@ import com.velocitypowered.api.proxy.server.RegisteredServer; import net.kyori.adventure.text.Component; import org.adde0109.ambassador.Forge.ForgeConnection; import org.adde0109.ambassador.Forge.ForgeHandshakeHandler; +import org.adde0109.ambassador.Forge.ForgeHandshakeUtils; import org.adde0109.ambassador.Forge.ForgeServerConnection; import org.checkerframework.checker.index.qual.PolyUpperBound; import org.slf4j.Logger; @@ -53,6 +54,7 @@ public class Ambassador { logger.warn("Ambassador will be disabled because of errors"); } + ForgeHandshakeUtils.HandshakeReceiver.logger = logger; } @Subscribe @@ -70,7 +72,7 @@ public class Ambassador { if (ex != null) { continuation.resume(); } else { - if (Arrays.equals(msg.modListPacket,forgeConnection.get().getTransmittedHandshake().modListPacket)) { + if (msg.equals(forgeConnection.get().getTransmittedHandshake())) { continuation.resume(); } else { event.setResult(ServerPreConnectEvent.ServerResult.denied()); diff --git a/src/main/java/org/adde0109/ambassador/Forge/ForgeHandshakeUtils.java b/src/main/java/org/adde0109/ambassador/Forge/ForgeHandshakeUtils.java index e32e125..a1a9c7d 100644 --- a/src/main/java/org/adde0109/ambassador/Forge/ForgeHandshakeUtils.java +++ b/src/main/java/org/adde0109/ambassador/Forge/ForgeHandshakeUtils.java @@ -28,77 +28,112 @@ public class ForgeHandshakeUtils { return i; } - public static class handshakeReceiver { + public static class HandshakeReceiver { private int partLength; - private final Logger logger; + public static Logger logger; private int numberOfRecivedParts; - private byte[] recivedParts; private int recivedBytes; - private final RegisteredServer forgeServer; - public handshakeReceiver(RegisteredServer server, Logger logger) { + private final int numberOfParts; + private final long checksum; + private final int[] separators; + private final byte[] recivedParts; - this.logger = logger; - this.forgeServer = server; + private HandshakeReceiver(ServerPing serverPing) throws Exception { + if ((serverPing.getModinfo().isEmpty()) || (!Objects.equals(serverPing.getModinfo().get().getType(), "ambassador"))) { + throw new Exception("The specified Forge server is not running the Forge-side version of this plugin!"); + } + + ModInfo.Mod pair = serverPing.getModinfo().orElseThrow(IllegalAccessError::new).getMods().get(0); + + this.separators = Arrays.stream(pair.getVersion().substring(pair.getVersion().indexOf(":") + 1).split(":")).map(Integer::parseInt).mapToInt(x -> x).toArray(); + this.checksum = Long.parseUnsignedLong((pair.getVersion().split(":")[0].split("-"))[3],16); + this.numberOfParts = Integer.parseInt((pair.getVersion().split(":")[0].split("-"))[1]); + int totalLength = Integer.parseInt((pair.getVersion().split(":")[0].split("-"))[2]); + this.recivedParts = new byte[totalLength]; } - public CompletableFuture downloadHandshake() { + + + public static CompletableFuture downloadHandshake(RegisteredServer forgeServer) { CompletableFuture future = new CompletableFuture(); - ping(future); - return future; - } - - private void ping(CompletableFuture future) { forgeServer.ping().whenComplete((msg,ex) -> { if (ex != null) { future.completeExceptionally(ex); } else { - onBackendPong(msg, future); + try { + HandshakeReceiver handshakeReceiver = new HandshakeReceiver(msg); + handshakeReceiver.handle(msg); + handshakeReceiver.downloadLoop(forgeServer,future); + } catch (Exception e) { + future.completeExceptionally(e); + } } }); + return future; } - public void onBackendPong(ServerPing status, CompletableFuture future) { - numberOfRecivedParts++; - if ((status.getModinfo().isEmpty()) || (!Objects.equals(status.getModinfo().get().getType(), "ambassador"))) { - future.completeExceptionally(new Exception("The specified Forge server is not running the Forge-side version of this plugin!")); - return; - } + public static CompletableFuture downloadHandshake(RegisteredServer forgeServer, CachedServerHandshake oldHandshake) { + CompletableFuture future = new CompletableFuture(); + forgeServer.ping().whenComplete((msg,ex) -> { + if (ex != null) { + future.completeExceptionally(ex); + } else { + try { + HandshakeReceiver handshakeReceiver = new HandshakeReceiver(msg); + if (handshakeReceiver.getChecksum() == oldHandshake.fingerprint) { + future.complete(oldHandshake); + } else { + handshakeReceiver.handle(msg); + handshakeReceiver.downloadLoop(forgeServer,future); + } + } catch (Exception e) { + future.completeExceptionally(e); + } + } + }); + return future; + } + private long getChecksum() { + return checksum; + } + + private void downloadLoop(RegisteredServer server, CompletableFuture future) { + if (numberOfRecivedParts < numberOfParts) { + server.ping().whenComplete((msg,ex) -> { + if (ex != null) { + future.completeExceptionally(ex); + } else { + handle(msg); + downloadLoop(server, future); + } + }); + } else { + List packets = splitPackets(recivedParts,separators); + future.complete(new CachedServerHandshake(checksum,packets.get(0),packets.subList(1,packets.size()-1))); + } + } + + + private void handle(ServerPing status) { + numberOfRecivedParts++; ModInfo.Mod pair = status.getModinfo().orElseThrow(IllegalAccessError::new).getMods().get(0); - - int[] values = Arrays.stream(pair.getVersion().substring(pair.getVersion().indexOf(":") + 1).split(":")).map(Integer::parseInt).mapToInt(x -> x).toArray(); - - int totalLength = Integer.parseInt((pair.getVersion().split(":")[0].split("-"))[2]); - int parts = Integer.parseInt((pair.getVersion().split(":")[0].split("-"))[1]); int recivedPartNr = Integer.parseInt((pair.getVersion().split(":")[0].split("-"))[0]); - - logger.info("Downloaded part " + String.valueOf(numberOfRecivedParts) + " out of " + String.valueOf(parts)); - - if(recivedParts == null) { - recivedParts = new byte[totalLength]; - partLength = pair.getId().getBytes(StandardCharsets.ISO_8859_1).length; - } - placePartInArray(pair.getId().getBytes(StandardCharsets.ISO_8859_1), recivedPartNr - 1); - if (numberOfRecivedParts >= parts) { - List packets = splitPackets(recivedParts,values); - future.complete(new CachedServerHandshake("",packets.get(0),packets.subList(1,packets.size()-1))); - } else { - ping(future); - } + logger.info("Downloaded part " + String.valueOf(numberOfRecivedParts) + " out of " + String.valueOf(numberOfParts)); } private void placePartInArray(byte[] temp, int partNr) { - int head = partNr * partLength; + int head = (partNr == numberOfParts-1) ? recivedParts.length-temp.length : partNr*temp.length; for (byte b : temp) { recivedParts[head] = b; head++; @@ -129,14 +164,18 @@ public class ForgeHandshakeUtils { } public static class CachedServerHandshake { - private String sessionID; + private long fingerprint; public byte[] modListPacket; public List otherPackets; - private CachedServerHandshake(String sessionID,byte[] modListPacket,List otherPackets) { - this.sessionID = sessionID; + private CachedServerHandshake(long fingerprint,byte[] modListPacket,List otherPackets) { + this.fingerprint = fingerprint; this.modListPacket = modListPacket; this.otherPackets = otherPackets; } + + public boolean equals(CachedServerHandshake cachedServerHandshake) { + return this.fingerprint == cachedServerHandshake.fingerprint; + } } } diff --git a/src/main/java/org/adde0109/ambassador/Forge/ForgeServerConnection.java b/src/main/java/org/adde0109/ambassador/Forge/ForgeServerConnection.java index 1ea98b6..b9e6df0 100644 --- a/src/main/java/org/adde0109/ambassador/Forge/ForgeServerConnection.java +++ b/src/main/java/org/adde0109/ambassador/Forge/ForgeServerConnection.java @@ -29,19 +29,15 @@ public class ForgeServerConnection { public CompletableFuture getHandshake() { CompletableFuture future; - if (handshakeServer.getPlayersConnected().isEmpty() || (handshake == null)) { - ForgeHandshakeUtils.handshakeReceiver - receiver = new ForgeHandshakeUtils.handshakeReceiver(handshakeServer, logger); - future = receiver.downloadHandshake(); - future.thenAccept(p -> { - handshake = p; - }); - return future; + if (handshake == null) { + future = ForgeHandshakeUtils.HandshakeReceiver.downloadHandshake(handshakeServer); } else { - future = new CompletableFuture<>(); - future.complete(handshake); - return future; + future = ForgeHandshakeUtils.HandshakeReceiver.downloadHandshake(handshakeServer,handshake); } + future.thenAccept(p -> { + handshake = p; + }); + return future; } public void handle(ServerLoginPluginMessageEvent event, Continuation continuation) {