Created
September 9, 2021 03:17
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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