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 7b87ead2..158aeb6b 100644 --- a/core/src/main/java/org/geysermc/floodgate/util/ReflectionUtils.java +++ b/core/src/main/java/org/geysermc/floodgate/util/ReflectionUtils.java @@ -30,6 +30,7 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.util.Arrays; import javax.annotation.Nullable; import lombok.Getter; import lombok.Setter; @@ -135,31 +136,31 @@ public final class ReflectionUtils { return clazz; } - public static Class getClassOrFallback(String className, String fallbackClassName1, String fallbackClassName2) { - Class clazz = getClassSilently(className); - - if (clazz != null) { - if (Constants.DEBUG_MODE) { - System.out.println("Found class (primary): " + clazz.getName()); + public static Class getClassOrFallback(String... classNames) { + for (String className : classNames) { + Class clazz = getClassSilently(className); + if (clazz != null) { + if (Constants.DEBUG_MODE) { + System.out.println("Found class: " + clazz.getName() + " for choices: " + + Arrays.toString(classNames)); + } + return clazz; } - return clazz; } - clazz = getClassSilently(fallbackClassName1); + throw new IllegalStateException("Could not find class between these choices: " + + Arrays.toString(classNames)); + } - if (clazz != null) { - if (Constants.DEBUG_MODE) { - System.out.println("Found class (fallback1): " + clazz.getName()); + @Nullable + public static Class getClassOrFallbackSilently(String... classNames) { + for (String className : classNames) { + Class clazz = getClassSilently(className); + if (clazz != null) { + return clazz; } - return clazz; } - - // do throw an exception when both classes couldn't be found - clazz = ReflectionUtils.getClassOrThrow(fallbackClassName2); - if (Constants.DEBUG_MODE) { - System.out.println("Found class (fallback2): " + clazz.getName()); - } - return clazz; + return null; } @Nullable 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 249e61b7..c7721c1e 100644 --- a/spigot/src/main/java/org/geysermc/floodgate/pluginmessage/SpigotSkinApplier.java +++ b/spigot/src/main/java/org/geysermc/floodgate/pluginmessage/SpigotSkinApplier.java @@ -25,12 +25,11 @@ package org.geysermc.floodgate.pluginmessage; -import com.google.common.collect.Multimap; -import com.google.common.collect.MultimapBuilder; import com.google.inject.Inject; import com.google.inject.Singleton; import com.mojang.authlib.GameProfile; import com.mojang.authlib.properties.Property; +import com.mojang.authlib.properties.PropertyMap; import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.checkerframework.checker.nullness.qual.NonNull; @@ -87,7 +86,12 @@ public final class SpigotSkinApplier implements SkinApplier { return; } - replaceSkin(player, floodgatePlayer, event.newSkin()); + if (ClassNames.GAME_PROFILE_FIELD != null) { + replaceSkin(player, floodgatePlayer, event.newSkin()); + } else { + // We're on a version with mutable GameProfiles + replaceSkinOld(profile.getProperties(), event.newSkin()); + } versionSpecificMethods.maybeSchedule(() -> { for (Player p : Bukkit.getOnlinePlayers()) { @@ -105,4 +109,10 @@ public final class SpigotSkinApplier implements SkinApplier { Object entityHuman = ReflectionUtils.invoke(player, ClassNames.GET_ENTITY_HUMAN_METHOD); ReflectionUtils.setValue(entityHuman, ClassNames.GAME_PROFILE_FIELD, profile); } + + private void replaceSkinOld(PropertyMap properties, SkinData skinData) { + properties.removeAll("textures"); + Property property = new Property("textures", skinData.value(), skinData.signature()); + properties.put("textures", property); + } } 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 191a9cdf..52be11ea 100644 --- a/spigot/src/main/java/org/geysermc/floodgate/util/ClassNames.java +++ b/spigot/src/main/java/org/geysermc/floodgate/util/ClassNames.java @@ -28,6 +28,7 @@ package org.geysermc.floodgate.util; import static org.geysermc.floodgate.util.ReflectionUtils.castedStaticBooleanValue; import static org.geysermc.floodgate.util.ReflectionUtils.getBooleanValue; import static org.geysermc.floodgate.util.ReflectionUtils.getClassOrFallback; +import static org.geysermc.floodgate.util.ReflectionUtils.getClassOrFallbackSilently; import static org.geysermc.floodgate.util.ReflectionUtils.getClassSilently; import static org.geysermc.floodgate.util.ReflectionUtils.getConstructor; import static org.geysermc.floodgate.util.ReflectionUtils.getField; @@ -77,8 +78,8 @@ public class ClassNames { public static final Method GET_PROFILE_METHOD; - public static final Method GET_ENTITY_HUMAN_METHOD; - public static final Field GAME_PROFILE_FIELD; + @Nullable public static final Method GET_ENTITY_HUMAN_METHOD; + @Nullable public static final Field GAME_PROFILE_FIELD; public static final Method LOGIN_DISCONNECT; public static final Method NETWORK_EXCEPTION_CAUGHT; @@ -122,13 +123,16 @@ public class ClassNames { checkNotNull(GET_PROFILE_METHOD, "Get profile method"); GET_ENTITY_HUMAN_METHOD = getMethod(craftPlayerClass, "getHandle"); - checkNotNull(GET_ENTITY_HUMAN_METHOD, "getHandle method"); - Class entityHumanClass = getClassOrFallback("net.minecraft.world.entity.player.EntityHuman", - "net.minecraft.world.entity.player.Player"); - checkNotNull(entityHumanClass, "EntityHuman class"); - // Since 1.21.9: Spigot obfuscates field name - GAME_PROFILE_FIELD = getFieldOfType(entityHumanClass, GameProfile.class); - checkNotNull(GAME_PROFILE_FIELD, "EntityHuman.gameProfile field"); + Class entityHumanClass = getClassOrFallbackSilently( + "net.minecraft.world.entity.player.EntityHuman", + "net.minecraft.world.entity.player.Player" + ); + if (entityHumanClass != null) { + // Spigot obfuscates field name + GAME_PROFILE_FIELD = getFieldOfType(entityHumanClass, GameProfile.class); + } else { + GAME_PROFILE_FIELD = null; + } // SpigotInjector MINECRAFT_SERVER = getClassOrFallback(