From d6cc47aba15d9b65bce68f5a2265a25269e411c8 Mon Sep 17 00:00:00 2001 From: Adrian Bergqvist Date: Thu, 16 Feb 2023 21:11:13 +0100 Subject: [PATCH] Server switch logic rewritten --- build.gradle.kts | 1 + .../forge/FML2CRPMClientConnectionPhase.java | 76 ----------- .../forge/FML2ClientConnectionPhase.java | 52 +------- .../ambassador/forge/ForgeConstants.java | 2 +- .../forge/ForgeFMLConnectionType.java | 3 +- .../velocity/VelocityEventHandler.java | 27 +--- .../VelocityForgeClientConnectionPhase.java | 126 +++++++++++++----- .../velocity/VelocityLoginPayloadManager.java | 62 --------- ...ler.java => ForgeLoginSessionHandler.java} | 19 ++- .../backend/ForgePlaySessionHandler.java | 41 ++++++ .../VelocityForgeBackendConnectionPhase.java | 27 ++-- .../VelocityForgeBackendHandshakeHandler.java | 2 +- .../client/OutboundForgeHandshakeHolder.java | 34 +++++ 13 files changed, 195 insertions(+), 277 deletions(-) delete mode 100644 src/main/java/org/adde0109/ambassador/forge/FML2CRPMClientConnectionPhase.java delete mode 100644 src/main/java/org/adde0109/ambassador/velocity/VelocityLoginPayloadManager.java rename src/main/java/org/adde0109/ambassador/velocity/backend/{ForgeHandshakeSessionHandler.java => ForgeLoginSessionHandler.java} (74%) create mode 100644 src/main/java/org/adde0109/ambassador/velocity/backend/ForgePlaySessionHandler.java create mode 100644 src/main/java/org/adde0109/ambassador/velocity/client/OutboundForgeHandshakeHolder.java diff --git a/build.gradle.kts b/build.gradle.kts index c7abd94..26158af 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -24,6 +24,7 @@ dependencies { compileOnly("io.netty:netty-buffer:4.1.86.Final") compileOnly("io.netty:netty-transport:4.1.86.Final") compileOnly("io.netty:netty-codec:4.1.86.Final") + compileOnly("io.netty:netty-handler:4.1.86.Final") } tasks { diff --git a/src/main/java/org/adde0109/ambassador/forge/FML2CRPMClientConnectionPhase.java b/src/main/java/org/adde0109/ambassador/forge/FML2CRPMClientConnectionPhase.java deleted file mode 100644 index d5d49e7..0000000 --- a/src/main/java/org/adde0109/ambassador/forge/FML2CRPMClientConnectionPhase.java +++ /dev/null @@ -1,76 +0,0 @@ -package org.adde0109.ambassador.forge; - -import com.velocitypowered.api.event.player.KickedFromServerEvent; -import com.velocitypowered.api.proxy.server.RegisteredServer; -import com.velocitypowered.api.util.UuidUtils; -import com.velocitypowered.proxy.VelocityServer; -import com.velocitypowered.proxy.config.PlayerInfoForwarding; -import com.velocitypowered.proxy.config.VelocityConfiguration; -import com.velocitypowered.proxy.connection.MinecraftConnection; -import com.velocitypowered.proxy.connection.client.ConnectedPlayer; -import com.velocitypowered.proxy.network.Connections; -import com.velocitypowered.proxy.protocol.StateRegistry; -import com.velocitypowered.proxy.protocol.packet.PluginMessage; -import com.velocitypowered.proxy.protocol.packet.ServerLoginSuccess; -import io.netty.buffer.Unpooled; -import org.adde0109.ambassador.Ambassador; -import org.adde0109.ambassador.velocity.VelocityForgeClientConnectionPhase; -import org.adde0109.ambassador.velocity.VelocityForgeHandshakeSessionHandler; -import org.adde0109.ambassador.velocity.VelocityLoginPayloadManager; - -import java.util.UUID; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; - -public class FML2CRPMClientConnectionPhase extends VelocityForgeClientConnectionPhase { - - //TODO: Use modData inside ConnectedPlayer instead - public byte[] modListData; - private RegisteredServer backupServer; - - public FML2CRPMClientConnectionPhase(VelocityForgeClientConnectionPhase.ClientPhase clientPhase, VelocityLoginPayloadManager payloadManager) { - super(clientPhase,payloadManager); - } - - public CompletableFuture reset(RegisteredServer server, ConnectedPlayer player) { - CompletableFuture future = new CompletableFuture<>(); - if (player.getConnectedServer() != null) { - backupServer = player.getConnectedServer().getServer(); - player.getConnectedServer().disconnect(); - player.setConnectedServer(null); - } - - MinecraftConnection connection = player.getConnection(); - connection.setSessionHandler(new VelocityForgeHandshakeSessionHandler(connection.getSessionHandler(),player)); - - ((VelocityServer) Ambassador.getInstance().server).unregisterConnection(player); - this.clientPhase = null; - - ScheduledFuture scheduledFuture = connection.eventLoop().schedule(()-> { - connection.getChannel().pipeline().remove(ForgeConstants.RESET_LISTENER); - future.complete(false); - }, Ambassador.getInstance().config.getResetTimeout(), TimeUnit.MILLISECONDS); - connection.getChannel().pipeline().addBefore(Connections.MINECRAFT_DECODER, ForgeConstants.RESET_LISTENER,new FML2CRPMResetCompleteDecoder()); - getPayloadManager().listenFor(98).thenAccept(ignore -> { - if (scheduledFuture.cancel(false)) { - connection.getChannel().pipeline().remove(ForgeConstants.RESET_LISTENER); - connection.setState(StateRegistry.LOGIN); - this.clientPhase = ClientPhase.HANDSHAKE; - future.complete(true); - } - }); - connection.write(new PluginMessage("fml:handshake",Unpooled.wrappedBuffer(ForgeHandshakeUtils.generatePluginResetPacket()))); - return future; - } - - - public void handleKick(KickedFromServerEvent event) { - if (backupServer != null && !(event.getResult() instanceof KickedFromServerEvent.RedirectPlayer)) { - net.kyori.adventure.text.Component reason = event.getServerKickReason().orElse(null); - event.setResult(KickedFromServerEvent.RedirectPlayer.create(backupServer,reason)); - backupServer = null; - } - } - -} diff --git a/src/main/java/org/adde0109/ambassador/forge/FML2ClientConnectionPhase.java b/src/main/java/org/adde0109/ambassador/forge/FML2ClientConnectionPhase.java index 56da338..141fa38 100644 --- a/src/main/java/org/adde0109/ambassador/forge/FML2ClientConnectionPhase.java +++ b/src/main/java/org/adde0109/ambassador/forge/FML2ClientConnectionPhase.java @@ -1,54 +1,17 @@ package org.adde0109.ambassador.forge; -import com.velocitypowered.api.event.Continuation; import com.velocitypowered.api.proxy.server.RegisteredServer; -import com.velocitypowered.proxy.VelocityServer; -import com.velocitypowered.proxy.connection.MinecraftConnection; -import com.velocitypowered.proxy.connection.backend.VelocityServerConnection; import com.velocitypowered.proxy.connection.client.ConnectedPlayer; -import com.velocitypowered.proxy.protocol.ProtocolUtils; -import com.velocitypowered.proxy.protocol.packet.LoginPluginMessage; -import com.velocitypowered.proxy.protocol.packet.LoginPluginResponse; -import io.netty.buffer.ByteBuf; import net.kyori.adventure.text.Component; import org.adde0109.ambassador.Ambassador; import org.adde0109.ambassador.velocity.VelocityForgeClientConnectionPhase; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.Arrays; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; -public class FML2ClientConnectionPhase extends VelocityForgeClientConnectionPhase { - - private Throwable throwable; - private RegisteredServer triedServer; - private CompletableFuture onServerSuccess; - private CompletableFuture onJoinGame; - - private static final Method CONNECT_TO_INITIAL_SERVER; - - static { - Class clazz; - try { - clazz = Class.forName("com.velocitypowered.proxy.connection.client.LoginSessionHandler"); - } catch (ClassNotFoundException ignored){ - try { - clazz = Class.forName("com.velocitypowered.proxy.connection.client.AuthSessionHandler"); - } catch (ClassNotFoundException e) { - throw new RuntimeException(e); - } - } - try { - CONNECT_TO_INITIAL_SERVER = clazz.getDeclaredMethod("connectToInitialServer", ConnectedPlayer.class); - CONNECT_TO_INITIAL_SERVER.setAccessible(true); - } catch (ReflectiveOperationException e) { - throw new RuntimeException(e); - } - } +public class FML2ClientConnectionPhase { +/* @Override public RegisteredServer chooseServer(ConnectedPlayer player) { forced = Ambassador.getTemporaryForced().remove(player.getUsername()); @@ -70,15 +33,8 @@ public class FML2ClientConnectionPhase extends VelocityForgeClientConnectionPhas } - public void handleJoinGame() { - this.onJoinGame.complete(null); - } - public CompletableFuture awaitJoinGame() { - return this.onJoinGame; - } - - @Override +/* public void handleForward(VelocityServerConnection serverConnection, LoginPluginMessage payload) { final ByteBuf buf = payload.content().duplicate(); ProtocolUtils.readString(buf); //Channel @@ -96,5 +52,7 @@ public class FML2ClientConnectionPhase extends VelocityForgeClientConnectionPhas } }); } + } + */ } diff --git a/src/main/java/org/adde0109/ambassador/forge/ForgeConstants.java b/src/main/java/org/adde0109/ambassador/forge/ForgeConstants.java index c96553c..e5f6e16 100644 --- a/src/main/java/org/adde0109/ambassador/forge/ForgeConstants.java +++ b/src/main/java/org/adde0109/ambassador/forge/ForgeConstants.java @@ -5,9 +5,9 @@ import com.velocitypowered.proxy.connection.ConnectionType; public class ForgeConstants { public static final String HANDLER = "Modern Forge handler"; public static final String MARKER_ADDER = "FML2/3 Marker Adder"; - public static final String OUTBOUND_CATCHER_NAME = "ambassador-catcher"; public static final String RESET_LISTENER = "ambassador-reset-listener"; public static final String SERVER_SUCCESS_LISTENER = "ambassador-server-success-listener"; + public static final String FORGE_HANDSHAKE_HOLDER = "ambassador-forge-handshake-holder"; public static final String FML2Marker = "\0FML2\0"; public static final String FML3Marker = "\0FML3\0"; diff --git a/src/main/java/org/adde0109/ambassador/forge/ForgeFMLConnectionType.java b/src/main/java/org/adde0109/ambassador/forge/ForgeFMLConnectionType.java index a583d22..9a7722e 100644 --- a/src/main/java/org/adde0109/ambassador/forge/ForgeFMLConnectionType.java +++ b/src/main/java/org/adde0109/ambassador/forge/ForgeFMLConnectionType.java @@ -5,6 +5,7 @@ import com.velocitypowered.proxy.config.PlayerInfoForwarding; import com.velocitypowered.proxy.connection.ConnectionType; import com.velocitypowered.proxy.connection.backend.BackendConnectionPhase; import com.velocitypowered.proxy.connection.client.ClientConnectionPhase; +import org.adde0109.ambassador.velocity.VelocityForgeClientConnectionPhase; import org.adde0109.ambassador.velocity.backend.VelocityForgeBackendConnectionPhase; import java.util.Collections; @@ -19,7 +20,7 @@ public class ForgeFMLConnectionType implements ConnectionType { @Override public ClientConnectionPhase getInitialClientPhase() { - return new FML2ClientConnectionPhase(); + return VelocityForgeClientConnectionPhase.NOT_STARTED; } @Override diff --git a/src/main/java/org/adde0109/ambassador/velocity/VelocityEventHandler.java b/src/main/java/org/adde0109/ambassador/velocity/VelocityEventHandler.java index d195321..495fa54 100644 --- a/src/main/java/org/adde0109/ambassador/velocity/VelocityEventHandler.java +++ b/src/main/java/org/adde0109/ambassador/velocity/VelocityEventHandler.java @@ -11,7 +11,6 @@ import com.velocitypowered.proxy.connection.backend.VelocityServerConnection; import com.velocitypowered.proxy.connection.client.ConnectedPlayer; import com.velocitypowered.proxy.protocol.StateRegistry; import org.adde0109.ambassador.Ambassador; -import org.adde0109.ambassador.forge.FML2CRPMClientConnectionPhase; public class VelocityEventHandler { @@ -40,31 +39,9 @@ public class VelocityEventHandler { continuation.resume(); } - @Subscribe(order = PostOrder.LAST) - public void onKickedFromServerEvent(KickedFromServerEvent event, Continuation continuation) { - if (((ConnectedPlayer) event.getPlayer()).getPhase() instanceof FML2CRPMClientConnectionPhase phase) { - phase.handleKick(event); - } - continuation.resume(); - } - @Subscribe(order = PostOrder.LAST) public void onServerPreConnectEvent(ServerPreConnectEvent event, Continuation continuation) { - ConnectedPlayer player = (ConnectedPlayer) event.getPlayer(); - if (!(player.getPhase() instanceof VelocityForgeClientConnectionPhase phase)) { - continuation.resume(); - return; - } - if (phase.internalServerConnection != null) { - event.setResult(ServerPreConnectEvent.ServerResult.denied()); - player.setConnectedServer((VelocityServerConnection) phase.internalServerConnection); - phase.internalServerConnection = null; - } else if (phase.clientPhase == VelocityForgeClientConnectionPhase.ClientPhase.MODDED) { - player.getConnection().eventLoop().submit(() -> phase.reset(event.getOriginalServer(), (ConnectedPlayer) event.getPlayer()) - .thenAccept((ignored) -> continuation.resume())); - } else { - continuation.resume(); - } + continuation.resume(); } @Subscribe(order = PostOrder.LAST) @@ -74,7 +51,7 @@ public class VelocityEventHandler { continuation.resume(); return; } - RegisteredServer chosenServer = phase.chooseServer(player); + RegisteredServer chosenServer = Ambassador.getTemporaryForced().remove(player.getUsername()); if (chosenServer != null) event.setInitialServer(chosenServer); continuation.resume(); diff --git a/src/main/java/org/adde0109/ambassador/velocity/VelocityForgeClientConnectionPhase.java b/src/main/java/org/adde0109/ambassador/velocity/VelocityForgeClientConnectionPhase.java index 97b1e2c..c84b466 100644 --- a/src/main/java/org/adde0109/ambassador/velocity/VelocityForgeClientConnectionPhase.java +++ b/src/main/java/org/adde0109/ambassador/velocity/VelocityForgeClientConnectionPhase.java @@ -1,5 +1,6 @@ package org.adde0109.ambassador.velocity; +import com.velocitypowered.api.event.player.KickedFromServerEvent; import com.velocitypowered.api.proxy.ServerConnection; import com.velocitypowered.api.proxy.server.RegisteredServer; import com.velocitypowered.proxy.VelocityServer; @@ -7,58 +8,115 @@ import com.velocitypowered.proxy.connection.MinecraftConnection; import com.velocitypowered.proxy.connection.backend.VelocityServerConnection; import com.velocitypowered.proxy.connection.client.ClientConnectionPhase; import com.velocitypowered.proxy.connection.client.ConnectedPlayer; +import com.velocitypowered.proxy.network.Connections; +import com.velocitypowered.proxy.protocol.StateRegistry; import com.velocitypowered.proxy.protocol.packet.LoginPluginMessage; import com.velocitypowered.proxy.protocol.packet.LoginPluginResponse; +import com.velocitypowered.proxy.protocol.packet.PluginMessage; +import io.netty.buffer.Unpooled; +import net.kyori.adventure.text.Component; +import org.adde0109.ambassador.Ambassador; +import org.adde0109.ambassador.forge.FML2CRPMResetCompleteDecoder; +import org.adde0109.ambassador.forge.ForgeConstants; +import org.adde0109.ambassador.forge.ForgeHandshakeUtils; +import org.adde0109.ambassador.velocity.client.OutboundForgeHandshakeHolder; -import java.lang.reflect.InvocationTargetException; -import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.ScheduledFuture; -public abstract class VelocityForgeClientConnectionPhase implements ClientConnectionPhase { +public enum VelocityForgeClientConnectionPhase implements ClientConnectionPhase { //TODO:Make class when PCF is done + NOT_STARTED { + @Override + VelocityForgeClientConnectionPhase nextPhase() { + return IN_PROGRESS; + } + }, + IN_PROGRESS { - VelocityLoginPayloadManager payloadManager; - public VelocityForgeClientConnectionPhase.ClientPhase clientPhase = ClientPhase.HANDSHAKE; + }, + COMPLETE { - public ServerConnection internalServerConnection; - public RegisteredServer forced; + @Override + public void resetConnectionPhase(ConnectedPlayer player) { + MinecraftConnection connection = player.getConnection(); - protected VelocityForgeClientConnectionPhase(ClientPhase clientPhase, VelocityLoginPayloadManager payloadManager) { - this.clientPhase = clientPhase; - this.payloadManager = payloadManager; - } - protected VelocityForgeClientConnectionPhase() { - } - public RegisteredServer chooseServer(ConnectedPlayer player) { - return null; - } + //We unregister so no plugin sees this client while the client is being reset. + ((VelocityServer) Ambassador.getInstance().server).unregisterConnection(player); + + connection.getChannel().pipeline().addBefore(Connections.MINECRAFT_DECODER, ForgeConstants.RESET_LISTENER,new FML2CRPMResetCompleteDecoder()); + connection.getChannel().pipeline().addLast(ForgeConstants.FORGE_HANDSHAKE_HOLDER,new OutboundForgeHandshakeHolder()); + + connection.write(new PluginMessage("fml:handshake", Unpooled.wrappedBuffer(ForgeHandshakeUtils.generatePluginResetPacket()))); + + player.setPhase(WAITING_RESET); + WAITING_RESET.onTransitionToNewPhase(player); + } + }, + WAITING_RESET { + + ScheduledFuture scheduledFuture; + @Override + void onTransitionToNewPhase(ConnectedPlayer player) { + scheduledFuture = player.getConnection().eventLoop().schedule(()-> { + player.getConnection().getChannel().pipeline().remove(ForgeConstants.RESET_LISTENER); + Ambassador.getTemporaryForced().put(player.getUsername(), player.getConnectionInFlight().getServer(), + Ambassador.getInstance().config.getServerSwitchCancellationTime(), TimeUnit.SECONDS); + //Disconnect - Reset Timeout + player.disconnect(Component.text(Ambassador.getInstance().config.getDisconnectResetMessage())); + }, Ambassador.getInstance().config.getResetTimeout(), TimeUnit.MILLISECONDS); + } + @Override + public boolean handle(ConnectedPlayer player, LoginPluginResponse response, VelocityServerConnection server) { + if (response.getId() == 98) { + if (scheduledFuture.cancel(false)) { + player.getConnection().getChannel().pipeline().remove(ForgeConstants.RESET_LISTENER); + player.getConnection().setState(StateRegistry.LOGIN); + player.setPhase(NOT_STARTED); + //Send all held messages + player.getConnection().getChannel().pipeline().remove(ForgeConstants.FORGE_HANDSHAKE_HOLDER); + + player.getConnection().setSessionHandler(new VelocityForgeHandshakeSessionHandler(player.getConnection().getSessionHandler(),player)); + } + return true; + } else { + return false; + } + } + }; - public CompletableFuture reset(RegisteredServer server, ConnectedPlayer player) { - return CompletableFuture.completedFuture(false); - } - final void onFirstLogin(ConnectedPlayer player, VelocityServer server) throws InterruptedException, InvocationTargetException, IllegalAccessException { - } - - public void handleForward(VelocityServerConnection serverConnection, LoginPluginMessage payload) { - } - - public final VelocityLoginPayloadManager getPayloadManager() { - return payloadManager; - } + public ServerConnection internalServerConnection; public boolean handle(ConnectedPlayer player, LoginPluginResponse response, VelocityServerConnection server) { - server.getConnection().write(response.retain()); + player.setPhase(nextPhase()); + + player.getConnectionInFlight().getConnection().write(response.retain()); return true; } - public enum ClientPhase { - VANILLA, - HANDSHAKE, - MODLIST, - MODDED + private RegisteredServer lastKnownWorking; + + void onTransitionToNewPhase(ConnectedPlayer player) { + } + + VelocityForgeClientConnectionPhase nextPhase() { + return this; + } + +/* + public void handleKick(KickedFromServerEvent event) { + //If kicked before the client has entered PLAY and has been reset. + if (lastKnownWorking != null && !(event.getResult() instanceof KickedFromServerEvent.RedirectPlayer)) { + net.kyori.adventure.text.Component reason = event.getServerKickReason().orElse(null); + event.setResult(KickedFromServerEvent.RedirectPlayer.create(lastKnownWorking,reason)); + lastKnownWorking = null; + } + } + */ } diff --git a/src/main/java/org/adde0109/ambassador/velocity/VelocityLoginPayloadManager.java b/src/main/java/org/adde0109/ambassador/velocity/VelocityLoginPayloadManager.java deleted file mode 100644 index 2fe0461..0000000 --- a/src/main/java/org/adde0109/ambassador/velocity/VelocityLoginPayloadManager.java +++ /dev/null @@ -1,62 +0,0 @@ -package org.adde0109.ambassador.velocity; - -import com.velocitypowered.proxy.connection.MinecraftConnection; -import com.velocitypowered.proxy.protocol.packet.LoginPluginMessage; -import com.velocitypowered.proxy.protocol.packet.LoginPluginResponse; -import io.netty.buffer.ByteBuf; - -import java.util.HashMap; -import java.util.List; -import java.util.concurrent.CompletableFuture; - -public class VelocityLoginPayloadManager { - private final HashMap> listenerList = new HashMap<>(); - private int counter = 0; - private final MinecraftConnection connection; - - public VelocityLoginPayloadManager(MinecraftConnection connection) { - this.connection = connection; - } - - public CompletableFuture sendPayload(String channel, ByteBuf data) { - connection.write(new LoginPluginMessage(counter,channel,data)); - CompletableFuture future = listenFor(counter); - counter++; - return future; - } - - public CompletableFuture sendPayloads(String channel, List dataList) { - final CompletableFuture callback = new CompletableFuture<>(); - for (ByteBuf data : dataList) { - connection.delayedWrite(new LoginPluginMessage(counter, channel, data)); - listenerList.put(counter,callback); - counter++; - } - connection.flush(); - return callback; - } - - public CompletableFuture listenFor(int id) { - CompletableFuture value = listenerList.get(id); - if (value == null) { - CompletableFuture callback = new CompletableFuture<>(); - listenerList.put(id,callback); - return callback; - } else { - return value; - } - } - - boolean handlePayload(LoginPluginResponse response) { - final CompletableFuture callback = listenerList.get(response.getId()); - if (callback != null) { - listenerList.remove(response.getId()); - if (!listenerList.containsValue(callback)) { - callback.complete(response.content()); - } - return true; - } else { - return false; - } - } - } diff --git a/src/main/java/org/adde0109/ambassador/velocity/backend/ForgeHandshakeSessionHandler.java b/src/main/java/org/adde0109/ambassador/velocity/backend/ForgeLoginSessionHandler.java similarity index 74% rename from src/main/java/org/adde0109/ambassador/velocity/backend/ForgeHandshakeSessionHandler.java rename to src/main/java/org/adde0109/ambassador/velocity/backend/ForgeLoginSessionHandler.java index f5f30c7..215af84 100644 --- a/src/main/java/org/adde0109/ambassador/velocity/backend/ForgeHandshakeSessionHandler.java +++ b/src/main/java/org/adde0109/ambassador/velocity/backend/ForgeLoginSessionHandler.java @@ -4,26 +4,19 @@ import com.velocitypowered.proxy.VelocityServer; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.backend.BackendConnectionPhases; import com.velocitypowered.proxy.connection.backend.LoginSessionHandler; +import com.velocitypowered.proxy.connection.backend.TransitionSessionHandler; import com.velocitypowered.proxy.connection.backend.VelocityServerConnection; import com.velocitypowered.proxy.protocol.MinecraftPacket; -import com.velocitypowered.proxy.protocol.packet.Disconnect; import com.velocitypowered.proxy.protocol.packet.LoginPluginMessage; import com.velocitypowered.proxy.protocol.packet.ServerLoginSuccess; -import io.netty.buffer.ByteBuf; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.format.NamedTextColor; -import net.kyori.adventure.text.format.TextColor; -import org.adde0109.ambassador.forge.ForgeConstants; -import org.adde0109.ambassador.forge.ForgeFMLConnectionType; -import org.adde0109.ambassador.velocity.VelocityForgeClientConnectionPhase; -public class ForgeHandshakeSessionHandler implements MinecraftSessionHandler { +public class ForgeLoginSessionHandler implements MinecraftSessionHandler { private final LoginSessionHandler original; private final VelocityServerConnection serverConnection; private final VelocityServer server; - public ForgeHandshakeSessionHandler(LoginSessionHandler original, VelocityServerConnection serverConnection, VelocityServer server) { + public ForgeLoginSessionHandler(LoginSessionHandler original, VelocityServerConnection serverConnection, VelocityServer server) { this.original = original; this.serverConnection = serverConnection; this.server = server; @@ -47,7 +40,11 @@ public class ForgeHandshakeSessionHandler implements MinecraftSessionHandler { if ((serverConnection.getPhase() instanceof VelocityForgeBackendConnectionPhase phase)) { phase.onLoginSuccess(serverConnection,serverConnection.getPlayer()); } - return original.handle(packet); + original.handle(packet); + serverConnection.getConnection().setSessionHandler( + new ForgePlaySessionHandler((TransitionSessionHandler) serverConnection + .getConnection().getSessionHandler(),serverConnection)); + return true; } diff --git a/src/main/java/org/adde0109/ambassador/velocity/backend/ForgePlaySessionHandler.java b/src/main/java/org/adde0109/ambassador/velocity/backend/ForgePlaySessionHandler.java new file mode 100644 index 0000000..547bb01 --- /dev/null +++ b/src/main/java/org/adde0109/ambassador/velocity/backend/ForgePlaySessionHandler.java @@ -0,0 +1,41 @@ +package org.adde0109.ambassador.velocity.backend; + +import com.velocitypowered.proxy.connection.MinecraftSessionHandler; +import com.velocitypowered.proxy.connection.backend.TransitionSessionHandler; +import com.velocitypowered.proxy.connection.backend.VelocityServerConnection; +import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.packet.JoinGame; +import org.adde0109.ambassador.velocity.VelocityForgeClientConnectionPhase; + +public class ForgePlaySessionHandler implements MinecraftSessionHandler { + + private final TransitionSessionHandler original; + private final VelocityServerConnection serverConnection; + + public ForgePlaySessionHandler(TransitionSessionHandler original, VelocityServerConnection serverConnection) { + this.original = original; + this.serverConnection = serverConnection; + } + + @Override + public boolean handle(JoinGame packet) { + if (serverConnection.getPlayer().getPhase() instanceof VelocityForgeClientConnectionPhase clientPhase) { + serverConnection.getPlayer().setPhase(VelocityForgeClientConnectionPhase.COMPLETE); + } + return MinecraftSessionHandler.super.handle(packet); + } + + @Override + public void disconnected() { + original.disconnected(); + } + + public void handleGeneric(MinecraftPacket packet) { + if (!packet.handle(original)) + original.handleGeneric(packet); + } + + public MinecraftSessionHandler getOriginal() { + return this.original; + } +} diff --git a/src/main/java/org/adde0109/ambassador/velocity/backend/VelocityForgeBackendConnectionPhase.java b/src/main/java/org/adde0109/ambassador/velocity/backend/VelocityForgeBackendConnectionPhase.java index 9a93e58..6a52f6a 100644 --- a/src/main/java/org/adde0109/ambassador/velocity/backend/VelocityForgeBackendConnectionPhase.java +++ b/src/main/java/org/adde0109/ambassador/velocity/backend/VelocityForgeBackendConnectionPhase.java @@ -3,9 +3,11 @@ package org.adde0109.ambassador.velocity.backend; import com.velocitypowered.proxy.connection.MinecraftConnection; import com.velocitypowered.proxy.connection.backend.BackendConnectionPhase; import com.velocitypowered.proxy.connection.backend.VelocityServerConnection; +import com.velocitypowered.proxy.connection.client.ClientConnectionPhase; import com.velocitypowered.proxy.connection.client.ConnectedPlayer; import com.velocitypowered.proxy.protocol.StateRegistry; import com.velocitypowered.proxy.protocol.packet.LoginPluginMessage; +import io.netty.channel.PendingWriteQueue; import org.adde0109.ambassador.forge.ForgeConstants; import org.adde0109.ambassador.velocity.VelocityForgeClientConnectionPhase; @@ -34,34 +36,21 @@ public enum VelocityForgeBackendConnectionPhase implements BackendConnectionPhas } }; + + VelocityForgeBackendConnectionPhase() { } public void handle(VelocityServerConnection server, ConnectedPlayer player, LoginPluginMessage message) { - VelocityForgeBackendConnectionPhase newPhase = nextPhase(); server.setConnectionPhase(newPhase); - VelocityForgeClientConnectionPhase clientPhase = ((VelocityForgeClientConnectionPhase) player.getPhase()); - if (player.getConnection().getState() != StateRegistry.LOGIN) { - final LoginPluginMessage msg = message; - - msg.content().retain().discardSomeReadBytes(); - - server.getConnection().getChannel().config().setAutoRead(false); - clientPhase.reset(server.getServer(),player).thenAccept((success) -> { - if (success) { - player.getConnection().write(msg); - server.getConnection().getChannel().config().setAutoRead(true); - } else { - msg.release(); - } - }); - } else { - player.getConnection().write(message.retain()); - } + //Reset client if not ready to receive new handshake + VelocityForgeClientConnectionPhase clientPhase = (VelocityForgeClientConnectionPhase) player.getPhase(); + clientPhase.resetConnectionPhase(player); + player.getConnection().write(message.retain()); } public void onLoginSuccess(VelocityServerConnection serverCon, ConnectedPlayer player) { diff --git a/src/main/java/org/adde0109/ambassador/velocity/backend/VelocityForgeBackendHandshakeHandler.java b/src/main/java/org/adde0109/ambassador/velocity/backend/VelocityForgeBackendHandshakeHandler.java index 5589ae6..66f3d6b 100644 --- a/src/main/java/org/adde0109/ambassador/velocity/backend/VelocityForgeBackendHandshakeHandler.java +++ b/src/main/java/org/adde0109/ambassador/velocity/backend/VelocityForgeBackendHandshakeHandler.java @@ -25,7 +25,7 @@ public class VelocityForgeBackendHandshakeHandler extends ChannelInboundHandlerA ctx.pipeline().remove(this); if (serverConnection.getPlayer().getConnection().getType() instanceof ForgeFMLConnectionType) { - connection.setSessionHandler(new ForgeHandshakeSessionHandler((LoginSessionHandler) connection.getSessionHandler(),serverConnection,server)); + connection.setSessionHandler(new ForgeLoginSessionHandler((LoginSessionHandler) connection.getSessionHandler(),serverConnection,server)); } ctx.pipeline().fireChannelActive(); diff --git a/src/main/java/org/adde0109/ambassador/velocity/client/OutboundForgeHandshakeHolder.java b/src/main/java/org/adde0109/ambassador/velocity/client/OutboundForgeHandshakeHolder.java new file mode 100644 index 0000000..45ffac4 --- /dev/null +++ b/src/main/java/org/adde0109/ambassador/velocity/client/OutboundForgeHandshakeHolder.java @@ -0,0 +1,34 @@ +package org.adde0109.ambassador.velocity.client; + +import com.velocitypowered.proxy.protocol.packet.LoginPluginMessage; +import com.velocitypowered.proxy.protocol.packet.ServerLoginSuccess; +import io.netty.channel.*; + +public class OutboundForgeHandshakeHolder extends ChannelOutboundHandlerAdapter { + + PendingWriteQueue writeQueue; + + @Override + public void handlerAdded(ChannelHandlerContext ctx) throws Exception { + writeQueue = new PendingWriteQueue(ctx); + } + + @Override + public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { + if ((msg instanceof LoginPluginMessage packet)) { + writeQueue.add(msg, promise); + } else { + ctx.write(msg, promise); + } + } + + @Override + public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { + if (ctx.channel().isActive()) { + writeQueue.removeAndWriteAll(); + ctx.flush(); + } else { + writeQueue.removeAndFailAll(new ChannelException()); + } + } +}