Skip to content

Commit c5d000c

Browse files
TimongcraftzlataovceStrokkur424
authored
chore: update pdc docs (#620)
Co-authored-by: Matouš Kučera <[email protected]> Co-authored-by: Strokkur24 <[email protected]>
1 parent 677fb3c commit c5d000c

File tree

1 file changed

+93
-44
lines changed
  • src/content/docs/paper/dev/api

1 file changed

+93
-44
lines changed

src/content/docs/paper/dev/api/pdc.md

Lines changed: 93 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,22 @@
22
title: Persistent data container (PDC)
33
description: A guide to the PDC API for storing data.
44
slug: paper/dev/pdc
5+
version: 1.21.8
56
---
67

78
The Persistent Data Container (PDC) is a way to store custom data on a whole range of objects; such as items, entities, and block entities.
89
The full list of classes that support the PDC are:
910

11+
- [`ItemStack`](#itemstack)
1012
- [`Chunk`](#chunk)
1113
- [`World`](#world)
1214
- [`Entity`](#entity)
1315
- [`TileState`](#tilestate)
1416
- [`Structure`](#structure)
15-
- [`ItemMeta`](#itemmeta)
1617
- [`GeneratedStructure`](#generatedstructure)
1718
- [`Raid`](#raid)
1819
- [`OfflinePlayer`](#offlineplayer)
19-
- [`ItemStack`](#itemstack)
20+
- [`ItemMeta`](#itemmeta)
2021

2122
## What is it used for?
2223
In the past, developers resorted to a variety of methods to store custom data on objects:
@@ -34,42 +35,57 @@ which is used to identify the data. The second is a [`PersistentDataContainer`](
3435
which is the object you want to store the data on. The third is the data itself.
3536

3637
```java
37-
// Create a NamespacedKey
38-
NamespacedKey key = new NamespacedKey(pluginInstance, "example-key");
38+
NamespacedKey key = new NamespacedKey(pluginInstance, "example-key"); // Create a NamespacedKey
39+
World world = Bukkit.getServer().getWorlds().getFirst();
3940

41+
PersistentDataContainer pdc = world.getPersistentDataContainer();
42+
pdc.set(key, PersistentDataType.STRING, "I love tacos!");
43+
```
44+
45+
[`ItemStack`](jd:paper:org.bukkit.inventory.ItemStack) however doesn't have this method and instead requires you to use its builder-style consumer:
46+
47+
```java
48+
NamespacedKey key = ...;
49+
50+
// For 1.20.4 and below, use 'new ItemStack(Material.DIAMOND)' instead
4051
ItemStack item = ItemStack.of(Material.DIAMOND);
41-
// ItemMeta implements PersistentDataHolder, so we can get the PDC from it
42-
item.editMeta(meta -> {
43-
meta.getPersistentDataContainer().set(key, PersistentDataType.STRING, "I love Tacos!");
52+
item.editPersistentDataContainer(pdc -> {
53+
pdc.set(key, PersistentDataType.STRING, "I love tacos!");
4454
});
4555
```
4656

4757
:::note
4858

59+
The [`ItemStack#editPersistentDataContainer()`](jd:paper:org.bukkit.inventory.ItemStack#editPersistentDataContainer(java.util.function.Consumer)) method on `ItemStack` is only available in 1.21.4+. For older versions, you need to access and modify the [`ItemMeta`](jd:paper:org.bukkit.inventory.meta.ItemMeta) instead.
60+
For 1.16.5+, there's the [`ItemStack#editMeta()`](jd:paper:org.bukkit.inventory.ItemStack#editMeta(java.util.function.Consumer)) method though.
61+
62+
:::
63+
64+
:::note
65+
4966
It is considered good practice to reuse `NamespacedKey` objects. They can be constructed with either:
5067
- A [`Plugin`](jd:paper:org.bukkit.plugin.Plugin) instance and a [`String`](jd:java:java.lang.String) identifier
5168
- A [`String`](jd:java:java.lang.String) namespace and a [`String`](jd:java:java.lang.String) identifier
5269

53-
The first option is often preferred as it will automatically use the plugin's namespace; however, the second option can be used if you
54-
want to use a different namespace or access the data from another plugin.
70+
The first option is often preferred as it will automatically use the plugin's lowercased name as namespace; however, the second option can be used if you want to use a different namespace or access the data from another plugin.
5571

5672
:::
5773

5874
## Getting data
59-
To get data from the PDC, you need to know the `NamespacedKey` and the `PersistentDataType` of the data.
75+
To get data from the PDC, you need to know the `NamespacedKey` and the [`PersistentDataType`](jd:paper:org.bukkit.persistence.PersistentDataType) of the data.
76+
Some API parts, such as Adventure's [`Component.text(String)`](https://jd.advntr.dev/api/latest/net/kyori/adventure/text/Component.html#text(java.lang.String)), require non-null values. In such cases, use the [`getOrDefault`](jd:paper:io.papermc.paper.persistence.PersistentDataContainerView#getOrDefault(org.bukkit.NamespacedKey,org.bukkit.persistence.PersistentDataType,C)) on the pdc instead of [`get`](jd:paper:io.papermc.paper.persistence.PersistentDataContainerView#get(org.bukkit.NamespacedKey,org.bukkit.persistence.PersistentDataType)), which is nullable.
6077

6178
```java
62-
// Create a NamespacedKey
63-
NamespacedKey key = new NamespacedKey(pluginInstance, "example-key");
64-
65-
ItemStack item = ...; // Retrieve the item from before
66-
// Get the data from the PDC
67-
PersistentDataContainer container = item.getItemMeta().getPersistentDataContainer();
68-
if (container.has(key, PersistentDataType.STRING)) {
69-
String value = container.get(key, PersistentDataType.STRING);
70-
// Do something with the value
71-
player.sendMessage(Component.text(value));
72-
}
79+
NamespacedKey key = ...; // Use the same key as the adding-data example
80+
World world = ...; // Use the same world as the adding-data example
81+
82+
PersistentDataContainer pdc = world.getPersistentDataContainer();
83+
84+
// Utilize the data from the PDC
85+
String value = pdc.getOrDefault(key, PersistentDataType.STRING, "<null>");
86+
87+
// Do something with the value
88+
player.sendPlainMessage(value);
7389
```
7490

7591
## Data types
@@ -85,7 +101,7 @@ The PDC supports a wide range of data types, such as:
85101
- `Boolean`
86102
- `Tag Containers` - a way to nest PDCs within each other. To create a new `PersistentDataContainer`, you can use:
87103
```java
88-
// Get the existing container
104+
// Get an existing container
89105
PersistentDataContainer container = ...;
90106
// Create a new container
91107
PersistentDataContainer newContainer = container.getAdapterContext().newPersistentDataContainer();
@@ -100,7 +116,7 @@ The PDC supports a wide range of data types, such as:
100116
List.of("a", "list", "of", "strings")
101117
);
102118

103-
// Storing a list of strings in a container by using the api
119+
// Storing a list of strings in a container by using the API
104120
// provided pre-definitions of commonly used list types.
105121
container.set(key, PersistentDataType.LIST.strings(), List.of("a", "list", "of", "strings"));
106122

@@ -118,13 +134,20 @@ The [`Boolean`](jd:paper:org.bukkit.persistence.PersistentDataType#BOOLEAN) PDC
118134
### Custom data types
119135

120136
You can store a wide range of data in the PDC with the native adapters; however, if you need a more complex data type, you can
121-
implement your own [`PersistentDataType`](jd:paper:org.bukkit.persistence.PersistentDataType) and use that instead.
137+
implement your own `PersistentDataType` and use that instead.
122138
The `PersistentDataType`'s job is to "deconstruct" a complex data type into something that is natively supported (see above) and then vice-versa.
123139

124140
Here is an example of how to do that for a UUID:
125141

126142
```java title="UUIDDataType.java"
143+
@NullMarked
127144
public class UUIDDataType implements PersistentDataType<byte[], UUID> {
145+
146+
public static final UUIDDataType INSTANCE = new UUIDDataType();
147+
148+
// We just need a singleton, so there's no need to allow instantiation
149+
private UUIDDataType() {}
150+
128151
@Override
129152
public Class<byte[]> getPrimitiveType() {
130153
return byte[].class;
@@ -160,11 +183,39 @@ In order to use your own `PersistentDataType`, you must pass an instance of it t
160183
[`set`](jd:paper:org.bukkit.persistence.PersistentDataContainer#set(org.bukkit.NamespacedKey,org.bukkit.persistence.PersistentDataType,C))/
161184
[`has`](jd:paper:io.papermc.paper.persistence.PersistentDataContainerView#has(org.bukkit.NamespacedKey,org.bukkit.persistence.PersistentDataType)) methods.
162185
```java
163-
container.set(key, new UUIDDataType(), uuid);
186+
container.set(key, UUIDDataType.INSTANCE, uuid);
164187
```
165188

166189
:::
167190

191+
192+
## Read-only containers
193+
194+
Certain classes, like `ItemStack` or [`OfflinePlayer`](jd:paper:org.bukkit.OfflinePlayer), provide a read-only view of their PDC.
195+
In contrast to `ItemStack`, `OfflinePlayer` does <u>not</u> provide any way to modify the underlying container.
196+
This is because the `OfflinePlayer` is directly read from disk and would require a blocking file operation.
197+
Mutable objects, like the [`PersistentDataHolder#getPersistentDataContainer()`](jd:paper:org.bukkit.persistence.PersistentDataHolder#getPersistentDataContainer()), generally need to be re-saved even without modification or monitored.
198+
That's why it's better to use unmodifiable "views" for read-only operations.
199+
200+
```java
201+
NamespacedKey key = ...;
202+
ItemStack item = ...;
203+
204+
PersistentDataContainerView pdcView = item.getPersistentDataContainer();
205+
206+
// Utilize the data from the PDC "view"
207+
String value = pdcView.getOrDefault(key, PersistentDataType.STRING, "<null>");
208+
209+
// Do something with the value
210+
player.sendPlainMessage(value);
211+
```
212+
213+
:::note
214+
215+
PDC-view support for `ItemStack` was only introduced in 1.21.1. For older versions, you need to use the `ItemMeta` instead.
216+
217+
:::
218+
168219
## Storing on different objects
169220

170221
:::caution
@@ -176,8 +227,21 @@ E.g. Placing an ItemStack as a Block (with a TileState) ***does not*** copy over
176227
:::
177228

178229
Objects that can have a PDC implement the [`PersistentDataHolder`](jd:paper:org.bukkit.persistence.PersistentDataHolder) interface
179-
and their PDC can be fetched with [`PersistentDataHolder#getPersistentDataContainer()`](jd:paper:org.bukkit.persistence.PersistentDataHolder#getPersistentDataContainer()).
230+
and their PDC can be fetched with `PersistentDataHolder#getPersistentDataContainer()`.
231+
232+
- ##### [`ItemStack`](jd:paper:org.bukkit.inventory.ItemStack)
233+
- The persistent data container of an `ItemStack` has historically been accessed by
234+
the `ItemStack`'s `ItemMeta`. This, however, includes the overhead of constructing the entire `ItemMeta`, which acts as a snapshot of the `ItemStack`'s data at the point of creation.
180235

236+
To avoid this overhead in 1.21.1+, ItemStack exposes a read-only view of its persistent data container at [`ItemStack#getPersistentDataContainer()`](jd:paper:org.bukkit.inventory.ItemStack#getPersistentDataContainer()).
237+
Edits to the persistent data container can also be simplified in 1.21.4+ using `ItemStack#editPersistentDataContainer(java.util.function.Consumer)`.
238+
The persistent data container available in the consumer is not valid outside the consumer.
239+
```java
240+
ItemStack itemStack = ...;
241+
itemStack.editPersistentDataContainer(pdc -> {
242+
pdc.set(key, PersistentDataType.STRING, "I love tacos!");
243+
});
244+
```
181245
- ##### [`Chunk`](jd:paper:org.bukkit.Chunk)
182246
- `Chunk#getPersistentDataContainer()`
183247
- ##### [`World`](jd:paper:org.bukkit.World)
@@ -190,33 +254,18 @@ and their PDC can be fetched with [`PersistentDataHolder#getPersistentDataContai
190254
```java
191255
Block block = ...;
192256
if (block.getState() instanceof Chest chest) {
193-
chest.getPersistentDataContainer().set(key, PersistentDataType.STRING, "I love Tacos!");
257+
chest.getPersistentDataContainer().set(key, PersistentDataType.STRING, "I love tacos!");
194258
chest.update();
195259
}
196260
```
197261
- ##### [`Structure`](jd:paper:org.bukkit.structure.Structure)
198262
- `Structure#getPersistentDataContainer()`
199-
- ##### [`ItemMeta`](jd:paper:org.bukkit.inventory.meta.ItemMeta)
200-
- `ItemMeta#getPersistentDataContainer()`
201263
- ##### [`GeneratedStructure`](jd:paper:org.bukkit.generator.structure.GeneratedStructure)
202264
- `GeneratedStructure#getPersistentDataContainer()`
203265
- ##### [`Raid`](jd:paper:org.bukkit.Raid)
204266
- `Raid#getPersistentDataContainer()`
205267
- ##### [`OfflinePlayer`](jd:paper:org.bukkit.OfflinePlayer)
206268
- OfflinePlayer only exposes a read-only version of the persistent data container.
207269
It can be accessed via `OfflinePlayer#getPersistentDataContainer()`.
208-
- ##### [`ItemStack`](jd:paper:org.bukkit.inventory.ItemStack)
209-
- The persistent data container of an `ItemStack` has historically been accessed by
210-
the `ItemStack`'s `ItemMeta`. This, however, includes the overhead of constructing the entire
211-
`ItemMeta`, which acts as a snapshot of the `ItemStack`'s data at the point of creation.
212-
213-
To avoid this overhead, ItemStack exposes a read-only view of its persistent data container at
214-
`ItemStack#getPersistentDataContainer()`.
215-
Edits to the persistent data container can be achieved via `ItemStack#editPersistentDataContainer(Consumer)`.
216-
The persistent data container available in the consumer is not valid outside the consumer.
217-
```java
218-
ItemStack itemStack = ...;
219-
itemStack.editPersistentDataContainer(pdc -> {
220-
pdc.set(key, PersistentDataType.STRING, "I love Tacos!");
221-
});
222-
```
270+
- ##### [`ItemMeta`](jd:paper:org.bukkit.inventory.meta.ItemMeta)
271+
- `ItemMeta#getPersistentDataContainer()`

0 commit comments

Comments
 (0)