Fixed 1.20+ skull texture application

This commit is contained in:
Jules 2023-11-06 02:55:33 +01:00
parent 79bb6d2d3c
commit 5de2a0003f
5 changed files with 105 additions and 148 deletions

View File

@ -1,19 +1,14 @@
package net.Indyuce.mmoitems.api.item.util;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.properties.Property;
import io.lumine.mythic.lib.MythicLib;
import io.lumine.mythic.lib.UtilityMethods;
import io.lumine.mythic.lib.api.item.ItemTag;
import io.lumine.mythic.lib.util.AdventureUtils;
import io.lumine.mythic.lib.version.VersionMaterial;
import net.Indyuce.mmoitems.MMOItems;
import org.bukkit.ChatColor;
import org.bukkit.inventory.ItemFlag;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.inventory.meta.SkullMeta;
import java.lang.reflect.Field;
import java.util.UUID;
import java.util.logging.Level;
import java.util.stream.Collectors;
public class CustomSkull extends ConfigItem {
@ -31,19 +26,11 @@ public class CustomSkull extends ConfigItem {
public void updateItem() {
setItem(VersionMaterial.PLAYER_HEAD.toItem());
ItemMeta meta = getItem().getItemMeta();
SkullMeta meta = (SkullMeta) getItem().getItemMeta();
AdventureUtils.setDisplayName(meta, getName());
meta.addItemFlags(ItemFlag.values());
GameProfile gameProfile = new GameProfile(UUID.randomUUID(), "SkullTexture");
gameProfile.getProperties().put("textures", new Property("textures", textureValue));
try {
Field profileField = meta.getClass().getDeclaredField("profile");
profileField.setAccessible(true);
profileField.set(meta, gameProfile);
} catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException exception) {
MMOItems.plugin.getLogger().log(Level.WARNING, "Could not load skull texture");
}
UtilityMethods.setTextureValue(meta, textureValue);
if (hasLore())
AdventureUtils.setLore(meta, getLore()

View File

@ -1,9 +1,9 @@
package net.Indyuce.mmoitems.api.util;
import java.lang.reflect.Field;
import java.util.Random;
import java.util.logging.Level;
import io.lumine.mythic.lib.MythicLib;
import io.lumine.mythic.lib.api.item.ItemTag;
import io.lumine.mythic.lib.api.item.NBTItem;
import net.Indyuce.mmoitems.MMOItems;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.enchantments.Enchantment;
@ -20,12 +20,8 @@ import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.inventory.meta.LeatherArmorMeta;
import org.bukkit.inventory.meta.SkullMeta;
import com.mojang.authlib.GameProfile;
import net.Indyuce.mmoitems.MMOItems;
import io.lumine.mythic.lib.MythicLib;
import io.lumine.mythic.lib.api.item.ItemTag;
import io.lumine.mythic.lib.api.item.NBTItem;
import java.util.Random;
import java.util.logging.Level;
public class NoClipItem implements Listener {
private final Item item;
@ -115,18 +111,11 @@ public class NoClipItem implements Listener {
}
// Copy Skull textures
if (oldItem.getItemMeta() instanceof SkullMeta) {
try {
final Field oldProfileField = oldItem.getItemMeta().getClass().getDeclaredField("profile");
oldProfileField.setAccessible(true);
final GameProfile oldProfile = (GameProfile) oldProfileField.get(oldItem.getItemMeta());
final Field profileField = newItemMeta.getClass().getDeclaredField("profile");
profileField.setAccessible(true);
profileField.set(newItemMeta, oldProfile);
} catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException exception) {
MMOItems.plugin.getLogger().log(Level.WARNING, "Could not set skull texture on stripItemData method in the NoClipItem class. Please report this issue!");
}
if (oldItem.getItemMeta() instanceof SkullMeta) try {
final Object oldProfile = MythicLib.plugin.getVersion().getWrapper().getProfile((SkullMeta) oldItem.getItemMeta());
MythicLib.plugin.getVersion().getWrapper().setProfile((SkullMeta) newItemMeta, oldProfile);
} catch (RuntimeException exception) {
MMOItems.plugin.getLogger().log(Level.WARNING, "Could not set skull texture on stripItemData method in the NoClipItem class. Please report this issue!");
}
// Copy Leather colors

View File

@ -1,7 +1,6 @@
package net.Indyuce.mmoitems.stat;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.properties.Property;
import io.lumine.mythic.lib.MythicLib;
import io.lumine.mythic.lib.api.item.ItemTag;
import io.lumine.mythic.lib.api.util.AltChar;
import io.lumine.mythic.lib.version.VersionMaterial;
@ -12,111 +11,110 @@ import net.Indyuce.mmoitems.api.item.build.ItemStackBuilder;
import net.Indyuce.mmoitems.api.item.mmoitem.ReadMMOItem;
import net.Indyuce.mmoitems.gui.edition.EditionInventory;
import net.Indyuce.mmoitems.stat.data.SkullTextureData;
import net.Indyuce.mmoitems.stat.data.random.RandomStatData;
import net.Indyuce.mmoitems.stat.data.type.StatData;
import net.Indyuce.mmoitems.stat.type.ItemStat;
import org.apache.commons.lang.Validate;
import org.bukkit.ChatColor;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.event.inventory.InventoryAction;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.inventory.meta.SkullMeta;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
public class SkullTextureStat extends ItemStat<SkullTextureData, SkullTextureData> {
public SkullTextureStat() {
super("SKULL_TEXTURE", VersionMaterial.PLAYER_HEAD.toMaterial(), "Skull Texture",
new String[] { "The head texture &nvalue&7.", "Can be found on heads databases." }, new String[] { "all" },
VersionMaterial.PLAYER_HEAD.toMaterial());
}
public SkullTextureStat() {
super("SKULL_TEXTURE", VersionMaterial.PLAYER_HEAD.toMaterial(), "Skull Texture", new String[]{"The skull texture &nvalue&7.", "Can be found on head databases."}, new String[]{"all"}, VersionMaterial.PLAYER_HEAD.toMaterial());
}
@Override
public SkullTextureData whenInitialized(Object object) {
Validate.isTrue(object instanceof ConfigurationSection, "Must specify a config section");
ConfigurationSection config = (ConfigurationSection) object;
@Override
public SkullTextureData whenInitialized(Object object) {
Validate.isTrue(object instanceof ConfigurationSection, "Must specify a config section");
ConfigurationSection config = (ConfigurationSection) object;
String value = config.getString("value");
Validate.notNull(value, "Could not load skull texture value");
final String value = config.getString("value");
Validate.notNull(value, "Could not load skull texture value");
String format = config.getString("uuid");
Validate.notNull(format, "Could not find skull texture UUID: re-enter your skull texture value and one will be selected randomly.");
final String uuid = config.getString("uuid");
Validate.notNull(uuid, "Could not find skull texture UUID: re-enter your skull texture value and one will be selected randomly.");
SkullTextureData skullTexture = new SkullTextureData(new GameProfile(UUID.fromString(format), "SkullTexture"));
skullTexture.getGameProfile().getProperties().put("textures", new Property("textures", value));
return skullTexture;
}
final Object profile = MythicLib.plugin.getVersion().getWrapper().newProfile(UUID.fromString(uuid), value);
final SkullTextureData skullTexture = new SkullTextureData(profile);
return skullTexture;
}
@Override
public void whenDisplayed(List<String> lore, Optional<SkullTextureData> statData) {
lore.add(ChatColor.GRAY + "Current Value: " + (statData.isPresent() ? ChatColor.GREEN + "Texture value provided" : ChatColor.RED + "None"));
lore.add("");
lore.add(ChatColor.YELLOW + AltChar.listDash + " Left click to change this value.");
lore.add(ChatColor.YELLOW + AltChar.listDash + " Right click to remove this value.");
}
@Override
public void whenDisplayed(List<String> lore, Optional<SkullTextureData> statData) {
lore.add(ChatColor.GRAY + "Current Value: " + (statData.isPresent() ? ChatColor.GREEN + "Texture value provided" : ChatColor.RED + "None"));
lore.add("");
lore.add(ChatColor.YELLOW + AltChar.listDash + " Left click to change this value.");
lore.add(ChatColor.YELLOW + AltChar.listDash + " Right click to remove this value.");
}
@Override
public void whenInput(@NotNull EditionInventory inv, @NotNull String message, Object... info) {
inv.getEditedSection().set("skull-texture.value", message);
inv.getEditedSection().set("skull-texture.uuid", UUID.randomUUID().toString());
inv.registerTemplateEdition();
inv.getPlayer().sendMessage(MMOItems.plugin.getPrefix() + getName() + " successfully changed to " + message + ".");
}
@Override
public void whenInput(@NotNull EditionInventory inv, @NotNull String message, Object... info) {
inv.getEditedSection().set("skull-texture.value", message);
inv.getEditedSection().set("skull-texture.uuid", UUID.randomUUID().toString());
inv.registerTemplateEdition();
inv.getPlayer().sendMessage(MMOItems.plugin.getPrefix() + getName() + " successfully changed to " + message + ".");
}
@Override
public void whenApplied(@NotNull ItemStackBuilder item, @NotNull SkullTextureData data) {
if (item.getItemStack().getType() != VersionMaterial.PLAYER_HEAD.toMaterial())
return;
@Override
public void whenApplied(@NotNull ItemStackBuilder item, @NotNull SkullTextureData data) {
if (item.getItemStack().getType() != VersionMaterial.PLAYER_HEAD.toMaterial()) return;
try {
Field profileField = item.getMeta().getClass().getDeclaredField("profile");
profileField.setAccessible(true);
profileField.set(item.getMeta(), ((SkullTextureData) data).getGameProfile());
} catch (NoSuchFieldException | IllegalAccessException exception) {
throw new IllegalArgumentException(exception.getMessage());
}
}
/**
* This stat is saved not as a custom tag, but as the vanilla HideFlag itself.
* Alas this is an empty array
*/
@NotNull
@Override
public ArrayList<ItemTag> getAppliedNBT(@NotNull SkullTextureData data) { return new ArrayList<>(); }
if (data.getGameProfile() != null)
MythicLib.plugin.getVersion().getWrapper().setProfile((SkullMeta) item.getMeta(), data.getGameProfile());
}
@Override
public void whenClicked(@NotNull EditionInventory inv, @NotNull InventoryClickEvent event) {
if (event.getAction() == InventoryAction.PICKUP_HALF) {
inv.getEditedSection().set(getPath(), null);
inv.registerTemplateEdition();
inv.getPlayer().sendMessage(MMOItems.plugin.getPrefix() + "Successfully removed " + getName() + ".");
} else
new StatEdition(inv, this).enable("Write in the chat the text you want.");
}
/**
* This stat is saved not as a custom tag, but as the vanilla HideFlag itself.
* Alas this is an empty array
*/
@NotNull
@Override
public ArrayList<ItemTag> getAppliedNBT(@NotNull SkullTextureData data) {
return new ArrayList<>();
}
@Override
public void whenLoaded(@NotNull ReadMMOItem mmoitem) {
try {
Field profileField = mmoitem.getNBT().getItem().getItemMeta().getClass().getDeclaredField("profile");
profileField.setAccessible(true);
mmoitem.setData(ItemStats.SKULL_TEXTURE, new SkullTextureData((GameProfile) profileField.get(mmoitem.getNBT().getItem().getItemMeta())));
} catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException ignored) {}
}
@Override
public void whenClicked(@NotNull EditionInventory inv, @NotNull InventoryClickEvent event) {
if (event.getAction() == InventoryAction.PICKUP_HALF) {
inv.getEditedSection().set(getPath(), null);
inv.registerTemplateEdition();
inv.getPlayer().sendMessage(MMOItems.plugin.getPrefix() + "Successfully removed " + getName() + ".");
} else new StatEdition(inv, this).enable("Write in the chat the text you want.");
}
/**
* This stat is saved not as a custom tag, but as the vanilla Head Texture itself.
* Alas this method returns null.
*/
@Nullable
@Override
public SkullTextureData getLoadedNBT(@NotNull ArrayList<ItemTag> storedTags) { return null; }
@Override
public void whenLoaded(@NotNull ReadMMOItem mmoitem) {
try {
final ItemMeta meta = mmoitem.getNBT().getItem().getItemMeta();
Validate.isTrue(meta instanceof SkullMeta);
final Object profile = MythicLib.plugin.getVersion().getWrapper().getProfile((SkullMeta) meta);
mmoitem.setData(ItemStats.SKULL_TEXTURE, new SkullTextureData(profile));
} catch (RuntimeException ignored) {
}
}
@NotNull
@Override
public SkullTextureData getClearStatData() { return new SkullTextureData(new GameProfile(UUID.fromString("df930b7b-a84d-4f76-90ac-33be6a5b6c88"), "gunging")); }
/**
* This stat is saved not as a custom tag, but as the vanilla Head Texture itself.
* Alas this method returns null.
*/
@Nullable
@Override
public SkullTextureData getLoadedNBT(@NotNull ArrayList<ItemTag> storedTags) {
return null;
}
@NotNull
@Override
public SkullTextureData getClearStatData() {
return new SkullTextureData(null);
}
}

View File

@ -1,6 +1,5 @@
package net.Indyuce.mmoitems.stat.data;
import com.mojang.authlib.GameProfile;
import net.Indyuce.mmoitems.api.item.build.MMOItemBuilder;
import net.Indyuce.mmoitems.stat.data.random.RandomStatData;
import net.Indyuce.mmoitems.stat.data.type.StatData;
@ -8,18 +7,24 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class SkullTextureData implements StatData, RandomStatData<SkullTextureData> {
private GameProfile profile;
public SkullTextureData(GameProfile profile) {
/**
* Spigot 1.20.2 introduced a PlayerProfile API which requires
* to both support PlayerProfile and GameProfile objects as
* reflection is no longer supported by >1.20.2
*/
private Object profile;
public SkullTextureData(Object profile) {
this.profile = profile;
}
@Nullable
public GameProfile getGameProfile() {
public Object getGameProfile() {
return profile;
}
public void setGameProfile(@Nullable GameProfile profile) {
public void setGameProfile(@Nullable Object profile) {
this.profile = profile;
}

View File

@ -1,8 +1,6 @@
package net.Indyuce.mmoitems.util;
import com.google.common.collect.ImmutableMap;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.properties.Property;
import io.lumine.mythic.lib.MythicLib;
import io.lumine.mythic.lib.UtilityMethods;
import io.lumine.mythic.lib.api.item.ItemTag;
@ -11,7 +9,6 @@ import io.lumine.mythic.lib.api.item.SupportedNBTTagValues;
import io.lumine.mythic.lib.skill.trigger.TriggerType;
import net.Indyuce.mmoitems.MMOItems;
import net.Indyuce.mmoitems.api.Type;
import org.apache.commons.codec.binary.Base64;
import org.bukkit.*;
import org.bukkit.attribute.Attribute;
import org.bukkit.entity.Entity;
@ -20,13 +17,11 @@ import org.bukkit.entity.Player;
import org.bukkit.entity.Projectile;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.potion.PotionEffectType;
import org.bukkit.util.Vector;
import org.jetbrains.annotations.NotNull;
import javax.annotation.Nullable;
import java.lang.reflect.Field;
import java.util.*;
@SuppressWarnings("unused")
@ -68,23 +63,6 @@ public class MMOUtils {
return isNonEmpty(str) ? str : Objects.requireNonNull(fallback);
}
/**
* @return The skull texture URL from a given player head
*/
@Deprecated
public static String getSkullTextureURL(ItemStack item) {
try {
ItemMeta meta = item.getItemMeta();
Field profileField = meta.getClass().getDeclaredField("profile");
profileField.setAccessible(true);
Collection<Property> properties = ((GameProfile) profileField.get(item.getItemMeta())).getProperties().get("textures");
Property property = properties.toArray(new Property[0])[0];
return new String(Base64.decodeBase64(property.getValue())).replace("{textures:{SKIN:{url:\"", "").replace("\"}}}", "");
} catch (Exception e) {
return "";
}
}
private static final String UNIVERSAL_REFERENCE = "all";
/**