From 62296d8c5c1f520ef82d4fed0ec15d9544d11ca3 Mon Sep 17 00:00:00 2001 From: Tim203 Date: Mon, 22 Jan 2024 20:51:10 +0100 Subject: [PATCH] Made Floodgate dev compatible with latest Spigot & Velocity changes --- .../spigot/addon/data/SpigotDataHandler.java | 25 +++++--- .../floodgate/spigot/util/ClassNames.java | 63 +++++++++++++------ .../floodgate/spigot/util/MappingUtils.java | 13 ++++ .../addon/data/VelocityProxyDataHandler.java | 11 +++- .../addon/data/VelocityServerDataHandler.java | 6 +- 5 files changed, 87 insertions(+), 31 deletions(-) diff --git a/spigot/base/src/main/java/org/geysermc/floodgate/spigot/addon/data/SpigotDataHandler.java b/spigot/base/src/main/java/org/geysermc/floodgate/spigot/addon/data/SpigotDataHandler.java index 82bfeb0e..07d15674 100644 --- a/spigot/base/src/main/java/org/geysermc/floodgate/spigot/addon/data/SpigotDataHandler.java +++ b/spigot/base/src/main/java/org/geysermc/floodgate/spigot/addon/data/SpigotDataHandler.java @@ -181,29 +181,34 @@ public final class SpigotDataHandler extends CommonNettyDataHandler { } } - // set the player his GameProfile, we can't change the username without this GameProfile gameProfile = new GameProfile(connection.javaUuid(), connection.javaUsername()); setValue(packetListener, ClassNames.LOGIN_PROFILE, gameProfile); // we have to fake the offline player (login) cycle - // just like on Spigot: - - Object loginHandler = ClassNames.LOGIN_HANDLER_CONSTRUCTOR.newInstance(packetListener); if (ClassNames.IS_PRE_1_20_2) { // 1.20.1 and below - - // LoginListener#initUUID - // new LoginHandler().fireEvents(); - + // - set profile, otherwise the username doesn't change + // - LoginListener#initUUID + // - new LoginHandler().fireEvents(); // and the tick of LoginListener will do the rest + Object loginHandler = ClassNames.LOGIN_HANDLER_CONSTRUCTOR.newInstance(packetListener); + setValue(packetListener, ClassNames.LOGIN_PROFILE, gameProfile); ClassNames.INIT_UUID.invoke(packetListener); ClassNames.FIRE_LOGIN_EVENTS.invoke(loginHandler); - } else { - // 1.20.2 and above we directly register the profile + } else if (!ClassNames.IS_POST_LOGIN_HANDLER) { + // 1.20.2 until somewhere in 1.20.4 we can directly register the profile + Object loginHandler = ClassNames.LOGIN_HANDLER_CONSTRUCTOR.newInstance(packetListener); ClassNames.FIRE_LOGIN_EVENTS_GAME_PROFILE.invoke(loginHandler, gameProfile); + } else { + // somewhere during 1.20.4 md_5 moved stuff to CraftBukkit + + // LoginListener#callPlayerPreLoginEvents(GameProfile) + // LoginListener#startClientVerification(GameProfile) + ClassNames.CALL_PLAYER_PRE_LOGIN_EVENTS.invoke(packetListener, gameProfile); + ClassNames.START_CLIENT_VERIFICATION.invoke(packetListener, gameProfile); } ctx.pipeline().remove(this); diff --git a/spigot/base/src/main/java/org/geysermc/floodgate/spigot/util/ClassNames.java b/spigot/base/src/main/java/org/geysermc/floodgate/spigot/util/ClassNames.java index 12aba60a..dc9fe18d 100644 --- a/spigot/base/src/main/java/org/geysermc/floodgate/spigot/util/ClassNames.java +++ b/spigot/base/src/main/java/org/geysermc/floodgate/spigot/util/ClassNames.java @@ -36,6 +36,8 @@ import static org.geysermc.floodgate.core.util.ReflectionUtils.getValue; import static org.geysermc.floodgate.core.util.ReflectionUtils.invoke; import static org.geysermc.floodgate.core.util.ReflectionUtils.makeAccessible; import static org.geysermc.floodgate.spigot.util.MappingUtils.classFor; +import static org.geysermc.floodgate.spigot.util.MappingUtils.fieldFor; +import static org.geysermc.floodgate.spigot.util.MappingUtils.methodFor; import com.mojang.authlib.GameProfile; import io.netty.channel.ChannelHandlerContext; @@ -58,11 +60,10 @@ public class ClassNames { public static final Class HANDSHAKE_PACKET; public static final Class LOGIN_START_PACKET; public static final Class LOGIN_LISTENER; - public static final Class LOGIN_HANDLER; @Nullable public static final Class CLIENT_INTENT; public static final Constructor CRAFT_OFFLINE_PLAYER_CONSTRUCTOR; - public static final Constructor LOGIN_HANDLER_CONSTRUCTOR; + @Nullable public static final Constructor LOGIN_HANDLER_CONSTRUCTOR; @Nullable public static final Constructor HANDSHAKE_PACKET_CONSTRUCTOR; public static final Field SOCKET_ADDRESS; @@ -83,13 +84,18 @@ public class ClassNames { @Nullable public static final Method INIT_UUID; @Nullable public static final Method FIRE_LOGIN_EVENTS; @Nullable public static final Method FIRE_LOGIN_EVENTS_GAME_PROFILE; + @Nullable public static final Method CALL_PLAYER_PRE_LOGIN_EVENTS; + @Nullable public static final Method START_CLIENT_VERIFICATION; public static final Field BUNGEE; public static final boolean IS_FOLIA; public static final boolean IS_PRE_1_20_2; + public static final boolean IS_POST_LOGIN_HANDLER; static { + // ahhhhhhh, this class should really be reworked at this point + // SpigotSkinApplier Class craftPlayerClass = MappingUtils.craftbukkitClass("entity.CraftPlayer"); GET_PROFILE_METHOD = getMethod(craftPlayerClass, "getProfile"); @@ -154,6 +160,14 @@ public class ClassNames { INIT_UUID = getMethod(LOGIN_LISTENER, "initUUID"); IS_PRE_1_20_2 = INIT_UUID != null; + // somewhere during 1.20.4 md_5 moved PreLogin logic to CraftBukkit + CALL_PLAYER_PRE_LOGIN_EVENTS = getMethod( + LOGIN_LISTENER, + "callPlayerPreLoginEvents", + GameProfile.class + ); + IS_POST_LOGIN_HANDLER = CALL_PLAYER_PRE_LOGIN_EVENTS != null; + if (IS_PRE_1_20_2) { Class packetListenerClass = classFor("net.minecraft.network", "PacketListener"); PACKET_LISTENER = getFieldOfType(networkManager, packetListenerClass); @@ -161,28 +175,41 @@ public class ClassNames { // We get the field by name on 1.20.2+ as there are now multiple fields of this type in network manager // PacketListener packetListener of NetworkManager - PACKET_LISTENER = MappingUtils.fieldFor(networkManager, "packetListener", "q"); + PACKET_LISTENER = fieldFor(networkManager, "packetListener", "q"); makeAccessible(PACKET_LISTENER); } requireNonNull(PACKET_LISTENER, "Packet listener"); - LOGIN_HANDLER = classFor( - "net.minecraft.server.network", - "ServerLoginPacketListenerImpl$LoginHandler", - "LoginListener$LoginHandler" - ); + if (IS_POST_LOGIN_HANDLER) { + makeAccessible(CALL_PLAYER_PRE_LOGIN_EVENTS); - LOGIN_HANDLER_CONSTRUCTOR = - getConstructor(LOGIN_HANDLER, true, LOGIN_LISTENER); - requireNonNull(LOGIN_HANDLER_CONSTRUCTOR, "LoginHandler constructor"); + START_CLIENT_VERIFICATION = methodFor(LOGIN_LISTENER, "startClientVerification", "b", GameProfile.class); + requireNonNull(START_CLIENT_VERIFICATION, "startClientVerification"); + makeAccessible(START_CLIENT_VERIFICATION); - FIRE_LOGIN_EVENTS = getMethod(LOGIN_HANDLER, "fireEvents"); + LOGIN_HANDLER_CONSTRUCTOR = null; + FIRE_LOGIN_EVENTS = null; + FIRE_LOGIN_EVENTS_GAME_PROFILE = null; + } else { + Class loginHandler = classFor( + "net.minecraft.server.network", + "ServerLoginPacketListenerImpl$LoginHandler", + "LoginListener$LoginHandler" + ); - // LoginHandler().fireEvents(GameProfile) - FIRE_LOGIN_EVENTS_GAME_PROFILE = getMethod(LOGIN_HANDLER, "fireEvents", GameProfile.class); - requireNonNull(FIRE_LOGIN_EVENTS, FIRE_LOGIN_EVENTS_GAME_PROFILE, - "fireEvents from LoginHandler", "fireEvents(GameProfile) from LoginHandler"); + LOGIN_HANDLER_CONSTRUCTOR = + getConstructor(loginHandler, true, LOGIN_LISTENER); + requireNonNull(LOGIN_HANDLER_CONSTRUCTOR, "LoginHandler constructor"); + FIRE_LOGIN_EVENTS = getMethod(loginHandler, "fireEvents"); + + // LoginHandler().fireEvents(GameProfile) + FIRE_LOGIN_EVENTS_GAME_PROFILE = getMethod(loginHandler, "fireEvents", GameProfile.class); + requireNonNull(FIRE_LOGIN_EVENTS, FIRE_LOGIN_EVENTS_GAME_PROFILE, + "fireEvents from LoginHandler", "fireEvents(GameProfile) from LoginHandler"); + + START_CLIENT_VERIFICATION = null; + } PAPER_DISABLE_USERNAME_VALIDATION = getField(LOGIN_LISTENER, "iKnowThisMayNotBeTheBestIdeaButPleaseDisableUsernameValidation"); @@ -249,11 +276,11 @@ public class ClassNames { String.class, int.class, CLIENT_INTENT); requireNonNull(HANDSHAKE_PACKET_CONSTRUCTOR, "Handshake packet constructor"); - HANDSHAKE_PORT = MappingUtils.fieldFor(HANDSHAKE_PACKET, "port", "a"); + HANDSHAKE_PORT = fieldFor(HANDSHAKE_PACKET, "port", "a"); requireNonNull(HANDSHAKE_PORT, "Handshake port"); makeAccessible(HANDSHAKE_PORT); - HANDSHAKE_PROTOCOL = MappingUtils.fieldFor(HANDSHAKE_PACKET, "protocolVersion", "c"); + HANDSHAKE_PROTOCOL = fieldFor(HANDSHAKE_PACKET, "protocolVersion", "c"); requireNonNull(HANDSHAKE_PROTOCOL, "Handshake protocol"); makeAccessible(HANDSHAKE_PROTOCOL); diff --git a/spigot/base/src/main/java/org/geysermc/floodgate/spigot/util/MappingUtils.java b/spigot/base/src/main/java/org/geysermc/floodgate/spigot/util/MappingUtils.java index 20749c68..b6dffb33 100644 --- a/spigot/base/src/main/java/org/geysermc/floodgate/spigot/util/MappingUtils.java +++ b/spigot/base/src/main/java/org/geysermc/floodgate/spigot/util/MappingUtils.java @@ -1,6 +1,7 @@ package org.geysermc.floodgate.spigot.util; import java.lang.reflect.Field; +import java.lang.reflect.Method; import org.bukkit.Bukkit; import org.checkerframework.checker.nullness.qual.Nullable; import org.geysermc.floodgate.core.util.ReflectionUtils; @@ -48,6 +49,18 @@ public class MappingUtils { throw new IllegalStateException(genericMessage("field " + mojangName + " for class " + clazz)); } + public static Method methodFor(Class clazz, String mojangName, String spigotName, Class... args) { + var mojmap = ReflectionUtils.getMethod(clazz, mojangName, args); + if (mojmap != null) { + return mojmap; + } + var spigot = ReflectionUtils.getMethod(clazz, spigotName, args); + if (spigot != null) { + return spigot; + } + throw new IllegalStateException(genericMessage("method " + mojangName + " for class " + clazz)); + } + private static String genericMessage(String specific) { return "Could not find " + specific + ". What server software are you using?"; } diff --git a/velocity/base/src/main/java/org/geysermc/floodgate/velocity/addon/data/VelocityProxyDataHandler.java b/velocity/base/src/main/java/org/geysermc/floodgate/velocity/addon/data/VelocityProxyDataHandler.java index ed73f8f2..2cbaf65d 100644 --- a/velocity/base/src/main/java/org/geysermc/floodgate/velocity/addon/data/VelocityProxyDataHandler.java +++ b/velocity/base/src/main/java/org/geysermc/floodgate/velocity/addon/data/VelocityProxyDataHandler.java @@ -27,6 +27,7 @@ package org.geysermc.floodgate.velocity.addon.data; import static java.util.Objects.requireNonNull; import static org.geysermc.floodgate.core.util.ReflectionUtils.getCastedValue; +import static org.geysermc.floodgate.core.util.ReflectionUtils.getClassOrFallbackPrefixed; import static org.geysermc.floodgate.core.util.ReflectionUtils.getField; import static org.geysermc.floodgate.core.util.ReflectionUtils.getMethodByName; import static org.geysermc.floodgate.core.util.ReflectionUtils.getPrefixedClass; @@ -65,7 +66,10 @@ public final class VelocityProxyDataHandler extends CommonNettyDataHandler { HANDSHAKE = getField(iic, "handshake"); requireNonNull(HANDSHAKE, "Handshake field cannot be null"); - HANDSHAKE_PACKET = getPrefixedClass("protocol.packet.Handshake"); + HANDSHAKE_PACKET = getClassOrFallbackPrefixed( + "protocol.packet.HandshakePacket", + "protocol.packet.Handshake" + ); requireNonNull(HANDSHAKE_PACKET, "Handshake packet class cannot be null"); HANDSHAKE_SERVER_ADDRESS = getField(HANDSHAKE_PACKET, "serverAddress"); @@ -75,7 +79,10 @@ public final class VelocityProxyDataHandler extends CommonNettyDataHandler { REMOTE_ADDRESS = getField(minecraftConnection, "remoteAddress"); requireNonNull(REMOTE_ADDRESS, "remoteAddress cannot be null"); - SERVER_LOGIN_PACKET = getPrefixedClass("protocol.packet.ServerLogin"); + SERVER_LOGIN_PACKET = getClassOrFallbackPrefixed( + "protocol.packet.ServerLoginPacket", + "protocol.packet.ServerLogin" + ); requireNonNull(SERVER_LOGIN_PACKET, "ServerLogin packet class cannot be null"); diff --git a/velocity/base/src/main/java/org/geysermc/floodgate/velocity/addon/data/VelocityServerDataHandler.java b/velocity/base/src/main/java/org/geysermc/floodgate/velocity/addon/data/VelocityServerDataHandler.java index 277bb0ff..dcc68d5c 100644 --- a/velocity/base/src/main/java/org/geysermc/floodgate/velocity/addon/data/VelocityServerDataHandler.java +++ b/velocity/base/src/main/java/org/geysermc/floodgate/velocity/addon/data/VelocityServerDataHandler.java @@ -28,6 +28,7 @@ package org.geysermc.floodgate.velocity.addon.data; import static java.util.Objects.requireNonNull; import static org.geysermc.floodgate.core.util.ReflectionUtils.castedInvoke; import static org.geysermc.floodgate.core.util.ReflectionUtils.getCastedValue; +import static org.geysermc.floodgate.core.util.ReflectionUtils.getClassOrFallbackPrefixed; import static org.geysermc.floodgate.core.util.ReflectionUtils.getField; import static org.geysermc.floodgate.core.util.ReflectionUtils.getMethod; import static org.geysermc.floodgate.core.util.ReflectionUtils.getPrefixedClass; @@ -60,7 +61,10 @@ public final class VelocityServerDataHandler extends ChannelOutboundHandlerAdapt private static final Method GET_PLAYER; static { - HANDSHAKE_PACKET = getPrefixedClass("protocol.packet.Handshake"); + HANDSHAKE_PACKET = getClassOrFallbackPrefixed( + "protocol.packet.HandshakePacket", + "protocol.packet.Handshake" + ); requireNonNull(HANDSHAKE_PACKET, "Handshake packet class cannot be null"); HANDSHAKE_ADDRESS = getField(HANDSHAKE_PACKET, "serverAddress");