Skip to content

Commit f9f0aef

Browse files
committed
feat: add persistence feature to NPCs using tame
1 parent a5acd48 commit f9f0aef

30 files changed

+433
-350
lines changed

folia-api/paper-patches/features/0006-Server-item-registry.patch

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -485,27 +485,33 @@ index f20f072b0978b3ef4b3f36c952d469e0d8d5cd22..c5c2c92eb32b3457c72d89b02c6a5aa8
485485
* Gets the amount of items in this stack
486486
*
487487
diff --git a/src/main/java/org/bukkit/persistence/PersistentDataType.java b/src/main/java/org/bukkit/persistence/PersistentDataType.java
488-
index 709e0eb08f08bcad15812ab01c81e043d8147a8a..1ba0cf3d21c193a7743ee4bc8516e629df696858 100644
488+
index 709e0eb08f08bcad15812ab01c81e043d8147a8a..0f65d8c142385a3e60d4f68d532bf5cafbeb7050 100644
489489
--- a/src/main/java/org/bukkit/persistence/PersistentDataType.java
490490
+++ b/src/main/java/org/bukkit/persistence/PersistentDataType.java
491-
@@ -1,5 +1,10 @@
491+
@@ -1,5 +1,12 @@
492492
package org.bukkit.persistence;
493493

494494
+import java.util.UUID;
495495
+import net.azisaba.vanilife.annotations.Modified;
496496
+import net.azisaba.vanilife.annotations.Op;
497497
+import net.azisaba.vanilife.annotations.VanilifoliaApi;
498+
+import net.azisaba.vanilife.persistence.KeyPersistentDataType;
498499
+import net.azisaba.vanilife.persistence.UuidPersistentDataType;
500+
+import net.kyori.adventure.key.Key;
499501
import org.jetbrains.annotations.NotNull;
500502

501503
/**
502-
@@ -114,6 +119,10 @@ public interface PersistentDataType<P, C> {
504+
@@ -114,6 +121,14 @@ public interface PersistentDataType<P, C> {
503505
*/
504506
ListPersistentDataTypeProvider LIST = new ListPersistentDataTypeProvider();
505507

506508
+ @Modified(Op.ADD_FIELD)
507509
+ @VanilifoliaApi
508-
+ PersistentDataType<byte[], UUID> UUID = new UuidPersistentDataType();
510+
+ PersistentDataType<?, UUID> UUID = new UuidPersistentDataType();
511+
+
512+
+ @Modified(Op.ADD_FIELD)
513+
+ @VanilifoliaApi
514+
+ PersistentDataType<?, Key> KEY = new KeyPersistentDataType();
509515
+
510516
/**
511517
* Returns the primitive data type of this tag.

folia-api/src/main/java/net/azisaba/vanilife/Season.java

Lines changed: 17 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,16 @@
77
import java.util.Locale;
88
import net.kyori.adventure.text.format.TextColor;
99
import net.kyori.adventure.translation.Translatable;
10-
import org.jspecify.annotations.NullMarked;
11-
import org.jspecify.annotations.Nullable;
10+
import org.jetbrains.annotations.NotNull;
11+
import org.jetbrains.annotations.Nullable;
1212

13-
@NullMarked
1413
public enum Season implements Translatable {
1514
SPRING(TextColor.color(242, 163, 179), Month.MARCH, Month.APRIL, Month.MAY),
1615
SUMMER(TextColor.color(126, 215, 193), Month.JUNE, Month.JULY, Month.AUGUST),
1716
FALL(TextColor.color(230, 126, 34), Month.SEPTEMBER, Month.OCTOBER, Month.NOVEMBER),
1817
WINTER(TextColor.color(143, 163, 191), Month.DECEMBER, Month.JANUARY, Month.FEBRUARY);
1918

20-
public static Season now() {
19+
public static @NotNull Season now() {
2120
final Month month = LocalDate.now().getMonth();
2221
return Arrays.stream(Season.values())
2322
.filter(season -> season.months().contains(month))
@@ -33,42 +32,41 @@ public static Season now() {
3332
this.months = Arrays.asList(months);
3433
}
3534

36-
public TextColor color() {
35+
public @NotNull TextColor color() {
3736
return this.color;
3837
}
3938

40-
public List<Month> months() {
39+
public @NotNull List<@NotNull Month> months() {
4140
return this.months;
4241
}
4342

44-
public Season next() {
43+
public @NotNull Season next() {
4544
return values()[(this.ordinal() + 1) % values().length];
4645
}
4746

48-
public Season previous() {
47+
public @NotNull Season previous() {
4948
return values()[(this.ordinal() - 1 + values().length) % values().length];
5049
}
5150

52-
public Sub withStage(final Stage stage) {
51+
public @NotNull Sub withStage(final Stage stage) {
5352
return new Sub(this, stage);
5453
}
5554

56-
public Sub[] subSeasons() {
55+
public Sub @NotNull [] subSeasons() {
5756
return new Sub[]{withStage(Stage.EARLY), withStage(Stage.MID), withStage(Stage.LATE)};
5857
}
5958

6059
@Override
61-
public String translationKey() {
60+
public @NotNull String translationKey() {
6261
return "season." + this.name().toLowerCase(Locale.ROOT);
6362
}
6463

65-
@NullMarked
66-
public record Sub(Season season, Stage stage) implements Comparable<Sub>, Translatable {
67-
public static Sub now() {
64+
public record Sub(@NotNull Season season, @NotNull Stage stage) implements Comparable<Sub>, Translatable {
65+
public static @NotNull Sub now() {
6866
return new Sub(Season.now(), Stage.now());
6967
}
7068

71-
public Sub next() {
69+
public @NotNull Sub next() {
7270
final Stage nextStage = this.stage.next();
7371
if (nextStage != null) {
7472
return this.season.withStage(nextStage);
@@ -77,7 +75,7 @@ public Sub next() {
7775
}
7876
}
7977

80-
public Sub previous() {
78+
public @NotNull Sub previous() {
8179
final Stage previousStage = this.stage.previous();
8280
if (previousStage != null) {
8381
return this.season.withStage(previousStage);
@@ -90,12 +88,12 @@ public int toIndex() {
9088
}
9189

9290
@Override
93-
public String translationKey() {
91+
public @NotNull String translationKey() {
9492
return this.season.translationKey() + "." + this.stage.name().toLowerCase(Locale.ROOT);
9593
}
9694

9795
@Override
98-
public int compareTo(final Season.Sub other) {
96+
public int compareTo(final Season.@NotNull Sub other) {
9997
int seasonCompare = Integer.compare(this.season.ordinal(), other.season().ordinal());
10098
if (seasonCompare != 0) {
10199
return seasonCompare;
@@ -105,11 +103,10 @@ public int compareTo(final Season.Sub other) {
105103
}
106104
}
107105

108-
@NullMarked
109106
public enum Stage {
110107
EARLY, MID, LATE;
111108

112-
public static Stage now() {
109+
public static @NotNull Stage now() {
113110
final Season season = Season.now();
114111
final Month month = LocalDate.now().getMonth();
115112
if (season.months().getFirst() == month) {

folia-api/src/main/java/net/azisaba/vanilife/Vanilife.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,17 @@
66
import net.azisaba.vanilife.world.ResourceWorld;
77
import net.kyori.adventure.key.Key;
88
import org.bukkit.Bukkit;
9-
import org.jspecify.annotations.NullMarked;
9+
import org.jetbrains.annotations.NotNull;
1010

11-
@NullMarked
1211
public final class Vanilife {
1312
public static final String NAMESPACE = "vanilife";
1413

15-
public static IslandsWorld getIslandsWorld() {
14+
public static @NotNull IslandsWorld getIslandsWorld() {
1615
return (IslandsWorld) Objects.requireNonNull(Bukkit.getWorld(IslandDefaults.WORLD_KEY));
1716
}
1817

1918
// TODO: Implement auto wipe
20-
public static ResourceWorld getResourceWorld() {
19+
public static @NotNull ResourceWorld getResourceWorld() {
2120
return (ResourceWorld) Objects.requireNonNull(Bukkit.getWorld(Key.key(NAMESPACE, "2026/spring")));
2221
}
2322

folia-api/src/main/java/net/azisaba/vanilife/event/BlockDropLootEvent.java

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,32 +11,31 @@
1111
import org.bukkit.event.block.BlockEvent;
1212
import org.bukkit.inventory.ItemStack;
1313
import org.jetbrains.annotations.ApiStatus;
14-
import org.jspecify.annotations.NullMarked;
15-
import org.jspecify.annotations.Nullable;
14+
import org.jetbrains.annotations.NotNull;
15+
import org.jetbrains.annotations.Nullable;
1616

17-
@NullMarked
1817
@VanilifoliaApi
1918
public class BlockDropLootEvent extends BlockEvent {
2019
private static final HandlerList HANDLER_LIST = new HandlerList();
2120

22-
public static HandlerList getHandlerList() {
21+
public static @NotNull HandlerList getHandlerList() {
2322
return HANDLER_LIST;
2423
}
2524

2625
private final BlockState blockState;
2726

28-
private final @Nullable Entity entity;
29-
private final @Nullable ItemStack tool;
27+
private final Entity entity;
28+
private final ItemStack tool;
3029

3130
private final List<ItemStack> drops;
3231

3332
@ApiStatus.Internal
3433
public BlockDropLootEvent(
35-
final Block block,
36-
final BlockState blockState,
37-
final @Nullable Entity entity,
38-
final @Nullable ItemStack tool,
39-
final List<ItemStack> drops
34+
final @NotNull Block block,
35+
final @NotNull BlockState blockState,
36+
final @Nullable Entity entity,
37+
final @Nullable ItemStack tool,
38+
final @NotNull List<@NotNull ItemStack> drops
4039
) {
4140
super(block);
4241
this.blockState = blockState;
@@ -45,7 +44,7 @@ public BlockDropLootEvent(
4544
this.drops = drops;
4645
}
4746

48-
public BlockState getBlockState() {
47+
public @NotNull BlockState getBlockState() {
4948
return this.blockState;
5049
}
5150

@@ -57,25 +56,25 @@ public BlockState getBlockState() {
5756
return this.tool;
5857
}
5958

60-
public List<ItemStack> getDrops() {
59+
public @NotNull List<@NotNull ItemStack> getDrops() {
6160
return this.drops.stream().map(ItemStack::clone).toList();
6261
}
6362

64-
public void setDrops(final List<ItemStack> drops) {
63+
public void setDrops(final @NotNull List<@NotNull ItemStack> drops) {
6564
this.drops.clear();
6665
this.drops.addAll(drops);
6766
}
6867

69-
public void mapDrops(final UnaryOperator<ItemStack> mapper) {
68+
public void mapDrops(final @NotNull UnaryOperator<@NotNull ItemStack> mapper) {
7069
this.setDrops(this.getDrops().stream().map(mapper).toList());
7170
}
7271

73-
public void filterDrops(final Predicate<ItemStack> predicate) {
72+
public void filterDrops(final @NotNull Predicate<@NotNull ItemStack> predicate) {
7473
this.setDrops(this.getDrops().stream().filter(predicate).toList());
7574
}
7675

7776
@Override
78-
public HandlerList getHandlers() {
77+
public @NotNull HandlerList getHandlers() {
7978
return HANDLER_LIST;
8079
}
8180
}

folia-api/src/main/java/net/azisaba/vanilife/event/BlockRandomTickEvent.java

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,27 +7,26 @@
77
import org.bukkit.event.HandlerList;
88
import org.bukkit.event.block.BlockEvent;
99
import org.jetbrains.annotations.ApiStatus;
10-
import org.jspecify.annotations.NullMarked;
10+
import org.jetbrains.annotations.NotNull;
1111

12-
@NullMarked
1312
@VanilifoliaApi
1413
public class BlockRandomTickEvent extends BlockEvent implements Cancellable {
15-
private static final HandlerList HANDLER_LIST = new HandlerList();
14+
private static final @NotNull HandlerList HANDLER_LIST = new HandlerList();
1615

17-
public static HandlerList getHandlerList() {
16+
public static @NotNull HandlerList getHandlerList() {
1817
return HANDLER_LIST;
1918
}
2019

21-
private final Random random;
20+
private final @NotNull Random random;
2221
private boolean cancelled = false;
2322

2423
@ApiStatus.Internal
25-
public BlockRandomTickEvent(final Block block, final Random random) {
24+
public BlockRandomTickEvent(final @NotNull Block block, final @NotNull Random random) {
2625
super(block);
2726
this.random = random;
2827
}
2928

30-
public Random getRandom() {
29+
public @NotNull Random getRandom() {
3130
return this.random;
3231
}
3332

@@ -42,7 +41,7 @@ public void setCancelled(final boolean cancel) {
4241
}
4342

4443
@Override
45-
public HandlerList getHandlers() {
44+
public @NotNull HandlerList getHandlers() {
4645
return HANDLER_LIST;
4746
}
4847
}

folia-api/src/main/java/net/azisaba/vanilife/item/ServerItem.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,8 @@
66
import org.bukkit.Material;
77
import org.bukkit.NamespacedKey;
88
import org.jetbrains.annotations.ApiStatus;
9-
import org.jspecify.annotations.NullMarked;
109

1110
@ApiStatus.NonExtendable
12-
@NullMarked
1311
public interface ServerItem extends Keyed, ServerItemRegistryEntry {
1412
NamespacedKey TYPE_KEY = new NamespacedKey(Vanilife.NAMESPACE, "item");
1513

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package net.azisaba.vanilife.persistence;
2+
3+
import net.azisaba.vanilife.annotations.VanilifoliaApi;
4+
import net.kyori.adventure.key.Key;
5+
import org.bukkit.persistence.PersistentDataAdapterContext;
6+
import org.bukkit.persistence.PersistentDataType;
7+
import org.jetbrains.annotations.ApiStatus;
8+
import org.jetbrains.annotations.NotNull;
9+
10+
import java.nio.ByteBuffer;
11+
import java.nio.charset.StandardCharsets;
12+
13+
@VanilifoliaApi
14+
public final class KeyPersistentDataType implements PersistentDataType<byte[], Key> {
15+
@ApiStatus.Internal
16+
public KeyPersistentDataType() {
17+
}
18+
19+
@Override
20+
public @NotNull Class<byte[]> getPrimitiveType() {
21+
return byte[].class;
22+
}
23+
24+
@Override
25+
public @NotNull Class<Key> getComplexType() {
26+
return Key.class;
27+
}
28+
29+
@Override
30+
public byte @NotNull [] toPrimitive(final @NotNull Key complex, final @NotNull PersistentDataAdapterContext context) {
31+
final byte[] namespaceBytes = complex.namespace().getBytes(StandardCharsets.UTF_8);
32+
final byte[] valueBytes = complex.value().getBytes(StandardCharsets.UTF_8);
33+
34+
if (namespaceBytes.length > 255) {
35+
throw new IllegalArgumentException("Namespace is too long to serialize: " + complex.asString());
36+
}
37+
38+
return ByteBuffer.allocate(1 + namespaceBytes.length + valueBytes.length)
39+
.put((byte) namespaceBytes.length)
40+
.put(namespaceBytes)
41+
.put(valueBytes)
42+
.array();
43+
}
44+
45+
@Override
46+
public @NotNull Key fromPrimitive(final byte @NotNull [] primitive, final @NotNull PersistentDataAdapterContext context) {
47+
if (primitive.length < 2) {
48+
throw new IllegalArgumentException("Invalid serialized key length: " + primitive.length);
49+
}
50+
51+
final ByteBuffer buffer = ByteBuffer.wrap(primitive);
52+
final int namespaceLength = Byte.toUnsignedInt(buffer.get());
53+
if (primitive.length < 1 + namespaceLength + 1) {
54+
throw new IllegalArgumentException("Invalid serialized key payload");
55+
}
56+
57+
final byte[] namespaceBytes = new byte[namespaceLength];
58+
buffer.get(namespaceBytes);
59+
60+
final byte[] valueBytes = new byte[buffer.remaining()];
61+
buffer.get(valueBytes);
62+
63+
final String namespace = new String(namespaceBytes, StandardCharsets.UTF_8);
64+
final String value = new String(valueBytes, StandardCharsets.UTF_8);
65+
return Key.key(namespace, value);
66+
}
67+
}

0 commit comments

Comments
 (0)