Use fingerprints to minimize transfers

This commit is contained in:
Adrian Bergqvist 2022-07-02 05:20:46 +02:00
parent f906de8127
commit aef28e9fe9
No known key found for this signature in database
GPG Key ID: FAE7D8EDE225E686
3 changed files with 92 additions and 55 deletions

View File

@ -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());

View File

@ -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<CachedServerHandshake> downloadHandshake() {
public static CompletableFuture<CachedServerHandshake> downloadHandshake(RegisteredServer forgeServer) {
CompletableFuture<CachedServerHandshake> future = new CompletableFuture<CachedServerHandshake>();
ping(future);
return future;
}
private void ping(CompletableFuture<CachedServerHandshake> 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<CachedServerHandshake> 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<CachedServerHandshake> downloadHandshake(RegisteredServer forgeServer, CachedServerHandshake oldHandshake) {
CompletableFuture<CachedServerHandshake> future = new CompletableFuture<CachedServerHandshake>();
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<CachedServerHandshake> future) {
if (numberOfRecivedParts < numberOfParts) {
server.ping().whenComplete((msg,ex) -> {
if (ex != null) {
future.completeExceptionally(ex);
} else {
handle(msg);
downloadLoop(server, future);
}
});
} else {
List<byte[]> 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<byte[]> 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<byte[]> otherPackets;
private CachedServerHandshake(String sessionID,byte[] modListPacket,List<byte[]> otherPackets) {
this.sessionID = sessionID;
private CachedServerHandshake(long fingerprint,byte[] modListPacket,List<byte[]> otherPackets) {
this.fingerprint = fingerprint;
this.modListPacket = modListPacket;
this.otherPackets = otherPackets;
}
public boolean equals(CachedServerHandshake cachedServerHandshake) {
return this.fingerprint == cachedServerHandshake.fingerprint;
}
}
}

View File

@ -29,19 +29,15 @@ public class ForgeServerConnection {
public CompletableFuture<ForgeHandshakeUtils.CachedServerHandshake> getHandshake() {
CompletableFuture<ForgeHandshakeUtils.CachedServerHandshake> 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) {