From fa1e5fa57181635fffbad4499d0a9545e2dc0c24 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Wed, 1 Oct 2025 20:56:19 -0400 Subject: [PATCH] Works on 1.21.9 --- .../floodgate/util/ReflectionUtils.java | 9 +++++ .../addon/data/SpigotDataHandler.java | 9 ++--- .../pluginmessage/SpigotSkinApplier.java | 5 +-- .../geysermc/floodgate/util/ClassNames.java | 3 +- .../util/SpigotVersionSpecificMethods.java | 38 +++++++++++++------ 5 files changed, 42 insertions(+), 22 deletions(-) diff --git a/core/src/main/java/org/geysermc/floodgate/util/ReflectionUtils.java b/core/src/main/java/org/geysermc/floodgate/util/ReflectionUtils.java index 709dfd43..98aa8f97 100644 --- a/core/src/main/java/org/geysermc/floodgate/util/ReflectionUtils.java +++ b/core/src/main/java/org/geysermc/floodgate/util/ReflectionUtils.java @@ -151,6 +151,15 @@ public final class ReflectionUtils { } } + @Nullable + public static T newInstanceOrThrow(Constructor constructor, Object... parameters) { + try { + return constructor.newInstance(parameters); + } catch (IllegalAccessException | InstantiationException | InvocationTargetException e) { + throw new RuntimeException(e); + } + } + @Nullable public static T newInstance(Constructor constructor, Object... parameters) { try { diff --git a/spigot/src/main/java/org/geysermc/floodgate/addon/data/SpigotDataHandler.java b/spigot/src/main/java/org/geysermc/floodgate/addon/data/SpigotDataHandler.java index 1e70c788..8efa4b41 100644 --- a/spigot/src/main/java/org/geysermc/floodgate/addon/data/SpigotDataHandler.java +++ b/spigot/src/main/java/org/geysermc/floodgate/addon/data/SpigotDataHandler.java @@ -28,8 +28,6 @@ package org.geysermc.floodgate.addon.data; import static org.geysermc.floodgate.util.ReflectionUtils.getCastedValue; import static org.geysermc.floodgate.util.ReflectionUtils.setValue; -import com.google.common.collect.Multimap; -import com.google.common.collect.MultimapBuilder; import com.mojang.authlib.GameProfile; import com.mojang.authlib.properties.Property; import io.netty.channel.Channel; @@ -181,16 +179,15 @@ public final class SpigotDataHandler extends CommonDataHandler { } } - Multimap properties = MultimapBuilder.hashKeys().arrayListValues().build(); - + Property texturesProperty = null; if (!player.isLinked()) { // Otherwise game server will try to fetch the skin from Mojang. // No need to worry that this overrides proxy data, because those won't reach this // method / are already removed (in the case of username validation) - properties.put("textures", DEFAULT_TEXTURE_PROPERTY); + texturesProperty = DEFAULT_TEXTURE_PROPERTY; } GameProfile gameProfile = versionSpecificMethods.createGameProfile( - player.getCorrectUniqueId(), player.getCorrectUsername(), properties); + player.getCorrectUniqueId(), player.getCorrectUsername(), texturesProperty); // we have to fake the offline player (login) cycle diff --git a/spigot/src/main/java/org/geysermc/floodgate/pluginmessage/SpigotSkinApplier.java b/spigot/src/main/java/org/geysermc/floodgate/pluginmessage/SpigotSkinApplier.java index a402355e..249e61b7 100644 --- a/spigot/src/main/java/org/geysermc/floodgate/pluginmessage/SpigotSkinApplier.java +++ b/spigot/src/main/java/org/geysermc/floodgate/pluginmessage/SpigotSkinApplier.java @@ -99,10 +99,9 @@ public final class SpigotSkinApplier implements SkinApplier { } private void replaceSkin(Player player, FloodgatePlayer floodgatePlayer, SkinData skinData) { - Multimap newProperties = MultimapBuilder.hashKeys().arrayListValues().build(); - newProperties.put("textures", new Property("textures", skinData.value(), skinData.signature())); + Property skinProperty = new Property("textures", skinData.value(), skinData.signature()); GameProfile profile = versionSpecificMethods.createGameProfile(floodgatePlayer.getCorrectUniqueId(), - floodgatePlayer.getCorrectUsername(), newProperties); + floodgatePlayer.getCorrectUsername(), skinProperty); Object entityHuman = ReflectionUtils.invoke(player, ClassNames.GET_ENTITY_HUMAN_METHOD); ReflectionUtils.setValue(entityHuman, ClassNames.GAME_PROFILE_FIELD, profile); } diff --git a/spigot/src/main/java/org/geysermc/floodgate/util/ClassNames.java b/spigot/src/main/java/org/geysermc/floodgate/util/ClassNames.java index 4fce39a9..45df8c47 100644 --- a/spigot/src/main/java/org/geysermc/floodgate/util/ClassNames.java +++ b/spigot/src/main/java/org/geysermc/floodgate/util/ClassNames.java @@ -124,7 +124,8 @@ public class ClassNames { Class entityHumanClass = getClassOrFallback("net.minecraft.world.entity.player.EntityHuman", "net.minecraft.world.entity.player.Player"); checkNotNull(entityHumanClass, "EntityHuman class"); - GAME_PROFILE_FIELD = getField(entityHumanClass, "gameProfile"); + // Since 1.21.9: Spigot obfuscates field name + GAME_PROFILE_FIELD = getFieldOfType(entityHumanClass, GameProfile.class); checkNotNull(GAME_PROFILE_FIELD, "EntityHuman.gameProfile field"); // SpigotInjector diff --git a/spigot/src/main/java/org/geysermc/floodgate/util/SpigotVersionSpecificMethods.java b/spigot/src/main/java/org/geysermc/floodgate/util/SpigotVersionSpecificMethods.java index a0bc1b29..0d285f72 100644 --- a/spigot/src/main/java/org/geysermc/floodgate/util/SpigotVersionSpecificMethods.java +++ b/spigot/src/main/java/org/geysermc/floodgate/util/SpigotVersionSpecificMethods.java @@ -25,12 +25,13 @@ package org.geysermc.floodgate.util; -import com.google.common.collect.Multimap; import com.mojang.authlib.GameProfile; import com.mojang.authlib.properties.Property; import com.mojang.authlib.properties.PropertyMap; import java.lang.reflect.Constructor; import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; import java.util.UUID; import java.util.concurrent.TimeUnit; import org.bukkit.entity.Player; @@ -49,6 +50,7 @@ public final class SpigotVersionSpecificMethods { private static final Method NEW_GAME_PROFILE_PROPERTIES; private static final Constructor RECORD_GAME_PROFILE_CONSTRUCTOR; private static final Constructor IMMUTABLE_PROPERTY_MAP_CONSTRUCTOR; + private static final Method MULTIMAP_FROM_MAP; static { GET_SPIGOT = ReflectionUtils.getMethod(Player.class, "spigot"); @@ -61,11 +63,16 @@ public final class SpigotVersionSpecificMethods { NEW_PROPERTY_VALUE = ReflectionUtils.getMethod(Property.class, "value"); NEW_PROPERTY_SIGNATURE = ReflectionUtils.getMethod(Property.class, "signature"); - NEW_GAME_PROFILE_PROPERTIES = ReflectionUtils.getMethod(GameProfile.class, "properties"); - RECORD_GAME_PROFILE_CONSTRUCTOR = ReflectionUtils.getConstructor(GameProfile.class, true, UUID.class, String.class, PropertyMap.class); - // TODO have to do this here because if we get constructor using Multimap.class we try to look for one that takes our - // TODO relocated Multimap, which doesn't exist - IMMUTABLE_PROPERTY_MAP_CONSTRUCTOR = (Constructor) PropertyMap.class.getConstructors()[0]; + NEW_GAME_PROFILE_PROPERTIES = ReflectionUtils.getMethod( + GameProfile.class, "properties"); + RECORD_GAME_PROFILE_CONSTRUCTOR = ReflectionUtils.getConstructor( + GameProfile.class, true, UUID.class, String.class, PropertyMap.class); + IMMUTABLE_PROPERTY_MAP_CONSTRUCTOR = (Constructor) + PropertyMap.class.getConstructors()[0]; + // Avoid relocation for this class. + Class multimaps = ReflectionUtils.getClass(String.join(".", "com", + "google", "common", "collect", "Multimaps")); + MULTIMAP_FROM_MAP = ReflectionUtils.getMethod(multimaps, "forMap", Map.class); } private final SpigotPlugin plugin; @@ -74,15 +81,22 @@ public final class SpigotVersionSpecificMethods { this.plugin = plugin; } - public GameProfile createGameProfile(UUID uuid, String name, Multimap properties) { + public GameProfile createGameProfile(UUID uuid, String name, Property texturesProperty) { if (RECORD_GAME_PROFILE_CONSTRUCTOR != null && IMMUTABLE_PROPERTY_MAP_CONSTRUCTOR != null) { - // TODO this breaks, as the passed properties to the IMMUTABLE_PROPERTY_MAP_CONSTRUCTOR - // TODO is of our relocated Multimap, and not the one PropertyMap uses - return ReflectionUtils.newInstance(RECORD_GAME_PROFILE_CONSTRUCTOR, uuid, name, - ReflectionUtils.newInstance(IMMUTABLE_PROPERTY_MAP_CONSTRUCTOR, properties)); + if (texturesProperty != null) { + Map properties = new HashMap<>(); + properties.put("textures", texturesProperty); + Object multimap = ReflectionUtils.invoke(null, MULTIMAP_FROM_MAP, properties); + return ReflectionUtils.newInstanceOrThrow(RECORD_GAME_PROFILE_CONSTRUCTOR, uuid, + name, + ReflectionUtils.newInstanceOrThrow(IMMUTABLE_PROPERTY_MAP_CONSTRUCTOR, + multimap)); + } } GameProfile profile = new GameProfile(uuid, name); - profile.getProperties().putAll(properties); + if (texturesProperty != null) { + profile.getProperties().put("textures", texturesProperty); + } return profile; }