me.deecaad.core.utils.AdventureUtil Maven / Gradle / Ivy
package me.deecaad.core.utils;
import me.clip.placeholderapi.PlaceholderAPI;
import me.deecaad.core.MechanicsCore;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* MechanicsCore is built to be craftbukkit compatible to reduce support requests. We do not use
* Paper code since we upload premium plugins on Spigot. Unfortunately, this means we must find
* "creative" solutions when using the Adventure chat API with Minecraft code.
*
*
* This class adds methods to:
*
* - Set item display name
* - Set item lore
*
*/
public final class AdventureUtil {
// 1.16+ use adventure in item lore and display name (hex code support)
public static Field loreField;
public static Field displayField;
public static Method fromJsonMethod;
public static Method toJsonMethod;
static {
if (MinecraftVersions.NETHER_UPDATE.isAtLeast()) { // before 1.16, hex was not supported by MC
Class> c = ReflectionUtil.getCBClass("inventory.CraftMetaItem");
loreField = ReflectionUtil.getField(c, "lore");
displayField = ReflectionUtil.getField(c, "displayName");
}
if (MinecraftVersions.TRAILS_AND_TAILS.get(5).isAtLeast()) {
Class> c = ReflectionUtil.getCBClass("util.CraftChatMessage");
Class> componentClass = ReflectionUtil.getNMSClass("network.chat", "IChatBaseComponent");
fromJsonMethod = ReflectionUtil.getMethod(c, "fromJSON", String.class);
toJsonMethod = ReflectionUtil.getMethod(c, "toJSON", componentClass);
}
}
/**
* Don't let anyone instantiate this class.
*/
private AdventureUtil() {
}
/**
* Returns the display name of the item in adventure format.
*
* @param item The item get the name from.
* @return The name component.
*/
public static @NotNull Component getName(@NotNull ItemStack item) {
ItemMeta meta = item.getItemMeta();
if (meta == null)
return Component.empty();
if (MinecraftVersions.TRAILS_AND_TAILS.get(5).isAtLeast()) {
Object component = ReflectionUtil.invokeField(displayField, meta);
String json = (String) ReflectionUtil.invokeMethod(toJsonMethod, null, component);
return GsonComponentSerializer.gson().deserialize(json);
} else if (MinecraftVersions.NETHER_UPDATE.isAtLeast()) {
return GsonComponentSerializer.gson().deserialize((String) ReflectionUtil.invokeField(displayField, meta));
} else {
return LegacyComponentSerializer.legacySection().deserialize(meta.getDisplayName());
}
}
/**
* Sets the display name of the item. Parsed using MiniMessage.
*
* @param item The item to set the name.
* @param name The value of the name.
*/
public static void setNameUnparsed(@NotNull ItemStack item, @NotNull String name) {
ItemMeta meta = item.getItemMeta();
setNameUnparsed(Objects.requireNonNull(meta), name);
item.setItemMeta(meta);
}
/**
* Sets the display name of the item's meta. Parsed using MiniMessage.
*
* @param meta The meta to set the name.
* @param name The value of the name.
*/
public static void setNameUnparsed(@NotNull ItemMeta meta, @NotNull String name) {
// effectively strips away Minecraft's predefined formatting
setName(meta, MechanicsCore.getPlugin().message.deserialize("" + name));
}
/**
* Sets the display name of the item.
*
* @param item The item to set the name.
* @param name The value of the name.
*/
public static void setName(@NotNull ItemStack item, @NotNull Component name) {
ItemMeta meta = item.getItemMeta();
setName(Objects.requireNonNull(meta), name);
item.setItemMeta(meta);
}
/**
* Sets the display name of the item's meta.
*
* @param meta The meta to set the name.
* @param name The value of the name.
*/
public static void setName(@NotNull ItemMeta meta, @NotNull Component name) {
// before 1.16, hex was not supported
if (MinecraftVersions.TRAILS_AND_TAILS.get(5).isAtLeast()) {
String json = GsonComponentSerializer.gson().serialize(name);
Object component = ReflectionUtil.invokeMethod(fromJsonMethod, null, json);
ReflectionUtil.setField(displayField, meta, component);
} else if (MinecraftVersions.NETHER_UPDATE.isAtLeast()) {
String str = GsonComponentSerializer.gson().serialize(name);
ReflectionUtil.setField(displayField, meta, str);
} else {
String str = LegacyComponentSerializer.legacySection().serialize(name);
meta.setDisplayName(str);
}
}
@Nullable public static List getLore(@NotNull ItemStack item) {
return getLore(Objects.requireNonNull(item.getItemMeta()));
}
@Nullable public static List getLore(@NotNull ItemMeta meta) {
boolean useLegacy = !MinecraftVersions.NETHER_UPDATE.isAtLeast(); // before 1.16, hex was not supported by MC
List lore = useLegacy
? meta.getLore()
: (List) ReflectionUtil.invokeField(loreField, meta);
if (lore == null)
return null;
List components = new ArrayList<>(lore.size());
for (String line : lore) {
Component component = useLegacy
? LegacyComponentSerializer.legacySection().deserialize(line)
: GsonComponentSerializer.gson().deserialize(line);
components.add(component);
}
return components;
}
/**
* Sets the lore of the item.
*
*
* The list should be a list of strings (or any list where {@link Object#toString()} is acceptable).
* The strings are then parsed using MiniMessage, then set as the lore. This is quite slow, so
* consider parsing the list with MiniMessage first then setting it using
* {@link #setLore(ItemStack, List)}.
*
* @param item The item to set the lore.
* @param unparsedText The list of strings.
*/
public static void setLoreUnparsed(@NotNull ItemStack item, @NotNull List> unparsedText) {
ItemMeta meta = item.getItemMeta();
setLoreUnparsed(Objects.requireNonNull(meta), unparsedText);
item.setItemMeta(meta);
}
/**
* Sets the lore of the item meta.
*
*
* The list should be a list of strings (or any list where {@link Object#toString()} is acceptable).
* The strings are then parsed using MiniMessage, then set as the lore. This is quite slow, so
* consider parsing the list with MiniMessage first then setting it using
* {@link #setLore(ItemStack, List)}.
*
* @param meta The item meta to set the lore.
* @param unparsedText The list of strings.
*/
public static void setLoreUnparsed(@NotNull ItemMeta meta, @NotNull List> unparsedText) {
List