Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DataComponent PR fixup PR #535

Open
wants to merge 4 commits into
base: feat/data-components
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
147 changes: 79 additions & 68 deletions docs/paper/dev/api/data-component-api.mdx
Original file line number Diff line number Diff line change
@@ -1,142 +1,150 @@
---
slug: /dev/data-component-api
description: A guide to the ItemStack DataComponentAPI
description: A guide to the ItemStack data component API.
---

# Data Component API

:::danger[Experimental]
The DataComponent API is currently experimental, and is additionally subject to change across versions.

The data component API is currently experimental, and is additionally subject to change across versions.

:::

The Data Component API provides a version-specific, programmatic interface for accessing and manipulating item data that is otherwise not representable by the `ItemMeta` API. Through this API, you can read and write properties of an item—called "data components"—in a stable and object-oriented manner.
The data component API provides a version-specific interface for accessing and manipulating item data that is otherwise not representable by the `ItemMeta` API.
Through this API, you can read and modify properties of an item, so called data components, in a stable and object-oriented manner.


## Introduction

### What is a Data Component?
### What is a data component?
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.

### Structure
![Component Structure](assets/data-component-api-tree.png)
See implementation [here](#example-cool-sword)
For implementation details, [click here](#example-cool-sword)

#### The Prototype (Default Values)
#### The prototype (default values)
Items come with an initial set of components that we call the prototype.
These components are defined on the `ItemType` of the `ItemStack`. They control the base behavior
of the item, representing a brand new item without any modifications.

The prototype gives items their initial properties such as if they are food, a tool, a weapon, etc.

#### The Patch
#### The patch
The patch represents the modifications made to the item. This may include giving it a custom name,
modifying the enchantments, damaging it, or adding to the lore. The patch is applied ontop of the prototype,
allowing us to make modifications to an item.

The patch also allows for removing components that were previously in the prototype, this is shown by
The patch also allows for removing components that were previously in the prototype. This is shown by
the `minecraft:tool` example in red. We are removing this component, so this sword item will no longer
break cobweb / other sword blocks faster.
break cobweb or other sword blocks faster.

We can also add new components, as seen from the new `minecraft:enchantment_glint_override` component, which
allows us to make it appear with a glint.
allows us to make it appear as if it were enchanted.


## Differences Compared to `ItemMeta`
## Differences compared to `ItemMeta`

The `ItemMeta` API provides methods to modify `ItemStack`s in a hierarchical manner, such `CompassMeta` allowing you to modify the components of a `minecraft:compass`.
While `ItemMeta` is still very useful, it does not properly represent this new prototype/patch relationship that Minecraft items now use.
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`.
While `ItemMeta` is still very useful, it does not properly represent the prototype/patch relationship that Minecraft items use.

### Key Differences
### Key differences

#### Expanded Data Model
The Data Component API exposes a much broader and more detailed set of item properties than `ItemMeta`.
DataComponents allow the entire component to be modified in a fashion that better represents how Minecraft does item modifications.
#### Expanded data model
The data component API exposes a much broader and more detailed set of item properties than `ItemMeta`.
Data components allow the entire item to be modified in a fashion that better represents how Minecraft does item modifications.

#### Version-Specific
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.
#### Version-specific
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.
Backwards compatibility is not promised.

Because ItemMeta is represented in a different format, breaking changes made to components by Mojang may not result in breaking changes to `ItemMeta`.
Because `ItemMeta` is represented in a different format, breaking changes made to components by Mojang may not result in breaking changes to `ItemMeta`.

#### Builders and Immutability
#### Builders and immutability
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.

#### Patch-Only
ItemMeta represents the patch of an ItemStack only. This means that you cannot get the original properties (prototype) of the ItemStack, such as its default
#### Patch-only
`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
durability or default attributes.

#### No Snapshot
Currently, ItemMeta represents a *Snapshot* of an ItemStack's patched map.
#### No snapshots
Currently, `ItemMeta` represents a **snapshot** of an `ItemStack`'s patched map.
This is expensive as it requires the entire patch to be read, even values that you may not be using.

The DataComponent 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.
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.

### When should I use `DataComponents` or `ItemMeta`?
#### `ItemMeta`
- Simple changes to `ItemStacks`
- Keep the most version compatibility with your plugin
#### `DataComponent`
- More complicated `ItemStack` modifications

You would want to use `ItemMeta` if you:
- Are doing only simple changes to `ItemStack`s
- Want to keep cross-version compatibility with your plugin

You would want to use data components if you:
- Want more complicated `ItemStack` modifications
- Do not care about cross-version compatibility
- Want to access default (prototype) values
- Want to remove components from an ItemStack's prototype
- Want to remove components from an `ItemStack`'s prototype


## Basic Usage
The DataComponent API will fetch values according to the behavior seen in game. So, if the patch removes the `minecraft:tool` component,
## Basic usage
The data component API will fetch values according to the behavior seen in game. So, if the patch removes the `minecraft:tool` component,
trying to get that component will return null.

### Getting a Default (Prototype) value
### Retrieving a prototype value

```java
// Get the default durability of diamond sword
int defaultDurability = Material.DIAMOND_SWORD.getDefaultData(DataComponentTypes.MAX_DAMAGE)
```

### Checking for a Data Component
### Checking for a data component

```java
// Check if this item has a custom name data component
boolean hasCustomName = stack.hasData(DataComponentTypes.CUSTOM_NAME);
System.out.println("Has custom name? " + hasCustomName);
logger.info("Has custom name? " + hasCustomName);
```

### Getting a Valued Data Component
### reading a valued data component

```java
// Suppose we want to read the damage (durability) of an item
// The damage of an item can be null, so we require a null check
Integer damageValue = stack.getData(DataComponentTypes.DAMAGE);
if (damageValue != null) {
System.out.println("Current damage: " + damageValue);
logger.info("Current damage: " + damageValue);
} else {
System.out.println("This item doesn't have a damage component set.");
logger.info("This item doesn't have a damage component set.");
}

// Return the max stack size, which will always be present either on the patch or the prototype
// Certain components, like the max stack size, will always be present on an item
Integer maxStackSize = stack.getData(DataComponentTypes.MAX_STACK_SIZE);
```

### Setting a Valued Data Component
### Setting a valued data component

```java
// Set a custom model data value on this item
stack.setData(DataComponentTypes.CUSTOM_MODEL_DATA, CustomModelData.customModelData()
.addFloat(0.5f)
.addFlag(true)
.build()
);
stack.setData(DataComponentTypes.CUSTOM_MODEL_DATA, CustomModelData.customModelData()
.addFloat(0.5f)
.addFlag(true)
.build()
);
```

### Removing or Resetting a Data Component
### Removing or resetting a data component

```java
// Remove an existing component (e.g., tool)
// Remove an existing component (e.g. tool)
stack.unsetData(DataComponentTypes.TOOL);

// Reset a component to the default (prototype value) for its item type (e.g., max stack size)
// Reset a component to the default (prototype) value for its item type (e.g. max stack size)
stack.resetData(DataComponentTypes.MAX_STACK_SIZE);
```

### Non-Valued Data Components
### Non-valued data components

Some components are only flags, and dont carry any sort of value:
Some components are only flags and don't carry any sort of value:

```java
// Make the item unbreakable
Expand All @@ -146,11 +154,11 @@ stack.setData(DataComponentTypes.UNBREAKABLE);
stack.unsetData(DataComponentTypes.UNBREAKABLE);
```

## Advanced Usage with Builders
## Advanced usage with builders

Many data components have complex structures that require builders.

### Modifying pre-existing (prototype) component values
### Modifying prototype component values

```java
ItemStack itemStack = ItemStack.of(Material.DIAMOND_HELMET);
Expand All @@ -168,10 +176,9 @@ builder.equipSound(Registry.SOUNDS.getKeyOrThrow(Sound.ENTITY_GHAST_HURT));
// Set our new item
itemStack.setData(DataComponentTypes.EQUIPPABLE, builder);
```
This will create a diamond helmet that looks like a netherrite helmet when equipped, but plays a spooky
ghast sound when equipped.
This will create a diamond helmet that looks like a netherite helmet and plays a spooky ghast sound when equipped.

### Example: Written Book
### Example: Written book

```java
ItemStack writtenBook = ItemStack.of(Material.WRITTEN_BOOK);
Expand All @@ -181,9 +188,9 @@ WrittenBookContent.Builder bookBuilder = WrittenBookContent.writtenBookContent("
bookBuilder.addPage(Component.text("This is a new page!"));

// Add a page that shows differently for people who have swear filtering on
// For people who have filtering off, hate will be shown, while those with filtering on will see love.
// Players who have disabled filtering, will see "I hate Paper!", while those with filtering on will see the "I love Paper!".
bookBuilder.addFilteredPage(
Filtered.of(Component.text("I hate Paper!"), Component.text("I love paper!"))
Filtered.of(Component.text("I hate Paper!"), Component.text("I love Paper!"))
);

// Change generation
Expand All @@ -193,29 +200,33 @@ bookBuilder.generation(1);
writtenBook.setData(DataComponentTypes.WRITTEN_BOOK_CONTENT, bookBuilder.build());
```

### Example: Cool Sword
### Example: Cool sword

```java
ItemStack itemStack = ItemStack.of(Material.DIAMOND_SWORD);
itemStack.setData(DataComponentTypes.LORE, ItemLore.lore().addLine(Component.text("Cool sword!")).build());
itemStack.setData(DataComponentTypes.ENCHANTMENTS, ItemEnchantments.itemEnchantments().add(Enchantment.SHARPNESS, 10).build());
itemStack.setData(DataComponentTypes.RARITY, ItemRarity.RARE);

itemStack.unsetData(DataComponentTypes.TOOL); // Remove the cool component
itemStack.unsetData(DataComponentTypes.TOOL); // Remove the tool component

itemStack.setData(DataComponentTypes.MAX_DAMAGE, 10);
itemStack.setData(DataComponentTypes.ENCHANTMENT_GLINT_OVERRIDE, true); // Make it glow!
```

## Matching Items Without Certain Data Components
## Matching items without certain data components

When comparing items, you sometimes want to ignore certain values. For this we can use the
<Javadoc name="org.bukkit.inventory.ItemStack#matchesWithoutData(org.bukkit.inventory.ItemStack,java.util.Set)">`ItemStack#matchesWithoutData`</Javadoc>
method.

Ignore certain properties when comparing items:
For example, ignoring damage.
For example, here we compare two diamond swords whilst ignoring their durability:

```java
ItemStack originalSword = new ItemStack(Material.DIAMOND_SWORD);
ItemStack damagedSword = new ItemStack(Material.DIAMOND_SWORD);
damagedSword.setData(DataComponentTypes.DAMAGE, 100);
ItemStack originalSword = new ItemStack(Material.DIAMOND_SWORD);

boolean match = damagedSword.matchesWithoutData(originalSword, Set.of(DataComponentTypes.DAMAGE), false);
System.out.println("Match ignoring damage? " + match);
```
logger.info("Do the sword match? " + match); // true
```
Loading