forked from Upstream/CommandPanels
196 lines
9.0 KiB
Java
196 lines
9.0 KiB
Java
package me.rockyhawk.commandpanels.classresources;
|
|
|
|
import com.mojang.authlib.GameProfile;
|
|
import com.mojang.authlib.properties.Property;
|
|
import com.mojang.authlib.properties.PropertyMap;
|
|
import me.rockyhawk.commandpanels.CommandPanels;
|
|
import me.rockyhawk.commandpanels.ioclasses.legacy.MinecraftVersions;
|
|
import org.bukkit.Bukkit;
|
|
import org.bukkit.ChatColor;
|
|
import org.bukkit.Material;
|
|
import org.bukkit.inventory.ItemStack;
|
|
import org.bukkit.inventory.meta.ItemMeta;
|
|
import org.bukkit.inventory.meta.SkullMeta;
|
|
|
|
import java.lang.reflect.Field;
|
|
import java.lang.reflect.Method;
|
|
import java.net.URL;
|
|
import java.net.URLConnection;
|
|
import java.nio.charset.StandardCharsets;
|
|
import java.util.HashMap;
|
|
import java.util.Iterator;
|
|
import java.util.Scanner;
|
|
import java.util.UUID;
|
|
|
|
public class GetCustomHeads {
|
|
CommandPanels plugin;
|
|
public GetCustomHeads(CommandPanels pl) {
|
|
this.plugin = pl;
|
|
}
|
|
|
|
//will not go above 2000 elements in the list which is roughly 270 KB RAM usage
|
|
public HashMap<String, String> playerHeadTextures = new HashMap<>();
|
|
|
|
public String getHeadBase64(ItemStack head) {
|
|
if (plugin.getHeads.ifSkullOrHead(head.getType().toString()) && head.hasItemMeta()) {
|
|
try {
|
|
SkullMeta meta = (SkullMeta) head.getItemMeta();
|
|
assert meta != null;
|
|
if (!meta.hasOwner()) {
|
|
Field fld = meta.getClass().getDeclaredField("profile");
|
|
fld.setAccessible(true);
|
|
GameProfile prof = (GameProfile) fld.get(meta);
|
|
Iterator itr = prof.getProperties().get("textures").iterator();
|
|
if (itr.hasNext()) {
|
|
Property var5 = (Property) itr.next();
|
|
return var5.getValue();
|
|
}
|
|
}else{
|
|
Field fld = meta.getClass().getDeclaredField("profile");
|
|
fld.setAccessible(true);
|
|
GameProfile prof = (GameProfile) fld.get(meta);
|
|
Iterator itr = prof.getProperties().get("textures").iterator();
|
|
if (itr.hasNext()) {
|
|
Property var5 = (Property) itr.next();
|
|
String var5string = var5.toString();
|
|
var5string = var5string.replace("Property[","");
|
|
var5string = var5string.replace("]","");
|
|
String[] var5strings = var5string.split(",");
|
|
for(String var : var5strings){
|
|
if(var.contains("value= ")){
|
|
var = var.replace("value= ", "");
|
|
return var;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}catch(Exception exc){/*skip return null*/}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
//getting the head from a Player Name
|
|
public ItemStack getPlayerHead(String name) {
|
|
byte id = 0;
|
|
if (plugin.legacy.LOCAL_VERSION.lessThanOrEqualTo(MinecraftVersions.v1_15)) {
|
|
id = 3;
|
|
}
|
|
|
|
//get texture if already cached
|
|
if(playerHeadTextures.containsKey(name)) {
|
|
return getCustomHead(playerHeadTextures.get(name));
|
|
}
|
|
|
|
//create ItemStack
|
|
ItemStack itemStack = new ItemStack(Material.matchMaterial(plugin.getHeads.playerHeadString()), 1, id);
|
|
|
|
//Run fallback code, if API call fails, use legacy setOwner
|
|
SkullMeta meta = (SkullMeta) itemStack.getItemMeta();
|
|
meta.setOwner(name);
|
|
itemStack.setItemMeta(meta);
|
|
|
|
// Fetch and cache the texture asynchronously
|
|
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
|
try {
|
|
if(plugin.debug.consoleDebug){
|
|
plugin.getServer().getConsoleSender().sendMessage(plugin.tex.colour(plugin.tag +
|
|
ChatColor.WHITE +
|
|
"Attempting to Download & Cache Head Texture for " + name));
|
|
}
|
|
|
|
// Fetch the player UUID from the Mojang API
|
|
URL uuidUrl = new URL("https://api.mojang.com/users/profiles/minecraft/" + name);
|
|
URLConnection uuidConnection = uuidUrl.openConnection();
|
|
uuidConnection.setConnectTimeout(2000); // Set connection timeout to 2 seconds
|
|
uuidConnection.setReadTimeout(2000); // Set read timeout to 2 seconds
|
|
//Json is simple and structured so a hard code solution will avoid the need for a library
|
|
String uuidReader = new Scanner(uuidConnection.getInputStream(),
|
|
StandardCharsets.UTF_8.name()).useDelimiter("\\A").next();
|
|
String uuid = uuidReader.split("\"id\" : \"")[1].split("\"")[0];
|
|
|
|
// Fetch the skin texture from the Mojang API using the player UUID
|
|
URL texturesUrl = new URL("https://sessionserver.mojang.com/session/minecraft/profile/" + uuid);
|
|
URLConnection texturesConnection = texturesUrl.openConnection();
|
|
texturesConnection.setConnectTimeout(2000); // Set connection timeout to 2 seconds
|
|
texturesConnection.setReadTimeout(2000); // Set read timeout to 2 seconds
|
|
//Json is simple and structured so a hard code solution will avoid the need for a library
|
|
String valueReader = new Scanner(texturesConnection.getInputStream(),
|
|
StandardCharsets.UTF_8.name()).useDelimiter("\\A").next();
|
|
String value = valueReader.split("\"value\" : \"")[1].split("\"")[0];
|
|
playerHeadTextures.put(name, value);
|
|
|
|
// Once the API call is finished, update the ItemStack on the main thread
|
|
Bukkit.getScheduler().runTask(plugin, () -> {
|
|
itemStack.setItemMeta(getCustomHead(value).getItemMeta());
|
|
});
|
|
} catch (Exception e) {
|
|
// Handle exceptions
|
|
if(plugin.debug.consoleDebug) {
|
|
plugin.debug(e,null);
|
|
}
|
|
}
|
|
});
|
|
|
|
return itemStack;
|
|
}
|
|
|
|
//used to get heads from Base64 Textures
|
|
@SuppressWarnings("deprecation")
|
|
public ItemStack getCustomHead(String b64stringtexture) {
|
|
//get head from base64
|
|
GameProfile profile = new GameProfile(UUID.randomUUID(), "");
|
|
PropertyMap propertyMap = profile.getProperties();
|
|
if (propertyMap == null) {
|
|
throw new IllegalStateException("Profile doesn't contain a property map");
|
|
} else {
|
|
propertyMap.put("textures", new Property("textures", b64stringtexture));
|
|
byte id = 0;
|
|
if(plugin.legacy.LOCAL_VERSION.lessThanOrEqualTo(MinecraftVersions.v1_15)){
|
|
id = 3;
|
|
}
|
|
ItemStack head = new ItemStack(Material.matchMaterial(plugin.getHeads.playerHeadString()), 1,id);
|
|
ItemMeta headMeta = head.getItemMeta();
|
|
assert headMeta != null;
|
|
|
|
Field profileField;
|
|
Method setProfileMethod = null;
|
|
try {
|
|
// Attempt to access the 'profile' field directly
|
|
// Also writes to 'serializedProfile' field as one cannot be null while the other is not
|
|
// This block is mainly for 1.20.2+ versions
|
|
profileField = headMeta.getClass().getDeclaredField("profile");
|
|
Field serializedProfileField = headMeta.getClass().getDeclaredField("serializedProfile");
|
|
|
|
profileField.setAccessible(true);
|
|
serializedProfileField.setAccessible(true);
|
|
|
|
profileField.set(headMeta, profile);
|
|
serializedProfileField.set(headMeta, profile); // Assuming serializedProfile is of the same type
|
|
} catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException e1) {
|
|
try {
|
|
// This block covers versions that have a 'setProfile' method instead of direct field access
|
|
// Likely for versions prior to 1.20.2
|
|
setProfileMethod = headMeta.getClass().getDeclaredMethod("setProfile", GameProfile.class);
|
|
} catch (NoSuchMethodException ignore) {}
|
|
} catch (SecurityException ignored) {}
|
|
try {
|
|
if (setProfileMethod == null) {
|
|
// Attempt to access the 'profile' field directly
|
|
// This block is a generic fallback for versions lacking the 'setProfile' method
|
|
profileField = headMeta.getClass().getDeclaredField("profile");
|
|
profileField.setAccessible(true);
|
|
profileField.set(headMeta, profile);
|
|
} else {
|
|
// Use the 'setProfile' method if it was found
|
|
setProfileMethod.setAccessible(true);
|
|
setProfileMethod.invoke(headMeta, profile);
|
|
}
|
|
} catch (Exception e1) {
|
|
plugin.debug(e1,null);
|
|
}
|
|
|
|
head.setItemMeta(headMeta);
|
|
return head;
|
|
}
|
|
}
|
|
} |