Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix resolvable profile error for setting/getting textures. #245

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
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;
import org.bukkit.Bukkit;
import org.bukkit.SkullType;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
Expand All @@ -18,6 +20,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;
Expand All @@ -42,9 +45,24 @@ public final class SkullUtils {
private static Method SET_PROFILE_METHOD;
private static boolean INITIALIZED = false;

private static Constructor<?> RESOLVABLE_PROFILE_CONSTRUCTOR;
private static Field GAME_PROFILE_FIELD_RESOLVABLE_PROFILE;

private static Method PROPERTY_VALUE_METHOD;
private static Function<Property, String> VALUE_RESOLVER;

static {
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.
}
}

/**
* Get the Base64 texture of the given skull ItemStack.
*
Expand All @@ -58,16 +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) 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 {
Expand Down Expand Up @@ -128,12 +147,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.getDeclaringClass());
SET_PROFILE_METHOD.setAccessible(true);
} catch (NoSuchMethodException e) {
// Server is running an older version.
Expand All @@ -142,7 +160,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");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,28 @@ public static <T> T getFieldValue(final @NotNull Object object, final @NotNull S
return null;
}

/**
* Find a field with in a class with a specific type.
* <p>
* 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.
* <p>
Expand Down