diff --git a/pom.xml b/pom.xml index 015b5f7a0..a351f034e 100644 --- a/pom.xml +++ b/pom.xml @@ -206,13 +206,6 @@ ${paper.version} provided - - - com.mojang - authlib - 3.16.29 - provided - org.bstats diff --git a/src/main/java/world/bentobox/bentobox/util/heads/HeadCache.java b/src/main/java/world/bentobox/bentobox/util/heads/HeadCache.java index 794aa7d1b..63086fddb 100644 --- a/src/main/java/world/bentobox/bentobox/util/heads/HeadCache.java +++ b/src/main/java/world/bentobox/bentobox/util/heads/HeadCache.java @@ -1,16 +1,12 @@ package world.bentobox.bentobox.util.heads; -import java.lang.reflect.Field; import java.util.UUID; import org.bukkit.Material; import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.ItemMeta; -import com.mojang.authlib.GameProfile; -import com.mojang.authlib.properties.Property; - -import world.bentobox.bentobox.BentoBox; +import org.bukkit.inventory.meta.SkullMeta; +import org.bukkit.profile.PlayerProfile; /** @@ -35,9 +31,9 @@ public class HeadCache private final UUID userId; /** - * Base64 Encoded texture link to given player skin. + * Player profile for cached head. */ - public final String encodedTextureLink; + public final PlayerProfile playerProfile; /** * Time when head was created. Setting it to 0 will result in keeping head in cache @@ -54,31 +50,31 @@ public class HeadCache /** * Constructor HeadCache creates a new HeadCache instance. * - * @param userName of type String - * @param userId of type String - * @param encodedTextureLink of type String + * @param userName of type String + * @param userId of type String + * @param playerProfile of type PlayerProfile */ - public HeadCache(String userName, UUID userId, String encodedTextureLink) + public HeadCache(String userName, UUID userId, PlayerProfile playerProfile) { - this(userName, userId, encodedTextureLink, System.currentTimeMillis()); + this(userName, userId, playerProfile, System.currentTimeMillis()); } /** * Constructor HeadCache creates a new HeadCache instance. * - * @param userName of type String - * @param userId of type UUID - * @param encodedTextureLink of type String - * @param timestamp of type long + * @param userName of type String + * @param userId of type UUID + * @param playerProfile of type String + * @param timestamp of type long */ public HeadCache(String userName, UUID userId, - String encodedTextureLink, + PlayerProfile playerProfile, long timestamp) { this.userName = userName; - this.encodedTextureLink = encodedTextureLink; + this.playerProfile = playerProfile; this.userId = userId; this.timestamp = timestamp; } @@ -99,26 +95,13 @@ public class HeadCache public ItemStack getPlayerHead() { ItemStack item = new ItemStack(Material.PLAYER_HEAD); - ItemMeta meta = item.getItemMeta(); + SkullMeta meta = (SkullMeta) item.getItemMeta(); // Set correct Skull texture - if (meta != null && this.encodedTextureLink != null && !this.encodedTextureLink.isEmpty()) + if (meta != null && this.playerProfile != null) { - GameProfile profile = new GameProfile(this.userId, this.userName); - profile.getProperties().put("textures", - new Property("textures", this.encodedTextureLink)); - - try - { - Field profileField = meta.getClass().getDeclaredField("profile"); - profileField.setAccessible(true); - profileField.set(meta, profile); - item.setItemMeta(meta); - } - catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException e) - { - BentoBox.getInstance().log("Error while creating Skull Icon"); - } + meta.setOwnerProfile(this.playerProfile); + item.setItemMeta(meta); } return item; diff --git a/src/main/java/world/bentobox/bentobox/util/heads/HeadGetter.java b/src/main/java/world/bentobox/bentobox/util/heads/HeadGetter.java index 04012b0e1..e0a47a929 100644 --- a/src/main/java/world/bentobox/bentobox/util/heads/HeadGetter.java +++ b/src/main/java/world/bentobox/bentobox/util/heads/HeadGetter.java @@ -4,6 +4,7 @@ import java.io.BufferedReader; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; +import java.util.Base64; import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -14,6 +15,7 @@ import java.util.concurrent.LinkedBlockingQueue; import java.util.stream.Collectors; import org.bukkit.Bukkit; +import org.bukkit.profile.PlayerProfile; import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; @@ -165,7 +167,7 @@ public class HeadGetter { // Create new cache object. cache = new HeadCache(userName, playerSkin.getKey(), - playerSkin.getValue()); + HeadGetter.createProfile(userName, playerSkin.getKey(), playerSkin.getValue())); } else { @@ -178,14 +180,14 @@ public class HeadGetter { // Create new cache object. cache = new HeadCache(userName, userId, - HeadGetter.getTextureFromUUID(userId)); + HeadGetter.createProfile(userName, userId, HeadGetter.getTextureFromUUID(userId))); } // Save in cache HeadGetter.cachedHeads.put(userName, cache); // Tell requesters the head came in, but only if the texture is usable. - if (cache.encodedTextureLink != null && HeadGetter.headRequesters.containsKey(userName)) + if (cache.playerProfile != null && HeadGetter.headRequesters.containsKey(userName)) { for (HeadRequester req : HeadGetter.headRequesters.get(userName)) { @@ -397,4 +399,40 @@ public class HeadGetter { return returnValue; } + + private static URL getSkinURLFromBase64(String base64) { + /* + * Base64 encoded string is in format: + * { + * "timestamp": 0, + * "profileId": "UUID", + * "profileName": "USERNAME", + * "textures": { + * "SKIN": { + * "url": "https://textures.minecraft.net/texture/TEXTURE_ID" + * }, + * "CAPE": { + * "url": "https://textures.minecraft.net/texture/TEXTURE_ID" + * } + * } + * } + */ + try { + String decoded = new String(Base64.getDecoder().decode(base64)); + JsonObject json = new Gson().fromJson(decoded, JsonObject.class); + String url = json.getAsJsonObject("textures").getAsJsonObject("SKIN").get("url").getAsString(); + return new URL(url); + } catch (Exception e) { + return null; + } + } + + private static PlayerProfile createProfile(@NonNull String userName, @NonNull UUID uuid, @Nullable String encodedBase64Texture) { + PlayerProfile profile = Bukkit.createPlayerProfile(uuid, userName); + if (encodedBase64Texture != null && !encodedBase64Texture.isEmpty()) { + URL skinURL = HeadGetter.getSkinURLFromBase64(encodedBase64Texture); + profile.getTextures().setSkin(skinURL); + } + return profile; + } }