Handle forge player when no forge servers are available

This commit is contained in:
Adrian Bergqvist 2022-07-12 20:02:03 +02:00
parent 3cafdd6dfb
commit 364f3d2cb2
No known key found for this signature in database
GPG Key ID: FAE7D8EDE225E686
5 changed files with 102 additions and 61 deletions

View File

@ -4,7 +4,7 @@ plugins {
} }
group 'org.adde0109' group 'org.adde0109'
version '0.2.1-SNAPSHOT' version '0.2.2'
repositories { repositories {
maven { maven {

View File

@ -22,7 +22,7 @@ import org.slf4j.Logger;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.*; import java.util.*;
@Plugin(id = "ambassador", name = "Ambassador", version = "0.2.1-SNAPSHOT", authors = {"adde0109"}) @Plugin(id = "ambassador", name = "Ambassador", version = "0.2.2", authors = {"adde0109"})
public class Ambassador { public class Ambassador {
private final ProxyServer server; private final ProxyServer server;
@ -68,6 +68,10 @@ public class Ambassador {
forgeServerConnection.getHandshake().whenComplete((msg, ex) -> { forgeServerConnection.getHandshake().whenComplete((msg, ex) -> {
if (ex != null) { if (ex != null) {
//The server was forge but aren't right now. Or it's just offline. //The server was forge but aren't right now. Or it's just offline.
if (ex instanceof ForgeHandshakeUtils.HandshakeReceiver.HandshakeNotAvailableException) {
//It's not running ambassador so it should be unregistered.
forgeHandshakeHandler.unRegisterForgeServer(forgeServerConnection.getServer());
}
continuation.resume(); continuation.resume();
} else { } else {
Optional<ForgeConnection> forgeConnection = forgeHandshakeHandler.getForgeConnection(event.getPlayer()); Optional<ForgeConnection> forgeConnection = forgeHandshakeHandler.getForgeConnection(event.getPlayer());
@ -77,7 +81,8 @@ public class Ambassador {
event.getPlayer().sendMessage(Component.text("This server requires Forge!", NamedTextColor.RED)); event.getPlayer().sendMessage(Component.text("This server requires Forge!", NamedTextColor.RED));
continuation.resume(); continuation.resume();
} else if (forgeConnection.isPresent()) { } else if (forgeConnection.isPresent()) {
if (msg.equals(forgeConnection.get().getTransmittedHandshake())) { if (forgeConnection.get().getTransmittedHandshake().isPresent()
&& msg.equals(forgeConnection.get().getTransmittedHandshake().get())) {
//The client's registry is the same as the server's //The client's registry is the same as the server's
continuation.resume(); continuation.resume();
} else { } else {

View File

@ -5,8 +5,10 @@ import com.velocitypowered.api.event.Continuation;
import com.velocitypowered.api.event.player.ServerLoginPluginMessageEvent; import com.velocitypowered.api.event.player.ServerLoginPluginMessageEvent;
import com.velocitypowered.api.proxy.LoginPhaseConnection; import com.velocitypowered.api.proxy.LoginPhaseConnection;
import com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier; import com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier;
import com.velocitypowered.api.proxy.server.RegisteredServer;
import java.io.EOFException; import java.io.EOFException;
import java.util.List; import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
public class ForgeConnection { public class ForgeConnection {
@ -16,65 +18,59 @@ public class ForgeConnection {
private byte[] recivedClientModlist; private byte[] recivedClientModlist;
private static byte[] recivedClientACK; private static byte[] recivedClientACK;
private ForgeHandshakeUtils.CachedServerHandshake transmittedHandshake; private Optional<ForgeHandshakeUtils.CachedServerHandshake> transmittedHandshake = Optional.empty();
private Optional<RegisteredServer> syncedTo = Optional.empty();
private ForgeConnection(LoginPhaseConnection connection) { public ForgeConnection(LoginPhaseConnection connection) {
this.connection = connection; this.connection = connection;
} }
public static CompletableFuture<Boolean> testIfForge(LoginPhaseConnection connection) {
CompletableFuture<Boolean> future = new CompletableFuture<>();
public static CompletableFuture<ForgeConnection> sync(LoginPhaseConnection connection, byte[] testPacket = ForgeHandshakeUtils.generateTestPacket();
ForgeServerConnection forgeServerConnection, connection.sendLoginPluginMessage(MinecraftChannelIdentifier.create("fml", "loginwrapper"), testPacket,
Continuation continuation) { responseBody -> {
CompletableFuture<ForgeConnection> future = new CompletableFuture<>(); future.complete(responseBody != null);
ForgeConnection forgeConnection = new ForgeConnection(connection); });
forgeServerConnection.getHandshake().whenComplete((msg, ex) -> { return future;
}
public CompletableFuture<Boolean> sync(ForgeServerConnection forgeServerConnection) {
CompletableFuture<Boolean> future = new CompletableFuture<>();
forgeServerConnection.getHandshake().whenComplete((msg,ex) -> {
if (ex != null) { if (ex != null) {
future.completeExceptionally(ex); future.complete(false);
} else {
forgeConnection.sendModlist(msg.modListPacket).thenAccept((response) -> {
if (response != null) {
future.complete(forgeConnection);
} else {
future.complete(null);
}
});
forgeConnection.sendOther(msg.otherPackets).thenAccept((response) -> {
if (response != null) {
future.complete(forgeConnection);
} else {
future.complete(null);
}
});
forgeConnection.transmittedHandshake = msg;
} }
//Write sendModlist(msg.modListPacket).thenAccept((response) -> {
continuation.resume(); recivedClientModlist = response;
});
sendOther(msg.otherPackets).thenAccept((response) -> {
ForgeConnection.recivedClientACK = response;
transmittedHandshake = Optional.of(msg);
syncedTo = Optional.of(forgeServerConnection.getServer());
});
future.complete(true);
}); });
return future; return future;
} }
public CompletableFuture<byte[]> sendModlist(byte[] modListPacket) { private CompletableFuture<byte[]> sendModlist(byte[] modListPacket) {
CompletableFuture<byte[]> future = new CompletableFuture<>(); CompletableFuture<byte[]> future = new CompletableFuture<>();
connection.sendLoginPluginMessage(MinecraftChannelIdentifier.create("fml", "loginwrapper"), modListPacket, connection.sendLoginPluginMessage(MinecraftChannelIdentifier.create("fml", "loginwrapper"), modListPacket,
responseBody -> { future::complete);
recivedClientModlist = responseBody;
future.complete(recivedClientModlist);
});
return future; return future;
} }
CompletableFuture<byte[]> sendOther(List<byte[]> otherPackets) { private CompletableFuture<byte[]> sendOther(List<byte[]> otherPackets) {
CompletableFuture<byte[]> future = new CompletableFuture<>(); CompletableFuture<byte[]> future = new CompletableFuture<>();
for (int i = 0; i < otherPackets.size(); i++) { for (int i = 0; i < otherPackets.size(); i++) {
connection.sendLoginPluginMessage(MinecraftChannelIdentifier.create("fml", "loginwrapper"), otherPackets.get(i), connection.sendLoginPluginMessage(MinecraftChannelIdentifier.create("fml", "loginwrapper"), otherPackets.get(i),
(i < (otherPackets.size() - 1)) ? responseBody -> { (i < (otherPackets.size() - 1)) ? responseBody -> {
} : responseBody -> { } : future::complete);
if (responseBody != null)
recivedClientACK = responseBody;
future.complete(responseBody);
});
} }
return future; return future;
} }
@ -100,7 +96,7 @@ public class ForgeConnection {
return connection; return connection;
} }
public ForgeHandshakeUtils.CachedServerHandshake getTransmittedHandshake() { public Optional<ForgeHandshakeUtils.CachedServerHandshake> getTransmittedHandshake() {
return transmittedHandshake; return transmittedHandshake;
} }

View File

@ -50,26 +50,27 @@ public class ForgeHandshakeHandler {
} }
RegisteredServer defaultServer = config.getServer(event.getConnection().getProtocolVersion().getProtocol()); RegisteredServer defaultServer = config.getServer(event.getConnection().getProtocolVersion().getProtocol());
if (defaultServer == null) { ForgeConnection forgeConnection = new ForgeConnection((LoginPhaseConnection) event.getConnection());
continuation.resume(); ForgeConnection.testIfForge((LoginPhaseConnection) event.getConnection())
return; .thenAccept((isForge) -> {
} registerForgeConnection(forgeConnection);
});
if (defaultServer != null) {
//If a connection does not already exist, create one.
//If a connection does not already exist, create one. if (!forgeServerConnectionMap.containsKey(defaultServer)) {
if (!forgeServerConnectionMap.containsKey(defaultServer)) { forgeServerConnectionMap.put(defaultServer, new ForgeServerConnection(defaultServer));
forgeServerConnectionMap.put(defaultServer, new ForgeServerConnection(defaultServer)); }
} //Forge Handshake
forgeConnection.sync(forgeServerConnectionMap.get(defaultServer)).thenAccept((done) -> {
ForgeServerConnection forgeServerConnection = forgeServerConnectionMap.get(defaultServer); continuation.resume();
});
//Syncing - continuation is forwarded to this method } else {
ForgeConnection.sync((LoginPhaseConnection) event.getConnection(),forgeServerConnection,continuation).thenAccept( continuation.resume();
this::onSyncComplete); }
} }
private void onSyncComplete(ForgeConnection forgeConnection) { private void registerForgeConnection(ForgeConnection forgeConnection) {
if (forgeConnection != null) { if (forgeConnection != null) {
incomingForgeConnections.values().removeIf((c) -> !c.getConnection().isActive()); incomingForgeConnections.values().removeIf((c) -> !c.getConnection().isActive());
incomingForgeConnections.put(forgeConnection.getConnection().getRemoteAddress(), forgeConnection); incomingForgeConnections.put(forgeConnection.getConnection().getRemoteAddress(), forgeConnection);
@ -92,6 +93,9 @@ public class ForgeHandshakeHandler {
public void registerForgeServer(RegisteredServer server, ForgeServerConnection forgeServerConnection) { public void registerForgeServer(RegisteredServer server, ForgeServerConnection forgeServerConnection) {
forgeServerConnectionMap.put(server,forgeServerConnection); forgeServerConnectionMap.put(server,forgeServerConnection);
} }
public void unRegisterForgeServer(RegisteredServer server) {
forgeServerConnectionMap.remove(server);
}
@Subscribe @Subscribe
public void onServerLoginPluginMessageEvent(ServerLoginPluginMessageEvent event, Continuation continuation) { public void onServerLoginPluginMessageEvent(ServerLoginPluginMessageEvent event, Continuation continuation) {

View File

@ -1,6 +1,8 @@
package org.adde0109.ambassador.forge; package org.adde0109.ambassador.forge;
import com.google.common.io.ByteArrayDataInput; import com.google.common.io.ByteArrayDataInput;
import com.google.common.io.ByteArrayDataOutput;
import com.google.common.io.ByteStreams;
import com.velocitypowered.api.proxy.server.RegisteredServer; import com.velocitypowered.api.proxy.server.RegisteredServer;
import com.velocitypowered.api.proxy.server.ServerPing; import com.velocitypowered.api.proxy.server.ServerPing;
import com.velocitypowered.api.util.ModInfo; import com.velocitypowered.api.util.ModInfo;
@ -28,6 +30,36 @@ public class ForgeHandshakeUtils {
return i; return i;
} }
public static void writeVarInt(ByteArrayDataOutput stream,int i) {
while((i & -128) != 0) {
stream.writeByte(i & 127 | 128);
i >>>= 7;
}
stream.writeByte(i);
}
public static void writeUtf(ByteArrayDataOutput stream,String p_211400_1_) {
byte[] abyte = p_211400_1_.getBytes(StandardCharsets.UTF_8);
writeVarInt(stream,abyte.length);
stream.write(abyte);
}
public static byte[] generateTestPacket() {
ByteArrayDataOutput dataAndPacketIdStream = ByteStreams.newDataOutput();
writeVarInt(dataAndPacketIdStream,4);
writeUtf(dataAndPacketIdStream,"ambassadortestpacket");
writeVarInt(dataAndPacketIdStream,0);
ByteArrayDataOutput stream = ByteStreams.newDataOutput();
byte[] dataAndPacketId = dataAndPacketIdStream.toByteArray();
writeUtf(stream,"fml:handshake");
writeVarInt(stream,dataAndPacketId.length);
stream.write(dataAndPacketId);
return stream.toByteArray();
}
public static class HandshakeReceiver { public static class HandshakeReceiver {
private int partLength; private int partLength;
@ -45,7 +77,7 @@ public class ForgeHandshakeUtils {
private HandshakeReceiver(ServerPing serverPing) throws Exception { private HandshakeReceiver(ServerPing serverPing) throws Exception {
if ((serverPing.getModinfo().isEmpty()) || (!Objects.equals(serverPing.getModinfo().get().getType(), "ambassador"))) { 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!"); throw new HandshakeNotAvailableException("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); ModInfo.Mod pair = serverPing.getModinfo().orElseThrow(IllegalAccessError::new).getMods().get(0);
@ -160,7 +192,11 @@ public class ForgeHandshakeUtils {
return list; return list;
} }
public static class HandshakeNotAvailableException extends Exception {
HandshakeNotAvailableException(String errorMessage) {
super(errorMessage);
}
}
} }
public static class CachedServerHandshake { public static class CachedServerHandshake {