v0.4.0 Kick to reset. Server switch rewrite

This commit is contained in:
Adrian Bergqvist 2022-08-05 16:29:11 +02:00
parent b99d042d0f
commit 56034ec165
No known key found for this signature in database
GPG Key ID: FAE7D8EDE225E686
5 changed files with 51 additions and 71 deletions

View File

@ -4,7 +4,7 @@ plugins {
}
group 'org.adde0109'
version '0.3.3'
version '0.4.0'
repositories {
maven {
@ -18,6 +18,7 @@ dependencies {
implementation 'com.velocitypowered:velocity-api:3.1.1'
implementation 'com.electronwill.night-config:toml:3.6.5'
implementation 'org.bstats:bstats-velocity:3.0.0'
implementation 'org.apache.commons:commons-collections4:4.4'
annotationProcessor 'com.velocitypowered:velocity-api:3.1.2-SNAPSHOT'
}

View File

@ -83,9 +83,10 @@ public class Ambassador {
if((event.getInitialServer().isPresent()) && (forgeHandshakeHandler.getForgeConnection(event.getPlayer()).isPresent())) {
//Forge client
ForgeConnection forgeConnection = forgeHandshakeHandler.getForgeConnection(event.getPlayer()).get();
if (config.getForced(forgeConnection.getConnection().getProtocolVersion().getProtocol())) {
event.setInitialServer(config.getServer(forgeConnection.getConnection().getProtocolVersion().getProtocol()));
if (forgeConnection.isForced()) {
event.setInitialServer(forgeConnection.getSyncedServer().get());
}
forgeConnection.setForced(config.getForced(forgeConnection.getConnection().getProtocolVersion().getProtocol()));
}
continuation.resume();
}

View File

@ -23,6 +23,7 @@ public class ForgeConnection {
private boolean ignoreSyncExepction = false;
private Optional<ForgeHandshakeUtils.CachedServerHandshake> transmittedHandshake = Optional.empty();
private boolean forced = false;
private Optional<RegisteredServer> syncedTo = Optional.empty();
@ -136,4 +137,13 @@ public class ForgeConnection {
public static byte[] getRecivedClientACK() {
return recivedClientACK;
}
public Optional<RegisteredServer> getSyncedServer() {
return syncedTo;
}
public void setForced(boolean forced) {
this.forced = forced;
}
public boolean isForced() {
return forced;
}
}

View File

@ -55,7 +55,12 @@ public class ForgeHandshakeHandler {
registerForgeConnection(forgeConnection);
});
if (defaultServer != null) {
if (ambassador.forgeServerSwitchHandler.reSyncMap.containsKey(event.getUsername())) {
forgeConnection.sync(ambassador.forgeServerSwitchHandler.reSyncMap.remove(event.getUsername())).thenAccept((done) -> {
continuation.resume();
});
forgeConnection.setForced(true);
} else if (defaultServer != null) {
//If a connection does not already exist, create one.
if (!forgeServerConnectionMap.containsKey(defaultServer)) {
forgeServerConnectionMap.put(defaultServer, new ForgeServerConnection(defaultServer));
@ -64,6 +69,7 @@ public class ForgeHandshakeHandler {
forgeConnection.sync(forgeServerConnectionMap.get(defaultServer)).thenAccept((done) -> {
continuation.resume();
});
forgeConnection.setForced(ambassador.config.getForced(forgeConnection.getConnection().getProtocolVersion().getProtocol()));
} else {
continuation.resume();
}

View File

@ -18,14 +18,17 @@ import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import org.adde0109.ambassador.Ambassador;
import org.apache.commons.collections4.map.PassiveExpiringMap;
public class ForgeServerSwitchHandler {
private static final int RESYNC_EXPIRY_TIME = 20;
private final Ambassador ambassador;
public final ReSyncTracker reSyncTracker;
public final PassiveExpiringMap<String,ForgeServerConnection> reSyncMap;
public ForgeServerSwitchHandler(Ambassador ambassador) {
this.ambassador = ambassador;
this.reSyncTracker = new ReSyncTracker();
this.reSyncMap = new PassiveExpiringMap<>(RESYNC_EXPIRY_TIME,TimeUnit.SECONDS);
}
@ -36,85 +39,44 @@ public class ForgeServerSwitchHandler {
return;
}
Optional<ForgeServerConnection> forgeServerConnectionOptional = ambassador.forgeHandshakeHandler.getForgeServerConnection(event.getOriginalServer());
if (forgeServerConnectionOptional.isPresent()) {
//Check 1; Check if the server is already known to us. Check if the client is compatible.
ForgeServerConnection forgeServerConnection = forgeServerConnectionOptional.get();
Optional<ForgeConnection> forgeConnection = ambassador.forgeHandshakeHandler.getForgeConnection(event.getPlayer());
if (forgeConnection.isPresent()) {
ForgeServerConnection forgeServerConnection = forgeServerConnectionOptional.orElseGet(() -> new ForgeServerConnection(event.getOriginalServer()));
forgeServerConnection.getHandshake().whenComplete((msg, ex) -> {
if (ex != null) {
//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.
ambassador.forgeHandshakeHandler.unRegisterForgeServer(forgeServerConnection.getServer());
if (forgeServerConnectionOptional.isPresent())
ambassador.forgeHandshakeHandler.unRegisterForgeServer(forgeServerConnection.getServer());
}
continuation.resume();
} else {
Optional<ForgeConnection> forgeConnection = ambassador.forgeHandshakeHandler.getForgeConnection(event.getPlayer());
if (forgeConnection.isEmpty() && (event.getPlayer().getCurrentServer().isPresent())) {
//If vanilla tries to connect to a server we know is forge
//If the server just got discovered, register it.
if (forgeServerConnectionOptional.isEmpty())
ambassador.forgeHandshakeHandler.registerForgeServer(event.getOriginalServer(),forgeServerConnection);
//To make legacy forwarding work
List<GameProfile.Property> properties = new ArrayList<>(event.getPlayer().getGameProfileProperties());
properties.add(new GameProfile.Property("extraData", "\1FML2\1",""));
event.getPlayer().setGameProfileProperties(properties);
if (!msg.equals(forgeConnection.get().getTransmittedHandshake().get())) {
event.setResult(ServerPreConnectEvent.ServerResult.denied());
event.getPlayer().sendMessage(Component.text("This server requires Forge!", NamedTextColor.RED));
continuation.resume();
} else if (forgeConnection.isPresent()) {
//To make legacy forwarding work
List<GameProfile.Property> properties = new ArrayList<>(event.getPlayer().getGameProfileProperties());
properties.add(new GameProfile.Property("extraData", "\1FML2\1",""));
event.getPlayer().setGameProfileProperties(properties);
if (forgeConnection.get().getTransmittedHandshake().isPresent()
&& forgeConnection.get().getRecivedClientModlist().isPresent()
&& msg.equals(forgeConnection.get().getTransmittedHandshake().get())) {
//The client's registry is the same as the server's
continuation.resume();
} else {
event.setResult(ServerPreConnectEvent.ServerResult.denied());
ambassador.logger.info("Kicking {} because of re-sync needed", event.getPlayer());
event.getPlayer().disconnect(Component.text("Please reconnect"));
reSyncTracker.put(event.getPlayer().getRemoteAddress(),event.getOriginalServer());
continuation.resume();
}
} else {
//If the initial server is forge while the client is vanilla.
//Can't handle, just let it pass.
continuation.resume();
ambassador.logger.info("Kicking {} because of re-sync needed", event.getPlayer());
event.getPlayer().disconnect(Component.text("Please reconnect"));
reSyncMap.put(event.getPlayer().getUsername(),forgeServerConnection);
}
}
continuation.resume();
});
} else if (forgeServerConnectionOptional.isPresent()) {
//If vanilla tries to connect to a server we know is forge
event.setResult(ServerPreConnectEvent.ServerResult.denied());
event.getPlayer().sendMessage(Component.text("This server requires Forge!", NamedTextColor.RED));
continuation.resume();
} else {
//The server is not known to us.
continuation.resume();
}
}
class ReSyncTracker {
private static final int TIMEOUT = 2;
private static final int TICK_TIME = 15;
private Optional<ScheduledTask> scheduledTask = Optional.empty();
private final AtomicInteger counter = new AtomicInteger();
private Map<InetSocketAddress, Integer> reSyncTimeoutMap = new HashMap<>();
private Map<InetSocketAddress,RegisteredServer> reSyncTargetMap = new HashMap<>();
public void tick() {
int c = counter.incrementAndGet();
if (reSyncTimeoutMap.values().removeIf((v) -> c>=v))
reSyncTargetMap.keySet().removeIf((k) -> !reSyncTargetMap.containsKey(k));
//Remove if the reSyncTargetMap is empty
if (reSyncTargetMap.isEmpty() && scheduledTask.isPresent())
if (scheduledTask.get().status() == TaskStatus.SCHEDULED)
scheduledTask = Optional.empty();
}
public void put(InetSocketAddress inetSocketAddress, RegisteredServer registeredServer) {
reSyncTimeoutMap.put(inetSocketAddress, counter.get()+TIMEOUT);
reSyncTargetMap.put(inetSocketAddress, registeredServer);
//Start if not already started
if (scheduledTask.isPresent())
if (scheduledTask.get().status() == TaskStatus.CANCELLED || scheduledTask.get().status() == TaskStatus.FINISHED)
scheduledTask = Optional.of(ambassador.server.getScheduler().buildTask((ambassador), this::tick)
.repeat(TICK_TIME, TimeUnit.SECONDS).schedule());
}
public RegisteredServer remove(InetSocketAddress inetSocketAddress) {
reSyncTimeoutMap.remove(inetSocketAddress);
return reSyncTargetMap.remove(inetSocketAddress);
}
}
}