Skip to content

Commit 1892a52

Browse files
committed
update capability documentation to match Technici4n capability refactor
1 parent de4f6dc commit 1892a52

File tree

2 files changed

+73
-136
lines changed

2 files changed

+73
-136
lines changed

docs/datastorage/attachments.md

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
The Attachments System
2+
=====================
3+
4+
Data attachments allow storing and retrieving data on objects in an easy way.
5+
6+
NeoForge adds data attachment support to BlockEntities, Entities, ItemStacks and Chunks. This will be explained in more detail in the following sections.
7+
8+
Creating your own Attachment
9+
----------------------------
10+
11+
Your attachment type has to be [registered][registry]. Attachments can be serializable, which means that they'll be saved and read from disk, when the objects leaves and enters the world. To serialize the attachment it has to either implement `INBTSerializable`, or you have to call `AttachmentType.Builder#serilize` with a `Codec` or an `IAttachmentSerializer`.
12+
13+
:::caution
14+
`Codec`s shouldn't be used for Attachments on ItemStacks, as the serialization will happen often and Codecs are slower compared to the other options.
15+
:::
16+
17+
`AttachmentType.Builder#copyOnDeath` can be called to persist the data across player respawns or entity transformations like Skeleton to Stray when in Powder Snow.
18+
19+
Using Attachment Data
20+
--------------------------------------------
21+
22+
Unlike Levels, Entities, and ItemStacks, LevelChunks and BlockEntities are only written to disk when they have been marked as dirty. A attachment implementation with persistent state for a LevelChunk or a BlockEntity should therefore ensure that whenever its state changes, its owner is marked as dirty.
23+
24+
To get the attachment data you call `IAttachmentHolder#getData` on the object. It will either return the value present in the object or a new instance created by the defaultValueSupplier of the `AttachmentType`.
25+
To set the attachment data you call `IAttachmentHolder#setData` on the object. Objects that have to be marked dirty, will have `setChanged` called on them to ensure no data loss.
26+
27+
Synchronizing Data with Clients
28+
-------------------------------
29+
30+
By default, attachment data is not sent to clients. In order to change this, the mods have to manage their own synchronization code using packets.
31+
32+
There are three different situations in which you may want to send synchronization packets, all of them optional:
33+
34+
1. When the entity spawns in the level, or the block is placed, you may want to share the initialization-assigned values with the clients.
35+
2. When the stored data changes, you may want to notify some or all of the watching clients.
36+
3. When a new client starts viewing the entity or block, you may want to notify it of the existing data.
37+
38+
Refer to the [Networking][network] page for more information on implementing network packets.
39+
40+
Persisting across Player Deaths
41+
-------------------------------
42+
43+
By default, the capability data does not persist on death. In order to change this, the data has to be manually copied when the player entity is cloned during the respawn process.
44+
45+
This can be done via `PlayerEvent$Clone` by reading the data from the original entity and assigning it to the new entity. In this event, the `#isWasDeath` method can be used to distinguish between respawning after death and returning from the End. This is important because the data will already exist when returning from the End, so care has to be taken to not duplicate values in this case.
46+
47+
[registry]: ../concepts/registries.md
48+
[network]: ../networking/index.md

docs/datastorage/capabilities.md

Lines changed: 25 additions & 136 deletions
Original file line numberDiff line numberDiff line change
@@ -5,173 +5,62 @@ Capabilities allow exposing features in a dynamic and flexible way without havin
55

66
In general terms, each capability provides a feature in the form of an interface.
77

8-
Forge adds capability support to BlockEntities, Entities, ItemStacks, Levels, and LevelChunks, which can be exposed either by attaching them through an event or by overriding the capability methods in your own implementations of the objects. This will be explained in more detail in the following sections.
8+
NeoForge adds capability support to Blocks, BlockEntities, Entities and ItemStacks, which can be exposed by attaching them through the RegisterCapabilitiesEvent. This will be explained in more detail in the following sections.
99

10-
Forge-provided Capabilities
10+
NeoForge-provided Capabilities
1111
---------------------------
1212

13-
Forge provides three capabilities: `IItemHandler`, `IFluidHandler` and `IEnergyStorage`
13+
NeoForge provides three capabilities: `IItemHandler`, `IFluidHandler` and `IEnergyStorage`
1414

15-
`IItemHandler` exposes an interface for handling inventory slots. It can be applied to BlockEntities (chests, machines, etc.), Entities (extra player slots, mob/creature inventories/bags), or ItemStacks (portable backpacks and such). It replaces the old `Container` and `WorldlyContainer` with an automation-friendly system.
15+
`IItemHandler` exposes an interface for handling inventory slots. It replaces the old `Container` and `WorldlyContainer` with an automation-friendly system.
1616

17-
`IFluidHandler` exposes an interface for handling fluid inventories. It can also be applied to BlockEntities, Entities, or ItemStacks.
17+
`IFluidHandler` exposes an interface for handling fluid inventories.
1818

19-
`IEnergyStorage` exposes an interface for handling energy containers. It can be applied to BlockEntities, Entities, or ItemStacks. It is based on the RedstoneFlux API by TeamCoFH.
19+
`IEnergyStorage` exposes an interface for handling energy containers. It is based on the RedstoneFlux API by TeamCoFH.
2020

2121
Using an Existing Capability
2222
----------------------------
2323

24-
As mentioned earlier, BlockEntities, Entities, and ItemStacks implement the capability provider feature through the `ICapabilityProvider` interface. This interface adds the method `#getCapability`, which can be used to query the capabilities present in the associated provider objects.
24+
The method `#getCapability` in `IForgeitemStack` and in `Entity` can be used to query the capabilities present in the associated provider objects. To query the capabilities for a `Block` or `BlockEntity` you use `IForgeLevel#getCapability` with the context you have.
2525

26-
In order to obtain a capability, you will need to refer it by its unique instance. In the case of the `IItemHandler`, this capability is primarily stored in `ForgeCapabilities#ITEM_HANDLER`, but it is possible to get other instance references by using `CapabilityManager#get`
26+
In order to obtain a capability, you will need to refer it by its unique instance. In the case of the `IItemHandler`, this capability is primarily stored in the fields of `ForgeCapabilities.ItemHandler`.
2727

28-
```java
29-
public static final Capability<IItemHandler> ITEM_HANDLER = CapabilityManager.get(new CapabilityToken<>(){});
30-
```
31-
32-
When called, `CapabilityManager#get` provides a non-null capability for your associated type. The anonymous `CapabilityToken` allows Forge to keep a soft dependency system while still having the necessary generic information to get the correct capability.
33-
34-
:::danger
35-
Even if you have a non-null capability available to you at all times, it does not mean the capability itself is usable or registered yet. This can be checked via `Capability#isRegistered`.
36-
:::
37-
38-
The `#getCapability` method has a second parameter, of type `Direction`, which can be used to request the specific instance for that one face. If passed `null`, it can be assumed that the request comes either from within the block or from some place where the side has no meaning, such as a different dimension. In this case a general capability instance that does not care about sides will be requested instead. The return type of `#getCapability` will correspond to a `LazyOptional` of the type declared in the capability passed to the method. For the Item Handler capability, this is `LazyOptional<IItemHandler>`. If the capability is not available for a particular provider, it will return an empty `LazyOptional` instead.
28+
The `#getCapability` method has a second parameter, of a generic type controlled by the capability, which may be used to request the specific instance. The nullability of the context depends on the capability. Capabilities with Void as context type only accept null. Null context usually request a view of the capability for information purposes. If the capability is not available for a particular provider, it will return `null` instead.
3929

4030
Exposing a Capability
4131
---------------------
4232

43-
In order to expose a capability, you will first need an instance of the underlying capability type. Note that you should assign a separate instance to each object that keeps the capability, since the capability will most probably be tied to the containing object.
44-
45-
In the case of `IItemHandler`, the default implementation uses the `ItemStackHandler` class, which has an optional argument in the constructor, to specify a number of slots. However, relying on the existence of these default implementations should be avoided, as the purpose of the capability system is to prevent loading errors in contexts where the capability is not present, so instantiation should be protected behind a check testing if the capability has been registered (see the remarks about `CapabilityManager#get` in the previous section).
33+
During game launch, after Registries have been initialized, the RegisterCapabilitiesEvent is being fired, you can call `registerBlock`, `registerBlockEntity`, `registerEntity` and `registerItem` to register your capabilities on the specified game objects. `registerBlockEntity` and `registerEntity` both use their type for registering to all instances.
4634

47-
Once you have your own instance of the capability interface, you will want to notify users of the capability system that you expose this capability and provide a `LazyOptional` of the interface reference. This is done by overriding the `#getCapability` method, and comparing the capability instance with the capability you are exposing. If your machine has different slots based on which side is being queried, you can test this with the `side` parameter. For Entities and ItemStacks, this parameter can be ignored, but it is still possible to have side as a context, such as different armor slots on a player (`Direction#UP` exposing the player's helmet slot), or about the surrounding blocks in the inventory (`Direction#WEST` exposing the input slot of a furnace). Do not forget to fall back to `super`, otherwise existing attached capabilities will stop working.
35+
These methods require you to provide the `ICapabilityProvider` or `IBlockCapabilityProvider` interfaces. You can use the context to expose different slots based on which direction is being queried, you can test this with the `side` parameter as an example for the context usage.
4836

49-
Capabilities must be invalidated at the end of the provider's lifecycle via `LazyOptional#invalidate`. For owned BlockEntities and Entities, the `LazyOptional` can be invalidated within `#invalidateCaps`. For non-owned providers, a runnable supplying the invalidation should be passed into `AttachCapabilitiesEvent#addListener`.
37+
Block capabilities must be invalidated at the end of the provider's lifecycle via `IForgeLevel#invalidateCapabilities`. This is done automatically on block breaking and placing as well as chunk loading and unloading. You only have to notify the Level if you provide a new one or your old one is no longer valid aside from this.
5038

5139
```java
52-
// Somewhere in your BlockEntity subclass
53-
LazyOptional<IItemHandler> inventoryHandlerLazyOptional;
54-
55-
// Supplied instance (e.g. () -> inventoryHandler)
56-
// Ensure laziness as initialization should only happen when needed
57-
inventoryHandlerLazyOptional = LazyOptional.of(inventoryHandlerSupplier);
58-
59-
@Override
60-
public <T> LazyOptional<T> getCapability(Capability<T> cap, Direction side) {
61-
if (cap == ForgeCapabilities.ITEM_HANDLER) {
62-
return inventoryHandlerLazyOptional.cast();
63-
}
64-
return super.getCapability(cap, side);
65-
}
66-
67-
@Override
68-
public void invalidateCaps() {
69-
super.invalidateCaps();
70-
inventoryHandlerLazyOptional.invalidate();
40+
// Some event listener
41+
public static void registerVanillaProviders(RegisterCapabilitiesEvent event) {
42+
event.registerBlockEntity(ForgeCapabilities.ItemHandler.BLOCK, BlockEntityTypes.MY_BLOCKENTITY, (be, side) ->
43+
return be.getItemHandler(side)
44+
);
7145
}
72-
```
73-
74-
:::tip
75-
If only one capability is exposed on a given object, you can use `Capability#orEmpty` as an alternative to the if/else statement.
76-
77-
```java
78-
@Override
79-
public <T> LazyOptional<T> getCapability(Capability<T> cap, Direction side) {
80-
return ForgeCapabilities.ITEM_HANDLER.orEmpty(cap, inventoryHandlerLazyOptional);
46+
// Somewhere in your BlockEntity subclass
47+
IItemHandler itemandler;
48+
public ItemHandler getItemHandler(Direction side) {
49+
return itemHandler;
8150
}
8251
```
83-
:::
84-
85-
`Item`s are a special case since their capability providers are stored on an `ItemStack`. Instead, a provider should be attached through `Item#initCapabilities`. This should hold your capabilities for the lifecycle of the stack.
8652

8753
It is strongly suggested that direct checks in code are used to test for capabilities instead of attempting to rely on maps or other data structures, since capability tests can be done by many objects every tick, and they need to be as fast as possible in order to avoid slowing down the game.
8854

89-
Attaching Capabilities
90-
----------------------
91-
92-
As mentioned, attaching capabilities to existing providers, `Level`s, and `LevelChunk`s can be done using `AttachCapabilitiesEvent`. The same event is used for all objects that can provide capabilities. `AttachCapabilitiesEvent` has 5 valid generic types providing the following events:
93-
94-
* `AttachCapabilitiesEvent<Entity>`: Fires only for entities.
95-
* `AttachCapabilitiesEvent<BlockEntity>`: Fires only for block entities.
96-
* `AttachCapabilitiesEvent<ItemStack>`: Fires only for item stacks.
97-
* `AttachCapabilitiesEvent<Level>`: Fires only for levels.
98-
* `AttachCapabilitiesEvent<LevelChunk>`: Fires only for level chunks.
99-
100-
The generic type cannot be more specific than the above types. For example: If you want to attach capabilities to `Player`, you have to subscribe to the `AttachCapabilitiesEvent<Entity>`, and then determine that the provided object is an `Player` before attaching the capability.
101-
102-
In all cases, the event has a method `#addCapability` which can be used to attach capabilities to the target object. Instead of adding capabilities themselves to the list, you add capability providers, which have the chance to return capabilities only from certain sides. While the provider only needs to implement `ICapabilityProvider`, if the capability needs to store data persistently, it is possible to implement `ICapabilitySerializable<T extends Tag>` which, on top of returning the capabilities, will provide tag save/load functions.
103-
104-
For information on how to implement `ICapabilityProvider`, refer to the [Exposing a Capability][expose] section.
105-
106-
Creating Your Own Capability
55+
Creating your own Capability
10756
----------------------------
10857

109-
A capability can be registered using one of two ways: `RegisterCapabilitiesEvent` or `@AutoRegisterCapability`.
110-
111-
### RegisterCapabilitiesEvent
112-
113-
A capability can be registered using `RegisterCapabilitiesEvent` by supplying the class of the capability type to the `#register` method. The event is [handled] on the mod event bus.
58+
Your capability has to be registered before or during `RegisterCapabilitiesEvent`. For that you have to call `#create` or `#createVoid` methods on `BlockCapability`, `EntityCapability`, `ItemCapability` or other custom CapabilityManager.
11459

60+
The first parameter is it's name. That means that the same type interface can be used in multiple capabilities. The second parameter is the type class, the API that will be returned by `#getCapability`. The third parameter is the context that has to be used to query the capability.
11561
```java
116-
@SubscribeEvent
117-
public void registerCaps(RegisterCapabilitiesEvent event) {
118-
event.register(IExampleCapability.class);
119-
}
62+
BlockCapability.create(forge("item_handler"), IItemHandler.class, Direction.class);
12063
```
12164

122-
### @AutoRegisterCapability
123-
124-
A capability is registered using `@AutoRegisterCapability` by annotating the capability type.
125-
126-
```java
127-
@AutoRegisterCapability
128-
public interface IExampleCapability {
129-
// ...
130-
}
131-
```
132-
133-
Persisting LevelChunk and BlockEntity capabilities
134-
--------------------------------------------
135-
136-
Unlike Levels, Entities, and ItemStacks, LevelChunks and BlockEntities are only written to disk when they have been marked as dirty. A capability implementation with persistent state for a LevelChunk or a BlockEntity should therefore ensure that whenever its state changes, its owner is marked as dirty.
137-
138-
`ItemStackHandler`, commonly used for inventories in BlockEntities, has an overridable method `void onContentsChanged(int slot)` designed to be used to mark the BlockEntity as dirty.
139-
140-
```java
141-
public class MyBlockEntity extends BlockEntity {
142-
143-
private final IItemHandler inventory = new ItemStackHandler(...) {
144-
@Override
145-
protected void onContentsChanged(int slot) {
146-
super.onContentsChanged(slot);
147-
setChanged();
148-
}
149-
}
150-
151-
// ...
152-
}
153-
```
154-
155-
Synchronizing Data with Clients
156-
-------------------------------
157-
158-
By default, capability data is not sent to clients. In order to change this, the mods have to manage their own synchronization code using packets.
159-
160-
There are three different situations in which you may want to send synchronization packets, all of them optional:
161-
162-
1. When the entity spawns in the level, or the block is placed, you may want to share the initialization-assigned values with the clients.
163-
2. When the stored data changes, you may want to notify some or all of the watching clients.
164-
3. When a new client starts viewing the entity or block, you may want to notify it of the existing data.
165-
166-
Refer to the [Networking][network] page for more information on implementing network packets.
167-
168-
Persisting across Player Deaths
169-
-------------------------------
170-
171-
By default, the capability data does not persist on death. In order to change this, the data has to be manually copied when the player entity is cloned during the respawn process.
172-
173-
This can be done via `PlayerEvent$Clone` by reading the data from the original entity and assigning it to the new entity. In this event, the `#isWasDeath` method can be used to distinguish between respawning after death and returning from the End. This is important because the data will already exist when returning from the End, so care has to be taken to not duplicate values in this case.
174-
175-
[expose]: #exposing-a-capability
17665
[handled]: ../concepts/events.md#creating-an-event-handler
17766
[network]: ../networking/index.md

0 commit comments

Comments
 (0)