diff --git a/build.gradle b/build.gradle index 2faba79..77e1dbc 100644 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,7 @@ plugins { } group 'org.adde0109' -version '1.0.6-alpha' +version '1.0.7-alpha' repositories { maven { diff --git a/src/main/java/org/adde0109/ambassador/Ambassador.java b/src/main/java/org/adde0109/ambassador/Ambassador.java index e444322..3a506b0 100644 --- a/src/main/java/org/adde0109/ambassador/Ambassador.java +++ b/src/main/java/org/adde0109/ambassador/Ambassador.java @@ -22,7 +22,7 @@ import org.slf4j.Logger; import java.nio.file.Path; -@Plugin(id = "ambassador", name = "Ambassador", version = "1.0.6-alpha", authors = {"adde0109"}) +@Plugin(id = "ambassador", name = "Ambassador", version = "1.0.7-alpha", authors = {"adde0109"}) public class Ambassador { public ProxyServer server; diff --git a/src/main/java/org/adde0109/ambassador/forge/FML2CRPMClientConnectionPhase.java b/src/main/java/org/adde0109/ambassador/forge/FML2CRPMClientConnectionPhase.java index 9c31abe..1548b87 100644 --- a/src/main/java/org/adde0109/ambassador/forge/FML2CRPMClientConnectionPhase.java +++ b/src/main/java/org/adde0109/ambassador/forge/FML2CRPMClientConnectionPhase.java @@ -1,6 +1,5 @@ package org.adde0109.ambassador.forge; -import com.velocitypowered.api.event.Continuation; import com.velocitypowered.api.event.player.KickedFromServerEvent; import com.velocitypowered.api.proxy.server.RegisteredServer; import com.velocitypowered.api.util.UuidUtils; @@ -12,23 +11,15 @@ import com.velocitypowered.proxy.connection.backend.VelocityServerConnection; 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 com.velocitypowered.proxy.protocol.packet.ServerLoginSuccess; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufUtil; import io.netty.buffer.Unpooled; -import net.kyori.adventure.identity.Identity; -import net.kyori.adventure.text.Component; import org.adde0109.ambassador.velocity.VelocityForgeClientConnectionPhase; import org.adde0109.ambassador.velocity.VelocityForgeHandshakeSessionHandler; import org.adde0109.ambassador.velocity.VelocityLoginPayloadManager; -import java.util.ArrayList; import java.util.NoSuchElementException; import java.util.UUID; -import java.util.concurrent.CompletableFuture; public class FML2CRPMClientConnectionPhase extends VelocityForgeClientConnectionPhase { private static String OUTBOUND_CATCHER_NAME = "ambassador-catcher"; @@ -36,18 +27,11 @@ public class FML2CRPMClientConnectionPhase extends VelocityForgeClientConnection //TODO: Use modData inside ConnectedPlayer instead public byte[] modListData; private RegisteredServer backupServer; - @Override - public void handleLogin(ConnectedPlayer player, VelocityServer server, Continuation continuation) { - final MinecraftConnection connection = player.getConnection(); - VelocityForgeHandshakeSessionHandler sessionHandler = new VelocityForgeHandshakeSessionHandler(connection.getSessionHandler(), player); - getPayloadManager().sendPayload("fml:loginwrapper",Unpooled.wrappedBuffer(ForgeHandshakeUtils.emptyModlist)).thenAccept((data) -> { - if (modListData == null) - modListData = ByteBufUtil.getBytes(data); - this.clientPhase = ClientPhase.MODDED; - continuation.resume(); - }); - connection.setSessionHandler(sessionHandler); - connection.flush(); + + public FML2CRPMClientConnectionPhase(VelocityForgeClientConnectionPhase.ClientPhase clientPhase, VelocityLoginPayloadManager payloadManager) { + super(clientPhase,payloadManager); + } + public FML2CRPMClientConnectionPhase() { } public void reset(VelocityServerConnection serverConnection, ConnectedPlayer player, Runnable whenComplete) { @@ -67,13 +51,18 @@ public class FML2CRPMClientConnectionPhase extends VelocityForgeClientConnection connection.write(new PluginMessage("fml:handshake",Unpooled.wrappedBuffer(ForgeHandshakeUtils.generatePluginResetPacket()))); connection.setState(StateRegistry.LOGIN); } - getPayloadManager().listenFor(98).thenAccept((ignored) -> { + getPayloadManager().listenFor(98).thenAccept((response) -> { this.clientPhase = ClientPhase.HANDSHAKE; whenComplete.run(); }); this.clientPhase = null; - connection.getChannel().pipeline().addBefore(Connections.HANDLER,OUTBOUND_CATCHER_NAME,new FML2CRPMOutgoingCatcher()); + connection.getChannel().pipeline().addBefore(Connections.HANDLER,OUTBOUND_CATCHER_NAME,new FML2CRPMConnectionHandler(() -> { + connection.setState(StateRegistry.PLAY); + final FML2ClientConnectionPhase newPhase = new FML2ClientConnectionPhase(ClientPhase.HANDSHAKE,getPayloadManager()); + player.setPhase(newPhase); + newPhase.reset(serverConnection,player,whenComplete); + })); } public void complete(VelocityServer server, ConnectedPlayer player, MinecraftConnection connection) { VelocityConfiguration configuration = (VelocityConfiguration) server.getConfiguration(); diff --git a/src/main/java/org/adde0109/ambassador/forge/FML2CRPMOutgoingCatcher.java b/src/main/java/org/adde0109/ambassador/forge/FML2CRPMConnectionHandler.java similarity index 63% rename from src/main/java/org/adde0109/ambassador/forge/FML2CRPMOutgoingCatcher.java rename to src/main/java/org/adde0109/ambassador/forge/FML2CRPMConnectionHandler.java index 7af2872..8b7729c 100644 --- a/src/main/java/org/adde0109/ambassador/forge/FML2CRPMOutgoingCatcher.java +++ b/src/main/java/org/adde0109/ambassador/forge/FML2CRPMConnectionHandler.java @@ -1,16 +1,23 @@ package org.adde0109.ambassador.forge; import com.velocitypowered.proxy.protocol.packet.LoginPluginMessage; +import com.velocitypowered.proxy.protocol.packet.LoginPluginResponse; import com.velocitypowered.proxy.protocol.packet.ServerLoginSuccess; +import io.netty.channel.ChannelDuplexHandler; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelOutboundHandlerAdapter; import io.netty.channel.ChannelPromise; +import org.jetbrains.annotations.NotNull; import java.util.*; -public class FML2CRPMOutgoingCatcher extends ChannelOutboundHandlerAdapter { +public class FML2CRPMConnectionHandler extends ChannelDuplexHandler { private final Map catchedPackets = Collections.synchronizedMap(new LinkedHashMap<>()); + private final Runnable abort; + + public FML2CRPMConnectionHandler(Runnable abort) { + this.abort = abort; + } @Override public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { @@ -32,4 +39,15 @@ public class FML2CRPMOutgoingCatcher extends ChannelOutboundHandlerAdapter { catchedPackets.put(promise,msg); } } + + @Override + public void channelRead(@NotNull ChannelHandlerContext ctx, @NotNull Object msg) throws Exception { + if (!(msg instanceof LoginPluginResponse)) { + abort.run(); + ctx.pipeline().remove(this); + ctx.pipeline().fireChannelRead(msg); + } else { + ctx.fireChannelRead(msg); + } + } } diff --git a/src/main/java/org/adde0109/ambassador/forge/FML2ClientConnectionPhase.java b/src/main/java/org/adde0109/ambassador/forge/FML2ClientConnectionPhase.java index 7f60d27..e71c3ac 100644 --- a/src/main/java/org/adde0109/ambassador/forge/FML2ClientConnectionPhase.java +++ b/src/main/java/org/adde0109/ambassador/forge/FML2ClientConnectionPhase.java @@ -4,35 +4,40 @@ import com.velocitypowered.api.event.Continuation; import com.velocitypowered.api.event.player.PlayerChooseInitialServerEvent; import com.velocitypowered.api.proxy.server.RegisteredServer; import com.velocitypowered.api.proxy.server.ServerPing; -import com.velocitypowered.api.util.ModInfo; import com.velocitypowered.proxy.VelocityServer; import com.velocitypowered.proxy.connection.MinecraftConnection; -import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.backend.VelocityServerConnection; import com.velocitypowered.proxy.connection.client.ConnectedPlayer; -import com.velocitypowered.proxy.connection.client.LoginSessionHandler; -import com.velocitypowered.proxy.protocol.packet.LoginPluginResponse; -import io.netty.channel.Channel; -import io.netty.channel.ChannelInitializer; +import com.velocitypowered.proxy.protocol.ProtocolUtils; +import com.velocitypowered.proxy.protocol.packet.LoginPluginMessage; +import io.netty.buffer.ByteBuf; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import org.adde0109.ambassador.velocity.VelocityForgeClientConnectionPhase; import org.adde0109.ambassador.velocity.VelocityLoginPayloadManager; import org.apache.commons.collections4.map.PassiveExpiringMap; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; +import java.util.Arrays; import java.util.Optional; import java.util.concurrent.TimeUnit; public class FML2ClientConnectionPhase extends VelocityForgeClientConnectionPhase { + private static String OUTBOUND_CATCHER_NAME = "ambassador-catcher"; + private static final PassiveExpiringMap TEMPORARY_FORCED = new PassiveExpiringMap<>(120, TimeUnit.SECONDS); private Throwable throwable; private RegisteredServer triedServer; private Continuation continuation; + public FML2ClientConnectionPhase(VelocityForgeClientConnectionPhase.ClientPhase clientPhase, VelocityLoginPayloadManager payloadManager) { + super(clientPhase,payloadManager); + } + public FML2ClientConnectionPhase(){ + super(); + } + @Override public void handleLogin(ConnectedPlayer player, VelocityServer server, Continuation continuation) { @@ -50,20 +55,8 @@ public class FML2ClientConnectionPhase extends VelocityForgeClientConnectionPhas }); }; - final RegisteredServer forced = TEMPORARY_FORCED.remove(player.getUsername()); - if (forced != null) { - forced.ping().whenCompleteAsync((msg,ex) -> { - if (ex == null) { - if (throwable == null) - throwable = ex; - handlePingResponse(msg); - } else { - defaultTask.run(); - } - },connection.eventLoop()); - } else { - connection.eventLoop().submit(defaultTask); - } + forced = TEMPORARY_FORCED.remove(player.getUsername()); + connection.eventLoop().submit(defaultTask); } @Override @@ -77,6 +70,14 @@ public class FML2ClientConnectionPhase extends VelocityForgeClientConnectionPhas if (triedServer != null) player.sendMessage(Component.translatable("velocity.error.connecting-server-error", Component.text(triedServer.getServerInfo().getName()))); + if (clientPhase == ClientPhase.VANILLA) { + player.setPhase(new FML2CRPMClientConnectionPhase(ClientPhase.VANILLA,getPayloadManager())); + } else if (clientPhase == ClientPhase.MODLIST) { + clientPhase = ClientPhase.MODDED; + internalServerConnection = player.getConnectionInFlight(); + player.resetInFlightConnection(); + continuation.resume(); + } } private void tryServer(ConnectedPlayer player, RegisteredServer server) { @@ -89,21 +90,37 @@ public class FML2ClientConnectionPhase extends VelocityForgeClientConnectionPhas if (ex != null) { if (throwable == null) throwable = ex; - tryServer(player,player.getNextServerToTry().orElse(null)); + tryServer(player, player.getNextServerToTry().orElse(null)); } else { - handlePingResponse(msg); + handlePingResponse(player, server, msg); } }, player.getConnection().eventLoop()); } - private void handlePingResponse(ServerPing ping) { - if (ping.getModinfo().isEmpty() || !ping.getModinfo().get().getType().equals("Ambassador")) { - continuation.resume(); - return; + + @Override + public void forwardPayload(VelocityServerConnection serverConnection, LoginPluginMessage payload) { + ByteBuf buf = payload.content().copy(); + String channel = ProtocolUtils.readString(buf); + int length = ProtocolUtils.readVarInt(buf); + int id = ProtocolUtils.readVarInt(buf); + if (id == 1) { + String[] mods = ProtocolUtils.readStringArray(buf); + + if (Arrays.stream(mods).anyMatch(s -> s.equals("clientresetpacket"))) { + serverConnection.getPlayer().setPhase(new FML2CRPMClientConnectionPhase(ClientPhase.VANILLA,getPayloadManager())); + } + } + super.forwardPayload(serverConnection, payload); + } + + private void handlePingResponse(ConnectedPlayer player, RegisteredServer server, ServerPing ping) { + if (ping.getModinfo().isEmpty()) { + clientPhase = ClientPhase.VANILLA; + continuation.resume(); + } else { + player.createConnectionRequest(server).fireAndForget(); } - ModInfo.Mod mod = ping.getModinfo().get().getMods().get(0); - String data = mod.getId(); - String markers = mod.getVersion(); } } diff --git a/src/main/java/org/adde0109/ambassador/velocity/VelocityEventHandler.java b/src/main/java/org/adde0109/ambassador/velocity/VelocityEventHandler.java index f9405d0..919ea75 100644 --- a/src/main/java/org/adde0109/ambassador/velocity/VelocityEventHandler.java +++ b/src/main/java/org/adde0109/ambassador/velocity/VelocityEventHandler.java @@ -2,11 +2,14 @@ package org.adde0109.ambassador.velocity; import com.velocitypowered.api.event.Continuation; import com.velocitypowered.api.event.PostOrder; +import com.velocitypowered.api.event.ResultedEvent; 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.KickedFromServerEvent; +import com.velocitypowered.api.event.player.PlayerChooseInitialServerEvent; +import com.velocitypowered.api.event.player.ServerPreConnectEvent; import com.velocitypowered.proxy.VelocityServer; import com.velocitypowered.proxy.connection.client.ConnectedPlayer; import org.adde0109.ambassador.Ambassador; @@ -40,4 +43,32 @@ public class VelocityEventHandler { } continuation.resume(); } + + @Subscribe(order = PostOrder.FIRST) + 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()); + phase.internalServerConnection = null; + } + continuation.resume(); + } + + @Subscribe(order = PostOrder.LAST) + public void onPlayerChooseInitialServerEvent(PlayerChooseInitialServerEvent event, Continuation continuation) { + ConnectedPlayer player = (ConnectedPlayer) event.getPlayer(); + if (!(player.getPhase() instanceof VelocityForgeClientConnectionPhase phase)) { + continuation.resume(); + return; + } + if (phase.forced != null) + event.setInitialServer(phase.forced); + if (event.getInitialServer().isEmpty()) + event.setInitialServer(phase.internalServerConnection.getServer()); + 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 41e48f3..1057567 100644 --- a/src/main/java/org/adde0109/ambassador/velocity/VelocityForgeClientConnectionPhase.java +++ b/src/main/java/org/adde0109/ambassador/velocity/VelocityForgeClientConnectionPhase.java @@ -1,6 +1,8 @@ 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; import com.velocitypowered.proxy.connection.MinecraftConnection; import com.velocitypowered.proxy.connection.backend.VelocityServerConnection; @@ -9,17 +11,24 @@ import com.velocitypowered.proxy.connection.client.ConnectedPlayer; import com.velocitypowered.proxy.protocol.packet.LoginPluginMessage; import com.velocitypowered.proxy.protocol.packet.LoginPluginResponse; import org.adde0109.ambassador.forge.FML2CRPMClientConnectionPhase; -import org.adde0109.ambassador.forge.ForgeHandshakeUtils; - -import javax.annotation.Nullable; -import java.util.ArrayList; public abstract class VelocityForgeClientConnectionPhase implements ClientConnectionPhase { //TODO:Make class when PCF is done VelocityLoginPayloadManager payloadManager; - public FML2CRPMClientConnectionPhase.ClientPhase clientPhase = ClientPhase.HANDSHAKE; + public VelocityForgeClientConnectionPhase.ClientPhase clientPhase = ClientPhase.HANDSHAKE; + + public ServerConnection internalServerConnection; + public RegisteredServer forced; + + protected VelocityForgeClientConnectionPhase(ClientPhase clientPhase, VelocityLoginPayloadManager payloadManager) { + this.clientPhase = clientPhase; + this.payloadManager = payloadManager; + } + protected VelocityForgeClientConnectionPhase() { + + } public void handleLogin(ConnectedPlayer player, VelocityServer server, Continuation continuation) { } @@ -34,6 +43,9 @@ public abstract class VelocityForgeClientConnectionPhase implements ClientConnec 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(), player); + player.getConnection().setSessionHandler(sessionHandler); } public void forwardPayload(VelocityServerConnection serverConnection, LoginPluginMessage payload) { @@ -44,6 +56,7 @@ public abstract class VelocityForgeClientConnectionPhase implements ClientConnec //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() { 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 6989ece..fa8d1e3 100644 --- a/src/main/java/org/adde0109/ambassador/velocity/backend/VelocityForgeBackendConnectionPhase.java +++ b/src/main/java/org/adde0109/ambassador/velocity/backend/VelocityForgeBackendConnectionPhase.java @@ -20,15 +20,13 @@ public class VelocityForgeBackendConnectionPhase implements BackendConnectionPha public void handleSuccess(VelocityServerConnection serverCon, VelocityServer server) { VelocityForgeClientConnectionPhase clientPhase = ((VelocityForgeClientConnectionPhase) serverCon.getPlayer().getPhase()); - if (clientPhase.clientPhase == VelocityForgeClientConnectionPhase.ClientPhase.HANDSHAKE || clientPhase.clientPhase == VelocityForgeClientConnectionPhase.ClientPhase.MODLIST) { - clientPhase.complete((VelocityServer) server,serverCon.getPlayer(),serverCon.getPlayer().getConnection()); - } + clientPhase.complete((VelocityServer) server,serverCon.getPlayer(),serverCon.getPlayer().getConnection()); } public boolean handle(VelocityServerConnection server, ConnectedPlayer player, LoginPluginMessage message) throws Exception { - VelocityForgeClientConnectionPhase clientPhase = ((FML2CRPMClientConnectionPhase) player.getPhase()); + VelocityForgeClientConnectionPhase clientPhase = ((VelocityForgeClientConnectionPhase) player.getPhase()); message.retain(); - if (clientPhase.clientPhase == FML2CRPMClientConnectionPhase.ClientPhase.VANILLA) { + if (clientPhase.clientPhase == VelocityForgeClientConnectionPhase.ClientPhase.VANILLA) { clientPhase.reset(server,player, () -> { for (LoginPluginMessage msg: queuedHandshakePackets) { clientPhase.forwardPayload(server,msg); 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 227ab15..c6b978b 100644 --- a/src/main/java/org/adde0109/ambassador/velocity/backend/VelocityForgeBackendHandshakeHandler.java +++ b/src/main/java/org/adde0109/ambassador/velocity/backend/VelocityForgeBackendHandshakeHandler.java @@ -11,6 +11,7 @@ import io.netty.channel.*; import io.netty.util.ReferenceCountUtil; import org.adde0109.ambassador.forge.ForgeConstants; import org.adde0109.ambassador.forge.FML2CRPMClientConnectionPhase; +import org.adde0109.ambassador.velocity.VelocityForgeClientConnectionPhase; public class VelocityForgeBackendHandshakeHandler extends ChannelDuplexHandler { @@ -30,9 +31,9 @@ public class VelocityForgeBackendHandshakeHandler extends ChannelDuplexHandler { ChannelHandler handler = ctx.pipeline().get(Connections.HANDLER); if (handler instanceof MinecraftConnection connection) { if (connection.getAssociation() instanceof VelocityServerConnection serverConnection) { - if (serverConnection.getPlayer().getPhase() instanceof FML2CRPMClientConnectionPhase phase) { + if (serverConnection.getPlayer().getPhase() instanceof VelocityForgeClientConnectionPhase phase) { init(connection,serverConnection); - if (phase.clientPhase == FML2CRPMClientConnectionPhase.ClientPhase.MODDED) { + if (phase.clientPhase == VelocityForgeClientConnectionPhase.ClientPhase.MODDED) { phase.reset(serverConnection ,serverConnection.getPlayer(), () -> { ctx.flush(); });