From fb91ba0c4cdb6b483895dde5934e1a87291000ae Mon Sep 17 00:00:00 2001 From: Olzie-12 Date: Fri, 20 Sep 2024 00:30:50 +0100 Subject: [PATCH 1/5] Fixed resolvable profile error for setting textures. --- .../holograms/api/utils/items/SkullUtils.java | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/main/java/eu/decentsoftware/holograms/api/utils/items/SkullUtils.java b/src/main/java/eu/decentsoftware/holograms/api/utils/items/SkullUtils.java index 2b214040..90babcbc 100644 --- a/src/main/java/eu/decentsoftware/holograms/api/utils/items/SkullUtils.java +++ b/src/main/java/eu/decentsoftware/holograms/api/utils/items/SkullUtils.java @@ -7,6 +7,7 @@ import eu.decentsoftware.holograms.api.utils.reflect.Version; import lombok.NonNull; import lombok.experimental.UtilityClass; +import org.bukkit.Bukkit; import org.bukkit.SkullType; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.ItemMeta; @@ -18,6 +19,7 @@ import java.io.BufferedReader; import java.io.InputStreamReader; +import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -41,10 +43,20 @@ public final class SkullUtils { private static Field PROFILE_FIELD; private static Method SET_PROFILE_METHOD; private static boolean INITIALIZED = false; + private static Constructor RESOLVABLE_PROFILE_CONSTRUCTOR; private static Method PROPERTY_VALUE_METHOD; private static Function VALUE_RESOLVER; + static { + try { + Class resolvableProfileClass = Class.forName("net.minecraft.world.item.component.ResolvableProfile"); + RESOLVABLE_PROFILE_CONSTRUCTOR = resolvableProfileClass.getConstructor(GameProfile.class); + } catch (ClassNotFoundException | NoSuchMethodException ignored) { + // old version, no resolvable profile class. + } + } + /** * Get the Base64 texture of the given skull ItemStack. * @@ -128,12 +140,11 @@ public static void setSkullTexture(@NonNull ItemStack itemStack, @NonNull String PropertyMap properties = profile.getProperties(); properties.put("textures", property); - if (SET_PROFILE_METHOD == null && !INITIALIZED) { try { // This method only exists in versions 1.16 and up. For older versions, we use reflection // to set the profile field directly. - SET_PROFILE_METHOD = meta.getClass().getDeclaredMethod("setProfile", GameProfile.class); + SET_PROFILE_METHOD = meta.getClass().getDeclaredMethod("setProfile", RESOLVABLE_PROFILE_CONSTRUCTOR == null ? GameProfile.class : RESOLVABLE_PROFILE_CONSTRUCTOR.getClass()); SET_PROFILE_METHOD.setAccessible(true); } catch (NoSuchMethodException e) { // Server is running an older version. @@ -142,7 +153,7 @@ public static void setSkullTexture(@NonNull ItemStack itemStack, @NonNull String } if (SET_PROFILE_METHOD != null) { - SET_PROFILE_METHOD.invoke(meta, profile); + SET_PROFILE_METHOD.invoke(meta, RESOLVABLE_PROFILE_CONSTRUCTOR == null ? profile : RESOLVABLE_PROFILE_CONSTRUCTOR.newInstance(profile)); } else { if (PROFILE_FIELD == null) { PROFILE_FIELD = meta.getClass().getDeclaredField("profile"); From fb59b57424ba34daeeecdd92e82cb98f8a88c991 Mon Sep 17 00:00:00 2001 From: Olzie-12 Date: Fri, 20 Sep 2024 00:43:26 +0100 Subject: [PATCH 2/5] use reflectionutil class --- .../holograms/api/utils/items/SkullUtils.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/eu/decentsoftware/holograms/api/utils/items/SkullUtils.java b/src/main/java/eu/decentsoftware/holograms/api/utils/items/SkullUtils.java index 90babcbc..78e041cb 100644 --- a/src/main/java/eu/decentsoftware/holograms/api/utils/items/SkullUtils.java +++ b/src/main/java/eu/decentsoftware/holograms/api/utils/items/SkullUtils.java @@ -4,6 +4,7 @@ import com.mojang.authlib.properties.Property; import com.mojang.authlib.properties.PropertyMap; import eu.decentsoftware.holograms.api.utils.Log; +import eu.decentsoftware.holograms.api.utils.reflect.ReflectionUtil; import eu.decentsoftware.holograms.api.utils.reflect.Version; import lombok.NonNull; import lombok.experimental.UtilityClass; @@ -50,9 +51,9 @@ public final class SkullUtils { static { try { - Class resolvableProfileClass = Class.forName("net.minecraft.world.item.component.ResolvableProfile"); - RESOLVABLE_PROFILE_CONSTRUCTOR = resolvableProfileClass.getConstructor(GameProfile.class); - } catch (ClassNotFoundException | NoSuchMethodException ignored) { + Class resolvableProfileClass = ReflectionUtil.getNMClass("world.item.component.ResolvableProfile"); + RESOLVABLE_PROFILE_CONSTRUCTOR = resolvableProfileClass == null ? null : resolvableProfileClass.getConstructor(GameProfile.class); + } catch ( NoSuchMethodException ignored) { // old version, no resolvable profile class. } } From aedef9a02e767ace5905ecaaaab32a08631dceb3 Mon Sep 17 00:00:00 2001 From: Olzie-12 Date: Fri, 20 Sep 2024 00:47:23 +0100 Subject: [PATCH 3/5] use getDeclaringClass --- .../decentsoftware/holograms/api/utils/items/SkullUtils.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/eu/decentsoftware/holograms/api/utils/items/SkullUtils.java b/src/main/java/eu/decentsoftware/holograms/api/utils/items/SkullUtils.java index 78e041cb..338443a9 100644 --- a/src/main/java/eu/decentsoftware/holograms/api/utils/items/SkullUtils.java +++ b/src/main/java/eu/decentsoftware/holograms/api/utils/items/SkullUtils.java @@ -44,6 +44,7 @@ public final class SkullUtils { private static Field PROFILE_FIELD; private static Method SET_PROFILE_METHOD; private static boolean INITIALIZED = false; + private static boolean INITIALIZED_SET_PROFILE = false; private static Constructor RESOLVABLE_PROFILE_CONSTRUCTOR; private static Method PROPERTY_VALUE_METHOD; @@ -145,7 +146,7 @@ public static void setSkullTexture(@NonNull ItemStack itemStack, @NonNull String try { // This method only exists in versions 1.16 and up. For older versions, we use reflection // to set the profile field directly. - SET_PROFILE_METHOD = meta.getClass().getDeclaredMethod("setProfile", RESOLVABLE_PROFILE_CONSTRUCTOR == null ? GameProfile.class : RESOLVABLE_PROFILE_CONSTRUCTOR.getClass()); + SET_PROFILE_METHOD = meta.getClass().getDeclaredMethod("setProfile", RESOLVABLE_PROFILE_CONSTRUCTOR == null ? GameProfile.class : RESOLVABLE_PROFILE_CONSTRUCTOR.getDeclaringClass()); SET_PROFILE_METHOD.setAccessible(true); } catch (NoSuchMethodException e) { // Server is running an older version. From 029ba912832dc4b01fdcec134faec28cb5280c39 Mon Sep 17 00:00:00 2001 From: Olzie-12 Date: Fri, 20 Sep 2024 00:57:34 +0100 Subject: [PATCH 4/5] implemented a fix for getting skull textures. --- .../holograms/api/utils/items/SkullUtils.java | 9 +++++--- .../api/utils/reflect/ReflectionUtil.java | 22 +++++++++++++++++++ 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/src/main/java/eu/decentsoftware/holograms/api/utils/items/SkullUtils.java b/src/main/java/eu/decentsoftware/holograms/api/utils/items/SkullUtils.java index 338443a9..bdd3373e 100644 --- a/src/main/java/eu/decentsoftware/holograms/api/utils/items/SkullUtils.java +++ b/src/main/java/eu/decentsoftware/holograms/api/utils/items/SkullUtils.java @@ -44,8 +44,9 @@ public final class SkullUtils { private static Field PROFILE_FIELD; private static Method SET_PROFILE_METHOD; private static boolean INITIALIZED = false; - private static boolean INITIALIZED_SET_PROFILE = false; + private static Constructor RESOLVABLE_PROFILE_CONSTRUCTOR; + private static Field GAME_PROFILE_FIELD_RESOLVABLE_PROFILE; private static Method PROPERTY_VALUE_METHOD; private static Function VALUE_RESOLVER; @@ -54,6 +55,9 @@ public final class SkullUtils { try { Class resolvableProfileClass = ReflectionUtil.getNMClass("world.item.component.ResolvableProfile"); RESOLVABLE_PROFILE_CONSTRUCTOR = resolvableProfileClass == null ? null : resolvableProfileClass.getConstructor(GameProfile.class); + + // find the game profile field in the resolvable profile class + GAME_PROFILE_FIELD_RESOLVABLE_PROFILE = ReflectionUtil.findField(resolvableProfileClass, GameProfile.class); } catch ( NoSuchMethodException ignored) { // old version, no resolvable profile class. } @@ -72,13 +76,12 @@ public static String getSkullTexture(@NonNull ItemStack itemStack) { if (!(meta instanceof SkullMeta)) { return null; } - if (PROFILE_FIELD == null) { PROFILE_FIELD = meta.getClass().getDeclaredField("profile"); PROFILE_FIELD.setAccessible(true); } - GameProfile profile = (GameProfile) PROFILE_FIELD.get(meta); + GameProfile profile = (GameProfile) (GAME_PROFILE_FIELD_RESOLVABLE_PROFILE == null ? PROFILE_FIELD.get(meta) : GAME_PROFILE_FIELD_RESOLVABLE_PROFILE.get(PROFILE_FIELD.get(meta))); if (profile == null) { return null; } diff --git a/src/main/java/eu/decentsoftware/holograms/api/utils/reflect/ReflectionUtil.java b/src/main/java/eu/decentsoftware/holograms/api/utils/reflect/ReflectionUtil.java index 244d2348..522dc27d 100644 --- a/src/main/java/eu/decentsoftware/holograms/api/utils/reflect/ReflectionUtil.java +++ b/src/main/java/eu/decentsoftware/holograms/api/utils/reflect/ReflectionUtil.java @@ -71,6 +71,28 @@ public static T getFieldValue(final @NotNull Object object, final @NotNull S return null; } + /** + * Find a field with in a class with a specific type. + *

+ * If the field is not found, this method will return null. + * + * @param clazz The class to get the field from. + * @param type The class type of the field. + * @return The field, or null if the field was not found. + */ + public static Field findField(Class clazz, Class type) { + if (clazz == null) return null; + + Field[] methods = clazz.getDeclaredFields(); + for (Field method : methods) { + if (!method.getType().equals(type)) continue; + + method.setAccessible(true); + return method; + } + return null; + } + /** * Get a field with the given name from the given class. *

From ea4a6684b163bc3db2d033a86dafcc1a5765ccdf Mon Sep 17 00:00:00 2001 From: Olzie-12 Date: Sun, 20 Oct 2024 22:18:56 +0100 Subject: [PATCH 5/5] Fixes NPE --- build.gradle | 2 +- .../holograms/api/utils/items/SkullUtils.java | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/build.gradle b/build.gradle index bc2f13b6..86a7eb03 100644 --- a/build.gradle +++ b/build.gradle @@ -7,7 +7,7 @@ plugins { defaultTasks "build" group "eu.decentsoftware.holograms" -version "2.8.11" +version "2.8.12" description "A lightweight yet very powerful hologram plugin with many features and configuration options." repositories { diff --git a/src/main/java/eu/decentsoftware/holograms/api/utils/items/SkullUtils.java b/src/main/java/eu/decentsoftware/holograms/api/utils/items/SkullUtils.java index bdd3373e..8b8ee62f 100644 --- a/src/main/java/eu/decentsoftware/holograms/api/utils/items/SkullUtils.java +++ b/src/main/java/eu/decentsoftware/holograms/api/utils/items/SkullUtils.java @@ -76,15 +76,17 @@ public static String getSkullTexture(@NonNull ItemStack itemStack) { if (!(meta instanceof SkullMeta)) { return null; } + // private ResolvableProfile profile; + if (PROFILE_FIELD == null) { PROFILE_FIELD = meta.getClass().getDeclaredField("profile"); PROFILE_FIELD.setAccessible(true); } + Object profileObject = PROFILE_FIELD.get(meta); + if (profileObject == null) return null; - GameProfile profile = (GameProfile) (GAME_PROFILE_FIELD_RESOLVABLE_PROFILE == null ? PROFILE_FIELD.get(meta) : GAME_PROFILE_FIELD_RESOLVABLE_PROFILE.get(PROFILE_FIELD.get(meta))); - if (profile == null) { - return null; - } + GameProfile profile = (GameProfile) (GAME_PROFILE_FIELD_RESOLVABLE_PROFILE == null ? profileObject : GAME_PROFILE_FIELD_RESOLVABLE_PROFILE.get(profileObject)); + if (profile == null) return null; if (VALUE_RESOLVER == null) { try {