Send empty modlist when connecting to vanilla

This commit is contained in:
Adrian Bergqvist 2023-05-07 14:12:17 +02:00
parent a0b38b7595
commit 7f434a8c5e
No known key found for this signature in database
GPG Key ID: 3B3DA43224B79417
9 changed files with 80 additions and 97 deletions

View File

@ -9,6 +9,7 @@ import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.packet.AvailableCommands; import com.velocitypowered.proxy.protocol.packet.AvailableCommands;
import com.velocitypowered.proxy.protocol.packet.LoginPluginMessage; import com.velocitypowered.proxy.protocol.packet.LoginPluginMessage;
import com.velocitypowered.proxy.protocol.packet.PluginMessage; 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.CommandDecoderErrorCatcher;
import org.adde0109.ambassador.forge.pipeline.ForgeLoginWrapperDecoder; import org.adde0109.ambassador.forge.pipeline.ForgeLoginWrapperDecoder;

View File

@ -1,6 +1,7 @@
package org.adde0109.ambassador.forge; package org.adde0109.ambassador.forge;
import com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier; import com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier;
import com.velocitypowered.api.util.ModInfo;
import com.velocitypowered.proxy.VelocityServer; import com.velocitypowered.proxy.VelocityServer;
import com.velocitypowered.proxy.connection.MinecraftConnection; import com.velocitypowered.proxy.connection.MinecraftConnection;
import com.velocitypowered.proxy.connection.backend.VelocityServerConnection; import com.velocitypowered.proxy.connection.backend.VelocityServerConnection;
@ -13,9 +14,9 @@ import com.velocitypowered.proxy.protocol.packet.PluginMessage;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import org.adde0109.ambassador.Ambassador; 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.IForgeLoginWrapperPacket;
import org.adde0109.ambassador.forge.packet.ModListReplyPacket; 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.FML2CRPMResetCompleteDecoder;
import org.adde0109.ambassador.velocity.client.OutboundSuccessHolder; import org.adde0109.ambassador.velocity.client.OutboundSuccessHolder;
import org.adde0109.ambassador.velocity.client.ClientPacketQueue; import org.adde0109.ambassador.velocity.client.ClientPacketQueue;
@ -29,9 +30,6 @@ public enum VelocityForgeClientConnectionPhase implements ClientConnectionPhase
} }
}, },
IN_PROGRESS { IN_PROGRESS {
@Override
public void resetConnectionPhase(ConnectedPlayer player) {
}
}, },
RESETTABLE { RESETTABLE {
@Override @Override
@ -41,6 +39,34 @@ public enum VelocityForgeClientConnectionPhase implements ClientConnectionPhase
((VelocityServer) Ambassador.getInstance().server).registerConnection(player); ((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 @Override
public boolean consideredComplete() { public boolean consideredComplete() {
return true; 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. //We unregister so no plugin sees this client while the client is being reset.
((VelocityServer) Ambassador.getInstance().server).unregisterConnection(player); ((VelocityServer) Ambassador.getInstance().server).unregisterConnection(player);
player.getConnection().getChannel().pipeline().addAfter(Connections.MINECRAFT_ENCODER, 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) if (player.getConnection().getChannel().pipeline().get(ForgeConstants.PLUGIN_PACKET_QUEUE) == null)
player.getConnection().getChannel().pipeline().addAfter(Connections.MINECRAFT_ENCODER, 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 @Override
public boolean handle(ConnectedPlayer player, IForgeLoginWrapperPacket msg, VelocityServerConnection server) { 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.getConnection().getChannel().pipeline().remove(ForgeConstants.RESET_LISTENER);
player.setPhase(NOT_STARTED); player.setPhase(NOT_STARTED);
@ -68,7 +94,7 @@ public enum VelocityForgeClientConnectionPhase implements ClientConnectionPhase
if (!(server.getConnection().getType() instanceof ForgeFMLConnectionType)) { if (!(server.getConnection().getType() instanceof ForgeFMLConnectionType)) {
// -> vanilla // -> vanilla
complete(player, ((GenericForgeLoginWrapperPacket) msg).success()); complete(player);
player.getConnectionInFlight().getConnection().getChannel().config().setAutoRead(true); player.getConnectionInFlight().getConnection().getChannel().config().setAutoRead(true);
} }
@ -101,6 +127,14 @@ public enum VelocityForgeClientConnectionPhase implements ClientConnectionPhase
player.setPhase(nextPhase()); player.setPhase(nextPhase());
if (msg instanceof ModListReplyPacket replyPacket) { 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"); replyPacket.getChannels().put(MinecraftChannelIdentifier.from("ambassador:commands"),"1");
} }
@ -108,13 +142,22 @@ public enum VelocityForgeClientConnectionPhase implements ClientConnectionPhase
return true; 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(); MinecraftConnection connection = player.getConnection();
((OutboundSuccessHolder) connection.getChannel().pipeline().get(ForgeConstants.SERVER_SUCCESS_LISTENER)) ((OutboundSuccessHolder) connection.getChannel().pipeline().get(ForgeConstants.SERVER_SUCCESS_LISTENER))
.sendPacket(); .sendPacket();
connection.setState(StateRegistry.PLAY); connection.setState(StateRegistry.PLAY);
if (resettable) { if (isResettable(player)) {
player.setPhase(RESETTABLE); player.setPhase(RESETTABLE);
RESETTABLE.onTransitionToNewPhase(player); RESETTABLE.onTransitionToNewPhase(player);
} else { } else {
@ -123,32 +166,11 @@ public enum VelocityForgeClientConnectionPhase implements ClientConnectionPhase
} }
} }
@Override private boolean isResettable(ConnectedPlayer player) {
public void resetConnectionPhase(ConnectedPlayer player) { if (player.getModInfo().isPresent()) {
MinecraftConnection connection = player.getConnection(); return player.getModInfo().get().getMods().stream().anyMatch((mod -> mod.getId().equals("clientresetpacket")));
//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. return false;
//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);
} }
void onTransitionToNewPhase(ConnectedPlayer player) { void onTransitionToNewPhase(ConnectedPlayer player) {

View File

@ -2,8 +2,7 @@ package org.adde0109.ambassador.forge.packet;
import com.velocitypowered.proxy.protocol.packet.LoginPluginResponse; import com.velocitypowered.proxy.protocol.packet.LoginPluginResponse;
public interface IForgeLoginWrapperPacket<T> { public interface IForgeLoginWrapperPacket {
public T read(LoginPluginResponse message);
public LoginPluginResponse encode(); public LoginPluginResponse encode();
public int getId(); public int getId();
} }

View File

@ -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<String> mods;
private Map<ChannelIdentifier, String> channels;
private List<ChannelIdentifier> 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;
}
}

View File

@ -28,7 +28,7 @@ public class ModListReplyPacket implements IForgeLoginWrapperPacket {
this.id = id; this.id = id;
} }
public ModListReplyPacket read(LoginPluginResponse msg) { public static ModListReplyPacket read(LoginPluginResponse msg) {
ByteBuf input = msg.content(); ByteBuf input = msg.content();
List<String> mods = new ArrayList<>(); List<String> mods = new ArrayList<>();
@ -76,7 +76,7 @@ public class ModListReplyPacket implements IForgeLoginWrapperPacket {
ProtocolUtils.writeVarInt(output, buf.readableBytes()); ProtocolUtils.writeVarInt(output, buf.readableBytes());
output.writeBytes(buf); output.writeBytes(buf);
return new LoginPluginResponse(id,true,output); return new LoginPluginResponse(id, true, output);
} }
@Override @Override

View File

@ -1,25 +1,16 @@
package org.adde0109.ambassador.velocity.backend; package org.adde0109.ambassador.velocity.backend;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.VelocityServer; import com.velocitypowered.proxy.VelocityServer;
import com.velocitypowered.proxy.config.PlayerInfoForwarding; import com.velocitypowered.proxy.config.PlayerInfoForwarding;
import com.velocitypowered.proxy.connection.MinecraftConnection;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.connection.backend.*; import com.velocitypowered.proxy.connection.backend.*;
import com.velocitypowered.proxy.connection.client.ConnectedPlayer; import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
import com.velocitypowered.proxy.protocol.MinecraftPacket; 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.Disconnect;
import com.velocitypowered.proxy.protocol.packet.LoginPluginMessage; import com.velocitypowered.proxy.protocol.packet.LoginPluginMessage;
import com.velocitypowered.proxy.protocol.packet.ServerLoginSuccess; import com.velocitypowered.proxy.protocol.packet.ServerLoginSuccess;
import com.velocitypowered.proxy.util.except.QuietRuntimeException; import com.velocitypowered.proxy.util.except.QuietRuntimeException;
import net.kyori.adventure.text.Component; import org.adde0109.ambassador.forge.*;
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;
public class ForgeLoginSessionHandler implements MinecraftSessionHandler { public class ForgeLoginSessionHandler implements MinecraftSessionHandler {
@ -57,21 +48,25 @@ public class ForgeLoginSessionHandler implements MinecraftSessionHandler {
} }
ConnectedPlayer player = serverConnection.getPlayer(); ConnectedPlayer player = serverConnection.getPlayer();
if (!(serverConnection.getConnection().getType() instanceof ForgeFMLConnectionType)) { if (!(serverConnection.getConnection().getType() instanceof ForgeFMLConnectionType)) {
//Initial vanilla - because we need to find out if reset packet works
//Forge -> vanilla
if (player.getConnectedServer() == null) { if (player.getConnectedServer() == null) {
//Initial Vanilla //Initial Vanilla
//Send empty mod list in order to get client mod list
//Send empty 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); player.getConnectionInFlight().getConnection().getChannel().config().setAutoRead(false);
} }
} else { } else {
//TODO: Read modlist ((VelocityForgeClientConnectionPhase) player.getPhase()).complete(player);
((VelocityForgeClientConnectionPhase) player.getPhase()).complete(player, true);
} }
return true; return true;
} }
@Override @Override
public boolean handle(Disconnect packet) { public boolean handle(Disconnect packet) {
if (!serverConnection.getPhase().consideredComplete()) { if (!serverConnection.getPhase().consideredComplete()) {

View File

@ -10,10 +10,10 @@ import io.netty.channel.*;
public class ClientPacketQueue extends ChannelOutboundHandlerAdapter { public class ClientPacketQueue extends ChannelOutboundHandlerAdapter {
private PendingWriteQueue queue; private PendingWriteQueue queue;
private final StateRegistry registry; private final StateRegistry allow;
public ClientPacketQueue(StateRegistry registry) { public ClientPacketQueue(StateRegistry registry) {
this.registry = registry; this.allow = registry;
} }
@Override @Override
@ -26,11 +26,11 @@ public class ClientPacketQueue extends ChannelOutboundHandlerAdapter {
MinecraftConnection connection = ctx.pipeline().get(MinecraftConnection.class); MinecraftConnection connection = ctx.pipeline().get(MinecraftConnection.class);
if (msg instanceof MinecraftPacket packet) { if (msg instanceof MinecraftPacket packet) {
try { try {
registry.getProtocolRegistry(ProtocolUtils.Direction.CLIENTBOUND , allow.getProtocolRegistry(ProtocolUtils.Direction.CLIENTBOUND ,
connection.getProtocolVersion()).getPacketId(packet); connection.getProtocolVersion()).getPacketId(packet);
queue.add(msg,promise); ctx.write(msg,promise);
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
ctx.write(msg, promise); queue.add(msg, promise);
} }
} else { } else {
ctx.write(msg,promise); ctx.write(msg,promise);

View File

@ -24,7 +24,7 @@ public class FML2CRPMResetCompleteDecoder extends ChannelInboundHandlerAdapter {
try { try {
int id = ProtocolUtils.readVarInt(buf); int id = ProtocolUtils.readVarInt(buf);
boolean success = buf.readBoolean(); boolean success = buf.readBoolean();
if (id == 80) { if (id == 98) {
try { try {
IForgeLoginWrapperPacket packet = new GenericForgeLoginWrapperPacket(Unpooled.EMPTY_BUFFER, id, success); IForgeLoginWrapperPacket packet = new GenericForgeLoginWrapperPacket(Unpooled.EMPTY_BUFFER, id, success);
ctx.fireChannelRead(packet); ctx.fireChannelRead(packet);

View File

@ -30,12 +30,12 @@ public class VelocityHandshakeSessionHandler implements MinecraftSessionHandler
case "FML2": case "FML2":
connection.setType(ForgeConstants.ForgeFML2); 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.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; break;
case "FML3": case "FML3":
connection.setType(ForgeConstants.ForgeFML3); 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.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; break;
} }
} }