Skip to content

Commit

Permalink
Merge pull request #350 from ClubObsidian/feature/components
Browse files Browse the repository at this point in the history
Add 1.21 component support
  • Loading branch information
virustotalop authored Sep 22, 2024
2 parents f4ec493 + ba61a06 commit c000dc4
Show file tree
Hide file tree
Showing 14 changed files with 372 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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<String> lore,
List<EnchantmentWrapper> enchants, List<String> itemFlags,
String modelProvider, String modelData,
Map<String, String> dataComponents, String modelProvider, String modelData,
FunctionTree functions, int updateInterval, Map<String, String> metadata);

}
24 changes: 22 additions & 2 deletions api/src/main/java/com/clubobsidian/dynamicgui/api/gui/Slot.java
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,17 @@ public interface Slot extends Serializable, FunctionOwner, AnimationHolder, Meta
*/
@Unmodifiable List<String> 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<String, String> getDataComponents();

/**
* Gets whether the slot should close
*
Expand Down Expand Up @@ -234,6 +245,7 @@ class Builder {
private transient final List<String> lore = new ArrayList<>();
private transient final List<EnchantmentWrapper> enchants = new ArrayList<>();
private transient final List<String> itemFlags = new ArrayList<>();
private transient final Map<String, String> dataComponents = new HashMap<>();
private transient String modelProvider;
private transient String modelData;
private transient int index;
Expand Down Expand Up @@ -483,6 +495,11 @@ public Builder addItemFlag(Collection<String> itemFlags) {
return this;
}

private Builder addDataComponents(Map<String, String> dataComponents) {
this.dataComponents.putAll(dataComponents);
return this;
}

/**
* Sets the function tree
*
Expand Down Expand Up @@ -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
*
Expand All @@ -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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

import java.io.Serializable;
import java.util.List;
import java.util.Map;

public abstract class ItemStackWrapper<T> implements Serializable {

Expand Down Expand Up @@ -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<String, String> getDataComponents();

/**
* Sets the data components underlying item stack.
*
* @param components the components to set
*/
public abstract void setDataComponents(Map<String, String> components);

/**
* Sets whether the underlying item stack should glow.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ public interface SlotToken extends Serializable {

List<String> getItemFlags();

Map<String, String> getDataComponents();

String getModelProvider();

String getModelData();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -194,11 +195,14 @@ public List<EnchantmentWrapper> 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);
Expand Down Expand Up @@ -231,6 +235,21 @@ public void setNBT(@NotNull String nbt) {
this.setItemStack(newItemStack);
}

@Override
public Map<String, String> getDataComponents() {
return BukkitDataComponentUtil.usesDataComponents()
? BukkitDataComponentUtil.getComponents(this.itemStack)
: Collections.emptyMap();
}

@Override
public void setDataComponents(Map<String, String> components) {
if (!BukkitDataComponentUtil.usesDataComponents()) {
return;
}
this.itemStack = (T) BukkitDataComponentUtil.setComponents(this.itemStack, components);
}

@Override
public void setGlowing(boolean glowing) {
ItemStack item = this.getItemStack();
Expand Down
Original file line number Diff line number Diff line change
@@ -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<String, String> 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<String, String> 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<String, String> getComponents(ItemStack bukkitItemStack) {
try {
Map<String, String> 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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down
Loading

0 comments on commit c000dc4

Please sign in to comment.