diff --git a/build.gradle b/build.gradle index 0c88849..a2b0796 100644 --- a/build.gradle +++ b/build.gradle @@ -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' } diff --git a/src/main/java/org/adde0109/ambassador/Ambassador.java b/src/main/java/org/adde0109/ambassador/Ambassador.java index 9d12eff..bc66878 100644 --- a/src/main/java/org/adde0109/ambassador/Ambassador.java +++ b/src/main/java/org/adde0109/ambassador/Ambassador.java @@ -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(); } diff --git a/src/main/java/org/adde0109/ambassador/forge/ForgeConnection.java b/src/main/java/org/adde0109/ambassador/forge/ForgeConnection.java index 0931052..6f2836d 100644 --- a/src/main/java/org/adde0109/ambassador/forge/ForgeConnection.java +++ b/src/main/java/org/adde0109/ambassador/forge/ForgeConnection.java @@ -23,6 +23,7 @@ public class ForgeConnection { private boolean ignoreSyncExepction = false; private Optional transmittedHandshake = Optional.empty(); + private boolean forced = false; private Optional syncedTo = Optional.empty(); @@ -136,4 +137,13 @@ public class ForgeConnection { public static byte[] getRecivedClientACK() { return recivedClientACK; } + public Optional getSyncedServer() { + return syncedTo; + } + public void setForced(boolean forced) { + this.forced = forced; + } + public boolean isForced() { + return forced; + } } diff --git a/src/main/java/org/adde0109/ambassador/forge/ForgeHandshakeHandler.java b/src/main/java/org/adde0109/ambassador/forge/ForgeHandshakeHandler.java index bb7968a..1d49df0 100644 --- a/src/main/java/org/adde0109/ambassador/forge/ForgeHandshakeHandler.java +++ b/src/main/java/org/adde0109/ambassador/forge/ForgeHandshakeHandler.java @@ -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(); } diff --git a/src/main/java/org/adde0109/ambassador/forge/ForgeServerSwitchHandler.java b/src/main/java/org/adde0109/ambassador/forge/ForgeServerSwitchHandler.java index 02e62e1..d8507a2 100644 --- a/src/main/java/org/adde0109/ambassador/forge/ForgeServerSwitchHandler.java +++ b/src/main/java/org/adde0109/ambassador/forge/ForgeServerSwitchHandler.java @@ -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 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 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 = 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 = 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 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 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 = Optional.empty(); - private final AtomicInteger counter = new AtomicInteger(); - private Map reSyncTimeoutMap = new HashMap<>(); - private Map 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); - } - } }