1717import ch .njol .skript .util .slot .Slot ;
1818import ch .njol .skript .variables .Variables ;
1919import ch .njol .util .Kleenean ;
20+ import ch .njol .util .coll .CollectionUtils ;
21+ import io .papermc .paper .persistence .PersistentDataContainerView ;
2022import org .bukkit .NamespacedKey ;
2123import org .bukkit .block .Block ;
2224import org .bukkit .block .TileState ;
2830import org .jetbrains .annotations .Nullable ;
2931import org .skriptlang .skript .bukkit .pdc .PDCSerializer ;
3032import org .skriptlang .skript .bukkit .pdc .SkriptDataType ;
31- import org .skriptlang .skript .docs .Origin ;
3233import org .skriptlang .skript .lang .arithmetic .Arithmetics ;
3334import org .skriptlang .skript .lang .arithmetic .Operator ;
3435import org .skriptlang .skript .lang .comparator .Comparators ;
4546 Provides access to the 'persistent data container' Bukkit provides on many objects. These values are stored on the \
4647 chunk/world/item/entity directly, like custom NBT, but are much faster and reliable to access.
4748 Persistent values natively support numbers and text, but any Skript type that can be saved in a variable can also be \
48- stored in pdc via this expression. Lists of objects can also be saved.
49+ stored in PDC via this expression. Lists of objects can also be saved.
4950 If you attempt to save invalid types, runtime errors will be thrown.
5051
5152 The names of tags must be valid namespaced keys, i.e. a-z, 0-9, '_', '.', '/', and '-' are the allowed characters. \
7374@ Keywords ({"pdc" , "persistent data container" , "custom data" , "nbt" })
7475public class ExprPersistentData extends PropertyExpression <Object , Object > {
7576
76- public static void register (SyntaxRegistry registry , Origin origin ) {
77+ public static void register (SyntaxRegistry registry ) {
7778 registry .register (
7879 SyntaxRegistry .EXPRESSION ,
7980 infoBuilder (
@@ -82,7 +83,6 @@ public static void register(SyntaxRegistry registry, Origin origin) {
8283 "chunks/worlds/entities/blocks/itemtypes/offlineplayers" ,
8384 false
8485 )
85- .origin (origin )
8686 .supplier (ExprPersistentData ::new )
8787 .build ());
8888 }
@@ -122,7 +122,7 @@ private record ElementsResult(List<Object> elements, boolean storedAsList) {}
122122 * Gets all elements from the PDC, whether stored as a single value or a list.
123123 * Also indicates whether the data was stored as a list.
124124 */
125- private ElementsResult getAllElements (PersistentDataContainer container , NamespacedKey key ) {
125+ private ElementsResult getAllElements (PersistentDataContainerView container , NamespacedKey key ) {
126126 List <Object > elements = new ArrayList <>();
127127
128128 // Try representable types first (singular storage)
@@ -171,7 +171,7 @@ protected Object[] get(Event event, Object[] source) {
171171
172172 List <Object > values = new ArrayList <>();
173173 for (Object holder : source ) {
174- editPersistentDataContainer (holder , container -> {
174+ getPersistentDataContainer (holder , container -> {
175175 ElementsResult result = getAllElements (container , key );
176176 List <Object > elements = result .elements ();
177177 if (elements .isEmpty ())
@@ -244,12 +244,18 @@ public boolean isSingle() {
244244 public Class <?> @ Nullable [] acceptChange (ChangeMode mode ) {
245245 return switch (mode ) {
246246 case DELETE -> new Class [0 ];
247- case SET , ADD , REMOVE -> {
247+ case SET , ADD , REMOVE , RESET -> {
248248 if (parsedType != null ) {
249- Class <?> typeClass = parsedType .getClassInfo ().getC ();
250- yield new Class <?>[]{plural ? typeClass .arrayType () : typeClass };
249+ ClassInfo <?> type = parsedType .getClassInfo ();
250+ Changer <?> changer = type .getChanger ();
251+ if (changer != null ) {
252+ yield changer .acceptChange (mode );
253+ }
254+ if (mode == ChangeMode .SET )
255+ yield CollectionUtils .array (type .getC ());
256+ yield null ;
251257 }
252- yield new Class <?>[]{ plural ? Object [].class : Object .class } ;
258+ yield CollectionUtils . array ( plural ? Object [].class : Object .class ) ;
253259 }
254260 default -> null ;
255261 };
@@ -266,7 +272,7 @@ public void change(Event event, Object @Nullable [] delta, ChangeMode mode) {
266272
267273 // ensure set to correct types
268274 ClassInfo <?> classInfo = null ;
269- if (mode == ChangeMode .SET || mode == ChangeMode .ADD || mode == ChangeMode .REMOVE ) {
275+ if (mode == ChangeMode .SET || ( plural && ( mode == ChangeMode .ADD || mode == ChangeMode .REMOVE )) ) {
270276 assert delta != null ;
271277 for (Object deltaValue : delta ) {
272278 classInfo = Classes .getSuperClassInfo (deltaValue .getClass ());
@@ -275,6 +281,9 @@ public void change(Event event, Object @Nullable [] delta, ChangeMode mode) {
275281 return ;
276282 }
277283 }
284+ } else if (mode == ChangeMode .ADD || mode == ChangeMode .REMOVE ) {
285+ assert delta != null ;
286+ classInfo = Classes .getSuperClassInfo (delta [0 ].getClass ()); // plural is false so this is safe
278287 }
279288
280289 Set <Block > invalidBlocks = new HashSet <>();
@@ -285,49 +294,53 @@ public void change(Event event, Object @Nullable [] delta, ChangeMode mode) {
285294 continue ;
286295 }
287296 editPersistentDataContainer (holder , container -> {
288- if (mode == ChangeMode .SET ) {
289- if (!plural ) {
290- // Singular: store first value only
291- //noinspection unchecked
292- PersistentDataType <?, Object > tagType = (PersistentDataType <?, Object >) PDCSerializer .getPDCType (finalClassInfo );
293- container .set (key , tagType , delta [0 ]);
294- } else {
295- // List: always store as list, even for single element
296- List <PersistentDataContainer > containers = new ArrayList <>();
297- for (Object object : delta ) {
298- containers .add (SkriptDataType .get ().toPrimitive (object , container .getAdapterContext ()));
297+ switch (mode ) {
298+ case SET -> {
299+ if (!plural ) {
300+ // Singular: store first value only
301+ //noinspection unchecked
302+ PersistentDataType <?, Object > tagType = (PersistentDataType <?, Object >) PDCSerializer .getPDCType (finalClassInfo );
303+ container .set (key , tagType , delta [0 ]);
304+ } else {
305+ // List: always store as list, even for single element
306+ List <PersistentDataContainer > containers = new ArrayList <>();
307+ for (Object object : delta ) {
308+ containers .add (SkriptDataType .get ().toPrimitive (object , container .getAdapterContext ()));
309+ }
310+ container .set (key , PersistentDataType .LIST .dataContainers (), containers );
299311 }
300- container .set (key , PersistentDataType .LIST .dataContainers (), containers );
301312 }
302- } else if (mode == ChangeMode .DELETE ) {
303- container .remove (key );
304- } else if (mode == ChangeMode .ADD || mode == ChangeMode .REMOVE ) {
305- if (!plural ) {
306- // Check for list/singular mismatch
307- if (container .has (key , PersistentDataType .LIST .dataContainers ())) {
308- error ("The data in tag '" + tagName + "' is a list, not a single value. "
313+ case DELETE -> container .remove (key );
314+ case ADD , REMOVE -> {
315+ if (!plural ) {
316+ // Check for list/singular mismatch
317+ if (container .has (key , PersistentDataType .LIST .dataContainers ())) {
318+ error ("The data in tag '" + tagName + "' is a list, not a single value. "
309319 + "Use 'list data tag' instead of 'data tag'." );
310- return ;
311- }
312- //noinspection unchecked
313- var tagType = (PersistentDataType <?, Object >) PDCSerializer .getPDCType (finalClassInfo );
314- if (!container .has (key , tagType ))
315- return ;
316- Object original = container .get (key , tagType );
317- addOrRemoveFromSingleValue (original , delta , mode , value -> {
318- // arithmetic may have modified the value's type, so need to check again
320+ return ;
321+ }
319322 //noinspection unchecked
320- var resultType = (PersistentDataType <?, Object >) PDCSerializer .getPDCType (Classes .getSuperClassInfo (value .getClass ()));
321- container .set (key , resultType , value );
322- });
323- } else {
324- // Check for list/singular mismatch
325- if (container .has (key ) && !container .has (key , PersistentDataType .LIST .dataContainers ())) {
326- error ("The data in tag '" + tagName + "' is a single value, not a list. "
323+ var tagType = (PersistentDataType <?, Object >) PDCSerializer .getPDCType (finalClassInfo );
324+ if (!container .has (key , tagType ))
325+ return ;
326+ Object original = container .get (key , tagType );
327+ addOrRemoveFromSingleValue (original , delta , mode , value -> {
328+ // arithmetic may have modified the value's type, so need to check again
329+ //noinspection unchecked
330+ var resultType = (PersistentDataType <?, Object >) PDCSerializer .getPDCType (Classes .getSuperClassInfo (value .getClass ()));
331+ container .set (key , resultType , value );
332+ });
333+ } else {
334+ // Check for list/singular mismatch
335+ if (container .has (key ) && !container .has (key , PersistentDataType .LIST .dataContainers ())) {
336+ error ("The data in tag '" + tagName + "' is a single value, not a list. "
327337 + "Use 'data tag' instead of 'list data tag'." );
328- return ;
338+ return ;
339+ }
340+ addOrRemoveFromList (container , key , delta , mode );
329341 }
330- addOrRemoveFromList (container , key , delta , mode );
342+ }
343+ case null , default -> {
331344 }
332345 }
333346 });
@@ -339,24 +352,53 @@ public void change(Event event, Object @Nullable [] delta, ChangeMode mode) {
339352 }
340353 }
341354
355+ /**
356+ * Gets the data container of an object. The returned container should not be modified.
357+ * Use {@link #editPersistentDataContainer(Object, Consumer)} if editing is desired.
358+ * @param holder Source of the container
359+ * @param consumer Code to run with the container
360+ */
361+ private void getPersistentDataContainer (Object holder , Consumer <PersistentDataContainerView > consumer ) {
362+ if (holder instanceof PersistentDataHolder dataHolder ) {
363+ consumer .accept (dataHolder .getPersistentDataContainer ());
364+ } else if (holder instanceof ItemType itemType ) {
365+ var meta = itemType .getItemMeta ();
366+ consumer .accept (meta .getPersistentDataContainer ());
367+ } else if (holder instanceof ItemStack itemStack ) {
368+ if (!itemStack .hasItemMeta ())
369+ return ;
370+ consumer .accept (itemStack .getPersistentDataContainer ());
371+ } else if (holder instanceof Slot slot ) {
372+ var item = slot .getItem ();
373+ if (item == null || !item .hasItemMeta ())
374+ return ;
375+ consumer .accept (item .getPersistentDataContainer ());
376+ } else if (holder instanceof Block block && block .getState () instanceof TileState tileState ) {
377+ consumer .accept (tileState .getPersistentDataContainer ());
378+ }
379+
380+ }
381+
342382 /**
343383 * Helper to easily edit PDCs.
344384 * @param holder The holder of the PDC.
345385 * @param consumer The method to run to edit the PDC.
346386 */
347387 private void editPersistentDataContainer (Object holder , Consumer <PersistentDataContainer > consumer ) {
348- if (holder instanceof PersistentDataHolder dataHolder )
388+ if (holder instanceof PersistentDataHolder dataHolder ) {
349389 consumer .accept (dataHolder .getPersistentDataContainer ());
350- else if (holder instanceof ItemType itemType ) {
390+ } else if (holder instanceof ItemType itemType ) {
351391 var meta = itemType .getItemMeta ();
352392 consumer .accept (meta .getPersistentDataContainer ());
353393 itemType .setItemMeta (meta );
354394 } else if (holder instanceof ItemStack itemStack ) {
355- if (!itemStack .hasItemMeta ()) return ;
395+ if (!itemStack .hasItemMeta ())
396+ return ;
356397 itemStack .editPersistentDataContainer (consumer );
357398 } else if (holder instanceof Slot slot ) {
358399 var item = slot .getItem ();
359- if (item == null || !item .hasItemMeta ()) return ;
400+ if (item == null || !item .hasItemMeta ())
401+ return ;
360402 item .editPersistentDataContainer (consumer );
361403 slot .setItem (item );
362404 } else if (holder instanceof Block block && block .getState () instanceof TileState tileState ) {
@@ -397,7 +439,7 @@ private void addOrRemoveFromSingleValue(Object originalValue, Object[] delta, Ch
397439 Class <?> clazz = originalValue == null ? null : originalValue .getClass ();
398440 Operator operator = mode == ChangeMode .ADD ? Operator .ADDITION : Operator .SUBTRACTION ;
399441 Changer <?> changer ;
400- Class <?>[] classes ;
442+ Class <?>[] acceptedClasses ;
401443 // attempt to find arithmetic for each value in delta
402444 if (clazz == null || !Arithmetics .getOperations (operator , clazz ).isEmpty ()) {
403445 boolean changed = false ;
@@ -417,22 +459,16 @@ private void addOrRemoveFromSingleValue(Object originalValue, Object[] delta, Ch
417459 if (changed )
418460 setSingle .accept (originalValue );
419461 // attempt to use the class's changer
420- } else if ((changer = Classes .getSuperClassInfo (clazz ).getChanger ()) != null && (classes = changer .acceptChange (mode )) != null ) {
462+ } else if ((changer = Classes .getSuperClassInfo (clazz ).getChanger ()) != null && (acceptedClasses = changer .acceptChange (mode )) != null ) {
421463 Object [] originalValueArray = (Object []) Array .newInstance (originalValue .getClass (), 1 );
422464 originalValueArray [0 ] = originalValue ;
423465
424- Class <?>[] classes2 = new Class <?>[classes .length ];
425- for (int i = 0 ; i < classes .length ; i ++)
426- classes2 [i ] = classes [i ].isArray () ? classes [i ].getComponentType () : classes [i ];
427-
428- ArrayList <Object > convertedDelta = new ArrayList <>();
429- for (Object value : delta ) {
430- Object convertedValue = Converters .convert (value , classes2 );
431- if (convertedValue != null )
432- convertedDelta .add (convertedValue );
433- }
466+ Class <?>[] singularAcceptedClasses = new Class <?>[acceptedClasses .length ];
467+ for (int i = 0 ; i < acceptedClasses .length ; i ++)
468+ singularAcceptedClasses [i ] = acceptedClasses [i ].isArray () ? acceptedClasses [i ].getComponentType () : acceptedClasses [i ];
434469
435- Changer .ChangerUtils .change (changer , originalValueArray , convertedDelta .toArray (), mode );
470+ Object [] convertedDelta = Converters .convert (delta , singularAcceptedClasses , Object .class );
471+ Changer .ChangerUtils .change (changer , originalValueArray , convertedDelta , mode );
436472 }
437473 }
438474
0 commit comments