diff --git a/api/src/main/java/com/clubobsidian/dynamicgui/api/factory/SlotFactory.java b/api/src/main/java/com/clubobsidian/dynamicgui/api/factory/SlotFactory.java index 249ba56b1..cd9649576 100644 --- a/api/src/main/java/com/clubobsidian/dynamicgui/api/factory/SlotFactory.java +++ b/api/src/main/java/com/clubobsidian/dynamicgui/api/factory/SlotFactory.java @@ -30,7 +30,7 @@ public interface SlotFactory { Slot create(int index, int amount, String icon, String name, String nbt, short data, boolean glow, boolean movable, Boolean close, List lore, List enchants, List itemFlags, - String modelProvider, String modelData, + Map dataComponents, String modelProvider, String modelData, FunctionTree functions, int updateInterval, Map metadata); } diff --git a/api/src/main/java/com/clubobsidian/dynamicgui/api/gui/Slot.java b/api/src/main/java/com/clubobsidian/dynamicgui/api/gui/Slot.java index 6f87c4c80..68daa0c28 100644 --- a/api/src/main/java/com/clubobsidian/dynamicgui/api/gui/Slot.java +++ b/api/src/main/java/com/clubobsidian/dynamicgui/api/gui/Slot.java @@ -173,6 +173,17 @@ public interface Slot extends Serializable, FunctionOwner, AnimationHolder, Meta */ @Unmodifiable List getItemFlags(); + /** + * Gets the data components that the slot uses to build + * the initial item. If the item was modified + * this may not reflect the current item flags for + * the item stack. If you need the current + * item flags use {@link ItemStackWrapper#getDataComponents()} + * + * @return the slot data components as a map + */ + Map getDataComponents(); + /** * Gets whether the slot should close * @@ -234,6 +245,7 @@ class Builder { private transient final List lore = new ArrayList<>(); private transient final List enchants = new ArrayList<>(); private transient final List itemFlags = new ArrayList<>(); + private transient final Map dataComponents = new HashMap<>(); private transient String modelProvider; private transient String modelData; private transient int index; @@ -483,6 +495,11 @@ public Builder addItemFlag(Collection itemFlags) { return this; } + private Builder addDataComponents(Map dataComponents) { + this.dataComponents.putAll(dataComponents); + return this; + } + /** * Sets the function tree * @@ -537,9 +554,11 @@ public Builder fromItemStackWrapper(ItemStackWrapper itemStackWrapper) { .setAmount(itemStackWrapper.getAmount()) .addLore(itemStackWrapper.getLore()) .addItemFlag(itemStackWrapper.getItemFlags()) + .addDataComponents(itemStackWrapper.getDataComponents()) .addEnchant(itemStackWrapper.getEnchants()); } + /** * Builds slot with built parameters * @@ -549,8 +568,9 @@ public Slot build() { return SLOT_FACTORY.create(this.index, this.amount, this.icon, this.name, this.nbt, this.data, this.glow, this.movable, this.close, this.lore, this.enchants, this.itemFlags, - this.modelProvider, this.modelData, this.functionTree, - this.updateInterval, this.metadata); + this.dataComponents, this.modelProvider, + this.modelData, this.functionTree, this.updateInterval, + this.metadata); } } } \ No newline at end of file diff --git a/api/src/main/java/com/clubobsidian/dynamicgui/api/inventory/ItemStackWrapper.java b/api/src/main/java/com/clubobsidian/dynamicgui/api/inventory/ItemStackWrapper.java index 43e46f57a..22180aa5b 100644 --- a/api/src/main/java/com/clubobsidian/dynamicgui/api/inventory/ItemStackWrapper.java +++ b/api/src/main/java/com/clubobsidian/dynamicgui/api/inventory/ItemStackWrapper.java @@ -23,6 +23,7 @@ import java.io.Serializable; import java.util.List; +import java.util.Map; public abstract class ItemStackWrapper implements Serializable { @@ -175,7 +176,24 @@ protected void setItemStack(@Nullable Object itemStack) { * * @param nbt the nbt to set */ - public abstract void setNBT(@NotNull String nbt); + public abstract void setNBT(String nbt); + + /** + * Gets a map of the components + * from the underlying item stack + * the items are serialized and + * returns as a map of strings. + * + * @return map of strings + */ + public abstract Map getDataComponents(); + + /** + * Sets the data components underlying item stack. + * + * @param components the components to set + */ + public abstract void setDataComponents(Map components); /** * Sets whether the underlying item stack should glow. diff --git a/api/src/main/java/com/clubobsidian/dynamicgui/api/parser/slot/SlotToken.java b/api/src/main/java/com/clubobsidian/dynamicgui/api/parser/slot/SlotToken.java index 6fb445739..eae62fdcb 100644 --- a/api/src/main/java/com/clubobsidian/dynamicgui/api/parser/slot/SlotToken.java +++ b/api/src/main/java/com/clubobsidian/dynamicgui/api/parser/slot/SlotToken.java @@ -51,6 +51,8 @@ public interface SlotToken extends Serializable { List getItemFlags(); + Map getDataComponents(); + String getModelProvider(); String getModelData(); diff --git a/bukkit/src/main/java/com/clubobsidian/dynamicgui/bukkit/inventory/BukkitItemStackWrapper.java b/bukkit/src/main/java/com/clubobsidian/dynamicgui/bukkit/inventory/BukkitItemStackWrapper.java index 132235217..c4cd36de3 100644 --- a/bukkit/src/main/java/com/clubobsidian/dynamicgui/bukkit/inventory/BukkitItemStackWrapper.java +++ b/bukkit/src/main/java/com/clubobsidian/dynamicgui/bukkit/inventory/BukkitItemStackWrapper.java @@ -19,6 +19,7 @@ import com.clubobsidian.dynamicgui.api.enchantment.EnchantmentWrapper; import com.clubobsidian.dynamicgui.api.inventory.ItemStackWrapper; import com.clubobsidian.dynamicgui.api.manager.material.MaterialManager; +import com.clubobsidian.dynamicgui.bukkit.util.BukkitDataComponentUtil; import com.clubobsidian.dynamicgui.bukkit.util.BukkitNBTUtil; import org.bukkit.Material; import org.bukkit.enchantments.Enchantment; @@ -194,11 +195,14 @@ public List getEnchants() { @Override public String getNBT() { - return BukkitNBTUtil.getTag(this.getItemStack()); + return BukkitDataComponentUtil.usesDataComponents() ? null : BukkitNBTUtil.getTag(this.getItemStack()); } @Override - public void setNBT(@NotNull String nbt) { + public void setNBT(String nbt) { + if (BukkitDataComponentUtil.usesDataComponents()) { + return; + } Objects.requireNonNull(nbt); ItemStack oldItemStack = this.getItemStack(); ItemStack newItemStack = BukkitNBTUtil.setTag(this.getItemStack(), nbt); @@ -231,6 +235,21 @@ public void setNBT(@NotNull String nbt) { this.setItemStack(newItemStack); } + @Override + public Map getDataComponents() { + return BukkitDataComponentUtil.usesDataComponents() + ? BukkitDataComponentUtil.getComponents(this.itemStack) + : Collections.emptyMap(); + } + + @Override + public void setDataComponents(Map components) { + if (!BukkitDataComponentUtil.usesDataComponents()) { + return; + } + this.itemStack = (T) BukkitDataComponentUtil.setComponents(this.itemStack, components); + } + @Override public void setGlowing(boolean glowing) { ItemStack item = this.getItemStack(); diff --git a/bukkit/src/main/java/com/clubobsidian/dynamicgui/bukkit/util/BukkitDataComponentUtil.java b/bukkit/src/main/java/com/clubobsidian/dynamicgui/bukkit/util/BukkitDataComponentUtil.java new file mode 100644 index 000000000..d6743fc6e --- /dev/null +++ b/bukkit/src/main/java/com/clubobsidian/dynamicgui/bukkit/util/BukkitDataComponentUtil.java @@ -0,0 +1,208 @@ +/* + * Copyright 2018-2023 virustotalop + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.clubobsidian.dynamicgui.bukkit.util; + +import com.clubobsidian.dynamicgui.core.util.ReflectionUtil; +import org.bukkit.inventory.ItemStack; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.LinkedHashMap; +import java.util.Locale; +import java.util.Map; +import java.util.Objects; + +public final class BukkitDataComponentUtil { + + private static final Class DATA_COMPONENT_HOLDER = ReflectionUtil + .getClassIfExists("net.minecraft.core.component.DataComponentHolder"); + private static final Class DATA_COMPONENT_TYPE = ReflectionUtil + .getClassIfExists("net.minecraft.core.component.DataComponentType"); + private static final Class REGISTRY_ACCESS = ReflectionUtil + .getClassIfExists("net.minecraft.core.RegistryAccess"); + private static final Class MAPPED_REGISTRY = ReflectionUtil + .getClassIfExists("net.minecraft.core.MappedRegistry"); + private static final Class REGISTRY_OPS = ReflectionUtil + .getClassIfExists("net.minecraft.resources.RegistryOps"); + private static final Class LOOKUP_PROVIDER = ReflectionUtil + .getClassIfExists("net.minecraft.core.HolderLookup$Provider"); + private static final Class MINECRAFT_SERVER = ReflectionUtil + .getClassIfExists("net.minecraft.server.MinecraftServer"); + private static final Class NBT_OPS = ReflectionUtil + .getClassIfExists("net.minecraft.nbt.NbtOps"); + private static final Class RESOURCE_LOCATION = ReflectionUtil + .getClassIfExists("net.minecraft.resources.ResourceLocation"); + private static final Class STRING_READER = ReflectionUtil + .getClassIfExists("com.mojang.brigadier.StringReader"); + private static final Class BUILT_IN_REGISTRIES = ReflectionUtil + .getClassIfExists("net.minecraft.core.registries.BuiltInRegistries"); + private static final Class TAG_PARSER = ReflectionUtil + .getClassIfExists("net.minecraft.nbt.TagParser"); + private static final Class CRAFT_ITEM_STACK = ReflectionUtil + .getClassIfExists("org.bukkit.craftbukkit.inventory.CraftItemStack"); + private static final Class NMS_ITEM_STACK = ReflectionUtil + .getClassIfExists("net.minecraft.world.item.ItemStack"); + private static final Class DECODER = ReflectionUtil + .getClassIfExists("com.mojang.serialization.Decoder"); + private static final Class DATA_RESULT = ReflectionUtil + .getClassIfExists("com.mojang.serialization.DataResult"); + private static final Class ENCODER = ReflectionUtil + .getClassIfExists("com.mojang.serialization.Encoder"); + private static final Class TYPED_DATA_COMPONENT = ReflectionUtil + .getClassIfExists("net.minecraft.core.component.TypedDataComponent"); + private static final Class NBT_UTILS = ReflectionUtil + .getClassIfExists("net.minecraft.nbt.NbtUtils"); + private static final Class COMPONENT = ReflectionUtil + .getClassIfExists("net.minecraft.network.chat.Component"); + private static final Class TAG = ReflectionUtil + .getClassIfExists("net.minecraft.nbt.Tag"); + + private static final Field NBT_OPS_INSTANCE = + ReflectionUtil.getDeclaredField(NBT_OPS, "INSTANCE"); + private static final Field DATA_COMPONENT_TYPE_FIELD = + ReflectionUtil.getDeclaredField(BUILT_IN_REGISTRIES, "DATA_COMPONENT_TYPE"); + + private static final Method GET_REGISTRY_ACCESS = ReflectionUtil + .getStaticMethod(MINECRAFT_SERVER, REGISTRY_ACCESS); + private static final Method REGISTRY_CREATE_CONTEXT = ReflectionUtil + .getMethod(LOOKUP_PROVIDER, "createSerializationContext"); + private static final Method UNWRAP = ReflectionUtil + .getStaticMethod(CRAFT_ITEM_STACK, NMS_ITEM_STACK, ItemStack.class); + private static final Method SET = ReflectionUtil + .getMethod(NMS_ITEM_STACK, "set"); + private static final Method RESOURCE_LOCATION_READ = ReflectionUtil + .getStaticMethod(RESOURCE_LOCATION, RESOURCE_LOCATION, STRING_READER); + private static final Method MAPPED_REGISTRY_GET = ReflectionUtil + .getMethodByParams(MAPPED_REGISTRY, "get", RESOURCE_LOCATION); + private static final Method CODEC_OR_THROW = ReflectionUtil + .getMethod(DATA_COMPONENT_TYPE, "codecOrThrow"); + private static final Method PARSE = ReflectionUtil + .getMethod(DECODER, 2, "parse"); + private static final Method DATA_RESULT_GET_OR_THROW = ReflectionUtil + .getMethod(DATA_RESULT, 0, "getOrThrow"); + private static final Method GET_BUKKIT_STACK = ReflectionUtil + .getMethod(NMS_ITEM_STACK, "getBukkitStack"); + private static final Method GET_COMPONENTS = ReflectionUtil + .getMethod(NMS_ITEM_STACK, "getComponents"); + private static final Method TYPED_DATA_COMPONENT_TYPE = ReflectionUtil + .getMethod(TYPED_DATA_COMPONENT, "type"); + private static final Method TYPED_DATA_COMPONENT_VALUE = ReflectionUtil + .getMethod(TYPED_DATA_COMPONENT, "value"); + private static final Method ENCODE_VALUE = ReflectionUtil + .getMethod(TYPED_DATA_COMPONENT, "encodeValue"); + private static final Method GET_KEY = ReflectionUtil + .getMethod(MAPPED_REGISTRY, "getKey"); + private static final Method ENCODE_START = ReflectionUtil + .getMethod(ENCODER, "encodeStart"); + private static final Method TO_PRETTY_COMPONENT = ReflectionUtil + .getStaticMethod(NBT_UTILS, COMPONENT, TAG); + private static final Method COMPONENT_GET_STRING = ReflectionUtil + .getMethod(COMPONENT, 0, "getString"); + private static final Method TAG_PARSER_READ_VALUE = ReflectionUtil + .getMethod(TAG_PARSER, "readValue"); + + private static final Constructor STRING_READER_CON = + ReflectionUtil.getConstructor(STRING_READER, String.class); + private static final Constructor TAG_PARSER_CON = + ReflectionUtil.getConstructor(TAG_PARSER, STRING_READER); + + private BukkitDataComponentUtil() { + throw new UnsupportedOperationException("Cannot instantiate utility class"); + } + + public static boolean usesDataComponents() { + return DATA_COMPONENT_TYPE != null; + } + + public static ItemStack setComponents(ItemStack bukkitItemStack, Map components) { + if (!usesDataComponents()) { + return bukkitItemStack; + } + try { + Object nbtOpsInstance = Objects.requireNonNull(NBT_OPS_INSTANCE).get(null); + Object registry = Objects.requireNonNull(GET_REGISTRY_ACCESS).invoke(null); + Object registryOps = Objects.requireNonNull(REGISTRY_CREATE_CONTEXT).invoke(registry, nbtOpsInstance); + Object nmsItemStack = Objects.requireNonNull(UNWRAP).invoke(null, bukkitItemStack); + for (Map.Entry entry : components.entrySet()) { + String entryKey = (entry.getKey().contains(":") ? entry.getKey() : "minecraft:" + entry.getKey()) + .toLowerCase(Locale.ROOT); + String entryValue = entry.getValue(); + Object typeStringReader = Objects.requireNonNull(STRING_READER_CON).newInstance(entryKey); + Object resourceLocation = Objects.requireNonNull(RESOURCE_LOCATION_READ).invoke(null, typeStringReader); + Object dataComponentRegistry = Objects + .requireNonNull(DATA_COMPONENT_TYPE_FIELD) + .get(BUILT_IN_REGISTRIES); + Object type = Objects + .requireNonNull(MAPPED_REGISTRY_GET) + .invoke(dataComponentRegistry, resourceLocation); + Object tagParser = Objects + .requireNonNull(TAG_PARSER_CON) + .newInstance(STRING_READER_CON.newInstance(entryValue)); + Object tag = Objects + .requireNonNull(TAG_PARSER_READ_VALUE) + .invoke(tagParser); + Object codec = Objects + .requireNonNull(CODEC_OR_THROW).invoke(type); + Object parsed = Objects + .requireNonNull(PARSE) + .invoke(codec, registryOps, tag); + Object result = Objects + .requireNonNull(DATA_RESULT_GET_OR_THROW) + .invoke(parsed); + Objects.requireNonNull(SET).invoke(nmsItemStack, type, result); + } + return (ItemStack) Objects.requireNonNull(GET_BUKKIT_STACK).invoke(nmsItemStack); + } catch (IllegalAccessException + | InvocationTargetException + | NullPointerException + | InstantiationException + | IllegalStateException e) { + throw new RuntimeException(e); + } + } + + public static Map getComponents(ItemStack bukkitItemStack) { + try { + Map componentMap = new LinkedHashMap<>(); + Object nbtOpsInstance = Objects.requireNonNull(NBT_OPS_INSTANCE).get(null); + Object registry = Objects.requireNonNull(GET_REGISTRY_ACCESS).invoke(null); + Object ops = Objects.requireNonNull(REGISTRY_CREATE_CONTEXT).invoke(registry, nbtOpsInstance); + Object dataComponentRegistry = Objects + .requireNonNull(DATA_COMPONENT_TYPE_FIELD) + .get(BUILT_IN_REGISTRIES); + Object nmsItemStack = Objects.requireNonNull(UNWRAP).invoke(null, bukkitItemStack); + Iterable components = (Iterable) Objects.requireNonNull(GET_COMPONENTS).invoke(nmsItemStack); + for (Object component : components) { + Object componentType = Objects.requireNonNull(TYPED_DATA_COMPONENT_TYPE).invoke(component); + Object componentValue = Objects.requireNonNull(TYPED_DATA_COMPONENT_VALUE).invoke(component); + Object codec = Objects.requireNonNull(CODEC_OR_THROW).invoke(componentType); + Object dataResult = Objects.requireNonNull(ENCODE_START).invoke(codec, ops, componentValue); + Object tag = Objects.requireNonNull(DATA_RESULT_GET_OR_THROW).invoke(dataResult); + Object chatComponent = Objects.requireNonNull(TO_PRETTY_COMPONENT).invoke(null, tag); + Object typeLocation = Objects.requireNonNull(GET_KEY).invoke(dataComponentRegistry, componentType); + String key = typeLocation.toString(); + String value = (String) COMPONENT_GET_STRING.invoke(chatComponent); + componentMap.put(key, value); + } + return componentMap; + } catch (IllegalAccessException | InvocationTargetException | NullPointerException e) { + throw new RuntimeException(e); + } + } +} \ No newline at end of file diff --git a/bukkit/src/main/java/com/clubobsidian/dynamicgui/bukkit/util/BukkitNBTUtil.java b/bukkit/src/main/java/com/clubobsidian/dynamicgui/bukkit/util/BukkitNBTUtil.java index 7919155d7..9d70aa1e0 100644 --- a/bukkit/src/main/java/com/clubobsidian/dynamicgui/bukkit/util/BukkitNBTUtil.java +++ b/bukkit/src/main/java/com/clubobsidian/dynamicgui/bukkit/util/BukkitNBTUtil.java @@ -80,9 +80,7 @@ public static String getTag(ItemStack itemStack) { return null; } return tag.toString(); - } catch (SecurityException | IllegalAccessException | - IllegalArgumentException | InvocationTargetException | - NullPointerException e) { + } catch (Exception e) { e.printStackTrace(); } return null; @@ -96,8 +94,7 @@ public static ItemStack setTag(ItemStack itemStack, String nbt) { nmsItemStack = SET_TAG.getReturnType().equals(void.class) ? nmsItemStack : invokedSetTag; ItemStack bukkitItemStack = (ItemStack) AS_BUKKIT_COPY.invoke(null, nmsItemStack); return bukkitItemStack; - } catch (SecurityException | IllegalAccessException | - IllegalArgumentException | InvocationTargetException e) { + } catch (Exception e) { e.printStackTrace(); } return null; diff --git a/core/src/main/java/com/clubobsidian/dynamicgui/core/factory/SlotFactoryImpl.java b/core/src/main/java/com/clubobsidian/dynamicgui/core/factory/SlotFactoryImpl.java index 3649ba2e9..8839170b0 100644 --- a/core/src/main/java/com/clubobsidian/dynamicgui/core/factory/SlotFactoryImpl.java +++ b/core/src/main/java/com/clubobsidian/dynamicgui/core/factory/SlotFactoryImpl.java @@ -31,13 +31,15 @@ public class SlotFactoryImpl implements SlotFactory { public Slot create(int index, int amount, String icon, String name, String nbt, short data, boolean glow, boolean movable, Boolean close, List lore, - List enchants, List itemFlags, String modelProvider, + List enchants, List itemFlags, + Map dataComponents, String modelProvider, String modelData, FunctionTree functionTree, int updateInterval, Map metadata) { return new SimpleSlot(index, amount, icon, name, nbt, data, glow, movable, close, lore, enchants, itemFlags, - modelProvider, modelData, functionTree, - updateInterval, metadata); + dataComponents, modelProvider, modelData, + functionTree, updateInterval, metadata + ); } } \ No newline at end of file diff --git a/core/src/main/java/com/clubobsidian/dynamicgui/core/gui/SimpleSlot.java b/core/src/main/java/com/clubobsidian/dynamicgui/core/gui/SimpleSlot.java index 0e8dcd1b7..289dd2be3 100644 --- a/core/src/main/java/com/clubobsidian/dynamicgui/core/gui/SimpleSlot.java +++ b/core/src/main/java/com/clubobsidian/dynamicgui/core/gui/SimpleSlot.java @@ -51,6 +51,7 @@ public class SimpleSlot implements Slot { private final List lore; private final List enchants; private final List itemFlags; + private final Map dataComponents; private final String modelProvider; private final String modelData; private Boolean close; @@ -67,6 +68,7 @@ public class SimpleSlot implements Slot { public SimpleSlot(int index, int amount, String icon, String name, String nbt, short data, boolean glow, boolean movable, Boolean close, List lore, List enchants, List itemFlags, + Map dataComponents, String modelProvider, String modelData, FunctionTree functions, int updateInterval, Map metadata) { this.icon = icon; @@ -78,6 +80,7 @@ public SimpleSlot(int index, int amount, String icon, String name, String nbt, s this.lore = Collections.unmodifiableList(lore); this.enchants = Collections.unmodifiableList(enchants); this.itemFlags = Collections.unmodifiableList(itemFlags); + this.dataComponents = Collections.unmodifiableMap(dataComponents); this.modelProvider = modelProvider; this.modelData = modelData; this.close = close; @@ -156,6 +159,11 @@ public List getItemFlags() { return this.itemFlags; } + @Override + public Map getDataComponents() { + return this.dataComponents; + } + @Override public Boolean getClose() { return this.close; @@ -244,6 +252,16 @@ public ItemStackWrapper buildItemStack(@NotNull PlayerWrapper playerWrappe if (this.nbt != null && !this.nbt.equals("")) { builderItem.setNBT(ReplacerManager.get().replace(this.nbt, playerWrapper)); } + + if (!this.dataComponents.isEmpty()) { + Map replacedMap = new HashMap<>(); + for (Map.Entry entry : this.dataComponents.entrySet()) { + String key = entry.getKey(); + String value = ReplacerManager.get().replace(entry.getValue(), playerWrapper); + replacedMap.put(key, value); + }; + builderItem.setDataComponents(replacedMap); + } } this.itemStack = builderItem; diff --git a/core/src/main/java/com/clubobsidian/dynamicgui/core/manager/GuiManagerImpl.java b/core/src/main/java/com/clubobsidian/dynamicgui/core/manager/GuiManagerImpl.java index b89fa715c..5b65f2314 100644 --- a/core/src/main/java/com/clubobsidian/dynamicgui/core/manager/GuiManagerImpl.java +++ b/core/src/main/java/com/clubobsidian/dynamicgui/core/manager/GuiManagerImpl.java @@ -473,6 +473,7 @@ private List createSlots(GuiToken guiToken) { } List itemFlags = slotToken.getItemFlags(); + Map dataComponents = slotToken.getDataComponents(); int amount = slotToken.getAmount(); @@ -491,7 +492,7 @@ private List createSlots(GuiToken guiToken) { Map metadata = slotToken.getMetadata(); slots.add(new SimpleSlot(index, amount, icon, name, nbt, data, glow, movable, - close, lore, enchants, itemFlags, modelProvider, modelData, + close, lore, enchants, itemFlags, dataComponents, modelProvider, modelData, slotToken.getFunctionTree(), updateInterval, metadata)); } diff --git a/core/src/main/java/com/clubobsidian/dynamicgui/core/util/ReflectionUtil.java b/core/src/main/java/com/clubobsidian/dynamicgui/core/util/ReflectionUtil.java index 9422143ea..b3d62a780 100644 --- a/core/src/main/java/com/clubobsidian/dynamicgui/core/util/ReflectionUtil.java +++ b/core/src/main/java/com/clubobsidian/dynamicgui/core/util/ReflectionUtil.java @@ -16,6 +16,9 @@ package com.clubobsidian.dynamicgui.core.util; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; @@ -67,7 +70,10 @@ public static Method getMethodLossy(Class cl, String... methods) { return null; } - public static Method getMethod(Class cl, String... methods) { + public static Method getMethod(@Nullable Class cl, String... methods) { + if (cl == null) { + return null; + } for (Method m : cl.getDeclaredMethods()) { for (String methodName : methods) { if (m.getName().equals(methodName)) { @@ -79,7 +85,25 @@ public static Method getMethod(Class cl, String... methods) { return null; } + public static Method getMethod(@Nullable Class cl, int paramCount, String... methods) { + if (cl == null) { + return null; + } + for (Method m : cl.getDeclaredMethods()) { + for (String methodName : methods) { + if (m.getName().equals(methodName) && m.getParameterTypes().length == paramCount) { + m.setAccessible(true); + return m; + } + } + } + return null; + } + public static Method getMethodByParams(Class cl, String methodName, Class... params) { + if (cl == null) { + return null; + } try { Method method = cl.getDeclaredMethod(methodName, params); method.setAccessible(true); @@ -110,6 +134,9 @@ public static Method getMethodByReturnType(Class searchIn, Class returnTyp } public static Method getMethodByReturnType(Class searchIn, Class returnType, Class... params) { + if (searchIn == null) { + return null; + } for (Method m : searchIn.getDeclaredMethods()) { if (m.getReturnType().equals(returnType) && Arrays.equals(m.getParameterTypes(), params)) { m.setAccessible(true); @@ -180,6 +207,9 @@ public static Field getDeclaredField(Object fieldIn, Class clazz, Class re } public static Method getStaticMethod(Class searchIn, Class returnType) { + if (searchIn == null) { + return null; + } for (Method m : searchIn.getDeclaredMethods()) { if (Modifier.isStatic(m.getModifiers()) && m.getReturnType().equals(returnType)) { m.setAccessible(true); @@ -190,6 +220,9 @@ public static Method getStaticMethod(Class searchIn, Class returnType) { } public static Method getStaticMethod(Class searchIn, Class returnType, Class... params) { + if (searchIn == null) { + return null; + } for (Method m : searchIn.getDeclaredMethods()) { if (Modifier.isStatic(m.getModifiers()) && m.getReturnType().equals(returnType) @@ -212,4 +245,16 @@ public static Method getMethod(Class searchIn, Class returnType, Class< } return null; } + + public static @Nullable Constructor getConstructor(@Nullable Class searchIn, Class... params) { + if (searchIn == null) { + return null; + } + for (Constructor con : searchIn.getDeclaredConstructors()) { + if (Arrays.equals(con.getParameterTypes(), params)) { + return con; + } + } + return null; + } } \ No newline at end of file diff --git a/core/src/test/java/com/clubobsidian/dynamicgui/core/test/mock/MockFactory.java b/core/src/test/java/com/clubobsidian/dynamicgui/core/test/mock/MockFactory.java index e2e0871fc..602668532 100644 --- a/core/src/test/java/com/clubobsidian/dynamicgui/core/test/mock/MockFactory.java +++ b/core/src/test/java/com/clubobsidian/dynamicgui/core/test/mock/MockFactory.java @@ -52,10 +52,7 @@ import org.mockito.Mockito; import java.io.File; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.UUID; +import java.util.*; public class MockFactory { @@ -163,6 +160,7 @@ public Slot createSlot(int index, String type, List lore, List(), + Collections.EMPTY_MAP, null, null, new SimpleFunctionTree(), diff --git a/core/src/test/java/com/clubobsidian/dynamicgui/core/test/mock/inventory/MockItemStackWrapper.java b/core/src/test/java/com/clubobsidian/dynamicgui/core/test/mock/inventory/MockItemStackWrapper.java index ae41bb754..3d6f724e2 100644 --- a/core/src/test/java/com/clubobsidian/dynamicgui/core/test/mock/inventory/MockItemStackWrapper.java +++ b/core/src/test/java/com/clubobsidian/dynamicgui/core/test/mock/inventory/MockItemStackWrapper.java @@ -104,7 +104,7 @@ public String getNBT() { } @Override - public void setNBT(@NotNull String nbt) { + public void setNBT(String nbt) { Objects.requireNonNull(nbt); this.getItemStack().setNBT(nbt); } diff --git a/parser/src/main/java/com/clubobsidian/dynamicgui/parser/slot/SimpleSlotToken.java b/parser/src/main/java/com/clubobsidian/dynamicgui/parser/slot/SimpleSlotToken.java index 086205565..0332744a9 100644 --- a/parser/src/main/java/com/clubobsidian/dynamicgui/parser/slot/SimpleSlotToken.java +++ b/parser/src/main/java/com/clubobsidian/dynamicgui/parser/slot/SimpleSlotToken.java @@ -26,10 +26,7 @@ import com.clubobsidian.dynamicgui.parser.macro.SimpleMacroToken; import com.clubobsidian.wrappy.ConfigurationSection; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; public class SimpleSlotToken implements SlotToken { @@ -50,6 +47,7 @@ public class SimpleSlotToken implements SlotToken { private final List lore; private final List enchants; private final List itemFlags; + private final Map dataComponents; private final String modelProvider; private final String modelData; private final int updateInterval; @@ -78,6 +76,9 @@ public SimpleSlotToken(int index, ConfigurationSection section, List this.lore = this.macroParser.parseListMacros(section.getStringList("lore")); this.enchants = this.macroParser.parseListMacros(section.getStringList("enchants")); this.itemFlags = this.macroParser.parseListMacros(section.getStringList("item-flags")); + this.dataComponents = this.parseStringMap( + section.getMap("data-components", String.class, String.class) + ); this.modelProvider = this.macroParser.parseStringMacros(section.getString("model.provider")); this.modelData = this.macroParser.parseStringMacros(section.getString("model.data")); this.updateInterval = this.parseUpdateInterval(section.getString("update-interval")); @@ -87,6 +88,19 @@ public SimpleSlotToken(int index, ConfigurationSection section, List this.metadata = this.parseMetadata(metadataSection); } + private Map parseStringMap(Map map) { + if (map == null) { + return Collections.emptyMap(); + } + Map parsedMap = new HashMap<>(); + for (Map.Entry entry : map.entrySet()) { + String key = this.macroParser.parseStringMacros(entry.getKey()); + String value = this.macroParser.parseStringMacros(entry.getValue()); + parsedMap.put(key, value); + } + return parsedMap; + } + private int parseAmount(int amount) { if (amount == 0) { return 1; @@ -218,6 +232,11 @@ public List getItemFlags() { return this.itemFlags; } + @Override + public Map getDataComponents() { + return this.dataComponents; + } + @Override public String getModelProvider() { return this.modelProvider;