# 24w09a/10a + Item Components 101
Here comes the (extremely belated) post on the two recent snapshots. Note that this post includes both 09a and 10a stuff, without distinguishing the two.
## Fabric API
Breaking changes were made to Data Generation API (now v17), Item API (now v5), Recipe API (now v4), and Transfer API (now v5).
### Data Generation
The following methods now take `RegistryWrapper.WrapperLookup registryLookup` arguments:
- `FabricAdvancementProvider#generateAdvancements`
- `FabricLanguageProvider#generateTranslations`
`FabricLanguageProvider` constructor now also requires passing `CompletableFuture<RegistryWrapper.WrapperLookup>`, like other providers.
### Item API
`FabricItemSettings` was removed (after deprecation during 1.20.5 cycle). Use vanilla `Item.Settings` instead. `ModifyItemAttributeModifiersCallback` was also removed without deprecation or replacement (yet).
`allowNbtUpdateAnimations` method of `FabricItem` was renamed to `allowComponentsUpdateAnimations`.
### Recipe API
`DefaultCustomIngredients#nbt` and support for NBT ingredients were removed, as the game no longer uses NBT to store item stack-specific data.
To check for custom name, enchantments, etc that were previously kept inside the NBT but are now recorded in the components (see below for details), use the new `components` ingredient. To check for custom data component (a NBT data not used by the game but can be used by data packs or commands), use the `customData` ingredient that will be released shortly.
### Transfer API
`TransferVariant` (such as `FluidVariant` or `ItemVariant`) is now a pair of the object and the components, instead of the object and NBT. `getNbt`, `hasNbt`, and `nbtMatches` methods were replaced with `getComponents`, `hasComponents`, and `componentsMatch` methods. The following methods were removed: `copyNbt`, `copyOrCreateNbt`, `toNbt`, and `toPacket`.
`FluidVariant#of` and `ItemVariant#of` now takes `ComponentChanges` instead of `@Nullable NbtCompound`.
`TransferVariant` is now serialized using codecs and packet codecs. Therefore, `fromNbt` and `fromPacket` static methods were removed.
`SingleVariantStorage#writeNbt` instance method was removed; subclasses now provide separate methods named `writeNbt`. Both `readNbt` and `writeNbt` now require passing a `RegistryWrapper.WrapperLookup` instance; they should be available in the methods from which they are called (or you can use the world's `DynamicRegistryManager` instance).
## Minecraft changes
### Item Components
We skip the general description of the item components system, which you can check in the slicedlime's video: [News in Data Pack Version 33 (24w09a): Item Components!](https://www.youtube.com/watch?v=iY9OHAd4Aco).
Now, we begin the journey into the implementation of item components - note that while this is currently used only by item stacks, nothing stops them from reusing this in other contexts (especially block entities):
There are five main objects in the item components:
- `DataComponentType`, a type of components, serving as keys;
- Component classes (can be any object), serving as values;
- `ComponentMap`, a read-only view of components;
- `ComponentMapImpl`, which is a `ComponentMap` that can be modified, and internally a pair of unmodifiable base `ComponentMap` and the "overrides" that are modified; and
- `ComponentChanges`, a map of component type to the changes for the value (either setting it to a specific value or removing it). This can be used as a diff applied to `ItemStack`; you apply an instance of `ComponentChanges` to `ItemStack`/`ComponentMapImpl`, so that the overrides part of the map reflects the changes. In other parts of the code, `ComponentChanges` is just used as `ComponentMapImpl` minus the base.
So, how does this work?
Each item has the base components. In addition to the base components common to all items, like having empty `EnchantmentsComponent`, some items provide additional base components. A notable example is `DataComponentTypes#DAMAGE` for armors, tools, and weapons. A component type that exists in the base has a corresponding default value; here the default damage is `0`.
An `ItemStack` can either 1) add an additional component not present in the base; 2) change the components from the one in the base; or 3) remove a component that exists in the base. The final components of `ItemStack`, obtainable as `ComponentMap` from `ItemStack#getComponents`, reflects all three.
**(Note: the folloing note and table is not necessary for understanding how modders use it, but might be interesting for some.)**
Both `ComponentMapImpl` and `ComponentChanges` utilize `@Nullable Optional` for the values. Here, `null` means "we don't change this", while `Optional.empty()` means "remove this" (either from the base, or the existing `ComponentMapImpl` "overrides" if there is no base).
Here is the table explaining each possible situation. Looks confusing, but Eureka moment is waiting for you:
| Base for the `Item` | Current override of the base (saved in `ItemStack`) | Applied `ComponentChanges.Builder` | Result | Effective value from `get` call |
|---------------------------|-----------------------------------------------------|------------------------------------|-------------------------------------------------------------------------------------------------|---------------------------------|
| Missing (`get() == null`) | Missing | Missing/`remove()` | Nothing occurs | `null` |
| Missing | Missing | `add(T)` | "Current override" set to T | T |
| Missing | `Optional.of(T)` | Missing/`add(T)` | Nothing occurs | T |
| Missing | `Optional.of(T)` | `remove()` | T removed, "Current override" is now missing | `null` |
| Missing | `Optional.of(T)` | `add(T2)` | "Current override" is now T2 | T2 |
| Default (T) | Missing | Missing | Nothing occurs | T |
| Default (T) | Missing | `remove()` | Overridden so that the base component is removed for the stack only | `null` |
| Default (T) | Missing | `add(T)` | Because the default value is added, "Current override" continues to be missing | T |
| Default (T) | Missing | `add(T2)` | "Current override" set to T2 | T2 |
| Default (T) | `Optional.of(T2)` | Missing/`add(T2)` | Nothing occurs | T2 |
| Default (T) | `Optional.of(T2)` | `add(T)` | Because the default value is added, "Current override" is now missing | T |
| Default (T) | `Optional.of(T2)` | `remove()` | Base component is removed | `null` |
| Default (T) | `Optional.empty()` | Missing/`remove()` | Nothing occurs | `null` |
| Default (T) | `Optional.empty()` | `add(T)` | Because the default value is added, the component is back and "Current override" is now missing | T |
| Default (T) | `Optional.empty()` | `add(T2)` | "Current override" set to T2 | T2 |
### Using Item Components
Getting/setting the custom name of an item (note that `Text` is directly used as the component value):
```java
@Nullable Text name = stack.get(DataComponentTypes.CUSTOM_NAME);
// Like Map#put, returns old value
@Nullable Text oldName = stack.set(DataComponentTypes.CUSTOM_NAME, Text.literal("Lorem ipsum"));
// get with default value. This does not modify the stack.
name = stack.getOrDefault(DataComponentTypes.CUSTOM_NAME, Text.empty());
// Remove custom name; does nothing when there is no custom name
@Nullable Text removedName = stack.remove(DataComponentTypes.CUSTOM_NAME);
// apply() works like Map#compute.
// Here, we change the color of the item name.
// (Note: stack#getName already checks for custom name, so in reality this does not need apply() call.)
stack.apply(DataComponentTypes.CUSTOM_NAME, stack.getName(), current -> current.copy().formatted(Formatting.RED))
```
Getting/setting the custom data (NBT). This is useful for datapack makers and server-side modders, because custom item components (see below) need to be synced to the clients, while custom data remains an unparsed NBT.
Unlike the previous example, this uses a component record `NbtComponent`.
Note: **component values are supposed to be immutable** - even if you can modify it, don't. Always copy and `set()`.
```java
NbtCompound nbt = ...;
NbtComponent component = NbtComponent.of(nbt);
// Setting
stack.set(DataComponentTypes.CUSTOM_DATA, component);
// Getting a copy
@Nullable var data = stack.get(DataComponentTypes.CUSTOM_DATA);
if (data != null) {
NbtCompound value = data.copyNbt();
}
// Using NbtComponent#apply, which copies automatically, to modify the NBT
// Note: you might want to use the static method NbtComponent#set instead,
// which makes this a bit shorter
stack.apply(DataComponentTypes.CUSTOM_DATA, NbtComponent.DEFAULT, comp -> comp.apply(currentNbt -> {
currentNbt.putInt("key", 0);
}));
// Checking if a certain NBT is a subset of the stack NBT
// (also known as: "non-strict matching")
NbtCompound requiredNbt = ...;
boolean match = stack.getOrDefault(DataComponentTypes.CUSTOM_DATA, NbtComponent.DEFAULT).matches(requiredNbt);
// use createPredicate to make Predicate<ItemStack>
```
Using `ComponentChanges` to mass-modify the components of `ItemStack`:
```java
// stack is a damageable item.
// Repair the item and remove custom name.
// Note that the resulting stack has no components because 0 damage is the default from the base components.
var changes = ComponentChanges.builder().add(DataComponentTypes.DAMAGE, 0).remove(DataComponentTypes.CUSTOM_NAME).build();
stack.applyChanges(changes);
```
Making an item with base components:
```java
Item item = new Item(new Item.Settings().component(DataComponentTypes.CUSTOM_NAME, Text.literal("Hello")));
// Call #component multiple times for multiple base components.
// Note: calling maxDamage automatically adds DAMAGE component.
```
Making a custom component. Note that like many registered entries, these must be present in both the client and the server.
```java
public static final DataComponentType<Integer> WEIRDNESS = DataComponentType.builder().codec(Codec.INT).packetCodec(PacketCodecs.VAR_INT).build();
// in the initializer
Registry.register(Registries.DATA_COMPONENT_TYPE, new Identifier("example", "weirdness"), WEIRDNESS);
```
### Some caveats
- Component values should not be modified directly. Using `Record` or other immutable object is highly recommended. Always copy, modify, then set.
- Setting an invalid value for a component does not cause an error immediately, but will lead to a crash during save, and possible data corruption. A common example is `NONNEGATIVE_INT` being used for an incrementing value; when it overflows and the value is set to negatives, saving it would crash.
### Other Item changes
- As noted in the video, an empty item stack is now serialized as omitting the field or an empty object. `ItemStack#fromNbt` now returns `Optional<ItemStack>` while `fromNbtOrEmpty` supports empty NBT object. Both now require passing the registries.
- `writeNbt` method was renamed to `encode`. The one with `NbtCompound` argument allows adding to the existing compound, like `writeNbt`.
- The methods in `ItemStack` that reference `Nbt` are generally renamed to reference `Components` instead.
- `Item#isNbtSynced` was removed, specify custom packet codecs instead.
- `Item#getBreakSound` was added.
- Attack damage and mining speed of `MiningToolItem` and `SwordItem` are now specified in item settings via `attributeModifiers` setting.
### BlockEntity interaction with components
Although `BlockEntity` does not fully use components yet, there are some mechanisms allowing it to interact with item stacks for the block (like shulker boxes).
A block entity should use components to store data in the corresponding item stacks, either dropped or Pick-Blocked. This can be done by:
- Setting the block entity's fields in `readComponents`,
- Writing the block entity's fields to the components in `addComponents`, and
- Removing fields from the serialized NBT that are now encoded using components with `removeFromCopiedStackNbt`.
Here is an example (note that `LockableContainerBlockEntity` already provides this, if you're using it):
```java
private final DefaultedList<ItemStack> inventory = ...;
public void readComponents(ComponentMap components) {
components.getOrDefault(DataComponentTypes.CONTAINER, ContainerComponent.DEFAULT).copyTo(this.inventory);
}
public void addComponents(ComponentMap.Builder builder) {
builder.add(DataComponentTypes.CONTAINER, ContainerComponent.fromStacks(this.inventory));
}
public void removeFromCopiedStackNbt(NbtCompound nbt) {
nbt.remove("Items");
}
```
### Other changes
- Pathfinding cache was added; and with this change `AbstractBlock#canPathfindThrough` no longer takes the `world` and `pos` arguments.
- Serialization of `Text` now requires passing registries. In contexts where registry is unavailable, use `DynamicRegistryManager.EMPTY`.
- Some string-related methods were moved to `StringHelper`.