Interact with forge packets

This commit is contained in:
Adrian Bergqvist 2023-03-26 19:31:23 +02:00
parent 529d7e7636
commit d6a89ddec9
No known key found for this signature in database
GPG Key ID: FAE7D8EDE225E686
12 changed files with 237 additions and 14 deletions

View File

@ -5,7 +5,7 @@ plugins {
}
group = "org.adde0109"
version = "1.2.0-beta"
version = "1.3.0-beta-rc1"
repositories {
mavenCentral()

View File

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

View File

@ -5,6 +5,7 @@ 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.packet.LoginPluginMessage;
import org.adde0109.ambassador.forge.pipeline.ForgeLoginWrapperDecoder;
public enum VelocityForgeBackendConnectionPhase implements BackendConnectionPhase {
NOT_STARTED() {
@ -51,6 +52,10 @@ public enum VelocityForgeBackendConnectionPhase implements BackendConnectionPhas
VelocityForgeClientConnectionPhase clientPhase = (VelocityForgeClientConnectionPhase) player.getPhase();
clientPhase.resetConnectionPhase(player);
player.getConnection().write(message.retain());
ForgeLoginWrapperDecoder decoder = (ForgeLoginWrapperDecoder) player.getConnection()
.getChannel().pipeline().get(ForgeConstants.FORGE_HANDSHAKE_DECODER);
decoder.registerLoginWrapperID(message.getId());
}
public void onLoginSuccess(VelocityServerConnection serverCon, ConnectedPlayer player) {

View File

@ -1,5 +1,7 @@
package org.adde0109.ambassador.forge;
import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
import com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier;
import com.velocitypowered.api.proxy.server.RegisteredServer;
import com.velocitypowered.proxy.VelocityServer;
import com.velocitypowered.proxy.connection.MinecraftConnection;
@ -8,14 +10,14 @@ 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.LoginPluginResponse;
import com.velocitypowered.proxy.protocol.packet.PluginMessage;
import io.netty.buffer.Unpooled;
import org.adde0109.ambassador.Ambassador;
import org.adde0109.ambassador.forge.packet.IForgeLoginWrapperPacket;
import org.adde0109.ambassador.forge.packet.ModListReplyPacket;
import org.adde0109.ambassador.velocity.client.FML2CRPMResetCompleteDecoder;
import org.adde0109.ambassador.velocity.client.OutboundForgeHandshakeHolder;
import org.adde0109.ambassador.velocity.client.OutboundSuccessHolder;
import org.adde0109.ambassador.velocity.client.VelocityForgeHandshakeSessionHandler;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.ScheduledFuture;
@ -50,7 +52,7 @@ public enum VelocityForgeClientConnectionPhase implements ClientConnectionPhase
connection.getChannel().pipeline().addBefore(Connections.MINECRAFT_DECODER, ForgeConstants.RESET_LISTENER,new FML2CRPMResetCompleteDecoder());
connection.getChannel().pipeline().addAfter(Connections.MINECRAFT_ENCODER, ForgeConstants.FORGE_HANDSHAKE_HOLDER,new OutboundForgeHandshakeHolder());
player.getConnection().setSessionHandler(new VelocityForgeHandshakeSessionHandler(player.getConnection().getSessionHandler(),player));
//player.getConnection().setSessionHandler(new VelocityForgeHandshakeSessionHandler(player.getConnection().getSessionHandler(),player));
connection.write(new PluginMessage("fml:handshake", Unpooled.wrappedBuffer(ForgeHandshakeUtils.generatePluginResetPacket())));
@ -77,8 +79,8 @@ public enum VelocityForgeClientConnectionPhase implements ClientConnectionPhase
}, Ambassador.getInstance().config.getResetTimeout(), TimeUnit.MILLISECONDS);
}
@Override
public boolean handle(ConnectedPlayer player, LoginPluginResponse response, VelocityServerConnection server) {
if (response.getId() == 98) {
public boolean handle(ConnectedPlayer player, IForgeLoginWrapperPacket msg, VelocityServerConnection server) {
if (msg.getId() == 98) {
if (scheduledFuture.cancel(false)) {
player.getConnection().getChannel().pipeline().remove(ForgeConstants.RESET_LISTENER);
player.getConnection().setState(StateRegistry.LOGIN);
@ -106,10 +108,14 @@ public enum VelocityForgeClientConnectionPhase implements ClientConnectionPhase
public boolean vanillaMode = true;
public boolean handle(ConnectedPlayer player, LoginPluginResponse response, VelocityServerConnection server) {
public boolean handle(ConnectedPlayer player, IForgeLoginWrapperPacket msg, VelocityServerConnection server) {
player.setPhase(nextPhase());
player.getConnectionInFlight().getConnection().write(response.retain());
if (msg instanceof ModListReplyPacket replyPacket) {
replyPacket.getChannels().put(MinecraftChannelIdentifier.from("ambassador:commands"),"1");
}
player.getConnectionInFlight().getConnection().write(msg.encode());
vanillaMode = false;
return true;
}

View File

@ -0,0 +1,24 @@
package org.adde0109.ambassador.forge.packet;
import com.velocitypowered.proxy.protocol.packet.LoginPluginResponse;
import com.velocitypowered.proxy.protocol.util.DeferredByteBufHolder;
import io.netty.buffer.ByteBuf;
public class GenericForgeLoginWrapperPacket extends DeferredByteBufHolder implements IForgeLoginWrapperPacket {
private final int id;
public GenericForgeLoginWrapperPacket(ByteBuf input, int id) {
super(input);
this.id = id;
}
@Override
public LoginPluginResponse encode() {
return new LoginPluginResponse(id, true, content());
}
@Override
public int getId() {
return id;
}
}

View File

@ -0,0 +1,8 @@
package org.adde0109.ambassador.forge.packet;
import com.velocitypowered.proxy.protocol.packet.LoginPluginResponse;
public interface IForgeLoginWrapperPacket {
public LoginPluginResponse encode();
public int getId();
}

View File

@ -0,0 +1,94 @@
package org.adde0109.ambassador.forge.packet;
import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
import com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.packet.LoginPluginResponse;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class ModListReplyPacket implements IForgeLoginWrapperPacket {
private List<String> mods;
private Map<ChannelIdentifier, String> channels;
private Map<String, String> registries;
private final int id;
private ModListReplyPacket(List<String> mods, Map<ChannelIdentifier,
String> channels, Map<String, String> registries, int id) {
this.mods = mods;
this.channels = channels;
this.registries = registries;
this.id = id;
}
public static ModListReplyPacket read(LoginPluginResponse msg) {
ByteBuf input = msg.content();
List<String> mods = new ArrayList<>();
int len = ProtocolUtils.readVarInt(input);
for (int x = 0; x < len; x++)
mods.add(ProtocolUtils.readString(input, 0x100));
Map<ChannelIdentifier, String> channels = new HashMap<>();
len = ProtocolUtils.readVarInt(input);
for (int x = 0; x < len; x++)
channels.put(MinecraftChannelIdentifier.from(ProtocolUtils.readString(input, 32767)),
ProtocolUtils.readString(input, 0x100));
Map<String, String> registries = new HashMap<>();
len = ProtocolUtils.readVarInt(input);
for (int x = 0; x < len; x++)
registries.put(ProtocolUtils.readString(input, 32767), ProtocolUtils.readString(input, 0x100));
return new ModListReplyPacket(mods, channels, registries, msg.getId());
}
@Override
public LoginPluginResponse encode() {
ByteBuf buf = Unpooled.buffer();
ProtocolUtils.writeVarInt(buf, 2);
ProtocolUtils.writeVarInt(buf, mods.size());
mods.forEach(m -> ProtocolUtils.writeString(buf, m));
ProtocolUtils.writeVarInt(buf, channels.size());
channels.forEach((k, v) -> {
ProtocolUtils.writeString(buf,k.getId());
ProtocolUtils.writeString(buf,v);
});
ProtocolUtils.writeVarInt(buf, registries.size());
registries.forEach((k, v) -> {
ProtocolUtils.writeString(buf, k);
ProtocolUtils.writeString(buf, v);
});
ByteBuf output = Unpooled.buffer();
ProtocolUtils.writeString(output, "fml:handshake");
ProtocolUtils.writeVarInt(output, buf.readableBytes());
output.writeBytes(buf);
return new LoginPluginResponse(id,true,output);
}
@Override
public int getId() {
return id;
}
public List<String> getMods() {
return mods;
}
public Map<ChannelIdentifier, String> getChannels() {
return channels;
}
}

View File

@ -0,0 +1,45 @@
package org.adde0109.ambassador.forge.pipeline;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.packet.LoginPluginResponse;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageDecoder;
import org.adde0109.ambassador.forge.packet.GenericForgeLoginWrapperPacket;
import org.adde0109.ambassador.forge.packet.ModListReplyPacket;
import java.util.ArrayList;
import java.util.List;
public class ForgeLoginWrapperDecoder extends MessageToMessageDecoder<LoginPluginResponse> {
private final List<Integer> loginWrapperIDs = new ArrayList<>();
@Override
protected void decode(ChannelHandlerContext ctx, LoginPluginResponse msg, List<Object> out) throws Exception {
ByteBuf buf = msg.content();
if (!loginWrapperIDs.remove((Integer) msg.getId())) {
out.add(msg.retain());
return;
}
int originalReaderIndex = msg.content().readerIndex();
String channel = ProtocolUtils.readString(buf);
if (!channel.equals("fml:handshake")) {
buf.readerIndex(originalReaderIndex);
out.add(new GenericForgeLoginWrapperPacket(buf.retain(), msg.getId()));
return;
}
int length = ProtocolUtils.readVarInt(buf);
int packetID = ProtocolUtils.readVarInt(buf);
if (packetID == 2) {
out.add(ModListReplyPacket.read(msg));
} else {
buf.readerIndex(originalReaderIndex);
out.add(new GenericForgeLoginWrapperPacket(buf.retain(), msg.getId()));
}
}
public void registerLoginWrapperID(int loginWrapperID) {
this.loginWrapperIDs.add(loginWrapperID);
}
}

View File

@ -0,0 +1,25 @@
package org.adde0109.ambassador.forge.pipeline;
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.ReferenceCountUtil;
import org.adde0109.ambassador.forge.packet.IForgeLoginWrapperPacket;
import org.adde0109.ambassador.forge.VelocityForgeClientConnectionPhase;
public class ForgeLoginWrapperHandler extends SimpleChannelInboundHandler<IForgeLoginWrapperPacket> {
private final ConnectedPlayer player;
public ForgeLoginWrapperHandler(ConnectedPlayer player) {
super(false);
this.player = player;
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, IForgeLoginWrapperPacket msg) throws Exception {
VelocityForgeClientConnectionPhase phase = (VelocityForgeClientConnectionPhase) player.getPhase();
phase.handle(player,msg,player.getConnectionInFlight());
}
}

View File

@ -8,10 +8,13 @@ import com.velocitypowered.api.event.connection.PostLoginEvent;
import com.velocitypowered.api.event.player.*;
import com.velocitypowered.api.proxy.server.RegisteredServer;
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
import com.velocitypowered.proxy.network.Connections;
import com.velocitypowered.proxy.protocol.StateRegistry;
import org.adde0109.ambassador.Ambassador;
import org.adde0109.ambassador.forge.ForgeConstants;
import org.adde0109.ambassador.forge.VelocityForgeClientConnectionPhase;
import org.adde0109.ambassador.velocity.client.VelocityForgeHandshakeSessionHandler;
import org.adde0109.ambassador.forge.pipeline.ForgeLoginWrapperDecoder;
import org.adde0109.ambassador.forge.pipeline.ForgeLoginWrapperHandler;
public class VelocityEventHandler {
@ -25,7 +28,16 @@ public class VelocityEventHandler {
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));
player.getConnection().eventLoop().submit(() -> {
player.getConnection().setState(StateRegistry.LOGIN);
player.getConnection().getChannel().pipeline().addBefore(
Connections.HANDLER,
ForgeConstants.FORGE_HANDSHAKE_DECODER, new ForgeLoginWrapperDecoder());
player.getConnection().getChannel().pipeline().addAfter(
ForgeConstants.FORGE_HANDSHAKE_DECODER,
ForgeConstants.FORGE_HANDSHAKE_HANDLER, new ForgeLoginWrapperHandler(player));
});
}
continuation.resume();
}
@ -34,8 +46,8 @@ public class VelocityEventHandler {
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));
//VelocityForgeHandshakeSessionHandler sessionHandler = new VelocityForgeHandshakeSessionHandler(player.getConnection().getSessionHandler(), player);
//player.getConnection().eventLoop().submit(() -> player.getConnection().setSessionHandler(sessionHandler));
}
continuation.resume();
}

View File

@ -76,7 +76,8 @@ public class ForgeLoginSessionHandler implements MinecraftSessionHandler {
@Override
public void disconnected() {
if (!serverConnection.getPlayer().getPhase().consideredComplete()) {
//TODO:Change this
if (!serverConnection.getPhase().consideredComplete()) {
serverConnection.getPlayer().handleConnectionException(serverConnection.getServer(),
Disconnect.create(Component.text("Probably mismatched mods"),
serverConnection.getPlayer().getProtocolVersion()),false);

View File

@ -7,7 +7,7 @@ import io.netty.buffer.ByteBuf;
import org.adde0109.ambassador.forge.VelocityForgeClientConnectionPhase;
public class VelocityForgeHandshakeSessionHandler implements MinecraftSessionHandler {
private final MinecraftSessionHandler original;
/*private final MinecraftSessionHandler original;
private final ConnectedPlayer player;
public VelocityForgeHandshakeSessionHandler(MinecraftSessionHandler original, ConnectedPlayer player) {
@ -37,4 +37,5 @@ public class VelocityForgeHandshakeSessionHandler implements MinecraftSessionHan
public MinecraftSessionHandler getOriginal() {
return this.original;
}
*/
}