From cf4b420fbc94591663ef0a5bf56849e39795cfac Mon Sep 17 00:00:00 2001 From: Aya <31237389+tal5@users.noreply.github.com> Date: Thu, 7 Aug 2025 23:45:19 +0100 Subject: [PATCH 01/10] Item data component framework --- .../denizen/paper/PaperModule.java | 7 + .../ComponentAdaptersRegistry.java | 10 ++ .../paper/datacomponents/FoodAdapter.java | 52 ++++++ .../denizen/paper/properties/ItemRemoved.java | 59 ++++++ .../paper/utilities/DataComponentAdapter.java | 170 ++++++++++++++++++ .../nms/v1_21/helpers/ItemHelperImpl.java | 6 + 6 files changed, 304 insertions(+) create mode 100644 paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/ComponentAdaptersRegistry.java create mode 100644 paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/FoodAdapter.java create mode 100644 paper/src/main/java/com/denizenscript/denizen/paper/properties/ItemRemoved.java create mode 100644 paper/src/main/java/com/denizenscript/denizen/paper/utilities/DataComponentAdapter.java diff --git a/paper/src/main/java/com/denizenscript/denizen/paper/PaperModule.java b/paper/src/main/java/com/denizenscript/denizen/paper/PaperModule.java index 58ff60ed66..8dfc598483 100644 --- a/paper/src/main/java/com/denizenscript/denizen/paper/PaperModule.java +++ b/paper/src/main/java/com/denizenscript/denizen/paper/PaperModule.java @@ -6,6 +6,7 @@ import com.denizenscript.denizen.nms.interfaces.packets.PacketOutChat; import com.denizenscript.denizen.objects.EntityTag; import com.denizenscript.denizen.objects.ItemTag; +import com.denizenscript.denizen.paper.datacomponents.ComponentAdaptersRegistry; import com.denizenscript.denizen.paper.events.*; import com.denizenscript.denizen.paper.properties.*; import com.denizenscript.denizen.paper.tags.PaperTagBase; @@ -127,6 +128,12 @@ public static void init() { PropertyParser.registerProperty(EntityWitherInvulnerable.class, EntityTag.class); PropertyParser.registerProperty(ItemArmorStand.class, ItemTag.class); + // Component adapters + if (NMSHandler.getVersion().isAtLeast(NMSVersion.v1_21)) { + PropertyParser.registerProperty(ItemRemoved.class, ItemTag.class); + ComponentAdaptersRegistry.register(); + } + // Paper object extensions PaperElementExtensions.register(); PaperEntityExtensions.register(); diff --git a/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/ComponentAdaptersRegistry.java b/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/ComponentAdaptersRegistry.java new file mode 100644 index 0000000000..730418cbbd --- /dev/null +++ b/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/ComponentAdaptersRegistry.java @@ -0,0 +1,10 @@ +package com.denizenscript.denizen.paper.datacomponents; + +import com.denizenscript.denizen.paper.utilities.DataComponentAdapter; + +public class ComponentAdaptersRegistry { + + public static void register() { + DataComponentAdapter.register(new FoodAdapter()); + } +} diff --git a/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/FoodAdapter.java b/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/FoodAdapter.java new file mode 100644 index 0000000000..acc49d11a5 --- /dev/null +++ b/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/FoodAdapter.java @@ -0,0 +1,52 @@ +package com.denizenscript.denizen.paper.datacomponents; + +import com.denizenscript.denizen.paper.utilities.DataComponentAdapter; +import com.denizenscript.denizencore.objects.Mechanism; +import com.denizenscript.denizencore.objects.core.ElementTag; +import com.denizenscript.denizencore.objects.core.MapTag; +import io.papermc.paper.datacomponent.DataComponentTypes; +import io.papermc.paper.datacomponent.item.FoodProperties; + +public class FoodAdapter extends DataComponentAdapter { + + // <--[property] + // @object ItemTag + // @name food + // @input MapTag + // @description + // Controls an item's food <@link language Item Components>. + // The map includes keys: + // - "nutrition", ElementTag(Number) representing the amount of food points restored by this item. + // - "saturation", ElementTag(Decimal) representing the amount of saturation points restored by this item. + // - "can_always_eat", ElementTag(Boolean) controlling whether the item can always be eaten, even if the player isn't hungry. + // --> + + // <--[tag] + // @attribute + // @returns MapTag + // @description + // Gets the material's default food value, in the same format as <@link tag ItemTag.food>. + // --> + + public FoodAdapter() { + super(DataComponentTypes.FOOD, MapTag.class, "food"); + } + + @Override + public MapTag toDenizen(FoodProperties value) { + MapTag foodData = new MapTag(); + foodData.putObject("nutrition", new ElementTag(value.nutrition())); + foodData.putObject("saturation", new ElementTag(value.saturation())); + foodData.putObject("can_always_eat", new ElementTag(value.canAlwaysEat())); + return foodData; + } + + @Override + public FoodProperties toPaper(MapTag value, Mechanism mechanism) { + FoodProperties.Builder builder = FoodProperties.food(); + setIfValid(builder::nutrition, value, "nutrition", "number", ElementTag::isInt, ElementTag::asInt, mechanism); + setIfValid(builder::saturation, value, "saturation", "decimal number", ElementTag::isFloat, ElementTag::asFloat, mechanism); + setIfValid(builder::canAlwaysEat, value, "can_always_eat", "boolean", ElementTag::isBoolean, ElementTag::asBoolean, mechanism); + return builder.build(); + } +} diff --git a/paper/src/main/java/com/denizenscript/denizen/paper/properties/ItemRemoved.java b/paper/src/main/java/com/denizenscript/denizen/paper/properties/ItemRemoved.java new file mode 100644 index 0000000000..73a98dcd88 --- /dev/null +++ b/paper/src/main/java/com/denizenscript/denizen/paper/properties/ItemRemoved.java @@ -0,0 +1,59 @@ +package com.denizenscript.denizen.paper.properties; + +import com.denizenscript.denizen.objects.ItemTag; +import com.denizenscript.denizen.objects.properties.item.ItemProperty; +import com.denizenscript.denizen.paper.utilities.DataComponentAdapter; +import com.denizenscript.denizencore.objects.Mechanism; +import com.denizenscript.denizencore.objects.core.ElementTag; +import com.denizenscript.denizencore.objects.core.ListTag; +import io.papermc.paper.datacomponent.DataComponentType; + +public class ItemRemoved extends ItemProperty { + + // <--[property] + // @object ItemTag + // @name removed + // @input ListTag + // @description + // Controls the properties explicitly removed from an item. + // This can be used to remove item's default behavior, such as making consumable items un-consumable. + // See also <@link language Item Components>. + // --> + + public static boolean describes(ItemTag item) { + return !item.getItemStack().isEmpty(); + } + + @Override + public ListTag getPropertyValue() { + return new ListTag(getMaterial().getDefaultDataTypes(), + componentType -> getItemStack().isDataOverridden(componentType) && !getItemStack().hasData(componentType), + componentType -> new ElementTag(componentType.key().asMinimalString(), true)); + } + + @Override + public boolean isDefaultValue(ListTag value) { + return value.isEmpty(); + } + + @Override + public void setPropertyValue(ListTag value, Mechanism mechanism) { + for (String input : value) { + DataComponentType componentType = DataComponentAdapter.getComponentType(input); + if (componentType == null) { + mechanism.echoError("Invalid type to remove '" + input + "' specified: must be a valid property or item component name."); + continue; + } + getItemStack().unsetData(componentType); + } + } + + @Override + public String getPropertyId() { + return "removed"; + } + + public static void register() { + autoRegister("removed", ItemRemoved.class, ListTag.class, false); + } +} diff --git a/paper/src/main/java/com/denizenscript/denizen/paper/utilities/DataComponentAdapter.java b/paper/src/main/java/com/denizenscript/denizen/paper/utilities/DataComponentAdapter.java new file mode 100644 index 0000000000..47168af9b7 --- /dev/null +++ b/paper/src/main/java/com/denizenscript/denizen/paper/utilities/DataComponentAdapter.java @@ -0,0 +1,170 @@ +package com.denizenscript.denizen.paper.utilities; + +import com.denizenscript.denizen.objects.ItemTag; +import com.denizenscript.denizen.objects.MaterialTag; +import com.denizenscript.denizen.objects.properties.item.ItemComponentsPatch; +import com.denizenscript.denizen.objects.properties.item.ItemProperty; +import com.denizenscript.denizen.utilities.Utilities; +import com.denizenscript.denizencore.objects.Mechanism; +import com.denizenscript.denizencore.objects.ObjectTag; +import com.denizenscript.denizencore.objects.core.ElementTag; +import com.denizenscript.denizencore.objects.core.MapTag; +import com.denizenscript.denizencore.objects.properties.PropertyParser; +import com.denizenscript.denizencore.utilities.CoreUtilities; +import io.papermc.paper.datacomponent.DataComponentType; +import org.bukkit.Material; +import org.bukkit.Registry; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; + +public abstract class DataComponentAdapter { + + // <--[language] + // @name Item Components + // @group Minecraft Logic + // @description + // Minecraft item components (see <@link url https://minecraft.wiki/w/Data_component_format>) are managed as follows: + // Each item type has a default set of component values; a food item will have food components by default, a tool item will have tool components by default, etc. + // Different items can override their type's default components, either by setting values that weren't there previously (e.g. making an inedible item edible), or by removing values that are there by default (e.g. making a shield item that can't block). + // Item's overrides can later be reset, so that their type's default value is used again. + // + // In Denizen, different item components are represented by item properties. + // These properties allow both setting a component override on an item, and clearing it by providing no input. + // Item properties' name will generally match their respective item component's name, but not always! + // Due to this, features that take item component names as input (such as <@link tag ItemTag.is_overridden>) accept both Minecraft component names and Denizen property names. + // + // Here is an example of applying all of this in Denizen: + // + // # We define a default apple item + // - define apple + // # We remove the apple's "food" component, making eating it restore no food points (it is still consumable due to the "consumable" component). + // - adjust def:apple removed:food + // # This check will pass, as the apple's "food" component is overridden to have no value. + // - if <[apple].is_overridden[food]>: + // - narrate "The apple has a changed food component! It will behave differently to a normal apple." + // # We reset the apple item's food component, making it a normal apple. + // - adjust def:apple food: + // + // --> + + public static final Map COMPONENTS_BY_PROPERTY = new HashMap<>(); + public static final String[] EMPTY_STRING_ARRAY = new String[0]; + + public static DataComponentType getComponentType(String name) { + String nameLower = CoreUtilities.toLowerCase(name); + DataComponentType componentType = Registry.DATA_COMPONENT_TYPE.get(Utilities.parseNamespacedKey(nameLower)); + if (componentType == null) { + componentType = DataComponentAdapter.COMPONENTS_BY_PROPERTY.get(nameLower); + } + return componentType; + } + + public static void register(DataComponentAdapter adapter) { + DataComponentAdapter.Property.currentlyRegisteringComponentAdapter = adapter; + PropertyParser.registerPropertyGetter( + item -> !item.getItemStack().isEmpty() ? adapter.new Property(item) : null, + ItemTag.class, EMPTY_STRING_ARRAY, EMPTY_STRING_ARRAY, DataComponentAdapter.Property.class); + DataComponentAdapter.Property.currentlyRegisteringComponentAdapter = null; + MaterialTag.tagProcessor.registerTag(adapter.denizenType, adapter.name, (attribute, materialTag) -> { + Material material = materialTag.getMaterial(); + if (!material.isItem()) { + attribute.echoError("Cannot get item component value from a block material."); + return null; + } + TP internalValue = material.getDefaultData(adapter.componentType); + return internalValue != null ? adapter.toDenizen(internalValue) : null; + }); + COMPONENTS_BY_PROPERTY.put(adapter.name, adapter.componentType); + ItemComponentsPatch.registerHandledComponent(adapter.componentType.key().value()); + } + + static { + + // <--[tag] + // @attribute ]> + // @returns ElementTag(Boolean) + // @description + // Returns whether an item has a specific item component type overridden, see <@link language Item Components>. + // --> + ItemTag.tagProcessor.registerTag(ElementTag.class, ElementTag.class, "is_overridden", (attribute, object, param) -> { + DataComponentType componentType = getComponentType(param.asString()); + if (componentType == null) { + attribute.echoError("Invalid type specified, must be a valid item component type or property name."); + return null; + } + return new ElementTag(object.getItemStack().isDataOverridden(componentType)); + }); + } + + DataComponentType.Valued componentType; + Class denizenType; + String name; + + public DataComponentAdapter(DataComponentType.Valued componentType, Class denizenType, String name) { + this.componentType = componentType; + this.denizenType = denizenType; + this.name = name; + } + + public abstract TD toDenizen(TP value); + + public abstract TP toPaper(TD value, Mechanism mechanism); + + public static void setIfValid(Consumer setter, MapTag data, String key, String type, Predicate checker, Function converter, Mechanism mechanism) { + ElementTag value = data.getElement(key); + if (value == null) { + return; + } + T converted; + if (!checker.test(value) || (converted = converter.apply(value)) == null) { + mechanism.echoError("Invalid '" + key + "' specified: must be a " + type + '.'); + return; + } + setter.accept(converted); + } + + public class Property extends ItemProperty { + + private static DataComponentAdapter currentlyRegisteringComponentAdapter; + + public Property(ItemTag item) { + this.object = item; + } + + @Override + public TD getPropertyValue() { + TP internalValue = getItemStack().getData(componentType); + return internalValue == null ? null : toDenizen(internalValue); + } + + @Override + public TD getPropertyValueNoDefault() { + return getItemStack().isDataOverridden(componentType) ? getPropertyValue() : null; + } + + @Override + public void setPropertyValue(TD value, Mechanism mechanism) { + if (value == null) { + getItemStack().resetData(componentType); + return; + } + TP converted = toPaper(value, mechanism); + if (converted != null) { + getItemStack().setData(componentType, converted); + } + } + + @Override + public String getPropertyId() { + return name; + } + + public static void register() { + autoRegisterNullable(currentlyRegisteringComponentAdapter.name, DataComponentAdapter.Property.class, currentlyRegisteringComponentAdapter.denizenType, false); + } + } +} diff --git a/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/helpers/ItemHelperImpl.java b/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/helpers/ItemHelperImpl.java index 14298cc907..0d49eb88c7 100644 --- a/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/helpers/ItemHelperImpl.java +++ b/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/helpers/ItemHelperImpl.java @@ -450,6 +450,12 @@ public MapTag getRawComponentsPatch(ItemStack item, boolean excludeHandled) { } RegistryOps registryOps = CraftRegistry.getMinecraftRegistry().createSerializationContext(NbtOps.INSTANCE); CompoundTag nmsPatch = (CompoundTag) DataComponentPatch.CODEC.encodeStart(registryOps, patch).getOrThrow(); + if (excludeHandled) { + nmsPatch.keySet().removeIf(s -> s.charAt(0) == '!'); + if (nmsPatch.isEmpty()) { + return new MapTag(); + } + } MapTag rawComponents = (MapTag) ItemRawNBT.nbtTagToObject(NBTAdapter.toAPI(nmsPatch)); rawComponents.putObject(ItemComponentsPatch.DATA_VERSION_KEY, new ElementTag(CraftMagicNumbers.INSTANCE.getDataVersion())); return rawComponents; From a6600d653441c4a67c4d89323defed6c58f98a3e Mon Sep 17 00:00:00 2001 From: Aya <31237389+tal5@users.noreply.github.com> Date: Thu, 7 Aug 2025 23:51:47 +0100 Subject: [PATCH 02/10] Only register mapping when needed --- .../denizen/paper/utilities/DataComponentAdapter.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/paper/src/main/java/com/denizenscript/denizen/paper/utilities/DataComponentAdapter.java b/paper/src/main/java/com/denizenscript/denizen/paper/utilities/DataComponentAdapter.java index 47168af9b7..487a737513 100644 --- a/paper/src/main/java/com/denizenscript/denizen/paper/utilities/DataComponentAdapter.java +++ b/paper/src/main/java/com/denizenscript/denizen/paper/utilities/DataComponentAdapter.java @@ -78,8 +78,11 @@ public static void register(DataComponentAdapter Date: Fri, 8 Aug 2025 00:12:03 +0100 Subject: [PATCH 03/10] Field modifiers --- .../denizen/paper/utilities/DataComponentAdapter.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/paper/src/main/java/com/denizenscript/denizen/paper/utilities/DataComponentAdapter.java b/paper/src/main/java/com/denizenscript/denizen/paper/utilities/DataComponentAdapter.java index 487a737513..ccd69ebf36 100644 --- a/paper/src/main/java/com/denizenscript/denizen/paper/utilities/DataComponentAdapter.java +++ b/paper/src/main/java/com/denizenscript/denizen/paper/utilities/DataComponentAdapter.java @@ -103,9 +103,9 @@ public static void register(DataComponentAdapter componentType; - Class denizenType; - String name; + public final DataComponentType.Valued componentType; + public final Class denizenType; + public final String name; public DataComponentAdapter(DataComponentType.Valued componentType, Class denizenType, String name) { this.componentType = componentType; From c14ac8793c3f0f6cd22a48692257f48adb9b2e70 Mon Sep 17 00:00:00 2001 From: Aya <31237389+tal5@users.noreply.github.com> Date: Fri, 8 Aug 2025 00:21:39 +0100 Subject: [PATCH 04/10] Reorder --- .../denizen/paper/datacomponents/FoodAdapter.java | 2 +- .../denizen/paper/utilities/DataComponentAdapter.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/FoodAdapter.java b/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/FoodAdapter.java index acc49d11a5..2c14856a78 100644 --- a/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/FoodAdapter.java +++ b/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/FoodAdapter.java @@ -7,7 +7,7 @@ import io.papermc.paper.datacomponent.DataComponentTypes; import io.papermc.paper.datacomponent.item.FoodProperties; -public class FoodAdapter extends DataComponentAdapter { +public class FoodAdapter extends DataComponentAdapter { // <--[property] // @object ItemTag diff --git a/paper/src/main/java/com/denizenscript/denizen/paper/utilities/DataComponentAdapter.java b/paper/src/main/java/com/denizenscript/denizen/paper/utilities/DataComponentAdapter.java index ccd69ebf36..65e6307f3d 100644 --- a/paper/src/main/java/com/denizenscript/denizen/paper/utilities/DataComponentAdapter.java +++ b/paper/src/main/java/com/denizenscript/denizen/paper/utilities/DataComponentAdapter.java @@ -21,7 +21,7 @@ import java.util.function.Function; import java.util.function.Predicate; -public abstract class DataComponentAdapter { +public abstract class DataComponentAdapter { // <--[language] // @name Item Components @@ -63,7 +63,7 @@ public static DataComponentType getComponentType(String name) { return componentType; } - public static void register(DataComponentAdapter adapter) { + public static void register(DataComponentAdapter adapter) { DataComponentAdapter.Property.currentlyRegisteringComponentAdapter = adapter; PropertyParser.registerPropertyGetter( item -> !item.getItemStack().isEmpty() ? adapter.new Property(item) : null, From 8e1fe581ca5928ac0afd643739003735c67a80bb Mon Sep 17 00:00:00 2001 From: Aya <31237389+tal5@users.noreply.github.com> Date: Fri, 8 Aug 2025 00:29:19 +0100 Subject: [PATCH 05/10] Move `DataComponentAdapter` --- .../denizen/paper/datacomponents/ComponentAdaptersRegistry.java | 2 -- .../{utilities => datacomponents}/DataComponentAdapter.java | 2 +- .../denizenscript/denizen/paper/datacomponents/FoodAdapter.java | 1 - .../com/denizenscript/denizen/paper/properties/ItemRemoved.java | 2 +- 4 files changed, 2 insertions(+), 5 deletions(-) rename paper/src/main/java/com/denizenscript/denizen/paper/{utilities => datacomponents}/DataComponentAdapter.java (99%) diff --git a/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/ComponentAdaptersRegistry.java b/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/ComponentAdaptersRegistry.java index 730418cbbd..c4b781ed1a 100644 --- a/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/ComponentAdaptersRegistry.java +++ b/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/ComponentAdaptersRegistry.java @@ -1,7 +1,5 @@ package com.denizenscript.denizen.paper.datacomponents; -import com.denizenscript.denizen.paper.utilities.DataComponentAdapter; - public class ComponentAdaptersRegistry { public static void register() { diff --git a/paper/src/main/java/com/denizenscript/denizen/paper/utilities/DataComponentAdapter.java b/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/DataComponentAdapter.java similarity index 99% rename from paper/src/main/java/com/denizenscript/denizen/paper/utilities/DataComponentAdapter.java rename to paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/DataComponentAdapter.java index 65e6307f3d..77bba0049b 100644 --- a/paper/src/main/java/com/denizenscript/denizen/paper/utilities/DataComponentAdapter.java +++ b/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/DataComponentAdapter.java @@ -1,4 +1,4 @@ -package com.denizenscript.denizen.paper.utilities; +package com.denizenscript.denizen.paper.datacomponents; import com.denizenscript.denizen.objects.ItemTag; import com.denizenscript.denizen.objects.MaterialTag; diff --git a/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/FoodAdapter.java b/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/FoodAdapter.java index 2c14856a78..88a7722eef 100644 --- a/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/FoodAdapter.java +++ b/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/FoodAdapter.java @@ -1,6 +1,5 @@ package com.denizenscript.denizen.paper.datacomponents; -import com.denizenscript.denizen.paper.utilities.DataComponentAdapter; import com.denizenscript.denizencore.objects.Mechanism; import com.denizenscript.denizencore.objects.core.ElementTag; import com.denizenscript.denizencore.objects.core.MapTag; diff --git a/paper/src/main/java/com/denizenscript/denizen/paper/properties/ItemRemoved.java b/paper/src/main/java/com/denizenscript/denizen/paper/properties/ItemRemoved.java index 73a98dcd88..593fd6d2f4 100644 --- a/paper/src/main/java/com/denizenscript/denizen/paper/properties/ItemRemoved.java +++ b/paper/src/main/java/com/denizenscript/denizen/paper/properties/ItemRemoved.java @@ -2,7 +2,7 @@ import com.denizenscript.denizen.objects.ItemTag; import com.denizenscript.denizen.objects.properties.item.ItemProperty; -import com.denizenscript.denizen.paper.utilities.DataComponentAdapter; +import com.denizenscript.denizen.paper.datacomponents.DataComponentAdapter; import com.denizenscript.denizencore.objects.Mechanism; import com.denizenscript.denizencore.objects.core.ElementTag; import com.denizenscript.denizencore.objects.core.ListTag; From ab944f383837b85f769628b8707f4f5fc4d3419e Mon Sep 17 00:00:00 2001 From: Aya <31237389+tal5@users.noreply.github.com> Date: Fri, 8 Aug 2025 00:36:32 +0100 Subject: [PATCH 06/10] Meta cleanups --- .../denizen/paper/datacomponents/DataComponentAdapter.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/DataComponentAdapter.java b/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/DataComponentAdapter.java index 77bba0049b..4797e3ed2e 100644 --- a/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/DataComponentAdapter.java +++ b/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/DataComponentAdapter.java @@ -30,14 +30,14 @@ public abstract class DataComponentAdapter { // Minecraft item components (see <@link url https://minecraft.wiki/w/Data_component_format>) are managed as follows: // Each item type has a default set of component values; a food item will have food components by default, a tool item will have tool components by default, etc. // Different items can override their type's default components, either by setting values that weren't there previously (e.g. making an inedible item edible), or by removing values that are there by default (e.g. making a shield item that can't block). - // Item's overrides can later be reset, so that their type's default value is used again. + // Items' overrides can later be reset, making them use their type's default values again. // // In Denizen, different item components are represented by item properties. - // These properties allow both setting a component override on an item, and clearing it by providing no input. + // These properties allow both setting a component override on an item, and clearing/resetting it by providing no input. // Item properties' name will generally match their respective item component's name, but not always! // Due to this, features that take item component names as input (such as <@link tag ItemTag.is_overridden>) accept both Minecraft component names and Denizen property names. // - // Here is an example of applying all of this in Denizen: + // Here is an example of applying all of this in a script: // // # We define a default apple item // - define apple From e6fa0efe523fcf24d3db6194dd07aec133a8fe3b Mon Sep 17 00:00:00 2001 From: Aya <31237389+tal5@users.noreply.github.com> Date: Fri, 8 Aug 2025 01:02:55 +0100 Subject: [PATCH 07/10] s --- .../denizen/paper/datacomponents/DataComponentAdapter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/DataComponentAdapter.java b/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/DataComponentAdapter.java index 4797e3ed2e..b0d5ae7eb8 100644 --- a/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/DataComponentAdapter.java +++ b/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/DataComponentAdapter.java @@ -72,7 +72,7 @@ public static void register(DataComponentAdapter { Material material = materialTag.getMaterial(); if (!material.isItem()) { - attribute.echoError("Cannot get item component value from a block material."); + attribute.echoError("Cannot get item component values from a block material."); return null; } TP internalValue = material.getDefaultData(adapter.componentType); From f9e944837ffcfb54eb1739404779f4f7a8c180ca Mon Sep 17 00:00:00 2001 From: Aya <31237389+tal5@users.noreply.github.com> Date: Mon, 22 Sep 2025 00:50:09 +0100 Subject: [PATCH 08/10] Minor cleanups --- .../denizen/paper/datacomponents/DataComponentAdapter.java | 2 +- .../denizenscript/denizen/paper/properties/ItemRemoved.java | 2 +- .../denizen/nms/v1_21/helpers/ItemHelperImpl.java | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/DataComponentAdapter.java b/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/DataComponentAdapter.java index b0d5ae7eb8..0adb4978f9 100644 --- a/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/DataComponentAdapter.java +++ b/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/DataComponentAdapter.java @@ -58,7 +58,7 @@ public static DataComponentType getComponentType(String name) { String nameLower = CoreUtilities.toLowerCase(name); DataComponentType componentType = Registry.DATA_COMPONENT_TYPE.get(Utilities.parseNamespacedKey(nameLower)); if (componentType == null) { - componentType = DataComponentAdapter.COMPONENTS_BY_PROPERTY.get(nameLower); + componentType = COMPONENTS_BY_PROPERTY.get(nameLower); } return componentType; } diff --git a/paper/src/main/java/com/denizenscript/denizen/paper/properties/ItemRemoved.java b/paper/src/main/java/com/denizenscript/denizen/paper/properties/ItemRemoved.java index 593fd6d2f4..d2e324fb48 100644 --- a/paper/src/main/java/com/denizenscript/denizen/paper/properties/ItemRemoved.java +++ b/paper/src/main/java/com/denizenscript/denizen/paper/properties/ItemRemoved.java @@ -16,7 +16,7 @@ public class ItemRemoved extends ItemProperty { // @input ListTag // @description // Controls the properties explicitly removed from an item. - // This can be used to remove item's default behavior, such as making consumable items un-consumable. + // This can be used to remove item's default behavior, such as making consumable items non-consumable. // See also <@link language Item Components>. // --> diff --git a/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/helpers/ItemHelperImpl.java b/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/helpers/ItemHelperImpl.java index 0d49eb88c7..9f743d42b0 100644 --- a/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/helpers/ItemHelperImpl.java +++ b/v1_21/src/main/java/com/denizenscript/denizen/nms/v1_21/helpers/ItemHelperImpl.java @@ -1,5 +1,6 @@ package com.denizenscript.denizen.nms.v1_21.helpers; +import com.denizenscript.denizen.Denizen; import com.denizenscript.denizen.nms.interfaces.ItemHelper; import com.denizenscript.denizen.nms.util.PlayerProfile; import com.denizenscript.denizen.nms.v1_21.Handler; @@ -450,7 +451,7 @@ public MapTag getRawComponentsPatch(ItemStack item, boolean excludeHandled) { } RegistryOps registryOps = CraftRegistry.getMinecraftRegistry().createSerializationContext(NbtOps.INSTANCE); CompoundTag nmsPatch = (CompoundTag) DataComponentPatch.CODEC.encodeStart(registryOps, patch).getOrThrow(); - if (excludeHandled) { + if (excludeHandled && Denizen.supportsPaper) { nmsPatch.keySet().removeIf(s -> s.charAt(0) == '!'); if (nmsPatch.isEmpty()) { return new MapTag(); From ea5ee7a3f0ec0a177e185707ba5525d3260c9b6e Mon Sep 17 00:00:00 2001 From: Aya <31237389+tal5@users.noreply.github.com> Date: Fri, 16 Jan 2026 16:14:55 +0000 Subject: [PATCH 09/10] Remove `MaterialTag` parts --- .../paper/datacomponents/DataComponentAdapter.java | 11 ----------- .../denizen/paper/datacomponents/FoodAdapter.java | 7 ------- 2 files changed, 18 deletions(-) diff --git a/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/DataComponentAdapter.java b/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/DataComponentAdapter.java index 0adb4978f9..1982e88ed5 100644 --- a/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/DataComponentAdapter.java +++ b/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/DataComponentAdapter.java @@ -1,7 +1,6 @@ package com.denizenscript.denizen.paper.datacomponents; import com.denizenscript.denizen.objects.ItemTag; -import com.denizenscript.denizen.objects.MaterialTag; import com.denizenscript.denizen.objects.properties.item.ItemComponentsPatch; import com.denizenscript.denizen.objects.properties.item.ItemProperty; import com.denizenscript.denizen.utilities.Utilities; @@ -12,7 +11,6 @@ import com.denizenscript.denizencore.objects.properties.PropertyParser; import com.denizenscript.denizencore.utilities.CoreUtilities; import io.papermc.paper.datacomponent.DataComponentType; -import org.bukkit.Material; import org.bukkit.Registry; import java.util.HashMap; @@ -69,15 +67,6 @@ public static void register(DataComponentAdapter !item.getItemStack().isEmpty() ? adapter.new Property(item) : null, ItemTag.class, EMPTY_STRING_ARRAY, EMPTY_STRING_ARRAY, DataComponentAdapter.Property.class); DataComponentAdapter.Property.currentlyRegisteringComponentAdapter = null; - MaterialTag.tagProcessor.registerTag(adapter.denizenType, adapter.name, (attribute, materialTag) -> { - Material material = materialTag.getMaterial(); - if (!material.isItem()) { - attribute.echoError("Cannot get item component values from a block material."); - return null; - } - TP internalValue = material.getDefaultData(adapter.componentType); - return internalValue != null ? adapter.toDenizen(internalValue) : null; - }); String componentName = adapter.componentType.key().value(); ItemComponentsPatch.registerHandledComponent(componentName); if (!adapter.name.equals(componentName)) { diff --git a/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/FoodAdapter.java b/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/FoodAdapter.java index 88a7722eef..8533239181 100644 --- a/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/FoodAdapter.java +++ b/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/FoodAdapter.java @@ -20,13 +20,6 @@ public class FoodAdapter extends DataComponentAdapter { // - "can_always_eat", ElementTag(Boolean) controlling whether the item can always be eaten, even if the player isn't hungry. // --> - // <--[tag] - // @attribute - // @returns MapTag - // @description - // Gets the material's default food value, in the same format as <@link tag ItemTag.food>. - // --> - public FoodAdapter() { super(DataComponentTypes.FOOD, MapTag.class, "food"); } From da74064dbf61159d958496051bf74f8f47a75413 Mon Sep 17 00:00:00 2001 From: Aya <31237389+tal5@users.noreply.github.com> Date: Mon, 2 Feb 2026 00:03:10 +0000 Subject: [PATCH 10/10] Semi-finalize components system --- .../denizen/paper/PaperModule.java | 2 +- .../ComponentAdaptersRegistry.java | 1 + .../datacomponents/DataComponentAdapter.java | 127 +++++++++++++----- .../paper/datacomponents/FoodAdapter.java | 6 +- .../paper/datacomponents/GliderAdapter.java | 20 +++ ...emoved.java => ItemRemovedComponents.java} | 23 ++-- 6 files changed, 137 insertions(+), 42 deletions(-) create mode 100644 paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/GliderAdapter.java rename paper/src/main/java/com/denizenscript/denizen/paper/properties/{ItemRemoved.java => ItemRemovedComponents.java} (65%) diff --git a/paper/src/main/java/com/denizenscript/denizen/paper/PaperModule.java b/paper/src/main/java/com/denizenscript/denizen/paper/PaperModule.java index cccdd7c557..e01fc60551 100644 --- a/paper/src/main/java/com/denizenscript/denizen/paper/PaperModule.java +++ b/paper/src/main/java/com/denizenscript/denizen/paper/PaperModule.java @@ -133,7 +133,7 @@ public static void init() { // Component adapters if (NMSHandler.getVersion().isAtLeast(NMSVersion.v1_21)) { - PropertyParser.registerProperty(ItemRemoved.class, ItemTag.class); + PropertyParser.registerProperty(ItemRemovedComponents.class, ItemTag.class); ComponentAdaptersRegistry.register(); } diff --git a/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/ComponentAdaptersRegistry.java b/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/ComponentAdaptersRegistry.java index c4b781ed1a..ad691abf2b 100644 --- a/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/ComponentAdaptersRegistry.java +++ b/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/ComponentAdaptersRegistry.java @@ -4,5 +4,6 @@ public class ComponentAdaptersRegistry { public static void register() { DataComponentAdapter.register(new FoodAdapter()); + DataComponentAdapter.register(new GliderAdapter()); } } diff --git a/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/DataComponentAdapter.java b/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/DataComponentAdapter.java index 1982e88ed5..660e59463c 100644 --- a/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/DataComponentAdapter.java +++ b/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/DataComponentAdapter.java @@ -12,6 +12,7 @@ import com.denizenscript.denizencore.utilities.CoreUtilities; import io.papermc.paper.datacomponent.DataComponentType; import org.bukkit.Registry; +import org.bukkit.inventory.ItemStack; import java.util.HashMap; import java.util.Map; @@ -19,8 +20,8 @@ import java.util.function.Function; import java.util.function.Predicate; -public abstract class DataComponentAdapter { - +public abstract class DataComponentAdapter
{ + // <--[language] // @name Item Components // @group Minecraft Logic @@ -43,8 +44,8 @@ public abstract class DataComponentAdapter { // - adjust def:apple removed:food // # This check will pass, as the apple's "food" component is overridden to have no value. // - if <[apple].is_overridden[food]>: - // - narrate "The apple has a changed food component! It will behave differently to a normal apple." - // # We reset the apple item's food component, making it a normal apple. + // - narrate "The apple has a modified food component! It will behave differently to a normal apple." + // # We reset the apple item's food component by adjusting with no value, making it a normal apple. // - adjust def:apple food: // // --> @@ -61,13 +62,13 @@ public static DataComponentType getComponentType(String name) { return componentType; } - public static void register(DataComponentAdapter adapter) { + public static void register(DataComponentAdapter adapter) { DataComponentAdapter.Property.currentlyRegisteringComponentAdapter = adapter; PropertyParser.registerPropertyGetter( item -> !item.getItemStack().isEmpty() ? adapter.new Property(item) : null, ItemTag.class, EMPTY_STRING_ARRAY, EMPTY_STRING_ARRAY, DataComponentAdapter.Property.class); DataComponentAdapter.Property.currentlyRegisteringComponentAdapter = null; - String componentName = adapter.componentType.key().value(); + String componentName = adapter.componentType.key().asMinimalString(); ItemComponentsPatch.registerHandledComponent(componentName); if (!adapter.name.equals(componentName)) { COMPONENTS_BY_PROPERTY.put(adapter.name, adapter.componentType); @@ -92,34 +93,94 @@ public static void register(DataComponentAdapter componentType; - public final Class denizenType; + public final CT componentType; + public final Class
denizenType; public final String name; - public DataComponentAdapter(DataComponentType.Valued componentType, Class denizenType, String name) { + public DataComponentAdapter(CT componentType, Class
denizenType, String name) { this.componentType = componentType; this.denizenType = denizenType; this.name = name; } - public abstract TD toDenizen(TP value); + public abstract DT getValue(ItemStack item); + + public abstract void setValue(ItemStack item, DT value, Mechanism mechanism); + + public boolean isDefaultValue(DT value) { + return false; + } + + public static abstract class NonValued extends DataComponentAdapter { + + public NonValued(DataComponentType.NonValued componentType, String name) { + super(componentType, ElementTag.class, name); + } + + @Override + public ElementTag getValue(ItemStack item) { + return new ElementTag(item.hasData(componentType)); + } + + @Override + public void setValue(ItemStack item, ElementTag value, Mechanism mechanism) { + if (!mechanism.requireBoolean()) { + return; + } + if (value.asBoolean()) { + item.setData(componentType); + } + else { + item.unsetData(componentType); + } + } + + // Overridden and false = removed, managed by ItemTag.removed + @Override + public boolean isDefaultValue(ElementTag value) { + return !value.asBoolean(); + } + } + + public static abstract class Valued extends DataComponentAdapter> { + + public static void setIfValid(Consumer setter, MapTag data, String key, String type, Predicate checker, Function converter, Mechanism mechanism) { + ElementTag value = data.getElement(key); + if (value == null) { + return; + } + T converted; + if (!checker.test(value) || (converted = converter.apply(value)) == null) { + mechanism.echoError("Invalid '" + key + "' specified: must be a " + type + '.'); + return; + } + setter.accept(converted); + } - public abstract TP toPaper(TD value, Mechanism mechanism); + public Valued(Class denizenType, DataComponentType.Valued componentType, String name) { + super(componentType, denizenType, name); + } + + public abstract TD toDenizen(TP value); + + public abstract TP toPaper(TD value, Mechanism mechanism); - public static void setIfValid(Consumer setter, MapTag data, String key, String type, Predicate checker, Function converter, Mechanism mechanism) { - ElementTag value = data.getElement(key); - if (value == null) { - return; + @Override + public TD getValue(ItemStack item) { + TP data = item.getData(componentType); + return data != null ? toDenizen(data) : null; } - T converted; - if (!checker.test(value) || (converted = converter.apply(value)) == null) { - mechanism.echoError("Invalid '" + key + "' specified: must be a " + type + '.'); - return; + + @Override + public void setValue(ItemStack item, TD value, Mechanism mechanism) { + TP converted = toPaper(value, mechanism); + if (converted != null) { + item.setData(componentType, converted); + } } - setter.accept(converted); } - public class Property extends ItemProperty { + public class Property extends ItemProperty
{ private static DataComponentAdapter currentlyRegisteringComponentAdapter; @@ -128,26 +189,30 @@ public Property(ItemTag item) { } @Override - public TD getPropertyValue() { - TP internalValue = getItemStack().getData(componentType); - return internalValue == null ? null : toDenizen(internalValue); + public DT getPropertyValue() { + return getValue(getItemStack()); } @Override - public TD getPropertyValueNoDefault() { - return getItemStack().isDataOverridden(componentType) ? getPropertyValue() : null; + public DT getPropertyValueNoDefault() { + if (!getItemStack().isDataOverridden(componentType)) { + return null; + } + return super.getPropertyValueNoDefault(); } @Override - public void setPropertyValue(TD value, Mechanism mechanism) { + public boolean isDefaultValue(DT value) { + return DataComponentAdapter.this.isDefaultValue(value); + } + + @Override + public void setPropertyValue(DT value, Mechanism mechanism) { if (value == null) { getItemStack().resetData(componentType); return; } - TP converted = toPaper(value, mechanism); - if (converted != null) { - getItemStack().setData(componentType, converted); - } + setValue(getItemStack(), value, mechanism); } @Override diff --git a/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/FoodAdapter.java b/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/FoodAdapter.java index 8533239181..078857314a 100644 --- a/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/FoodAdapter.java +++ b/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/FoodAdapter.java @@ -6,7 +6,7 @@ import io.papermc.paper.datacomponent.DataComponentTypes; import io.papermc.paper.datacomponent.item.FoodProperties; -public class FoodAdapter extends DataComponentAdapter { +public class FoodAdapter extends DataComponentAdapter.Valued { // <--[property] // @object ItemTag @@ -18,10 +18,12 @@ public class FoodAdapter extends DataComponentAdapter { // - "nutrition", ElementTag(Number) representing the amount of food points restored by this item. // - "saturation", ElementTag(Decimal) representing the amount of saturation points restored by this item. // - "can_always_eat", ElementTag(Boolean) controlling whether the item can always be eaten, even if the player isn't hungry. + // @mechanism + // Provide no input to reset the item to its default value. // --> public FoodAdapter() { - super(DataComponentTypes.FOOD, MapTag.class, "food"); + super(MapTag.class, DataComponentTypes.FOOD, "food"); } @Override diff --git a/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/GliderAdapter.java b/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/GliderAdapter.java new file mode 100644 index 0000000000..838f02ca72 --- /dev/null +++ b/paper/src/main/java/com/denizenscript/denizen/paper/datacomponents/GliderAdapter.java @@ -0,0 +1,20 @@ +package com.denizenscript.denizen.paper.datacomponents; + +import io.papermc.paper.datacomponent.DataComponentTypes; + +public class GliderAdapter extends DataComponentAdapter.NonValued { + + // <--[property] + // @object ItemTag + // @name glider + // @input ElementTag(Boolean) + // @description + // Controls whether an item can be used to glide when equipped (like elytras by default), see <@link language Item Components>. + // @mechanism + // Provide no input to reset the item to its default value. + // --> + + public GliderAdapter() { + super(DataComponentTypes.GLIDER, "glider"); + } +} diff --git a/paper/src/main/java/com/denizenscript/denizen/paper/properties/ItemRemoved.java b/paper/src/main/java/com/denizenscript/denizen/paper/properties/ItemRemovedComponents.java similarity index 65% rename from paper/src/main/java/com/denizenscript/denizen/paper/properties/ItemRemoved.java rename to paper/src/main/java/com/denizenscript/denizen/paper/properties/ItemRemovedComponents.java index d2e324fb48..f4b6b17215 100644 --- a/paper/src/main/java/com/denizenscript/denizen/paper/properties/ItemRemoved.java +++ b/paper/src/main/java/com/denizenscript/denizen/paper/properties/ItemRemovedComponents.java @@ -8,14 +8,14 @@ import com.denizenscript.denizencore.objects.core.ListTag; import io.papermc.paper.datacomponent.DataComponentType; -public class ItemRemoved extends ItemProperty { +public class ItemRemovedComponents extends ItemProperty { // <--[property] // @object ItemTag - // @name removed + // @name removed_components // @input ListTag // @description - // Controls the properties explicitly removed from an item. + // Controls the item components explicitly removed from an item. // This can be used to remove item's default behavior, such as making consumable items non-consumable. // See also <@link language Item Components>. // --> @@ -24,11 +24,13 @@ public static boolean describes(ItemTag item) { return !item.getItemStack().isEmpty(); } + public boolean isRemoved(DataComponentType componentType) { + return getItemStack().isDataOverridden(componentType) && !getItemStack().hasData(componentType); + } + @Override public ListTag getPropertyValue() { - return new ListTag(getMaterial().getDefaultDataTypes(), - componentType -> getItemStack().isDataOverridden(componentType) && !getItemStack().hasData(componentType), - componentType -> new ElementTag(componentType.key().asMinimalString(), true)); + return new ListTag(getMaterial().getDefaultDataTypes(), this::isRemoved, componentType -> new ElementTag(componentType.key().asMinimalString(), true)); } @Override @@ -38,6 +40,11 @@ public boolean isDefaultValue(ListTag value) { @Override public void setPropertyValue(ListTag value, Mechanism mechanism) { + for (DataComponentType componentType : getMaterial().getDefaultDataTypes()) { + if (isRemoved(componentType)) { + getItemStack().resetData(componentType); + } + } for (String input : value) { DataComponentType componentType = DataComponentAdapter.getComponentType(input); if (componentType == null) { @@ -50,10 +57,10 @@ public void setPropertyValue(ListTag value, Mechanism mechanism) { @Override public String getPropertyId() { - return "removed"; + return "removed_components"; } public static void register() { - autoRegister("removed", ItemRemoved.class, ListTag.class, false); + autoRegister("removed_components", ItemRemovedComponents.class, ListTag.class, false); } }