Switch to Spigot's PlayerProfile on HeadGetter (#2231)

* Switch to Spigot's PlayerProfile on HeadGetter

Remove AuthLib

* forgot to set meta back

* should check if the texture is not empty
This commit is contained in:
Huynh Tien 2023-11-24 23:15:20 +07:00 committed by GitHub
parent 1cf7ccbb99
commit 5de7302469
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 60 additions and 46 deletions

View File

@ -206,13 +206,6 @@
<version>${paper.version}</version> <version>${paper.version}</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<!-- AuthLib. Used for Head Getter. -->
<dependency>
<groupId>com.mojang</groupId>
<artifactId>authlib</artifactId>
<version>3.16.29</version>
<scope>provided</scope>
</dependency>
<!-- Metrics --> <!-- Metrics -->
<dependency> <dependency>
<groupId>org.bstats</groupId> <groupId>org.bstats</groupId>

View File

@ -1,16 +1,12 @@
package world.bentobox.bentobox.util.heads; package world.bentobox.bentobox.util.heads;
import java.lang.reflect.Field;
import java.util.UUID; import java.util.UUID;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import com.mojang.authlib.GameProfile; import org.bukkit.inventory.meta.SkullMeta;
import com.mojang.authlib.properties.Property; import org.bukkit.profile.PlayerProfile;
import world.bentobox.bentobox.BentoBox;
/** /**
@ -35,9 +31,9 @@ public class HeadCache
private final UUID userId; 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 * 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. * Constructor HeadCache creates a new HeadCache instance.
* *
* @param userName of type String * @param userName of type String
* @param userId of type String * @param userId of type String
* @param encodedTextureLink 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. * Constructor HeadCache creates a new HeadCache instance.
* *
* @param userName of type String * @param userName of type String
* @param userId of type UUID * @param userId of type UUID
* @param encodedTextureLink of type String * @param playerProfile of type String
* @param timestamp of type long * @param timestamp of type long
*/ */
public HeadCache(String userName, public HeadCache(String userName,
UUID userId, UUID userId,
String encodedTextureLink, PlayerProfile playerProfile,
long timestamp) long timestamp)
{ {
this.userName = userName; this.userName = userName;
this.encodedTextureLink = encodedTextureLink; this.playerProfile = playerProfile;
this.userId = userId; this.userId = userId;
this.timestamp = timestamp; this.timestamp = timestamp;
} }
@ -99,26 +95,13 @@ public class HeadCache
public ItemStack getPlayerHead() public ItemStack getPlayerHead()
{ {
ItemStack item = new ItemStack(Material.PLAYER_HEAD); ItemStack item = new ItemStack(Material.PLAYER_HEAD);
ItemMeta meta = item.getItemMeta(); SkullMeta meta = (SkullMeta) item.getItemMeta();
// Set correct Skull texture // 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); meta.setOwnerProfile(this.playerProfile);
profile.getProperties().put("textures", item.setItemMeta(meta);
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");
}
} }
return item; return item;

View File

@ -4,6 +4,7 @@ import java.io.BufferedReader;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
import java.net.URL; import java.net.URL;
import java.util.Base64;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map; import java.util.Map;
@ -14,6 +15,7 @@ import java.util.concurrent.LinkedBlockingQueue;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.profile.PlayerProfile;
import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jdt.annotation.Nullable;
@ -165,7 +167,7 @@ public class HeadGetter {
// Create new cache object. // Create new cache object.
cache = new HeadCache(userName, cache = new HeadCache(userName,
playerSkin.getKey(), playerSkin.getKey(),
playerSkin.getValue()); HeadGetter.createProfile(userName, playerSkin.getKey(), playerSkin.getValue()));
} }
else else
{ {
@ -178,14 +180,14 @@ public class HeadGetter {
// Create new cache object. // Create new cache object.
cache = new HeadCache(userName, cache = new HeadCache(userName,
userId, userId,
HeadGetter.getTextureFromUUID(userId)); HeadGetter.createProfile(userName, userId, HeadGetter.getTextureFromUUID(userId)));
} }
// Save in cache // Save in cache
HeadGetter.cachedHeads.put(userName, cache); HeadGetter.cachedHeads.put(userName, cache);
// Tell requesters the head came in, but only if the texture is usable. // 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)) for (HeadRequester req : HeadGetter.headRequesters.get(userName))
{ {
@ -397,4 +399,40 @@ public class HeadGetter {
return returnValue; 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;
}
} }