Skip to content

Instantly share code, notes, and snippets.

@ryuuta0217
Created September 9, 2021 03:17
Show Gist options
  • Save ryuuta0217/25af867c39a27b22a40958cc586a771f to your computer and use it in GitHub Desktop.
Save ryuuta0217/25af867c39a27b22a40958cc586a771f to your computer and use it in GitHub Desktop.
A sort of memo on a solution to getting installed mods of Forge players in Bungeecord/Waterfall.
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.handler.codec.haproxy.HAProxyMessageDecoder;
import net.md_5.bungee.BungeeCord;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.config.ListenerInfo;
import net.md_5.bungee.api.event.ClientConnectEvent;
import net.md_5.bungee.api.event.PreLoginEvent;
import net.md_5.bungee.api.plugin.Listener;
import net.md_5.bungee.api.plugin.Plugin;
import net.md_5.bungee.event.EventHandler;
import net.md_5.bungee.netty.HandlerBoss;
import net.md_5.bungee.netty.PipelineUtils;
import net.md_5.bungee.protocol.*;
import net.md_5.bungee.protocol.packet.LoginPayloadRequest;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.net.SocketAddress;
public class ExampleForgeModDetectionPlugin extends Plugin implements Listener {
private static KickStringWriter legacyKicker = null;
private static boolean forgeDetection = true;
@Override
public void onEnable() {
ChannelInitializer<Channel> moddedInitializer = new ChannelInitializer<Channel>() {
@Override
protected void initChannel(Channel ch) throws Exception {
SocketAddress remoteAddress = (ch.remoteAddress() == null) ? ch.parent().localAddress() : ch.remoteAddress();
if (BungeeCord.getInstance().getConnectionThrottle() != null && BungeeCord.getInstance().getConnectionThrottle().throttle(remoteAddress)) {
ch.close();
return;
}
ListenerInfo listener = ch.attr(PipelineUtils.LISTENER).get();
if (BungeeCord.getInstance().getPluginManager().callEvent(new ClientConnectEvent(remoteAddress, listener)).isCancelled()) {
ch.close();
return;
}
PipelineUtils.BASE.initChannel(ch);
ch.pipeline().addBefore(PipelineUtils.FRAME_DECODER, PipelineUtils.LEGACY_DECODER, new LegacyDecoder());
ch.pipeline().addAfter(PipelineUtils.FRAME_DECODER, PipelineUtils.PACKET_DECODER, new MinecraftDecoder(Protocol.HANDSHAKE, true, ProxyServer.getInstance().getProtocolVersion()));
ch.pipeline().addAfter(PipelineUtils.FRAME_PREPENDER, PipelineUtils.PACKET_ENCODER, new MinecraftEncoder(Protocol.HANDSHAKE, true, ProxyServer.getInstance().getProtocolVersion()));
ch.pipeline().addBefore(PipelineUtils.FRAME_PREPENDER, PipelineUtils.LEGACY_KICKER, legacyKicker);
ch.pipeline().get(HandlerBoss.class).setHandler(new ModdedInitialHandler(BungeeCord.getInstance(), listener));
if (listener.isProxyProtocol()) {
ch.pipeline().addFirst(new HAProxyMessageDecoder());
}
}
};
if(!PipelineUtils.SERVER_CHILD.equals(moddedInitializer)) {
try {
Field serverChildField = PipelineUtils.class.getDeclaredField("SERVER_CHILD");
/* MAKE THE VALUE OF THE FINAL FIELD CHANGEABLE */
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(serverChildField, serverChildField.getModifiers() & ~Modifier.FINAL);
/* GET PRIVATE FIELD */
Field legacyKickerF = PipelineUtils.class.getDeclaredField("legacyKicker");
legacyKickerF.setAccessible(true);
ExampleForgeModDetectionPlugin.legacyKicker = (KickStringWriter) legacyKickerF.get(null);
/* MODIFY */
serverChildField.set(null, moddedInitializer);
/* CHECK */
if(PipelineUtils.SERVER_CHILD.equals(moddedInitializer)) {
System.out.println("PipelineUtils#SERVER_CHILD IS NOW MODDED!");
} else {
System.out.println("PipelineUtils#SERVER_CIHLD IS NOW DEFAULT, running without forge detection support.");
forgeDetection = false;
}
} catch (NoSuchFieldException | IllegalAccessException e) {
System.out.println("PipelineUtils#SERVER_CHILD MODDING FAILED: " + e.getLocalizedMessage());
e.printStackTrace();
}
getProxy().getPluginManager().registerListener(this, this);
}
}
@Override
public void onDisable() {
getProxy().getPluginManager().unregisterListeners(this);
}
@EventHandler
public void onPreLogin(PreLoginEvent event) {
LoginPayloadRequest lpr = new LoginPayloadRequest();
lpr.setData(new byte[] {1, 0, 0, 0});
lpr.setChannel("fml:handshake");
lpr.setId(666666);
event.getConnection().unsafe().sendPacket(lpr);
}
}
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import net.md_5.bungee.BungeeCord;
import net.md_5.bungee.api.config.ListenerInfo;
import net.md_5.bungee.connection.InitialHandler;
import net.md_5.bungee.protocol.DefinedPacket;
import net.md_5.bungee.protocol.packet.LoginPayloadResponse;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class ModdedInitialHandler extends InitialHandler {
public ModdedInitialHandler(BungeeCord bungee, ListenerInfo listener) {
super(bungee, listener);
}
@Override
public void handle(LoginPayloadResponse response) {
System.out.println("Communication Id: " + response.getId());
ByteBuf buf = Unpooled.wrappedBuffer(response.getData());
int packetId = DefinedPacket.readVarInt(buf);
System.out.println("PacketID: " + packetId);
if(packetId == 2) {
//
// [Client -> Server] Mod List Reply
//
// [Mod Count] [VarInt]
// [Mod Name] [String]
// ...
// [Channel Count] [VarInt]
// [ Name ] [String]
// [ Version] [String]
// ...
// [Registry Count] [VarInt]
// [Registry] [String]
// [ Version] [String]
// ...
//
int modCount = DefinedPacket.readVarInt(buf);
List<String> mods = new ArrayList<>();
for(int i = 0; i < modCount; i++) {
mods.add(DefinedPacket.readString(buf));
}
int channelCount = DefinedPacket.readVarInt(buf);
Map<String, String> channels = new HashMap<>();
for(int i = 0; i < channelCount; i++) {
channels.put(DefinedPacket.readString(buf), DefinedPacket.readString(buf));
}
int registryCount = DefinedPacket.readVarInt(buf);
Map<String, String> registries = new HashMap<>();
for(int i = 0; i < registryCount; i++) {
registries.put(DefinedPacket.readString(buf), DefinedPacket.readString(buf));
}
System.out.println(mods + "(" + modCount + ")");
System.out.println(channels + "(" + channelCount + ")");
System.out.println(registries + "(" + registryCount + ")");
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment