Reorganized login process...

...in order to improve stability and maintainability.
This commit is contained in:
Adrian Bergqvist 2023-02-15 20:25:15 +01:00
parent c77fcbd09f
commit ce5420768d
No known key found for this signature in database
GPG Key ID: FAE7D8EDE225E686
11 changed files with 139 additions and 146 deletions

View File

@ -42,7 +42,7 @@ public class FML2CRPMClientConnectionPhase extends VelocityForgeClientConnection
}
MinecraftConnection connection = player.getConnection();
connection.setSessionHandler(new VelocityForgeHandshakeSessionHandler(connection.getSessionHandler(),this));
connection.setSessionHandler(new VelocityForgeHandshakeSessionHandler(connection.getSessionHandler(),player));
((VelocityServer) Ambassador.getInstance().server).unregisterConnection(player);
this.clientPhase = null;
@ -63,25 +63,7 @@ public class FML2CRPMClientConnectionPhase extends VelocityForgeClientConnection
connection.write(new PluginMessage("fml:handshake",Unpooled.wrappedBuffer(ForgeHandshakeUtils.generatePluginResetPacket())));
return future;
}
public void complete(VelocityServer server, ConnectedPlayer player, MinecraftConnection connection) {
VelocityConfiguration configuration = (VelocityConfiguration) server.getConfiguration();
UUID playerUniqueId = player.getUniqueId();
if (configuration.getPlayerInfoForwardingMode() == PlayerInfoForwarding.NONE) {
playerUniqueId = UuidUtils.generateOfflinePlayerUuid(player.getUsername());
}
ServerLoginSuccess success = new ServerLoginSuccess();
success.setUsername(player.getUsername());
success.setUuid(playerUniqueId);
connection.write(success);
this.clientPhase = this.clientPhase == ClientPhase.MODLIST ? ClientPhase.MODDED : ClientPhase.VANILLA;
connection.setState(StateRegistry.PLAY);
connection.setSessionHandler(((VelocityForgeHandshakeSessionHandler) connection.getSessionHandler()).getOriginal());
((VelocityServer) Ambassador.getInstance().server).registerConnection(player);
backupServer = null;
}
public void handleKick(KickedFromServerEvent event) {
if (backupServer != null && !(event.getResult() instanceof KickedFromServerEvent.RedirectPlayer)) {

View File

@ -8,21 +8,24 @@ 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 Continuation continuation;
private CompletableFuture<Void> onServerSuccess;
private CompletableFuture<Void> onJoinGame;
private static final Method CONNECT_TO_INITIAL_SERVER;
@ -47,20 +50,9 @@ public class FML2ClientConnectionPhase extends VelocityForgeClientConnectionPhas
}
@Override
public void handleLogin(ConnectedPlayer player, VelocityServer server, Continuation continuation) {
this.continuation = continuation;
final MinecraftConnection connection = player.getConnection();
public RegisteredServer chooseServer(ConnectedPlayer player) {
forced = Ambassador.getTemporaryForced().remove(player.getUsername());
if (forced != null) {
player.createConnectionRequest(forced).fireAndForget();
} else {
try {
CONNECT_TO_INITIAL_SERVER.invoke(player.getConnection().getSessionHandler(),player);
} catch (ReflectiveOperationException e) {
continuation.resumeWithException(e);
}
}
return forced;
}
@Override
@ -77,17 +69,6 @@ public class FML2ClientConnectionPhase extends VelocityForgeClientConnectionPhas
return future;
}
@Override
public void complete(VelocityServer server, ConnectedPlayer player, MinecraftConnection connection) {
if (triedServer != null)
player.sendMessage(Component.translatable("velocity.error.connecting-server-error",
Component.text(triedServer.getServerInfo().getName())));
clientPhase = clientPhase == ClientPhase.MODLIST ? ClientPhase.MODDED : ClientPhase.VANILLA;
internalServerConnection = player.getConnectionInFlight();
player.resetInFlightConnection();
this.onJoinGame = new CompletableFuture<>();
continuation.resume();
}
public void handleJoinGame() {
this.onJoinGame.complete(null);

View File

@ -7,6 +7,7 @@ public class ForgeConstants {
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 FML2Marker = "\0FML2\0";
public static final String FML3Marker = "\0FML3\0";

View File

@ -24,7 +24,7 @@ public class ForgeFMLConnectionType implements ConnectionType {
@Override
public BackendConnectionPhase getInitialBackendPhase() {
return new VelocityForgeBackendConnectionPhase();
return VelocityForgeBackendConnectionPhase.NOT_STARTED;
}
@Override

View File

@ -3,19 +3,15 @@ package org.adde0109.ambassador.velocity;
import com.velocitypowered.api.event.Continuation;
import com.velocitypowered.api.event.PostOrder;
import com.velocitypowered.api.event.Subscribe;
import com.velocitypowered.api.event.connection.LoginEvent;
import com.velocitypowered.api.event.connection.PostLoginEvent;
import com.velocitypowered.api.event.permission.PermissionsSetupEvent;
import com.velocitypowered.api.event.player.*;
import com.velocitypowered.proxy.VelocityServer;
import com.velocitypowered.api.proxy.server.RegisteredServer;
import com.velocitypowered.proxy.connection.backend.VelocityServerConnection;
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
import com.velocitypowered.proxy.protocol.packet.ClientSettings;
import com.velocitypowered.proxy.protocol.StateRegistry;
import org.adde0109.ambassador.Ambassador;
import org.adde0109.ambassador.forge.FML2CRPMClientConnectionPhase;
import org.adde0109.ambassador.forge.FML2ClientConnectionPhase;
import org.adde0109.ambassador.forge.ForgeConstants;
import org.adde0109.ambassador.forge.ForgeFMLConnectionType;
import org.adde0109.ambassador.velocity.backend.VelocityForgeBackendConnectionPhase;
public class VelocityEventHandler {
@ -26,16 +22,22 @@ public class VelocityEventHandler {
}
@Subscribe
public void onPermissionsSetupEvent(PermissionsSetupEvent event, Continuation continuation) {
if(!(event.getSubject() instanceof ConnectedPlayer player)) {
continuation.resume();
return;
public void onLoginEvent(LoginEvent event, Continuation continuation) {
ConnectedPlayer player = (ConnectedPlayer) event.getPlayer();
if (player.getPhase() instanceof VelocityForgeClientConnectionPhase) {
player.getConnection().eventLoop().submit(() -> player.getConnection().setState(StateRegistry.LOGIN));
}
if (!(player.getPhase() instanceof VelocityForgeClientConnectionPhase phase)) {
continuation.resume();
return;
continuation.resume();
}
@Subscribe
public void onPostLoginEvent(PostLoginEvent event, Continuation continuation) {
ConnectedPlayer player = (ConnectedPlayer) event.getPlayer();
if (player.getPhase() instanceof VelocityForgeClientConnectionPhase phase) {
VelocityForgeHandshakeSessionHandler sessionHandler = new VelocityForgeHandshakeSessionHandler(player.getConnection().getSessionHandler(), player);
player.getConnection().eventLoop().submit(() -> player.getConnection().setSessionHandler(sessionHandler));
}
player.getConnection().eventLoop().submit(() -> phase.fireLoginEvent(player, (VelocityServer) ambassador.server,continuation));
continuation.resume();
}
@Subscribe(order = PostOrder.LAST)
@ -72,40 +74,10 @@ public class VelocityEventHandler {
continuation.resume();
return;
}
if (event.getInitialServer().isEmpty())
event.setInitialServer(phase.internalServerConnection.getServer());
RegisteredServer chosenServer = phase.chooseServer(player);
if (chosenServer != null)
event.setInitialServer(chosenServer);
continuation.resume();
}
@Subscribe(order = PostOrder.LAST)
public void onServerPostConnectEvent(ServerPostConnectEvent event, Continuation continuation) {
ConnectedPlayer player = (ConnectedPlayer) event.getPlayer();
if (!(player.getPhase() instanceof VelocityForgeClientConnectionPhase phase)) {
continuation.resume();
return;
}
if (phase instanceof FML2ClientConnectionPhase specialPhase) {
specialPhase.handleJoinGame();
}
//if (((ConnectedPlayer) event.getPlayer()).getConnectedServer() != null && ((ConnectedPlayer) event.getPlayer()).getConnectedServer().getConnection() != null) {
// ((ConnectedPlayer) event.getPlayer()).getConnectedServer().getConnection().write(new ClientSettings("en_GB", (byte) 10, 0, true, (short) 0xFF,1,false,true));
//}
continuation.resume();
}
@Subscribe
public void onLoginEvent(PostLoginEvent event, Continuation continuation) {
ConnectedPlayer player = (ConnectedPlayer) event.getPlayer();
if (!(player.getPhase() instanceof VelocityForgeClientConnectionPhase phase)) {
continuation.resume();
return;
}
if (phase instanceof FML2ClientConnectionPhase specialPhase) {
specialPhase.awaitJoinGame().thenAcceptAsync((ignored) -> {
player.setConnectedServer(null);
continuation.resume();
},player.getConnection().eventLoop());
}
}
}

View File

@ -1,6 +1,5 @@
package org.adde0109.ambassador.velocity;
import com.velocitypowered.api.event.Continuation;
import com.velocitypowered.api.proxy.ServerConnection;
import com.velocitypowered.api.proxy.server.RegisteredServer;
import com.velocitypowered.proxy.VelocityServer;
@ -11,6 +10,7 @@ import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
import com.velocitypowered.proxy.protocol.packet.LoginPluginMessage;
import com.velocitypowered.proxy.protocol.packet.LoginPluginResponse;
import java.lang.reflect.InvocationTargetException;
import java.util.concurrent.CompletableFuture;
public abstract class VelocityForgeClientConnectionPhase implements ClientConnectionPhase {
@ -30,45 +30,31 @@ public abstract class VelocityForgeClientConnectionPhase implements ClientConnec
protected VelocityForgeClientConnectionPhase() {
}
public void handleLogin(ConnectedPlayer player, VelocityServer server, Continuation continuation) {
public RegisteredServer chooseServer(ConnectedPlayer player) {
return null;
}
public CompletableFuture<Boolean> reset(RegisteredServer server, ConnectedPlayer player) {
return CompletableFuture.completedFuture(false);
}
public void complete(VelocityServer server, ConnectedPlayer player, MinecraftConnection connection) {
}
final void fireLoginEvent(ConnectedPlayer player, VelocityServer server, Continuation continuation) {
payloadManager = new VelocityLoginPayloadManager(player.getConnection());
handleLogin(player,server,continuation);
VelocityForgeHandshakeSessionHandler sessionHandler = new VelocityForgeHandshakeSessionHandler(player.getConnection().getSessionHandler(), this);
player.getConnection().setSessionHandler(sessionHandler);
final void onFirstLogin(ConnectedPlayer player, VelocityServer server) throws InterruptedException, InvocationTargetException, IllegalAccessException {
}
public void handleForward(VelocityServerConnection serverConnection, LoginPluginMessage payload) {
}
final public void forwardPayload(VelocityServerConnection serverConnection, LoginPluginMessage payload) {
handleForward(serverConnection,payload);
if (payloadManager == null) {
payload.release();
return;
}
payloadManager.sendPayload("fml:loginwrapper",payload.content()).thenAccept((responseData) -> {
//Move this to the backend. Backend should have its own forwarder.
serverConnection.getConnection().write(new LoginPluginResponse(payload.getId(),responseData.isReadable(),responseData.retain()));
});
clientPhase = ClientPhase.MODLIST;
}
public final VelocityLoginPayloadManager getPayloadManager() {
return payloadManager;
}
public boolean handle(ConnectedPlayer player, LoginPluginResponse response, VelocityServerConnection server) {
server.getConnection().write(response.retain());
return true;
}
public enum ClientPhase {
VANILLA,
HANDSHAKE,

View File

@ -1,26 +1,29 @@
package org.adde0109.ambassador.velocity;
import com.velocitypowered.proxy.VelocityServer;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.connection.backend.VelocityServerConnection;
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
import com.velocitypowered.proxy.protocol.packet.LoginPluginResponse;
import io.netty.buffer.ByteBuf;
public class VelocityForgeHandshakeSessionHandler implements MinecraftSessionHandler {
private final MinecraftSessionHandler original;
private final VelocityForgeClientConnectionPhase phase;
private final ConnectedPlayer player;
public VelocityForgeHandshakeSessionHandler(MinecraftSessionHandler original, VelocityForgeClientConnectionPhase phase) {
public VelocityForgeHandshakeSessionHandler(MinecraftSessionHandler original, ConnectedPlayer player) {
this.original = original;
this.phase = phase;
this.player = player;
}
@Override
public boolean handle(LoginPluginResponse packet) {
if (phase.getPayloadManager().handlePayload(packet)) {
return true;
} else {
return original.handle(packet);
if (player.getPhase() instanceof VelocityForgeClientConnectionPhase phase) {
if (phase.handle(player,packet,player.getConnectionInFlight())) {
return true;
}
}
return original.handle(packet);
}
@Override
public void handleUnknown(ByteBuf buf) {

View File

@ -4,11 +4,13 @@ import com.velocitypowered.proxy.connection.ConnectionTypes;
import com.velocitypowered.proxy.connection.MinecraftConnection;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.connection.client.HandshakeSessionHandler;
import com.velocitypowered.proxy.network.Connections;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.StateRegistry;
import com.velocitypowered.proxy.protocol.packet.Handshake;
import io.netty.buffer.ByteBuf;
import org.adde0109.ambassador.forge.ForgeConstants;
import org.adde0109.ambassador.velocity.client.OutboundSuccessHolder;
public class VelocityHandshakeSessionHandler implements MinecraftSessionHandler {
private final HandshakeSessionHandler original;
@ -26,8 +28,14 @@ public class VelocityHandshakeSessionHandler implements MinecraftSessionHandler
final String[] markerSplit = handshake.getServerAddress().split("\0");
if (connection.getState() == StateRegistry.LOGIN && markerSplit.length > 1) {
switch (markerSplit[1]) {
case "FML2" -> connection.setType(ForgeConstants.ForgeFML2);
case "FML3" -> connection.setType(ForgeConstants.ForgeFML3);
case "FML2":
connection.setType(ForgeConstants.ForgeFML2);
connection.getChannel().pipeline().addAfter(Connections.MINECRAFT_ENCODER,ForgeConstants.SERVER_SUCCESS_LISTENER, new OutboundSuccessHolder());
break;
case "FML3":
connection.setType(ForgeConstants.ForgeFML3);
connection.getChannel().pipeline().addAfter(Connections.MINECRAFT_ENCODER,ForgeConstants.SERVER_SUCCESS_LISTENER, new OutboundSuccessHolder());
break;
}
}
}

View File

@ -2,6 +2,7 @@ package org.adde0109.ambassador.velocity.backend;
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.VelocityServerConnection;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
@ -31,16 +32,11 @@ public class ForgeHandshakeSessionHandler implements MinecraftSessionHandler {
@Override
public boolean handle(LoginPluginMessage packet) {
if (packet.getChannel().equals("fml:loginwrapper")) {
if (!(serverConnection.getConnection().getType() instanceof ForgeFMLConnectionType)) {
if (!(serverConnection.getPlayer().getConnection().getType() instanceof ForgeFMLConnectionType clientType)) {
final String reason = "This server has mods that require Forge to be installed on the client. Contact your server admin for more details.";
original.handle(Disconnect.create(Component.text(reason, NamedTextColor.RED),serverConnection.getPlayer().getProtocolVersion()));
return true;
}
serverConnection.getConnection().setType(clientType);
serverConnection.setConnectionPhase(clientType.getInitialBackendPhase());
if (serverConnection.getPhase() == BackendConnectionPhases.UNKNOWN) {
VelocityForgeBackendConnectionPhase.NOT_STARTED.handle(serverConnection,serverConnection.getPlayer(),packet);
} else if (serverConnection.getPhase() instanceof VelocityForgeBackendConnectionPhase phase1) {
phase1.handle(serverConnection,serverConnection.getPlayer(),packet);
}
((VelocityForgeBackendConnectionPhase) serverConnection.getPhase()).handle(serverConnection,serverConnection.getPlayer(),packet);
return true;
}
return original.handle(packet);
@ -48,8 +44,8 @@ public class ForgeHandshakeSessionHandler implements MinecraftSessionHandler {
@Override
public boolean handle(ServerLoginSuccess packet) {
if ((serverConnection.getPlayer().getPhase() instanceof VelocityForgeClientConnectionPhase phase)) {
phase.complete(server,serverConnection.getPlayer(),serverConnection.getPlayer().getConnection());
if ((serverConnection.getPhase() instanceof VelocityForgeBackendConnectionPhase phase)) {
phase.onLoginSuccess(serverConnection,serverConnection.getPlayer());
}
return original.handle(packet);
}

View File

@ -1,26 +1,51 @@
package org.adde0109.ambassador.velocity.backend;
import com.velocitypowered.proxy.VelocityServer;
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.ConnectedPlayer;
import com.velocitypowered.proxy.protocol.StateRegistry;
import com.velocitypowered.proxy.protocol.packet.LoginPluginMessage;
import io.netty.buffer.ByteBuf;
import io.netty.util.ReferenceCountUtil;
import org.adde0109.ambassador.forge.ForgeConstants;
import org.adde0109.ambassador.velocity.VelocityForgeClientConnectionPhase;
import java.util.ArrayList;
import java.util.List;
public enum VelocityForgeBackendConnectionPhase implements BackendConnectionPhase {
NOT_STARTED() {
@Override
VelocityForgeBackendConnectionPhase nextPhase() {
return WAITING_FOR_ACK;
}
},
WAITING_FOR_ACK() {
@Override
public void onLoginSuccess(VelocityServerConnection serverCon, ConnectedPlayer player) {
serverCon.setConnectionPhase(VelocityForgeBackendConnectionPhase.COMPLETE);
public class VelocityForgeBackendConnectionPhase implements BackendConnectionPhase {
MinecraftConnection connection = player.getConnection();
connection.getChannel().pipeline().remove(ForgeConstants.SERVER_SUCCESS_LISTENER);
connection.setState(StateRegistry.PLAY);
}
},
COMPLETE() {
@Override
public boolean consideredComplete() {
return true;
}
};
public VelocityForgeBackendConnectionPhase() {
VelocityForgeBackendConnectionPhase() {
}
public void handle(VelocityServerConnection server, ConnectedPlayer player, LoginPluginMessage message) {
VelocityForgeBackendConnectionPhase newPhase = nextPhase();
server.setConnectionPhase(newPhase);
VelocityForgeClientConnectionPhase clientPhase = ((VelocityForgeClientConnectionPhase) player.getPhase());
if (clientPhase.clientPhase == VelocityForgeClientConnectionPhase.ClientPhase.VANILLA) {
if (player.getConnection().getState() != StateRegistry.LOGIN) {
final LoginPluginMessage msg = message;
msg.content().retain().discardSomeReadBytes();
@ -28,14 +53,25 @@ public class VelocityForgeBackendConnectionPhase implements BackendConnectionPha
server.getConnection().getChannel().config().setAutoRead(false);
clientPhase.reset(server.getServer(),player).thenAccept((success) -> {
if (success) {
clientPhase.forwardPayload(server,msg);
player.getConnection().write(msg);
server.getConnection().getChannel().config().setAutoRead(true);
} else {
msg.release();
}
});
} else {
clientPhase.forwardPayload(server, (LoginPluginMessage) message.retain());
player.getConnection().write(message.retain());
}
}
public void onLoginSuccess(VelocityServerConnection serverCon, ConnectedPlayer player) {
}
VelocityForgeBackendConnectionPhase nextPhase() {
return this;
}
public boolean consideredComplete() {
return false;
}
}

View File

@ -0,0 +1,28 @@
package org.adde0109.ambassador.velocity.client;
import com.velocitypowered.proxy.protocol.packet.ServerLoginSuccess;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelOutboundHandlerAdapter;
import io.netty.channel.ChannelPromise;
public class OutboundSuccessHolder extends ChannelOutboundHandlerAdapter {
private ServerLoginSuccess packet;
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
if ((msg instanceof ServerLoginSuccess packet)) {
this.packet = packet;
} else {
ctx.write(msg, promise);
}
}
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
if (ctx.channel().isActive()) {
ctx.write(packet, ctx.voidPromise());
ctx.flush();
}
}
}