From 7f434a8c5e81a82e2c7bfd205ffd7d43fa3db9eb Mon Sep 17 00:00:00 2001 From: Adrian Bergqvist Date: Sun, 7 May 2023 14:12:17 +0200 Subject: [PATCH] Send empty modlist when connecting to vanilla --- .../VelocityForgeBackendConnectionPhase.java | 1 + .../VelocityForgeClientConnectionPhase.java | 92 ++++++++++++------- .../packet/IForgeLoginWrapperPacket.java | 3 +- .../forge/packet/ModListPacket.java | 34 ------- .../forge/packet/ModListReplyPacket.java | 4 +- .../backend/ForgeLoginSessionHandler.java | 27 +++--- .../velocity/client/ClientPacketQueue.java | 10 +- .../client/FML2CRPMResetCompleteDecoder.java | 2 +- .../VelocityHandshakeSessionHandler.java | 4 +- 9 files changed, 80 insertions(+), 97 deletions(-) delete mode 100644 src/main/java/org/adde0109/ambassador/forge/packet/ModListPacket.java diff --git a/src/main/java/org/adde0109/ambassador/forge/VelocityForgeBackendConnectionPhase.java b/src/main/java/org/adde0109/ambassador/forge/VelocityForgeBackendConnectionPhase.java index b439854..01531d2 100644 --- a/src/main/java/org/adde0109/ambassador/forge/VelocityForgeBackendConnectionPhase.java +++ b/src/main/java/org/adde0109/ambassador/forge/VelocityForgeBackendConnectionPhase.java @@ -9,6 +9,7 @@ import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.packet.AvailableCommands; import com.velocitypowered.proxy.protocol.packet.LoginPluginMessage; import com.velocitypowered.proxy.protocol.packet.PluginMessage; +import io.netty.buffer.ByteBuf; import org.adde0109.ambassador.forge.pipeline.CommandDecoderErrorCatcher; import org.adde0109.ambassador.forge.pipeline.ForgeLoginWrapperDecoder; diff --git a/src/main/java/org/adde0109/ambassador/forge/VelocityForgeClientConnectionPhase.java b/src/main/java/org/adde0109/ambassador/forge/VelocityForgeClientConnectionPhase.java index 3e7a950..4bdf19c 100644 --- a/src/main/java/org/adde0109/ambassador/forge/VelocityForgeClientConnectionPhase.java +++ b/src/main/java/org/adde0109/ambassador/forge/VelocityForgeClientConnectionPhase.java @@ -1,6 +1,7 @@ package org.adde0109.ambassador.forge; import com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier; +import com.velocitypowered.api.util.ModInfo; import com.velocitypowered.proxy.VelocityServer; import com.velocitypowered.proxy.connection.MinecraftConnection; import com.velocitypowered.proxy.connection.backend.VelocityServerConnection; @@ -13,9 +14,9 @@ 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.packet.GenericForgeLoginWrapperPacket; import org.adde0109.ambassador.forge.packet.IForgeLoginWrapperPacket; import org.adde0109.ambassador.forge.packet.ModListReplyPacket; +import org.adde0109.ambassador.forge.pipeline.ForgeLoginWrapperDecoder; import org.adde0109.ambassador.velocity.client.FML2CRPMResetCompleteDecoder; import org.adde0109.ambassador.velocity.client.OutboundSuccessHolder; import org.adde0109.ambassador.velocity.client.ClientPacketQueue; @@ -29,9 +30,6 @@ public enum VelocityForgeClientConnectionPhase implements ClientConnectionPhase } }, IN_PROGRESS { - @Override - public void resetConnectionPhase(ConnectedPlayer player) { - } }, RESETTABLE { @Override @@ -41,6 +39,34 @@ public enum VelocityForgeClientConnectionPhase implements ClientConnectionPhase ((VelocityServer) Ambassador.getInstance().server).registerConnection(player); } + @Override + public void resetConnectionPhase(ConnectedPlayer player) { + MinecraftConnection connection = player.getConnection(); + + //There is no going back even if the handshake fails. No reason to still be connected. + if (player.getConnectedServer() != null) { + player.getConnectedServer().disconnect(); + player.setConnectedServer(null); + } + //Don't handle anything from the server until the reset has completed. + //player.getConnectionInFlight().getConnection().getChannel().config().setAutoRead(false); + + if (connection.getState() == StateRegistry.PLAY) { + connection.write(new PluginMessage("fml:handshake", Unpooled.wrappedBuffer(ForgeHandshakeUtils.generatePluginResetPacket()))); + connection.setState(StateRegistry.LOGIN); + } else { + connection.write(new LoginPluginMessage(98,"fml:handshake", Unpooled.wrappedBuffer(ForgeHandshakeUtils.generateResetPacket()))); + } + + //Prepare to receive reset ACK + connection.getChannel().pipeline().addBefore(Connections.MINECRAFT_DECODER, + ForgeConstants.RESET_LISTENER, new FML2CRPMResetCompleteDecoder()); + + //Transition + player.setPhase(WAITING_RESET); + WAITING_RESET.onTransitionToNewPhase(player); + } + @Override public boolean consideredComplete() { return true; @@ -52,15 +78,15 @@ public enum VelocityForgeClientConnectionPhase implements ClientConnectionPhase //We unregister so no plugin sees this client while the client is being reset. ((VelocityServer) Ambassador.getInstance().server).unregisterConnection(player); player.getConnection().getChannel().pipeline().addAfter(Connections.MINECRAFT_ENCODER, - ForgeConstants.LOGIN_PACKET_QUEUE, new ClientPacketQueue(StateRegistry.LOGIN)); + ForgeConstants.LOGIN_PACKET_QUEUE, new ClientPacketQueue(StateRegistry.PLAY)); if (player.getConnection().getChannel().pipeline().get(ForgeConstants.PLUGIN_PACKET_QUEUE) == null) player.getConnection().getChannel().pipeline().addAfter(Connections.MINECRAFT_ENCODER, - ForgeConstants.PLUGIN_PACKET_QUEUE, new ClientPacketQueue(StateRegistry.PLAY)); + ForgeConstants.PLUGIN_PACKET_QUEUE, new ClientPacketQueue(StateRegistry.LOGIN)); } @Override public boolean handle(ConnectedPlayer player, IForgeLoginWrapperPacket msg, VelocityServerConnection server) { - if (msg.getId() == 80) { + if (msg.getId() == 98) { player.getConnection().getChannel().pipeline().remove(ForgeConstants.RESET_LISTENER); player.setPhase(NOT_STARTED); @@ -68,7 +94,7 @@ public enum VelocityForgeClientConnectionPhase implements ClientConnectionPhase if (!(server.getConnection().getType() instanceof ForgeFMLConnectionType)) { // -> vanilla - complete(player, ((GenericForgeLoginWrapperPacket) msg).success()); + complete(player); player.getConnectionInFlight().getConnection().getChannel().config().setAutoRead(true); } @@ -101,6 +127,14 @@ public enum VelocityForgeClientConnectionPhase implements ClientConnectionPhase player.setPhase(nextPhase()); if (msg instanceof ModListReplyPacket replyPacket) { + ModInfo modInfo = new ModInfo("FML2", replyPacket.getMods().stream().map( + (v) -> new ModInfo.Mod(v,"1")).toList()); + player.setModInfo(modInfo); + if (!(server.getConnection().getType() instanceof ForgeFMLConnectionType)) { + complete(player); + player.getConnectionInFlight().getConnection().getChannel().config().setAutoRead(true); + return true; + } replyPacket.getChannels().put(MinecraftChannelIdentifier.from("ambassador:commands"),"1"); } @@ -108,13 +142,22 @@ public enum VelocityForgeClientConnectionPhase implements ClientConnectionPhase return true; } - public void complete(ConnectedPlayer player, boolean resettable) { + public void sendVanillaModlist(ConnectedPlayer player) { + player.getConnection().write(new LoginPluginMessage(0, "fml:loginwrapper", + Unpooled.wrappedBuffer(ForgeHandshakeUtils.emptyModlist))); + + ForgeLoginWrapperDecoder decoder = (ForgeLoginWrapperDecoder) player.getConnection() + .getChannel().pipeline().get(ForgeConstants.FORGE_HANDSHAKE_DECODER); + decoder.registerLoginWrapperID(0); + } + + public void complete(ConnectedPlayer player) { MinecraftConnection connection = player.getConnection(); ((OutboundSuccessHolder) connection.getChannel().pipeline().get(ForgeConstants.SERVER_SUCCESS_LISTENER)) .sendPacket(); connection.setState(StateRegistry.PLAY); - if (resettable) { + if (isResettable(player)) { player.setPhase(RESETTABLE); RESETTABLE.onTransitionToNewPhase(player); } else { @@ -123,32 +166,11 @@ public enum VelocityForgeClientConnectionPhase implements ClientConnectionPhase } } - @Override - public void resetConnectionPhase(ConnectedPlayer player) { - MinecraftConnection connection = player.getConnection(); - - //There is no going back even if the handshake fails. No reason to still be connected. - if (player.getConnectedServer() != null) { - player.getConnectedServer().disconnect(); - player.setConnectedServer(null); + private boolean isResettable(ConnectedPlayer player) { + if (player.getModInfo().isPresent()) { + return player.getModInfo().get().getMods().stream().anyMatch((mod -> mod.getId().equals("clientresetpacket"))); } - //Don't handle anything from the server until the reset has completed. - //player.getConnectionInFlight().getConnection().getChannel().config().setAutoRead(false); - - if (connection.getState() == StateRegistry.PLAY) { - connection.write(new PluginMessage("fml:handshake", Unpooled.wrappedBuffer(ForgeHandshakeUtils.generatePluginResetPacket()))); - connection.setState(StateRegistry.LOGIN); - } else { - connection.write(new LoginPluginMessage(80,"fml:handshake", Unpooled.wrappedBuffer(ForgeHandshakeUtils.generateResetPacket()))); - } - - //Prepare to receive reset ACK - connection.getChannel().pipeline().addBefore(Connections.MINECRAFT_DECODER, - ForgeConstants.RESET_LISTENER, new FML2CRPMResetCompleteDecoder()); - - //Transition - player.setPhase(WAITING_RESET); - WAITING_RESET.onTransitionToNewPhase(player); + return false; } void onTransitionToNewPhase(ConnectedPlayer player) { diff --git a/src/main/java/org/adde0109/ambassador/forge/packet/IForgeLoginWrapperPacket.java b/src/main/java/org/adde0109/ambassador/forge/packet/IForgeLoginWrapperPacket.java index df253f9..22ac150 100644 --- a/src/main/java/org/adde0109/ambassador/forge/packet/IForgeLoginWrapperPacket.java +++ b/src/main/java/org/adde0109/ambassador/forge/packet/IForgeLoginWrapperPacket.java @@ -2,8 +2,7 @@ package org.adde0109.ambassador.forge.packet; import com.velocitypowered.proxy.protocol.packet.LoginPluginResponse; -public interface IForgeLoginWrapperPacket { - public T read(LoginPluginResponse message); +public interface IForgeLoginWrapperPacket { public LoginPluginResponse encode(); public int getId(); } diff --git a/src/main/java/org/adde0109/ambassador/forge/packet/ModListPacket.java b/src/main/java/org/adde0109/ambassador/forge/packet/ModListPacket.java deleted file mode 100644 index 2e30c39..0000000 --- a/src/main/java/org/adde0109/ambassador/forge/packet/ModListPacket.java +++ /dev/null @@ -1,34 +0,0 @@ -package org.adde0109.ambassador.forge.packet; - -import com.velocitypowered.api.proxy.messages.ChannelIdentifier; -import com.velocitypowered.proxy.protocol.packet.LoginPluginResponse; - -import java.util.List; -import java.util.Map; - -public class ModListPacket implements IForgeLoginWrapperPacket{ - - private List mods; - private Map channels; - private List registries; - private final int id; - - public ModListPacket(int id) { - this.id = id; - } - - @Override - public Object read(LoginPluginResponse message) { - return null; - } - - @Override - public LoginPluginResponse encode() { - return null; - } - - @Override - public int getId() { - return 0; - } -} diff --git a/src/main/java/org/adde0109/ambassador/forge/packet/ModListReplyPacket.java b/src/main/java/org/adde0109/ambassador/forge/packet/ModListReplyPacket.java index 72db870..d68f94e 100644 --- a/src/main/java/org/adde0109/ambassador/forge/packet/ModListReplyPacket.java +++ b/src/main/java/org/adde0109/ambassador/forge/packet/ModListReplyPacket.java @@ -28,7 +28,7 @@ public class ModListReplyPacket implements IForgeLoginWrapperPacket { this.id = id; } - public ModListReplyPacket read(LoginPluginResponse msg) { + public static ModListReplyPacket read(LoginPluginResponse msg) { ByteBuf input = msg.content(); List mods = new ArrayList<>(); @@ -76,7 +76,7 @@ public class ModListReplyPacket implements IForgeLoginWrapperPacket { ProtocolUtils.writeVarInt(output, buf.readableBytes()); output.writeBytes(buf); - return new LoginPluginResponse(id,true,output); + return new LoginPluginResponse(id, true, output); } @Override diff --git a/src/main/java/org/adde0109/ambassador/velocity/backend/ForgeLoginSessionHandler.java b/src/main/java/org/adde0109/ambassador/velocity/backend/ForgeLoginSessionHandler.java index 2b1c27d..2aee101 100644 --- a/src/main/java/org/adde0109/ambassador/velocity/backend/ForgeLoginSessionHandler.java +++ b/src/main/java/org/adde0109/ambassador/velocity/backend/ForgeLoginSessionHandler.java @@ -1,25 +1,16 @@ package org.adde0109.ambassador.velocity.backend; -import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.VelocityServer; import com.velocitypowered.proxy.config.PlayerInfoForwarding; -import com.velocitypowered.proxy.connection.MinecraftConnection; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.backend.*; import com.velocitypowered.proxy.connection.client.ConnectedPlayer; import com.velocitypowered.proxy.protocol.MinecraftPacket; -import com.velocitypowered.proxy.protocol.StateRegistry; import com.velocitypowered.proxy.protocol.packet.Disconnect; import com.velocitypowered.proxy.protocol.packet.LoginPluginMessage; import com.velocitypowered.proxy.protocol.packet.ServerLoginSuccess; import com.velocitypowered.proxy.util.except.QuietRuntimeException; -import net.kyori.adventure.text.Component; -import org.adde0109.ambassador.Ambassador; -import org.adde0109.ambassador.forge.ForgeConstants; -import org.adde0109.ambassador.forge.ForgeFMLConnectionType; -import org.adde0109.ambassador.forge.VelocityForgeBackendConnectionPhase; -import org.adde0109.ambassador.forge.VelocityForgeClientConnectionPhase; -import org.adde0109.ambassador.velocity.client.OutboundSuccessHolder; +import org.adde0109.ambassador.forge.*; public class ForgeLoginSessionHandler implements MinecraftSessionHandler { @@ -57,21 +48,25 @@ public class ForgeLoginSessionHandler implements MinecraftSessionHandler { } ConnectedPlayer player = serverConnection.getPlayer(); if (!(serverConnection.getConnection().getType() instanceof ForgeFMLConnectionType)) { - //Initial vanilla - because we need to find out if reset packet works - //Forge -> vanilla if (player.getConnectedServer() == null) { //Initial Vanilla - - //Send empty Mod list + //Send empty mod list in order to get client mod list + ((VelocityForgeClientConnectionPhase) player.getPhase()).sendVanillaModlist(player); + player.getConnectionInFlight().getConnection().getChannel().config().setAutoRead(false); + //((VelocityForgeClientConnectionPhase) player.getPhase()).complete(player); + } else if (player.getConnectedServer().getConnection().getType() instanceof ForgeFMLConnectionType) { + //Forge -> vanilla + player.getPhase().resetConnectionPhase(player); player.getConnectionInFlight().getConnection().getChannel().config().setAutoRead(false); } } else { - //TODO: Read modlist - ((VelocityForgeClientConnectionPhase) player.getPhase()).complete(player, true); + ((VelocityForgeClientConnectionPhase) player.getPhase()).complete(player); } return true; } + + @Override public boolean handle(Disconnect packet) { if (!serverConnection.getPhase().consideredComplete()) { diff --git a/src/main/java/org/adde0109/ambassador/velocity/client/ClientPacketQueue.java b/src/main/java/org/adde0109/ambassador/velocity/client/ClientPacketQueue.java index d6d0892..18534f8 100644 --- a/src/main/java/org/adde0109/ambassador/velocity/client/ClientPacketQueue.java +++ b/src/main/java/org/adde0109/ambassador/velocity/client/ClientPacketQueue.java @@ -10,10 +10,10 @@ import io.netty.channel.*; public class ClientPacketQueue extends ChannelOutboundHandlerAdapter { private PendingWriteQueue queue; - private final StateRegistry registry; + private final StateRegistry allow; public ClientPacketQueue(StateRegistry registry) { - this.registry = registry; + this.allow = registry; } @Override @@ -26,11 +26,11 @@ public class ClientPacketQueue extends ChannelOutboundHandlerAdapter { MinecraftConnection connection = ctx.pipeline().get(MinecraftConnection.class); if (msg instanceof MinecraftPacket packet) { try { - registry.getProtocolRegistry(ProtocolUtils.Direction.CLIENTBOUND , + allow.getProtocolRegistry(ProtocolUtils.Direction.CLIENTBOUND , connection.getProtocolVersion()).getPacketId(packet); - queue.add(msg,promise); + ctx.write(msg,promise); } catch (IllegalArgumentException e) { - ctx.write(msg, promise); + queue.add(msg, promise); } } else { ctx.write(msg,promise); diff --git a/src/main/java/org/adde0109/ambassador/velocity/client/FML2CRPMResetCompleteDecoder.java b/src/main/java/org/adde0109/ambassador/velocity/client/FML2CRPMResetCompleteDecoder.java index 114e0a0..d62d003 100644 --- a/src/main/java/org/adde0109/ambassador/velocity/client/FML2CRPMResetCompleteDecoder.java +++ b/src/main/java/org/adde0109/ambassador/velocity/client/FML2CRPMResetCompleteDecoder.java @@ -24,7 +24,7 @@ public class FML2CRPMResetCompleteDecoder extends ChannelInboundHandlerAdapter { try { int id = ProtocolUtils.readVarInt(buf); boolean success = buf.readBoolean(); - if (id == 80) { + if (id == 98) { try { IForgeLoginWrapperPacket packet = new GenericForgeLoginWrapperPacket(Unpooled.EMPTY_BUFFER, id, success); ctx.fireChannelRead(packet); diff --git a/src/main/java/org/adde0109/ambassador/velocity/client/VelocityHandshakeSessionHandler.java b/src/main/java/org/adde0109/ambassador/velocity/client/VelocityHandshakeSessionHandler.java index dc7523d..5a4086e 100644 --- a/src/main/java/org/adde0109/ambassador/velocity/client/VelocityHandshakeSessionHandler.java +++ b/src/main/java/org/adde0109/ambassador/velocity/client/VelocityHandshakeSessionHandler.java @@ -30,12 +30,12 @@ public class VelocityHandshakeSessionHandler implements MinecraftSessionHandler case "FML2": connection.setType(ForgeConstants.ForgeFML2); connection.getChannel().pipeline().addAfter(Connections.MINECRAFT_ENCODER,ForgeConstants.SERVER_SUCCESS_LISTENER, new OutboundSuccessHolder()); - connection.getChannel().pipeline().addAfter(Connections.MINECRAFT_ENCODER,ForgeConstants.PLUGIN_PACKET_QUEUE, new ClientPacketQueue(StateRegistry.PLAY)); + connection.getChannel().pipeline().addAfter(Connections.MINECRAFT_ENCODER,ForgeConstants.PLUGIN_PACKET_QUEUE, new ClientPacketQueue(StateRegistry.LOGIN)); break; case "FML3": connection.setType(ForgeConstants.ForgeFML3); connection.getChannel().pipeline().addAfter(Connections.MINECRAFT_ENCODER,ForgeConstants.SERVER_SUCCESS_LISTENER, new OutboundSuccessHolder()); - connection.getChannel().pipeline().addAfter(Connections.MINECRAFT_ENCODER,ForgeConstants.PLUGIN_PACKET_QUEUE, new ClientPacketQueue(StateRegistry.PLAY)); + connection.getChannel().pipeline().addAfter(Connections.MINECRAFT_ENCODER,ForgeConstants.PLUGIN_PACKET_QUEUE, new ClientPacketQueue(StateRegistry.LOGIN)); break; } }