Skip to content

Commit 2949769

Browse files
Owen1212055Strokkur424zlataovce
authored
feat: data components (#514)
* Add Data Component Documentation * fix typos * fix: misc improvements (#535) * Cleanup data components page * Fixup small mistake * Fix minor spelling * Small changes * Apply suggestions from code review --------- Co-authored-by: Matouš Kučera <mk@kcra.me> --------- Co-authored-by: Strokkur24 <133226102+Strokkur424@users.noreply.github.com> Co-authored-by: Matouš Kučera <mk@kcra.me>
1 parent 611bd61 commit 2949769

File tree

3 files changed

+234
-1
lines changed

3 files changed

+234
-1
lines changed

config/sidebar.paper.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,14 +179,15 @@ const paper: SidebarsConfig = {
179179
},
180180
{
181181
type: "category",
182-
label: "Component API (Adventure)",
182+
label: "Text Component API (Adventure)",
183183
collapsed: true,
184184
items: [
185185
"dev/api/component-api/intro",
186186
"dev/api/component-api/i18n",
187187
"dev/api/component-api/audiences",
188188
],
189189
},
190+
"dev/api/data-component-api",
190191
"dev/api/pdc",
191192
"dev/api/custom-inventory-holder",
192193
"dev/api/scheduler",
577 KB
Loading
Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
---
2+
slug: /dev/data-component-api
3+
description: A guide to the ItemStack data component API.
4+
---
5+
6+
# Data Component API
7+
8+
:::danger[Experimental]
9+
10+
The data component API is currently experimental, and is additionally subject to change across versions.
11+
12+
:::
13+
14+
The data component API provides a version-specific interface for accessing and manipulating item data that is otherwise not representable by the `ItemMeta` API.
15+
Through this API, you can read and modify properties of an item, so called data components, in a stable and object-oriented manner.
16+
17+
18+
## Introduction
19+
20+
### What is a data component?
21+
A data component represents a piece of data associated with an item. Vanilla items can have properties such as custom model data, container loot contents, banner patterns, or potion effects.
22+
23+
### Structure
24+
![Component Structure](assets/data-component-api-tree.png)
25+
For implementation details, [click here](#example-cool-sword).
26+
27+
#### The prototype (default values)
28+
Items come with an initial set of components that we call the prototype.
29+
These components are defined on the `ItemType` of the `ItemStack`. They control the base behavior
30+
of the item, representing a brand new item without any modifications.
31+
32+
The prototype gives items their initial properties such as if they are food, a tool, a weapon, etc.
33+
34+
#### The patch
35+
The patch represents the modifications made to the item. This may include giving it a custom name,
36+
modifying the enchantments, damaging it, or adding to the lore. The patch is applied ontop of the prototype,
37+
allowing us to make modifications to an item.
38+
39+
The patch also allows for removing components that were previously in the prototype. This is shown by
40+
the `minecraft:tool` example in red. We are removing this component, so this sword item will no longer
41+
break cobweb or other sword blocks faster.
42+
43+
We can also add new components, as seen from the new `minecraft:enchantment_glint_override` component, which
44+
allows us to make it appear as if it were enchanted.
45+
46+
47+
## Differences compared to `ItemMeta`
48+
49+
The `ItemMeta` API provides methods to modify `ItemStack`s in a hierarchical manner, such as `CompassMeta`, which allows you to modify the components of a `minecraft:compass`.
50+
While `ItemMeta` is still very useful, it does not properly represent the prototype/patch relationship that Minecraft items use.
51+
52+
### Key differences
53+
54+
#### Expanded data model
55+
The data component API exposes a much broader and more detailed set of item properties than `ItemMeta`.
56+
Data components allow the entire item to be modified in a fashion that better represents how Minecraft does item modifications.
57+
58+
#### Version-specific
59+
The data component API is designed to adapt to version changes. The data component API may experience breaking changes on version updates as Minecraft makes changes to components.
60+
Backwards compatibility is not promised.
61+
62+
Because `ItemMeta` is represented in a different format, breaking changes made to components by Mojang may not result in breaking changes to `ItemMeta`.
63+
64+
#### Builders and immutability
65+
Many complex data components require a builder approach for construction and editing. All data types that are returned by the api are also immutable, so they will not directly modify the component.
66+
67+
#### Patch-only
68+
`ItemMeta` only represents the patch of an `ItemStack`. This means that you cannot get the original properties (prototype) of the `ItemStack`, such as its default
69+
durability or default attributes.
70+
71+
#### No snapshots
72+
Currently, `ItemMeta` represents a **snapshot** of an `ItemStack`'s patched map.
73+
This is expensive as it requires the entire patch to be read, even values that you may not be using.
74+
75+
The data component API integrates directly with `ItemStack`. Although conceptually similar, the data component API focuses on explicit, strongly typed data retrieval and updates without this additional overhead.
76+
77+
### When should I use `DataComponents` or `ItemMeta`?
78+
79+
You would want to use `ItemMeta` if you:
80+
- Are doing only simple changes to `ItemStack`s
81+
- Want to keep cross-version compatibility with your plugin
82+
83+
You would want to use data components if you:
84+
- Want more complicated `ItemStack` modifications
85+
- Do not care about cross-version compatibility
86+
- Want to access default (prototype) values
87+
- Want to remove components from an `ItemStack`'s prototype
88+
89+
90+
## Basic usage
91+
The data component API will fetch values according to the behavior seen in game. So, if the patch removes the `minecraft:tool` component,
92+
trying to get that component will return null.
93+
94+
### Retrieving a prototype value
95+
96+
```java
97+
// Get the default durability of diamond sword
98+
int defaultDurability = Material.DIAMOND_SWORD.getDefaultData(DataComponentTypes.MAX_DAMAGE)
99+
```
100+
101+
### Checking for a data component
102+
103+
```java
104+
// Check if this item has a custom name data component
105+
boolean hasCustomName = stack.hasData(DataComponentTypes.CUSTOM_NAME);
106+
logger.info("Has custom name? " + hasCustomName);
107+
```
108+
109+
### Reading a valued data component
110+
111+
```java
112+
// The damage of an item can be null, so we require a null check
113+
Integer damageValue = stack.getData(DataComponentTypes.DAMAGE);
114+
if (damageValue != null) {
115+
logger.info("Current damage: " + damageValue);
116+
} else {
117+
logger.info("This item doesn't have a damage component set.");
118+
}
119+
120+
// Certain components, like the max stack size, will always be present on an item
121+
Integer maxStackSize = stack.getData(DataComponentTypes.MAX_STACK_SIZE);
122+
```
123+
124+
### Setting a valued data component
125+
126+
```java
127+
// Set a custom model data value on this item
128+
stack.setData(DataComponentTypes.CUSTOM_MODEL_DATA, CustomModelData.customModelData()
129+
.addFloat(0.5f)
130+
.addFlag(true)
131+
.build()
132+
);
133+
```
134+
135+
### Removing or resetting a data component
136+
137+
```java
138+
// Remove an existing component (e.g. tool)
139+
stack.unsetData(DataComponentTypes.TOOL);
140+
141+
// Reset a component to the default (prototype) value for its item type (e.g. max stack size)
142+
stack.resetData(DataComponentTypes.MAX_STACK_SIZE);
143+
```
144+
145+
### Non-valued data components
146+
147+
Some components are only flags and don't carry any sort of value:
148+
149+
```java
150+
// Make the item unbreakable
151+
stack.setData(DataComponentTypes.UNBREAKABLE);
152+
153+
// Remove the unbreakable flag
154+
stack.unsetData(DataComponentTypes.UNBREAKABLE);
155+
```
156+
157+
## Advanced usage with builders
158+
159+
Many data components have complex structures that require builders.
160+
161+
### Modifying prototype component values
162+
163+
```java
164+
ItemStack itemStack = ItemStack.of(Material.DIAMOND_HELMET);
165+
// Get the equippable component for this item, and make it a builder.
166+
// Note: Not all types have .toBuilder() methods
167+
// This is the prototype value of the diamond helmet.
168+
Equippable.Builder builder = itemStack.getData(DataComponentTypes.EQUIPPABLE).toBuilder();
169+
170+
// Make the helmet look like netherite
171+
// We get the prototype equippable value from NETHERITE_HELMET
172+
builder.assetId(Material.NETHERITE_HELMET.getDefaultData(DataComponentTypes.EQUIPPABLE).assetId());
173+
// And give it a spooky sound when putting it on
174+
builder.equipSound(Registry.SOUNDS.getKeyOrThrow(Sound.ENTITY_GHAST_HURT));
175+
176+
// Set our new item
177+
itemStack.setData(DataComponentTypes.EQUIPPABLE, builder);
178+
```
179+
This will create a diamond helmet that looks like a netherite helmet and plays a spooky ghast sound when equipped.
180+
181+
### Example: Written book
182+
183+
```java
184+
ItemStack writtenBook = ItemStack.of(Material.WRITTEN_BOOK);
185+
WrittenBookContent.Builder bookBuilder = WrittenBookContent.writtenBookContent("My Book", "AuthorName");
186+
187+
// Add a page
188+
bookBuilder.addPage(Component.text("This is a new page!"));
189+
190+
// Add a page that shows differently for people who have swear filtering on
191+
// Players who have disabled filtering, will see "I hate Paper!", while those with filtering on will see the "I love Paper!".
192+
bookBuilder.addFilteredPage(
193+
Filtered.of(Component.text("I hate Paper!"), Component.text("I love Paper!"))
194+
);
195+
196+
// Change generation
197+
bookBuilder.generation(1);
198+
199+
// Apply changes
200+
writtenBook.setData(DataComponentTypes.WRITTEN_BOOK_CONTENT, bookBuilder.build());
201+
```
202+
203+
### Example: Cool sword
204+
205+
```java
206+
ItemStack itemStack = ItemStack.of(Material.DIAMOND_SWORD);
207+
itemStack.setData(DataComponentTypes.LORE, ItemLore.lore().addLine(Component.text("Cool sword!")).build());
208+
itemStack.setData(DataComponentTypes.ENCHANTMENTS, ItemEnchantments.itemEnchantments().add(Enchantment.SHARPNESS, 10).build());
209+
itemStack.setData(DataComponentTypes.RARITY, ItemRarity.RARE);
210+
211+
itemStack.unsetData(DataComponentTypes.TOOL); // Remove the tool component
212+
213+
itemStack.setData(DataComponentTypes.MAX_DAMAGE, 10);
214+
itemStack.setData(DataComponentTypes.ENCHANTMENT_GLINT_OVERRIDE, true); // Make it glow!
215+
```
216+
217+
## Matching items without certain data components
218+
219+
When comparing items, you sometimes want to ignore certain values. For this, we can use the
220+
<Javadoc name="org.bukkit.inventory.ItemStack#matchesWithoutData(org.bukkit.inventory.ItemStack,java.util.Set)">`ItemStack#matchesWithoutData`</Javadoc>
221+
method.
222+
223+
For example, here we compare two diamond swords whilst ignoring their durability:
224+
225+
```java
226+
ItemStack originalSword = new ItemStack(Material.DIAMOND_SWORD);
227+
ItemStack damagedSword = new ItemStack(Material.DIAMOND_SWORD);
228+
damagedSword.setData(DataComponentTypes.DAMAGE, 100);
229+
230+
boolean match = damagedSword.matchesWithoutData(originalSword, Set.of(DataComponentTypes.DAMAGE), false);
231+
logger.info("Do the sword match? " + match); // true
232+
```

0 commit comments

Comments
 (0)