Compare commits

...

8 commits

Author SHA1 Message Date
Pedro
4430e96e1b
Add AnvilView#bypassEnchantmentLevelRestriction (#11475) 2024-10-12 23:10:05 +02:00
Lulu13022002
b16c0ae023
Add offline PDC API (#8117) 2024-10-12 21:38:18 +02:00
TonytheMacaroni
57c75a4b67
Fix CraftMagicNumbers#getTag (#11484) 2024-10-09 21:58:13 +01:00
Nassim Jahnke
7cd4f2c15f
Check if leash tag has a uuid 2024-10-03 17:10:27 +02:00
Nassim Jahnke
fb768404aa
[ci skip] Add section on nullability annotations (#11461) 2024-10-02 14:32:20 +02:00
Jake Potrebic
709f0f2919
Use components properly in ProfileWhitelistVerifyEvent (#11456) 2024-10-02 14:32:05 +02:00
Nassim Jahnke
69ffbec34a
Fix hex color check 2024-10-02 13:49:39 +02:00
Jake Potrebic
e7e1ab56ca
Finish converting most of the undeprecated api to jspecify 2024-09-30 16:02:52 -07:00
61 changed files with 1337 additions and 1046 deletions

View file

@ -249,6 +249,17 @@ instead of adding a new import to the top of the file. If you are using a type a
can add an import with a comment. However, if its only used a couple of times, the FQN is preferred to prevent future
patch conflicts in the import section of the file.
### Nullability annotations
We are in the process of switching nullability annotation libraries, so you might need to use one or the other:
**For classes we add**: Fields, method parameters and return types that are nullable should be marked via the
`@Nullable` annotation from `org.jspecify.annotations`. Whenever you create a new class, add `@NullMarked`, meaning types
are assumed to be non-null by default. For less obvious placing such as on generics or arrays, see the [JSpecify docs](https://jspecify.dev/docs/user-guide/).
**For classes added by upstream**: Keep using both `@Nullable` and `@NotNull` from `org.jetbrains.annotations`. These
will be replaced later.
```java
import org.bukkit.event.Event;
// don't add import here, use FQN like below

View file

@ -85,10 +85,10 @@ index 0000000000000000000000000000000000000000..2512dba27edfdccbc4430815b6cba048
+}
diff --git a/src/main/java/io/papermc/paper/registry/RegistryKey.java b/src/main/java/io/papermc/paper/registry/RegistryKey.java
new file mode 100644
index 0000000000000000000000000000000000000000..1e2765d296decbb3e61b8434e78b5e0214f8fee0
index 0000000000000000000000000000000000000000..8410d7213370f01cbedbf7fac29bac96f150c49a
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/RegistryKey.java
@@ -0,0 +1,177 @@
@@ -0,0 +1,179 @@
+package io.papermc.paper.registry;
+
+import net.kyori.adventure.key.Keyed;
@ -121,6 +121,7 @@ index 0000000000000000000000000000000000000000..1e2765d296decbb3e61b8434e78b5e02
+import org.bukkit.potion.PotionEffectType;
+import org.bukkit.potion.PotionType;
+import org.jetbrains.annotations.ApiStatus;
+import org.jspecify.annotations.NullMarked;
+
+import static io.papermc.paper.registry.RegistryKeyImpl.create;
+
@ -137,6 +138,7 @@ index 0000000000000000000000000000000000000000..1e2765d296decbb3e61b8434e78b5e02
+ * @param <T> the value type
+ */
+@SuppressWarnings("unused")
+@NullMarked
+public sealed interface RegistryKey<T> extends Keyed permits RegistryKeyImpl {
+
+ /* ******************* *
@ -268,19 +270,21 @@ index 0000000000000000000000000000000000000000..1e2765d296decbb3e61b8434e78b5e02
+}
diff --git a/src/main/java/io/papermc/paper/registry/RegistryKeyImpl.java b/src/main/java/io/papermc/paper/registry/RegistryKeyImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..791813220b2504214b1adecc69093cd600fb0f8c
index 0000000000000000000000000000000000000000..80e3e64f47ac55a4978c9e5b430e2f2d1c871d1b
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/RegistryKeyImpl.java
@@ -0,0 +1,24 @@
@@ -0,0 +1,26 @@
+package io.papermc.paper.registry;
+
+import com.google.common.collect.Sets;
+import java.util.Set;
+import net.kyori.adventure.key.Key;
+import org.intellij.lang.annotations.Subst;
+import org.jetbrains.annotations.NotNull;
+import org.jspecify.annotations.NullMarked;
+import org.jspecify.annotations.Nullable;
+
+record RegistryKeyImpl<T>(@NotNull Key key) implements RegistryKey<T> {
+@NullMarked
+record RegistryKeyImpl<T>(Key key) implements RegistryKey<T> {
+
+ static final Set<RegistryKey<?>> REGISTRY_KEYS = Sets.newIdentityHashSet();
+
@ -298,16 +302,16 @@ index 0000000000000000000000000000000000000000..791813220b2504214b1adecc69093cd6
+}
diff --git a/src/main/java/io/papermc/paper/registry/TypedKey.java b/src/main/java/io/papermc/paper/registry/TypedKey.java
new file mode 100644
index 0000000000000000000000000000000000000000..6f5a062ba7ee7173468ecea3c1855a233bf3855e
index 0000000000000000000000000000000000000000..cb2e1a4a6d583787573eeefab24e3188c43d148f
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/TypedKey.java
@@ -0,0 +1,44 @@
@@ -0,0 +1,45 @@
+package io.papermc.paper.registry;
+
+import net.kyori.adventure.key.Key;
+import net.kyori.adventure.key.Keyed;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * Represents a key for a value in a specific registry.
@ -315,6 +319,7 @@ index 0000000000000000000000000000000000000000..6f5a062ba7ee7173468ecea3c1855a23
+ * @param <T> the value type for the registry
+ */
+@ApiStatus.Experimental
+@NullMarked
+public sealed interface TypedKey<T> extends Keyed permits TypedKeyImpl {
+
+ /**
@ -323,7 +328,7 @@ index 0000000000000000000000000000000000000000..6f5a062ba7ee7173468ecea3c1855a23
+ * @return the value's key
+ */
+ @Override
+ @NotNull Key key();
+ Key key();
+
+ /**
+ * Gets the registry key for the value this key
@ -331,7 +336,7 @@ index 0000000000000000000000000000000000000000..6f5a062ba7ee7173468ecea3c1855a23
+ *
+ * @return the registry key
+ */
+ @NotNull RegistryKey<T> registryKey();
+ RegistryKey<T> registryKey();
+
+ /**
+ * Create a typed key from a key and a registry key.
@ -342,23 +347,23 @@ index 0000000000000000000000000000000000000000..6f5a062ba7ee7173468ecea3c1855a23
+ * @return a new key for the value key and registry key
+ */
+ @ApiStatus.Experimental
+ static <T> @NotNull TypedKey<T> create(final @NotNull RegistryKey<T> registryKey, final @NotNull Key key) {
+ static <T> TypedKey<T> create(final RegistryKey<T> registryKey, final Key key) {
+ return new TypedKeyImpl<>(key, registryKey);
+ }
+}
diff --git a/src/main/java/io/papermc/paper/registry/TypedKeyImpl.java b/src/main/java/io/papermc/paper/registry/TypedKeyImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..1a97b3359c4ece5c29131da7c3f208aaa8fab66e
index 0000000000000000000000000000000000000000..99375deaa6b90b33cd6a77e0df651236d304874e
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/TypedKeyImpl.java
@@ -0,0 +1,8 @@
+package io.papermc.paper.registry;
+
+import net.kyori.adventure.key.Key;
+import net.kyori.adventure.key.Keyed;
+import org.jetbrains.annotations.NotNull;
+import org.jspecify.annotations.NullMarked;
+
+record TypedKeyImpl<T>(@NotNull Key key, @NotNull RegistryKey<T> registryKey) implements TypedKey<T> {
+@NullMarked
+record TypedKeyImpl<T>(Key key, RegistryKey<T> registryKey) implements TypedKey<T> {
+}
diff --git a/src/main/java/org/bukkit/MinecraftExperimental.java b/src/main/java/org/bukkit/MinecraftExperimental.java
index a86b87e4c3332202e40e484c3f9c6562b419c70f..305532968f9f7dd497c77259ed147ea2f081bc74 100644

View file

@ -91,23 +91,25 @@ index 3383fb91249ea53740326b538abd905f84ff0e3c..74f0e2b812c1e2e922b136fefe505fc8
doLast {
diff --git a/src/main/java/io/papermc/paper/chat/ChatRenderer.java b/src/main/java/io/papermc/paper/chat/ChatRenderer.java
new file mode 100644
index 0000000000000000000000000000000000000000..ffe0a921cc1ebbb95104f22b57e0e3af85e287a6
index 0000000000000000000000000000000000000000..1288cdeafe587e9e78e7c5087742ea054ba8423d
--- /dev/null
+++ b/src/main/java/io/papermc/paper/chat/ChatRenderer.java
@@ -0,0 +1,71 @@
@@ -0,0 +1,70 @@
+package io.papermc.paper.chat;
+
+import net.kyori.adventure.audience.Audience;
+import net.kyori.adventure.text.Component;
+import org.bukkit.entity.Player;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * A chat renderer is responsible for rendering chat messages sent by {@link Player}s to the server.
+ */
+@NullMarked
+@FunctionalInterface
+public interface ChatRenderer {
+
+ /**
+ * Renders a chat message. This will be called once for each receiving {@link Audience}.
+ *
@ -118,15 +120,13 @@ index 0000000000000000000000000000000000000000..ffe0a921cc1ebbb95104f22b57e0e3af
+ * @return a rendered chat message
+ */
+ @ApiStatus.OverrideOnly
+ @NotNull
+ Component render(@NotNull Player source, @NotNull Component sourceDisplayName, @NotNull Component message, @NotNull Audience viewer);
+ Component render(Player source, Component sourceDisplayName, Component message, Audience viewer);
+
+ /**
+ * Create a new instance of the default {@link ChatRenderer}.
+ *
+ * @return a new {@link ChatRenderer}
+ */
+ @NotNull
+ static ChatRenderer defaultRenderer() {
+ return new ViewerUnawareImpl.Default((source, sourceDisplayName, message) -> Component.translatable("chat.type.text", sourceDisplayName, message));
+ }
@ -142,8 +142,7 @@ index 0000000000000000000000000000000000000000..ffe0a921cc1ebbb95104f22b57e0e3af
+ * @param renderer the viewer unaware renderer
+ * @return a new {@link ChatRenderer}
+ */
+ @NotNull
+ static ChatRenderer viewerUnaware(final @NotNull ViewerUnaware renderer) {
+ static ChatRenderer viewerUnaware(final ViewerUnaware renderer) {
+ return new ViewerUnawareImpl(renderer);
+ }
+
@ -153,6 +152,7 @@ index 0000000000000000000000000000000000000000..ffe0a921cc1ebbb95104f22b57e0e3af
+ * @see ChatRenderer#viewerUnaware(ViewerUnaware)
+ */
+ interface ViewerUnaware {
+
+ /**
+ * Renders a chat message.
+ *
@ -162,24 +162,26 @@ index 0000000000000000000000000000000000000000..ffe0a921cc1ebbb95104f22b57e0e3af
+ * @return a rendered chat message
+ */
+ @ApiStatus.OverrideOnly
+ @NotNull
+ Component render(@NotNull Player source, @NotNull Component sourceDisplayName, @NotNull Component message);
+ Component render(Player source, Component sourceDisplayName, Component message);
+ }
+}
diff --git a/src/main/java/io/papermc/paper/chat/ViewerUnawareImpl.java b/src/main/java/io/papermc/paper/chat/ViewerUnawareImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..2ad76b1751ba707f7ae0d283aa1cbaf6c9619da9
index 0000000000000000000000000000000000000000..e1ef23b4bdf60a299d6d37099cef777b3de13ac7
--- /dev/null
+++ b/src/main/java/io/papermc/paper/chat/ViewerUnawareImpl.java
@@ -0,0 +1,35 @@
@@ -0,0 +1,38 @@
+package io.papermc.paper.chat;
+
+import net.kyori.adventure.audience.Audience;
+import net.kyori.adventure.text.Component;
+import org.bukkit.entity.Player;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.annotations.ApiStatus;
+import org.jspecify.annotations.NullMarked;
+import org.jspecify.annotations.Nullable;
+
+@ApiStatus.Internal
+@NullMarked
+sealed class ViewerUnawareImpl implements ChatRenderer, ChatRenderer.ViewerUnaware permits ViewerUnawareImpl.Default {
+ private final ViewerUnaware unaware;
+ private @Nullable Component message;
@ -189,12 +191,12 @@ index 0000000000000000000000000000000000000000..2ad76b1751ba707f7ae0d283aa1cbaf6
+ }
+
+ @Override
+ public @NotNull Component render(final @NotNull Player source, final @NotNull Component sourceDisplayName, final @NotNull Component message, final @NotNull Audience viewer) {
+ public Component render(final Player source, final Component sourceDisplayName, final Component message, final Audience viewer) {
+ return this.render(source, sourceDisplayName, message);
+ }
+
+ @Override
+ public @NotNull Component render(final @NotNull Player source, final @NotNull Component sourceDisplayName, final @NotNull Component message) {
+ public Component render(final Player source, final Component sourceDisplayName, final Component message) {
+ if (this.message == null) {
+ this.message = this.unaware.render(source, sourceDisplayName, message);
+ }
@ -579,12 +581,13 @@ index 0000000000000000000000000000000000000000..42a82ce2316a4aad2883d24c7e2ff95d
+}
diff --git a/src/main/java/io/papermc/paper/text/PaperComponents.java b/src/main/java/io/papermc/paper/text/PaperComponents.java
new file mode 100644
index 0000000000000000000000000000000000000000..1a11cfde07db55194cd26757863a268b9042d0bb
index 0000000000000000000000000000000000000000..934d1d3ca490a8e25c438bc8c57eb6bde50e0147
--- /dev/null
+++ b/src/main/java/io/papermc/paper/text/PaperComponents.java
@@ -0,0 +1,177 @@
@@ -0,0 +1,180 @@
+package io.papermc.paper.text;
+
+import java.io.IOException;
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.flattener.ComponentFlattener;
+import net.kyori.adventure.text.format.NamedTextColor;
@ -595,15 +598,15 @@ index 0000000000000000000000000000000000000000..1a11cfde07db55194cd26757863a268b
+import org.bukkit.Bukkit;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Entity;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.IOException;
+import org.jspecify.annotations.NullMarked;
+import org.jspecify.annotations.Nullable;
+
+/**
+ * Paper API-specific methods for working with {@link Component}s and related.
+ */
+@NullMarked
+public final class PaperComponents {
+
+ private PaperComponents() {
+ throw new RuntimeException("PaperComponents is not to be instantiated!");
+ }
@ -633,7 +636,7 @@ index 0000000000000000000000000000000000000000..1a11cfde07db55194cd26757863a268b
+ * @return the resolved component
+ * @throws IOException if a syntax error tripped during resolving
+ */
+ public static @NotNull Component resolveWithContext(@NotNull Component input, @Nullable CommandSender context, @Nullable Entity scoreboardSubject) throws IOException {
+ public static Component resolveWithContext(final Component input, final @Nullable CommandSender context, final @Nullable Entity scoreboardSubject) throws IOException {
+ return resolveWithContext(input, context, scoreboardSubject, true);
+ }
+
@ -664,7 +667,8 @@ index 0000000000000000000000000000000000000000..1a11cfde07db55194cd26757863a268b
+ * @return the resolved component
+ * @throws IOException if a syntax error tripped during resolving
+ */
+ public static @NotNull Component resolveWithContext(@NotNull Component input, @Nullable CommandSender context, @Nullable Entity scoreboardSubject, boolean bypassPermissions) throws IOException {
+ @SuppressWarnings("deprecation") // using unsafe as a bridge
+ public static Component resolveWithContext(final Component input, final @Nullable CommandSender context, final @Nullable Entity scoreboardSubject, final boolean bypassPermissions) throws IOException {
+ return Bukkit.getUnsafe().resolveWithContext(input, context, scoreboardSubject, bypassPermissions);
+ }
+
@ -673,7 +677,8 @@ index 0000000000000000000000000000000000000000..1a11cfde07db55194cd26757863a268b
+ *
+ * @return a component flattener
+ */
+ public static @NotNull ComponentFlattener flattener() {
+ @SuppressWarnings("deprecation") // using unsafe as a bridge
+ public static ComponentFlattener flattener() {
+ return Bukkit.getUnsafe().componentFlattener();
+ }
+
@ -688,7 +693,7 @@ index 0000000000000000000000000000000000000000..1a11cfde07db55194cd26757863a268b
+ * @deprecated will be removed in adventure 5.0.0, use {@link PlainTextComponentSerializer#plainText()}
+ */
+ @Deprecated(forRemoval = true, since = "1.18.1")
+ public static @NotNull PlainComponentSerializer plainSerializer() {
+ public static PlainComponentSerializer plainSerializer() {
+ return Bukkit.getUnsafe().plainComponentSerializer();
+ }
+
@ -703,7 +708,7 @@ index 0000000000000000000000000000000000000000..1a11cfde07db55194cd26757863a268b
+ * @deprecated use {@link PlainTextComponentSerializer#plainText()}
+ */
+ @Deprecated(forRemoval = true, since = "1.18.2")
+ public static @NotNull PlainTextComponentSerializer plainTextSerializer() {
+ public static PlainTextComponentSerializer plainTextSerializer() {
+ return Bukkit.getUnsafe().plainTextSerializer();
+ }
+
@ -719,7 +724,7 @@ index 0000000000000000000000000000000000000000..1a11cfde07db55194cd26757863a268b
+ * @deprecated use {@link GsonComponentSerializer#gson()}
+ */
+ @Deprecated(forRemoval = true, since = "1.18.2")
+ public static @NotNull GsonComponentSerializer gsonSerializer() {
+ public static GsonComponentSerializer gsonSerializer() {
+ return Bukkit.getUnsafe().gsonComponentSerializer();
+ }
+
@ -736,7 +741,7 @@ index 0000000000000000000000000000000000000000..1a11cfde07db55194cd26757863a268b
+ * @deprecated use {@link GsonComponentSerializer#colorDownsamplingGson()}
+ */
+ @Deprecated(forRemoval = true, since = "1.18.2")
+ public static @NotNull GsonComponentSerializer colorDownsamplingGsonSerializer() {
+ public static GsonComponentSerializer colorDownsamplingGsonSerializer() {
+ return Bukkit.getUnsafe().colorDownsamplingGsonComponentSerializer();
+ }
+
@ -756,7 +761,7 @@ index 0000000000000000000000000000000000000000..1a11cfde07db55194cd26757863a268b
+ * @deprecated use {@link LegacyComponentSerializer#legacySection()}
+ */
+ @Deprecated(forRemoval = true, since = "1.18.2")
+ public static @NotNull LegacyComponentSerializer legacySectionSerializer() {
+ public static LegacyComponentSerializer legacySectionSerializer() {
+ return Bukkit.getUnsafe().legacyComponentSerializer();
+ }
+}

View file

@ -27,26 +27,26 @@ index 1f627e81622e77b81b1228a467fbb9e6fd979e7a..3c50362de25617d878ef58f14f67c240
diff --git a/src/main/java/io/papermc/paper/plugin/PermissionManager.java b/src/main/java/io/papermc/paper/plugin/PermissionManager.java
new file mode 100644
index 0000000000000000000000000000000000000000..cdbc93b317b3bab47bf6552c29cfbb2c27846933
index 0000000000000000000000000000000000000000..c78ada48e27c5552529fe8d5bc23db1dac94e481
--- /dev/null
+++ b/src/main/java/io/papermc/paper/plugin/PermissionManager.java
@@ -0,0 +1,171 @@
@@ -0,0 +1,166 @@
+package io.papermc.paper.plugin;
+
+import org.bukkit.permissions.Permissible;
+import org.bukkit.permissions.Permission;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
+import java.util.Set;
+import org.bukkit.permissions.Permissible;
+import org.bukkit.permissions.Permission;
+import org.jetbrains.annotations.ApiStatus;
+import org.jspecify.annotations.NullMarked;
+import org.jspecify.annotations.Nullable;
+
+/**
+ * A permission manager implementation to keep backwards compatibility partially alive with existing plugins that used
+ * the bukkit one before.
+ */
+@ApiStatus.Experimental
+@NullMarked
+public interface PermissionManager {
+
+ /**
@ -55,8 +55,7 @@ index 0000000000000000000000000000000000000000..cdbc93b317b3bab47bf6552c29cfbb2c
+ * @param name Name of the permission
+ * @return Permission, or null if none
+ */
+ @Nullable
+ Permission getPermission(@NotNull String name);
+ @Nullable Permission getPermission(String name);
+
+ /**
+ * Adds a {@link Permission} to this plugin manager.
@ -68,7 +67,7 @@ index 0000000000000000000000000000000000000000..cdbc93b317b3bab47bf6552c29cfbb2c
+ * @throws IllegalArgumentException Thrown when a permission with the same
+ * name already exists
+ */
+ void addPermission(@NotNull Permission perm);
+ void addPermission(Permission perm);
+
+ /**
+ * Removes a {@link Permission} registration from this plugin manager.
@ -81,7 +80,7 @@ index 0000000000000000000000000000000000000000..cdbc93b317b3bab47bf6552c29cfbb2c
+ *
+ * @param perm Permission to remove
+ */
+ void removePermission(@NotNull Permission perm);
+ void removePermission(Permission perm);
+
+ /**
+ * Removes a {@link Permission} registration from this plugin manager.
@ -94,7 +93,7 @@ index 0000000000000000000000000000000000000000..cdbc93b317b3bab47bf6552c29cfbb2c
+ *
+ * @param name Permission to remove
+ */
+ void removePermission(@NotNull String name);
+ void removePermission(String name);
+
+ /**
+ * Gets the default permissions for the given op status
@ -102,7 +101,6 @@ index 0000000000000000000000000000000000000000..cdbc93b317b3bab47bf6552c29cfbb2c
+ * @param op Which set of default permissions to get
+ * @return The default permissions
+ */
+ @NotNull
+ Set<Permission> getDefaultPermissions(boolean op);
+
+ /**
@ -113,7 +111,7 @@ index 0000000000000000000000000000000000000000..cdbc93b317b3bab47bf6552c29cfbb2c
+ *
+ * @param perm Permission to recalculate
+ */
+ void recalculatePermissionDefaults(@NotNull Permission perm);
+ void recalculatePermissionDefaults(Permission perm);
+
+ /**
+ * Subscribes the given Permissible for information about the requested
@ -125,7 +123,7 @@ index 0000000000000000000000000000000000000000..cdbc93b317b3bab47bf6552c29cfbb2c
+ * @param permission Permission to subscribe to
+ * @param permissible Permissible subscribing
+ */
+ void subscribeToPermission(@NotNull String permission, @NotNull Permissible permissible);
+ void subscribeToPermission(String permission, Permissible permissible);
+
+ /**
+ * Unsubscribes the given Permissible for information about the requested
@ -134,7 +132,7 @@ index 0000000000000000000000000000000000000000..cdbc93b317b3bab47bf6552c29cfbb2c
+ * @param permission Permission to unsubscribe from
+ * @param permissible Permissible subscribing
+ */
+ void unsubscribeFromPermission(@NotNull String permission, @NotNull Permissible permissible);
+ void unsubscribeFromPermission(String permission, Permissible permissible);
+
+ /**
+ * Gets a set containing all subscribed {@link Permissible}s to the given
@ -143,8 +141,7 @@ index 0000000000000000000000000000000000000000..cdbc93b317b3bab47bf6552c29cfbb2c
+ * @param permission Permission to query for
+ * @return Set containing all subscribed permissions
+ */
+ @NotNull
+ Set<Permissible> getPermissionSubscriptions(@NotNull String permission);
+ Set<Permissible> getPermissionSubscriptions(String permission);
+
+ /**
+ * Subscribes to the given Default permissions by operator status
@ -155,7 +152,7 @@ index 0000000000000000000000000000000000000000..cdbc93b317b3bab47bf6552c29cfbb2c
+ * @param op Default list to subscribe to
+ * @param permissible Permissible subscribing
+ */
+ void subscribeToDefaultPerms(boolean op, @NotNull Permissible permissible);
+ void subscribeToDefaultPerms(boolean op, Permissible permissible);
+
+ /**
+ * Unsubscribes from the given Default permissions by operator status
@ -163,7 +160,7 @@ index 0000000000000000000000000000000000000000..cdbc93b317b3bab47bf6552c29cfbb2c
+ * @param op Default list to unsubscribe from
+ * @param permissible Permissible subscribing
+ */
+ void unsubscribeFromDefaultPerms(boolean op, @NotNull Permissible permissible);
+ void unsubscribeFromDefaultPerms(boolean op, Permissible permissible);
+
+ /**
+ * Gets a set containing all subscribed {@link Permissible}s to the given
@ -172,7 +169,6 @@ index 0000000000000000000000000000000000000000..cdbc93b317b3bab47bf6552c29cfbb2c
+ * @param op Default list to query for
+ * @return Set containing all subscribed permissions
+ */
+ @NotNull
+ Set<Permissible> getDefaultPermSubscriptions(boolean op);
+
+ /**
@ -182,7 +178,6 @@ index 0000000000000000000000000000000000000000..cdbc93b317b3bab47bf6552c29cfbb2c
+ *
+ * @return Set containing all current registered permissions
+ */
+ @NotNull
+ Set<Permission> getPermissions();
+
+ /**
@ -192,7 +187,7 @@ index 0000000000000000000000000000000000000000..cdbc93b317b3bab47bf6552c29cfbb2c
+ *
+ * @param perm permission
+ */
+ void addPermissions(@NotNull List<Permission> perm);
+ void addPermissions(List<Permission> perm);
+
+ /**
+ * Clears the current registered permissinos.
@ -204,13 +199,14 @@ index 0000000000000000000000000000000000000000..cdbc93b317b3bab47bf6552c29cfbb2c
+}
diff --git a/src/main/java/io/papermc/paper/plugin/bootstrap/BootstrapContext.java b/src/main/java/io/papermc/paper/plugin/bootstrap/BootstrapContext.java
new file mode 100644
index 0000000000000000000000000000000000000000..70d5f9802f90605a5120ff2a000a2e9395f0aecc
index 0000000000000000000000000000000000000000..4c47414fc08e1183b1e59369bacc4d7f7042f262
--- /dev/null
+++ b/src/main/java/io/papermc/paper/plugin/bootstrap/BootstrapContext.java
@@ -0,0 +1,14 @@
@@ -0,0 +1,16 @@
+package io.papermc.paper.plugin.bootstrap;
+
+import org.jetbrains.annotations.ApiStatus;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * Represents the context provided to a {@link PluginBootstrap} during both the bootstrapping and plugin
@ -219,12 +215,13 @@ index 0000000000000000000000000000000000000000..70d5f9802f90605a5120ff2a000a2e93
+ * like the plugin's configuration or logger during the plugins bootstrap.
+ */
+@ApiStatus.Experimental
+@NullMarked
+@ApiStatus.NonExtendable
+public interface BootstrapContext extends PluginProviderContext {
+}
diff --git a/src/main/java/io/papermc/paper/plugin/bootstrap/PluginBootstrap.java b/src/main/java/io/papermc/paper/plugin/bootstrap/PluginBootstrap.java
new file mode 100644
index 0000000000000000000000000000000000000000..81e09abd2128d8cc54d1fd6454aea7d8d287a11f
index 0000000000000000000000000000000000000000..e6696dcd8049e869bd1733f7c67ff802c54d7c37
--- /dev/null
+++ b/src/main/java/io/papermc/paper/plugin/bootstrap/PluginBootstrap.java
@@ -0,0 +1,41 @@
@ -233,7 +230,7 @@ index 0000000000000000000000000000000000000000..81e09abd2128d8cc54d1fd6454aea7d8
+import io.papermc.paper.plugin.provider.util.ProviderUtil;
+import org.bukkit.plugin.java.JavaPlugin;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * A plugin bootstrap is meant for loading certain parts of the plugin before the server is loaded.
@ -245,8 +242,9 @@ index 0000000000000000000000000000000000000000..81e09abd2128d8cc54d1fd6454aea7d8
+ * <p>
+ * <b>All calls to Bukkit may throw a NullPointerExceptions or return null unexpectedly. You should only call api methods that are explicitly documented to work in the bootstrapper</b>
+ */
+@ApiStatus.OverrideOnly
+@ApiStatus.Experimental
+@NullMarked
+@ApiStatus.OverrideOnly
+public interface PluginBootstrap {
+
+ /**
@ -254,7 +252,7 @@ index 0000000000000000000000000000000000000000..81e09abd2128d8cc54d1fd6454aea7d8
+ *
+ * @param context the server provided context
+ */
+ void bootstrap(@NotNull BootstrapContext context);
+ void bootstrap(BootstrapContext context);
+
+ /**
+ * Called by the server to instantiate your main class.
@ -264,25 +262,23 @@ index 0000000000000000000000000000000000000000..81e09abd2128d8cc54d1fd6454aea7d8
+ * @param context the server created bootstrap object
+ * @return the server requested instance of the plugins main class.
+ */
+ @NotNull
+ default JavaPlugin createPlugin(@NotNull PluginProviderContext context) {
+ default JavaPlugin createPlugin(final PluginProviderContext context) {
+ return ProviderUtil.loadClass(context.getConfiguration().getMainClass(), JavaPlugin.class, this.getClass().getClassLoader());
+ }
+}
diff --git a/src/main/java/io/papermc/paper/plugin/bootstrap/PluginProviderContext.java b/src/main/java/io/papermc/paper/plugin/bootstrap/PluginProviderContext.java
new file mode 100644
index 0000000000000000000000000000000000000000..c1ce2a8bdf2e3bd9ad2fc7f32fba46282bea5d1a
index 0000000000000000000000000000000000000000..19bb302ab264b72306d9bd15e9c40ade6dde7ee7
--- /dev/null
+++ b/src/main/java/io/papermc/paper/plugin/bootstrap/PluginProviderContext.java
@@ -0,0 +1,52 @@
@@ -0,0 +1,48 @@
+package io.papermc.paper.plugin.bootstrap;
+
+import io.papermc.paper.plugin.configuration.PluginMeta;
+import java.nio.file.Path;
+import net.kyori.adventure.text.logger.slf4j.ComponentLogger;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
+
+import java.nio.file.Path;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * Represents the context provided to a {@link PluginBootstrap} during both the bootstrapping and plugin
@ -290,8 +286,9 @@ index 0000000000000000000000000000000000000000..c1ce2a8bdf2e3bd9ad2fc7f32fba4628
+ * A bootstrap context may be used to access data or logic usually provided to {@link org.bukkit.plugin.Plugin} instances
+ * like the plugin's configuration or logger during the plugins bootstrap.
+ */
+@ApiStatus.NonExtendable
+@ApiStatus.Experimental
+@NullMarked
+@ApiStatus.NonExtendable
+public interface PluginProviderContext {
+
+ /**
@ -299,7 +296,6 @@ index 0000000000000000000000000000000000000000..c1ce2a8bdf2e3bd9ad2fc7f32fba4628
+ *
+ * @return the plugin's configuration
+ */
+ @NotNull
+ PluginMeta getConfiguration();
+
+ /**
@ -307,7 +303,6 @@ index 0000000000000000000000000000000000000000..c1ce2a8bdf2e3bd9ad2fc7f32fba4628
+ *
+ * @return the previously described path
+ */
+ @NotNull
+ Path getDataDirectory();
+
+ /**
@ -315,7 +310,6 @@ index 0000000000000000000000000000000000000000..c1ce2a8bdf2e3bd9ad2fc7f32fba4628
+ *
+ * @return the logger instance
+ */
+ @NotNull
+ ComponentLogger getLogger();
+
+ /**
@ -323,33 +317,32 @@ index 0000000000000000000000000000000000000000..c1ce2a8bdf2e3bd9ad2fc7f32fba4628
+ *
+ * @return the previously described path
+ */
+ @NotNull
+ Path getPluginSource();
+
+}
diff --git a/src/main/java/io/papermc/paper/plugin/configuration/PluginMeta.java b/src/main/java/io/papermc/paper/plugin/configuration/PluginMeta.java
new file mode 100644
index 0000000000000000000000000000000000000000..bcf91d048d84144f6acf9bfd2095df9ada2e585f
index 0000000000000000000000000000000000000000..3c768d5ccf490e962d9638e92d4ea7c85670bad8
--- /dev/null
+++ b/src/main/java/io/papermc/paper/plugin/configuration/PluginMeta.java
@@ -0,0 +1,203 @@
@@ -0,0 +1,186 @@
+package io.papermc.paper.plugin.configuration;
+
+import java.util.List;
+import org.bukkit.permissions.Permission;
+import org.bukkit.permissions.PermissionDefault;
+import org.bukkit.plugin.PluginLoadOrder;
+import org.bukkit.plugin.java.JavaPlugin;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
+import org.jspecify.annotations.NullMarked;
+import org.jspecify.annotations.Nullable;
+
+/**
+ * This class acts as an abstraction for a plugin configuration.
+ */
+@ApiStatus.NonExtendable
+@ApiStatus.Experimental // Subject to change!
+@NullMarked
+@ApiStatus.NonExtendable
+public interface PluginMeta {
+
+ /**
@ -369,7 +362,6 @@ index 0000000000000000000000000000000000000000..bcf91d048d84144f6acf9bfd2095df9a
+ *
+ * @return the name of the plugin
+ */
+ @NotNull
+ String getName();
+
+ /**
@ -377,7 +369,6 @@ index 0000000000000000000000000000000000000000..bcf91d048d84144f6acf9bfd2095df9a
+ *
+ * @return a descriptive name of the plugin and respective version
+ */
+ @NotNull
+ default String getDisplayName() {
+ return this.getName() + " v" + this.getVersion();
+ }
@ -388,7 +379,6 @@ index 0000000000000000000000000000000000000000..bcf91d048d84144f6acf9bfd2095df9a
+ *
+ * @return the fully qualified class name of the plugin's main class.
+ */
+ @NotNull
+ String getMainClass();
+
+ /**
@ -397,7 +387,6 @@ index 0000000000000000000000000000000000000000..bcf91d048d84144f6acf9bfd2095df9a
+ * @return the plugin load order
+ * @see PluginLoadOrder for further details regards the available load orders.
+ */
+ @NotNull
+ PluginLoadOrder getLoadOrder();
+
+ /**
@ -407,7 +396,6 @@ index 0000000000000000000000000000000000000000..bcf91d048d84144f6acf9bfd2095df9a
+ *
+ * @return the string representation of the plugin's version
+ */
+ @NotNull
+ String getVersion();
+
+ /**
@ -418,8 +406,7 @@ index 0000000000000000000000000000000000000000..bcf91d048d84144f6acf9bfd2095df9a
+ * @return the specific overwrite of the logger prefix as defined by the plugin. If the plugin did not define a
+ * custom logger prefix, this method will return null
+ */
+ @Nullable
+ String getLoggerPrefix();
+ @Nullable String getLoggerPrefix();
+
+ /**
+ * Provides a list of dependencies that are required for this plugin to load.
@ -430,7 +417,6 @@ index 0000000000000000000000000000000000000000..bcf91d048d84144f6acf9bfd2095df9a
+ *
+ * @return an immutable list of required dependency names
+ */
+ @NotNull
+ List<String> getPluginDependencies();
+
+ /**
@ -443,7 +429,6 @@ index 0000000000000000000000000000000000000000..bcf91d048d84144f6acf9bfd2095df9a
+ *
+ * @return immutable list of soft dependencies
+ */
+ @NotNull
+ List<String> getPluginSoftDependencies();
+
+ /**
@ -456,7 +441,6 @@ index 0000000000000000000000000000000000000000..bcf91d048d84144f6acf9bfd2095df9a
+ *
+ * @return immutable list of plugins to load before this plugin
+ */
+ @NotNull
+ List<String> getLoadBeforePlugins();
+
+ /**
@ -466,7 +450,6 @@ index 0000000000000000000000000000000000000000..bcf91d048d84144f6acf9bfd2095df9a
+ *
+ * @return immutable list of provided plugins/dependencies
+ */
+ @NotNull
+ List<String> getProvidedPlugins();
+
+ /**
@ -475,7 +458,6 @@ index 0000000000000000000000000000000000000000..bcf91d048d84144f6acf9bfd2095df9a
+ *
+ * @return an immutable list of the plugin's authors
+ */
+ @NotNull
+ List<String> getAuthors();
+
+ /**
@ -484,7 +466,6 @@ index 0000000000000000000000000000000000000000..bcf91d048d84144f6acf9bfd2095df9a
+ *
+ * @return an immutable list of the plugin's contributors
+ */
+ @NotNull
+ List<String> getContributors();
+
+ /**
@ -493,8 +474,7 @@ index 0000000000000000000000000000000000000000..bcf91d048d84144f6acf9bfd2095df9a
+ *
+ * @return description or null if the plugin did not define a human readable description.
+ */
+ @Nullable
+ String getDescription();
+ @Nullable String getDescription();
+
+ /**
+ * Provides the website for the plugin or the plugin's author.
@ -502,8 +482,7 @@ index 0000000000000000000000000000000000000000..bcf91d048d84144f6acf9bfd2095df9a
+ *
+ * @return a string representation of the website that serves as the main hub for this plugin/its author.
+ */
+ @Nullable
+ String getWebsite();
+ @Nullable String getWebsite();
+
+ /**
+ * Provides the list of permissions that are defined via the plugin meta instance.
@ -511,7 +490,6 @@ index 0000000000000000000000000000000000000000..bcf91d048d84144f6acf9bfd2095df9a
+ * @return an immutable list of permissions
+ */
+ // TODO: Do we even want this? Why not just use the bootstrapper
+ @NotNull
+ List<Permission> getPermissions();
+
+ /**
@ -521,7 +499,6 @@ index 0000000000000000000000000000000000000000..bcf91d048d84144f6acf9bfd2095df9a
+ * @see #getPermissions()
+ */
+ // TODO: Do we even want this? Why not just use the bootstrapper
+ @NotNull
+ PermissionDefault getPermissionDefault();
+
+ /**
@ -532,8 +509,7 @@ index 0000000000000000000000000000000000000000..bcf91d048d84144f6acf9bfd2095df9a
+ * @return the version string made up of the major and minor version (e.g. 1.18 or 1.19). Minor versions like 1.18.2
+ * are unified to their major release version (in this example 1.18)
+ */
+ @Nullable
+ String getAPIVersion();
+ @Nullable String getAPIVersion();
+
+}
diff --git a/src/main/java/io/papermc/paper/plugin/configuration/package-info.java b/src/main/java/io/papermc/paper/plugin/configuration/package-info.java
@ -552,10 +528,10 @@ index 0000000000000000000000000000000000000000..ddb3076124365d0d1a5caa32d4dcb1f4
+package io.papermc.paper.plugin.configuration;
diff --git a/src/main/java/io/papermc/paper/plugin/loader/PluginClasspathBuilder.java b/src/main/java/io/papermc/paper/plugin/loader/PluginClasspathBuilder.java
new file mode 100644
index 0000000000000000000000000000000000000000..28cbc09b7c1ded1f4515969cef4a669adac85703
index 0000000000000000000000000000000000000000..ddb768057cdfd9202e4386494fd5f643692c73a1
--- /dev/null
+++ b/src/main/java/io/papermc/paper/plugin/loader/PluginClasspathBuilder.java
@@ -0,0 +1,38 @@
@@ -0,0 +1,37 @@
+package io.papermc.paper.plugin.loader;
+
+import io.papermc.paper.plugin.bootstrap.PluginProviderContext;
@ -563,14 +539,15 @@ index 0000000000000000000000000000000000000000..28cbc09b7c1ded1f4515969cef4a669a
+import io.papermc.paper.plugin.loader.library.LibraryStore;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * A mutable builder that may be used to collect and register all {@link ClassPathLibrary} instances a
+ * {@link PluginLoader} aims to provide to its plugin at runtime.
+ */
+@ApiStatus.NonExtendable
+@ApiStatus.Experimental
+@NullMarked
+@ApiStatus.NonExtendable
+public interface PluginClasspathBuilder {
+
+ /**
@ -587,23 +564,21 @@ index 0000000000000000000000000000000000000000..28cbc09b7c1ded1f4515969cef4a669a
+ * @see io.papermc.paper.plugin.loader.library.impl.JarLibrary
+ * @see io.papermc.paper.plugin.loader.library.impl.MavenLibraryResolver
+ */
+ @NotNull
+ @Contract("_ -> this")
+ PluginClasspathBuilder addLibrary(@NotNull ClassPathLibrary classPathLibrary);
+ PluginClasspathBuilder addLibrary(ClassPathLibrary classPathLibrary);
+
+ @NotNull
+ PluginProviderContext getContext();
+}
diff --git a/src/main/java/io/papermc/paper/plugin/loader/PluginLoader.java b/src/main/java/io/papermc/paper/plugin/loader/PluginLoader.java
new file mode 100644
index 0000000000000000000000000000000000000000..c9e31f78ff6ff969436c6d99755845786c4d383f
index 0000000000000000000000000000000000000000..c2d029e4474cc00c84e5b3f7fceec4c3f30b486b
--- /dev/null
+++ b/src/main/java/io/papermc/paper/plugin/loader/PluginLoader.java
@@ -0,0 +1,30 @@
@@ -0,0 +1,31 @@
+package io.papermc.paper.plugin.loader;
+
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * A plugin loader is responsible for creating certain aspects of a plugin before it is created.
@ -615,8 +590,9 @@ index 0000000000000000000000000000000000000000..c9e31f78ff6ff969436c6d9975584578
+ * It should be noted that this class will be called from a different classloader, this will cause any static values
+ * set in this class/any other classes loaded not to persist when the plugin loads.
+ */
+@ApiStatus.OverrideOnly
+@ApiStatus.Experimental
+@NullMarked
+@ApiStatus.OverrideOnly
+public interface PluginLoader {
+
+ /**
@ -627,23 +603,24 @@ index 0000000000000000000000000000000000000000..c9e31f78ff6ff969436c6d9975584578
+ * @param classpathBuilder a mutable classpath builder that may be used to register custom runtime dependencies
+ * for the plugin the loader was registered for.
+ */
+ void classloader(@NotNull PluginClasspathBuilder classpathBuilder);
+ void classloader(PluginClasspathBuilder classpathBuilder);
+
+}
diff --git a/src/main/java/io/papermc/paper/plugin/loader/library/ClassPathLibrary.java b/src/main/java/io/papermc/paper/plugin/loader/library/ClassPathLibrary.java
new file mode 100644
index 0000000000000000000000000000000000000000..1347b535d90c2c281c184d0459e7ac59c0350c9f
index 0000000000000000000000000000000000000000..8a07333a592056bab1d26d811316bb5ea5f30e18
--- /dev/null
+++ b/src/main/java/io/papermc/paper/plugin/loader/library/ClassPathLibrary.java
@@ -0,0 +1,20 @@
@@ -0,0 +1,21 @@
+package io.papermc.paper.plugin.loader.library;
+
+import org.jetbrains.annotations.NotNull;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * The classpath library interface represents libraries that are capable of registering themselves via
+ * {@link #register(LibraryStore)} on any given {@link LibraryStore}.
+ */
+@NullMarked
+public interface ClassPathLibrary {
+
+ /**
@ -654,7 +631,7 @@ index 0000000000000000000000000000000000000000..1347b535d90c2c281c184d0459e7ac59
+ * @param store the library store instance to register this library into
+ * @throws LibraryLoadingException if library loading failed for this classpath library
+ */
+ void register(@NotNull LibraryStore store) throws LibraryLoadingException;
+ void register(LibraryStore store) throws LibraryLoadingException;
+}
diff --git a/src/main/java/io/papermc/paper/plugin/loader/library/LibraryLoadingException.java b/src/main/java/io/papermc/paper/plugin/loader/library/LibraryLoadingException.java
new file mode 100644
@ -679,16 +656,15 @@ index 0000000000000000000000000000000000000000..79ba423a364b50588f3ee87fdc69155c
+}
diff --git a/src/main/java/io/papermc/paper/plugin/loader/library/LibraryStore.java b/src/main/java/io/papermc/paper/plugin/loader/library/LibraryStore.java
new file mode 100644
index 0000000000000000000000000000000000000000..0546fa1e9dcd7155086a8650806a8c086b6fc458
index 0000000000000000000000000000000000000000..93ef8e369a158c25bc4fdd553faf13c1826ad872
--- /dev/null
+++ b/src/main/java/io/papermc/paper/plugin/loader/library/LibraryStore.java
@@ -0,0 +1,26 @@
+package io.papermc.paper.plugin.loader.library;
+
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
+
+import java.nio.file.Path;
+import org.jetbrains.annotations.ApiStatus;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * Represents a storage that stores library jars.
@ -699,6 +675,7 @@ index 0000000000000000000000000000000000000000..0546fa1e9dcd7155086a8650806a8c08
+ * @see io.papermc.paper.plugin.loader.PluginLoader
+ */
+@ApiStatus.Internal
+@NullMarked
+public interface LibraryStore {
+
+ /**
@ -706,12 +683,12 @@ index 0000000000000000000000000000000000000000..0546fa1e9dcd7155086a8650806a8c08
+ *
+ * @param library path to the libraries jar file on the disk
+ */
+ void addLibrary(@NotNull Path library);
+ void addLibrary(Path library);
+
+}
diff --git a/src/main/java/io/papermc/paper/plugin/loader/library/impl/JarLibrary.java b/src/main/java/io/papermc/paper/plugin/loader/library/impl/JarLibrary.java
new file mode 100644
index 0000000000000000000000000000000000000000..e3da0d67cab01e1233dccde1a12ff25961ee79fb
index 0000000000000000000000000000000000000000..9efcd9a62d6439eb27fc4e08498b4b7fbbdecb3c
--- /dev/null
+++ b/src/main/java/io/papermc/paper/plugin/loader/library/impl/JarLibrary.java
@@ -0,0 +1,45 @@
@ -720,10 +697,9 @@ index 0000000000000000000000000000000000000000..e3da0d67cab01e1233dccde1a12ff259
+import io.papermc.paper.plugin.loader.library.ClassPathLibrary;
+import io.papermc.paper.plugin.loader.library.LibraryLoadingException;
+import io.papermc.paper.plugin.loader.library.LibraryStore;
+import org.jetbrains.annotations.NotNull;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * A simple jar library implementation of the {@link ClassPathLibrary} that allows {@link io.papermc.paper.plugin.loader.PluginLoader}s to
@ -738,6 +714,7 @@ index 0000000000000000000000000000000000000000..e3da0d67cab01e1233dccde1a12ff259
+ * <p>
+ * The jar library implementation will error if the file does not exist at the specified path.
+ */
+@NullMarked
+public class JarLibrary implements ClassPathLibrary {
+
+ private final Path path;
@ -747,12 +724,12 @@ index 0000000000000000000000000000000000000000..e3da0d67cab01e1233dccde1a12ff259
+ *
+ * @param path the path, relative to the JVMs start directory.
+ */
+ public JarLibrary(@NotNull Path path) {
+ public JarLibrary(final Path path) {
+ this.path = path;
+ }
+
+ @Override
+ public void register(@NotNull LibraryStore store) throws LibraryLoadingException {
+ public void register(final LibraryStore store) throws LibraryLoadingException {
+ if (Files.notExists(this.path)) {
+ throw new LibraryLoadingException("Could not find library at " + this.path);
+ }
@ -762,7 +739,7 @@ index 0000000000000000000000000000000000000000..e3da0d67cab01e1233dccde1a12ff259
+}
diff --git a/src/main/java/io/papermc/paper/plugin/loader/library/impl/MavenLibraryResolver.java b/src/main/java/io/papermc/paper/plugin/loader/library/impl/MavenLibraryResolver.java
new file mode 100644
index 0000000000000000000000000000000000000000..70f352630de71f575d1aea5a3126da19a94791ab
index 0000000000000000000000000000000000000000..107705db2d82b7c191e5e625ec888e0bc3b03831
--- /dev/null
+++ b/src/main/java/io/papermc/paper/plugin/loader/library/impl/MavenLibraryResolver.java
@@ -0,0 +1,133 @@
@ -771,6 +748,9 @@ index 0000000000000000000000000000000000000000..70f352630de71f575d1aea5a3126da19
+import io.papermc.paper.plugin.loader.library.ClassPathLibrary;
+import io.papermc.paper.plugin.loader.library.LibraryLoadingException;
+import io.papermc.paper.plugin.loader.library.LibraryStore;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.maven.repository.internal.MavenRepositorySystemUtils;
+import org.eclipse.aether.DefaultRepositorySystemSession;
+import org.eclipse.aether.RepositorySystem;
@ -791,14 +771,10 @@ index 0000000000000000000000000000000000000000..70f352630de71f575d1aea5a3126da19
+import org.eclipse.aether.transfer.TransferCancelledException;
+import org.eclipse.aether.transfer.TransferEvent;
+import org.eclipse.aether.transport.http.HttpTransporterFactory;
+import org.jetbrains.annotations.NotNull;
+import org.jspecify.annotations.NullMarked;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * The maven library resolver acts as a resolver for yet to be resolved jar libraries that may be pulled from a
+ * remote maven repository.
@ -813,12 +789,13 @@ index 0000000000000000000000000000000000000000..70f352630de71f575d1aea5a3126da19
+ * "central", "default", "https://repo1.maven.org/maven2/"
+ * ).build());
+ * }</pre>
+ *
+ * <p>
+ * Plugins may create and register a {@link MavenLibraryResolver} after configuring it.
+ */
+@NullMarked
+public class MavenLibraryResolver implements ClassPathLibrary {
+
+ private static final Logger logger = LoggerFactory.getLogger("MavenLibraryResolver");
+ private static final Logger LOGGER = LoggerFactory.getLogger("MavenLibraryResolver");
+
+ private final RepositorySystem repository;
+ private final DefaultRepositorySystemSession session;
@ -834,7 +811,7 @@ index 0000000000000000000000000000000000000000..70f352630de71f575d1aea5a3126da19
+ * submitting the {@link MavenLibraryResolver} to the {@link io.papermc.paper.plugin.loader.PluginClasspathBuilder}.
+ */
+ public MavenLibraryResolver() {
+ DefaultServiceLocator locator = MavenRepositorySystemUtils.newServiceLocator();
+ final DefaultServiceLocator locator = MavenRepositorySystemUtils.newServiceLocator();
+ locator.addService(RepositoryConnectorFactory.class, BasicRepositoryConnectorFactory.class);
+ locator.addService(TransporterFactory.class, HttpTransporterFactory.class);
+
@ -846,8 +823,8 @@ index 0000000000000000000000000000000000000000..70f352630de71f575d1aea5a3126da19
+ this.session.setLocalRepositoryManager(this.repository.newLocalRepositoryManager(this.session, new LocalRepository("libraries")));
+ this.session.setTransferListener(new AbstractTransferListener() {
+ @Override
+ public void transferInitiated(@NotNull TransferEvent event) throws TransferCancelledException {
+ logger.info("Downloading {}", event.getResource().getRepositoryUrl() + event.getResource().getResourceName());
+ public void transferInitiated(final TransferEvent event) throws TransferCancelledException {
+ LOGGER.info("Downloading {}", event.getResource().getRepositoryUrl() + event.getResource().getResourceName());
+ }
+ });
+ this.session.setReadOnly();
@ -860,7 +837,7 @@ index 0000000000000000000000000000000000000000..70f352630de71f575d1aea5a3126da19
+ * @param dependency the definition of the dependency the maven library resolver should resolve when running
+ * @see MavenLibraryResolver#addRepository(RemoteRepository)
+ */
+ public void addDependency(@NotNull Dependency dependency) {
+ public void addDependency(final Dependency dependency) {
+ this.dependencies.add(dependency);
+ }
+
@ -870,9 +847,9 @@ index 0000000000000000000000000000000000000000..70f352630de71f575d1aea5a3126da19
+ * repository.
+ *
+ * @param remoteRepository the configuration that defines the maven repository this library resolver should fetch
+ * dependencies from
+ * dependencies from
+ */
+ public void addRepository(@NotNull RemoteRepository remoteRepository) {
+ public void addRepository(final RemoteRepository remoteRepository) {
+ this.repositories.add(remoteRepository);
+ }
+
@ -883,32 +860,32 @@ index 0000000000000000000000000000000000000000..70f352630de71f575d1aea5a3126da19
+ * @throws LibraryLoadingException if resolving a dependency failed
+ */
+ @Override
+ public void register(@NotNull LibraryStore store) throws LibraryLoadingException {
+ List<RemoteRepository> repos = this.repository.newResolutionRepositories(this.session, this.repositories);
+ public void register(final LibraryStore store) throws LibraryLoadingException {
+ final List<RemoteRepository> repos = this.repository.newResolutionRepositories(this.session, this.repositories);
+
+ DependencyResult result;
+ final DependencyResult result;
+ try {
+ result = this.repository.resolveDependencies(this.session, new DependencyRequest(new CollectRequest((Dependency) null, this.dependencies, repos), null));
+ } catch (DependencyResolutionException ex) {
+ } catch (final DependencyResolutionException ex) {
+ throw new LibraryLoadingException("Error resolving libraries", ex);
+ }
+
+ for (ArtifactResult artifact : result.getArtifactResults()) {
+ File file = artifact.getArtifact().getFile();
+ for (final ArtifactResult artifact : result.getArtifactResults()) {
+ final File file = artifact.getArtifact().getFile();
+ store.addLibrary(file.toPath());
+ }
+ }
+}
diff --git a/src/main/java/io/papermc/paper/plugin/provider/classloader/ClassLoaderAccess.java b/src/main/java/io/papermc/paper/plugin/provider/classloader/ClassLoaderAccess.java
new file mode 100644
index 0000000000000000000000000000000000000000..64e46fdfa4d404cb08c67a456e5990b729296b98
index 0000000000000000000000000000000000000000..75fe408ec742319c3cba2461b33b2a6e8b22d231
--- /dev/null
+++ b/src/main/java/io/papermc/paper/plugin/provider/classloader/ClassLoaderAccess.java
@@ -0,0 +1,34 @@
@@ -0,0 +1,35 @@
+package io.papermc.paper.plugin.provider.classloader;
+
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * The class loader access interface is an <b>internal</b> representation of a class accesses' ability to see types
@ -919,6 +896,7 @@ index 0000000000000000000000000000000000000000..64e46fdfa4d404cb08c67a456e5990b7
+ * is owned by a direct or transitive dependency of the plugin, preventing the plugin for accidentally discovering and
+ * using class types that are supplied by plugins/libraries the plugin did not actively define as a dependency.
+ */
+@NullMarked
+@ApiStatus.Internal
+public interface ClassLoaderAccess {
+
@ -941,19 +919,18 @@ index 0000000000000000000000000000000000000000..64e46fdfa4d404cb08c67a456e5990b7
+}
diff --git a/src/main/java/io/papermc/paper/plugin/provider/classloader/ConfiguredPluginClassLoader.java b/src/main/java/io/papermc/paper/plugin/provider/classloader/ConfiguredPluginClassLoader.java
new file mode 100644
index 0000000000000000000000000000000000000000..a21bdc57564aef7caf43dde3b2bcb2fc7f30461c
index 0000000000000000000000000000000000000000..7c23e0a1a38f8b89484aee160647f751088903cd
--- /dev/null
+++ b/src/main/java/io/papermc/paper/plugin/provider/classloader/ConfiguredPluginClassLoader.java
@@ -0,0 +1,71 @@
@@ -0,0 +1,70 @@
+package io.papermc.paper.plugin.provider.classloader;
+
+import io.papermc.paper.plugin.configuration.PluginMeta;
+import java.io.Closeable;
+import org.bukkit.plugin.java.JavaPlugin;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.Closeable;
+import org.jetbrains.annotations.Nullable;
+import org.jspecify.annotations.NullMarked;
+import org.jspecify.annotations.Nullable;
+
+/**
+ * The configured plugin class loader represents an <b>internal</b> abstraction over the classloaders used by the server
@ -962,6 +939,7 @@ index 0000000000000000000000000000000000000000..a21bdc57564aef7caf43dde3b2bcb2fc
+ * It implements {@link Closeable} to define the ability to shutdown and close the classloader that implements this
+ * interface.
+ */
+@NullMarked
+@ApiStatus.Internal
+public interface ConfiguredPluginClassLoader extends Closeable {
+
@ -987,7 +965,7 @@ index 0000000000000000000000000000000000000000..a21bdc57564aef7caf43dde3b2bcb2fc
+ * @see ClassLoader#loadClass(String)
+ * @see Class#forName(String, boolean, ClassLoader)
+ */
+ Class<?> loadClass(@NotNull String name,
+ Class<?> loadClass(String name,
+ boolean resolve,
+ boolean checkGlobal,
+ boolean checkLibraries) throws ClassNotFoundException;
@ -1013,8 +991,7 @@ index 0000000000000000000000000000000000000000..a21bdc57564aef7caf43dde3b2bcb2fc
+ * that is used by the underlying classloader
+ * @return classloader
+ */
+ @Nullable
+ PluginClassLoaderGroup getGroup();
+ @Nullable PluginClassLoaderGroup getGroup();
+}
diff --git a/src/main/java/io/papermc/paper/plugin/provider/classloader/PaperClassLoaderStorage.java b/src/main/java/io/papermc/paper/plugin/provider/classloader/PaperClassLoaderStorage.java
new file mode 100644
@ -1139,15 +1116,16 @@ index 0000000000000000000000000000000000000000..2c0e5ba6f8eba7a632180491843071b8
+}
diff --git a/src/main/java/io/papermc/paper/plugin/provider/classloader/PluginClassLoaderGroup.java b/src/main/java/io/papermc/paper/plugin/provider/classloader/PluginClassLoaderGroup.java
new file mode 100644
index 0000000000000000000000000000000000000000..885151cb932d9b8c09a7887edc879e154225f416
index 0000000000000000000000000000000000000000..dd3bfbf8a30c9ac6a82dcbdf879bbf120d920e20
--- /dev/null
+++ b/src/main/java/io/papermc/paper/plugin/provider/classloader/PluginClassLoaderGroup.java
@@ -0,0 +1,65 @@
@@ -0,0 +1,66 @@
+package io.papermc.paper.plugin.provider.classloader;
+
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.Nullable;
+import org.jspecify.annotations.NullMarked;
+import org.jspecify.annotations.Nullable;
+
+/**
+ * A plugin classloader group represents a group of classloaders that a plugins classloader may access.
@ -1155,6 +1133,7 @@ index 0000000000000000000000000000000000000000..885151cb932d9b8c09a7887edc879e15
+ * An example of this would be a classloader group that holds all direct and transitive dependencies a plugin declared,
+ * allowing a plugins classloader to access classes included in these dependencies via this group.
+ */
+@NullMarked
+@ApiStatus.Internal
+public interface PluginClassLoaderGroup {
+
@ -1172,8 +1151,7 @@ index 0000000000000000000000000000000000000000..885151cb932d9b8c09a7887edc879e15
+ * will be returned.
+ * @see ConfiguredPluginClassLoader#loadClass(String, boolean, boolean, boolean)
+ */
+ @Nullable
+ Class<?> getClassByName(String name, boolean resolve, ConfiguredPluginClassLoader requester);
+ @Nullable Class<?> getClassByName(String name, boolean resolve, ConfiguredPluginClassLoader requester);
+
+ /**
+ * Removes a configured plugin classloader from this class loader group.
@ -1210,15 +1188,15 @@ index 0000000000000000000000000000000000000000..885151cb932d9b8c09a7887edc879e15
+}
diff --git a/src/main/java/io/papermc/paper/plugin/provider/entrypoint/DependencyContext.java b/src/main/java/io/papermc/paper/plugin/provider/entrypoint/DependencyContext.java
new file mode 100644
index 0000000000000000000000000000000000000000..44d630c3eb2670c36134b9907519dc986b3761b4
index 0000000000000000000000000000000000000000..338dc25d5161b7378dd17b17d6f603c92eb0a894
--- /dev/null
+++ b/src/main/java/io/papermc/paper/plugin/provider/entrypoint/DependencyContext.java
@@ -0,0 +1,48 @@
@@ -0,0 +1,49 @@
+package io.papermc.paper.plugin.provider.entrypoint;
+
+import io.papermc.paper.plugin.configuration.PluginMeta;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * A dependency context is a read-only abstraction of a type/concept that can resolve dependencies between plugins.
@ -1226,6 +1204,7 @@ index 0000000000000000000000000000000000000000..44d630c3eb2670c36134b9907519dc98
+ * This may for example be the server wide plugin manager itself, capable of validating if a dependency exists between
+ * two {@link PluginMeta} instances, however the implementation is not limited to such a concrete use-case.
+ */
+@NullMarked
+@ApiStatus.Internal
+public interface DependencyContext {
+
@ -1245,7 +1224,7 @@ index 0000000000000000000000000000000000000000..44d630c3eb2670c36134b9907519dc98
+ * @param depend the potential transitive dependency of the {@code plugin} parameter.
+ * @return a simple boolean flag indicating if {@code plugin} considers {@code depend} as a transitive dependency.
+ */
+ boolean isTransitiveDependency(@NotNull PluginMeta plugin, @NotNull PluginMeta depend);
+ boolean isTransitiveDependency(PluginMeta plugin, PluginMeta depend);
+
+ /**
+ * Computes if this dependency context is aware of a dependency that provides/matches the passed identifier.
@ -1259,29 +1238,30 @@ index 0000000000000000000000000000000000000000..44d630c3eb2670c36134b9907519dc98
+ * @return a plain boolean flag indicating if this dependency context is aware of a potential dependency with the
+ * passed identifier.
+ */
+ boolean hasDependency(@NotNull String pluginIdentifier);
+ boolean hasDependency(String pluginIdentifier);
+
+}
diff --git a/src/main/java/io/papermc/paper/plugin/provider/util/ProviderUtil.java b/src/main/java/io/papermc/paper/plugin/provider/util/ProviderUtil.java
new file mode 100644
index 0000000000000000000000000000000000000000..6bf3d212a6156ad9ab0e82d1ca0a04f83f6e4b83
index 0000000000000000000000000000000000000000..48a67c1b6070292dbf4ea3081f89b530207f9f6d
--- /dev/null
+++ b/src/main/java/io/papermc/paper/plugin/provider/util/ProviderUtil.java
@@ -0,0 +1,78 @@
@@ -0,0 +1,77 @@
+package io.papermc.paper.plugin.provider.util;
+
+import com.destroystokyo.paper.util.SneakyThrow;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jspecify.annotations.NullMarked;
+import org.jspecify.annotations.Nullable;
+
+/**
+ * An <b>internal</b> utility type that holds logic for loading a provider-like type from a classloaders.
+ * Provides, at least in the context of this utility, define themselves as implementations of a specific parent
+ * interface/type, e.g. {@link org.bukkit.plugin.java.JavaPlugin} and implement a no-args constructor.
+ */
+@NullMarked
+@ApiStatus.Internal
+public class ProviderUtil {
+public final class ProviderUtil {
+
+ /**
+ * Loads the class found at the provided fully qualified class name from the passed classloader, creates a new
@ -1294,8 +1274,7 @@ index 0000000000000000000000000000000000000000..6bf3d212a6156ad9ab0e82d1ca0a04f8
+ * @param <T> the generic type of the parent class the created object will be cast to
+ * @return the object instantiated from the class found at the provided FQN, cast to the parent type
+ */
+ @NotNull
+ public static <T> T loadClass(@NotNull String clazz, @NotNull Class<T> classType, @NotNull ClassLoader loader) {
+ public static <T> T loadClass(final String clazz, final Class<T> classType, final ClassLoader loader) {
+ return loadClass(clazz, classType, loader, null);
+ }
+
@ -1312,30 +1291,29 @@ index 0000000000000000000000000000000000000000..6bf3d212a6156ad9ab0e82d1ca0a04f8
+ * @return the object instantiated from the class found at the provided fully qualified class name, cast to the
+ * parent type
+ */
+ @NotNull
+ public static <T> T loadClass(@NotNull String clazz, @NotNull Class<T> classType, @NotNull ClassLoader loader, @Nullable Runnable onError) {
+ public static <T> T loadClass(final String clazz, final Class<T> classType, final ClassLoader loader, final @Nullable Runnable onError) {
+ try {
+ T clazzInstance;
+ final T clazzInstance;
+
+ try {
+ Class<?> jarClass = Class.forName(clazz, true, loader);
+ final Class<?> jarClass = Class.forName(clazz, true, loader);
+
+ Class<? extends T> pluginClass;
+ final Class<? extends T> pluginClass;
+ try {
+ pluginClass = jarClass.asSubclass(classType);
+ } catch (ClassCastException ex) {
+ } catch (final ClassCastException ex) {
+ throw new ClassCastException("class '%s' does not extend '%s'".formatted(clazz, classType));
+ }
+
+ clazzInstance = pluginClass.getDeclaredConstructor().newInstance();
+ } catch (IllegalAccessException exception) {
+ } catch (final IllegalAccessException exception) {
+ throw new RuntimeException("No public constructor");
+ } catch (InstantiationException exception) {
+ } catch (final InstantiationException exception) {
+ throw new RuntimeException("Abnormal class instantiation", exception);
+ }
+
+ return clazzInstance;
+ } catch (Throwable e) {
+ } catch (final Throwable e) {
+ if (onError != null) {
+ onError.run();
+ }

View file

@ -6,25 +6,27 @@ Subject: [PATCH] Add Position
diff --git a/src/main/java/io/papermc/paper/math/BlockPosition.java b/src/main/java/io/papermc/paper/math/BlockPosition.java
new file mode 100644
index 0000000000000000000000000000000000000000..f0fdabce4af640da2a406412e67020761ded3ac1
index 0000000000000000000000000000000000000000..c358bfdefc9bc7598dbd0d89a6b0b8a9408b5bb3
--- /dev/null
+++ b/src/main/java/io/papermc/paper/math/BlockPosition.java
@@ -0,0 +1,98 @@
@@ -0,0 +1,100 @@
+package io.papermc.paper.math;
+
+import org.bukkit.Axis;
+import org.bukkit.block.BlockFace;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * A position represented with integers.
+ * <p>
+ * <b>May see breaking changes until Experimental annotation is removed.</b>
+ *
+ * @see FinePosition
+ */
+@ApiStatus.Experimental
+@NullMarked
+public interface BlockPosition extends Position {
+
+ @Override
@ -53,17 +55,17 @@ index 0000000000000000000000000000000000000000..f0fdabce4af640da2a406412e6702076
+ }
+
+ @Override
+ default @NotNull BlockPosition toBlock() {
+ default BlockPosition toBlock() {
+ return this;
+ }
+
+ @Override
+ default @NotNull BlockPosition offset(int x, int y, int z) {
+ default BlockPosition offset(final int x, final int y, final int z) {
+ return x == 0 && y == 0 && z == 0 ? this : new BlockPositionImpl(this.blockX() + x, this.blockY() + y, this.blockZ() + z);
+ }
+
+ @Override
+ default @NotNull FinePosition offset(double x, double y, double z) {
+ default FinePosition offset(final double x, final double y, final double z) {
+ return new FinePositionImpl(this.blockX() + x, this.blockY() + y, this.blockZ() + z);
+ }
+
@ -74,7 +76,7 @@ index 0000000000000000000000000000000000000000..f0fdabce4af640da2a406412e6702076
+ * @return the offset block position
+ */
+ @Contract(value = "_ -> new", pure = true)
+ default @NotNull BlockPosition offset(@NotNull BlockFace blockFace) {
+ default BlockPosition offset(final BlockFace blockFace) {
+ return this.offset(blockFace, 1);
+ }
+
@ -87,7 +89,7 @@ index 0000000000000000000000000000000000000000..f0fdabce4af640da2a406412e6702076
+ * @return the offset block position
+ */
+ @Contract(pure = true)
+ default @NotNull BlockPosition offset(@NotNull BlockFace blockFace, int amount) {
+ default BlockPosition offset(final BlockFace blockFace, final int amount) {
+ return amount == 0 ? this : new BlockPositionImpl(this.blockX() + (blockFace.getModX() * amount), this.blockY() + (blockFace.getModY() * amount), this.blockZ() + (blockFace.getModZ() * amount));
+ }
+
@ -100,7 +102,7 @@ index 0000000000000000000000000000000000000000..f0fdabce4af640da2a406412e6702076
+ * @return the offset block position
+ */
+ @Contract(pure = true)
+ default @NotNull BlockPosition offset(@NotNull Axis axis, int amount) {
+ default BlockPosition offset(final Axis axis, final int amount) {
+ return amount == 0 ? this : switch (axis) {
+ case X -> new BlockPositionImpl(this.blockX() + amount, this.blockY(), this.blockZ());
+ case Y -> new BlockPositionImpl(this.blockX(), this.blockY() + amount, this.blockZ());
@ -120,24 +122,25 @@ index 0000000000000000000000000000000000000000..eb5a3f26c7ba56c6715827f52c0013a8
+}
diff --git a/src/main/java/io/papermc/paper/math/FinePosition.java b/src/main/java/io/papermc/paper/math/FinePosition.java
new file mode 100644
index 0000000000000000000000000000000000000000..d8df70d731573cf2446044925f218876d62fd7cf
index 0000000000000000000000000000000000000000..b9c0065d8a9dedc3bd1a2d8bfbedfbc7f952ff93
--- /dev/null
+++ b/src/main/java/io/papermc/paper/math/FinePosition.java
@@ -0,0 +1,56 @@
@@ -0,0 +1,57 @@
+package io.papermc.paper.math;
+
+import org.bukkit.util.NumberConversions;
+import org.bukkit.util.Vector;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * A position represented with doubles.
+ * <p>
+ * <b>May see breaking changes until Experimental annotation is removed.</b>
+ *
+ * @see BlockPosition
+ */
+@ApiStatus.Experimental
+@NullMarked
+public interface FinePosition extends Position {
+
+ @Override
@ -166,17 +169,17 @@ index 0000000000000000000000000000000000000000..d8df70d731573cf2446044925f218876
+ }
+
+ @Override
+ default @NotNull BlockPosition toBlock() {
+ default BlockPosition toBlock() {
+ return new BlockPositionImpl(this.blockX(), this.blockY(), this.blockZ());
+ }
+
+ @Override
+ default @NotNull FinePosition offset(int x, int y, int z) {
+ default FinePosition offset(final int x, final int y, final int z) {
+ return this.offset((double) x, y, z);
+ }
+
+ @Override
+ default @NotNull FinePosition offset(double x, double y, double z) {
+ default FinePosition offset(final double x, final double y, final double z) {
+ return x == 0.0 && y == 0.0 && z == 0.0 ? this : new FinePositionImpl(this.x() + x, this.y() + y, this.z() + z);
+ }
+}
@ -192,10 +195,10 @@ index 0000000000000000000000000000000000000000..93476aaf8d21efb5a30b6d2cc2eeda81
+}
diff --git a/src/main/java/io/papermc/paper/math/Position.java b/src/main/java/io/papermc/paper/math/Position.java
new file mode 100644
index 0000000000000000000000000000000000000000..26bc5a0fa67855af87c8fd4cd8229b4d9f242740
index 0000000000000000000000000000000000000000..0e6a6a6738353b118e0ed093994dda06750700c4
--- /dev/null
+++ b/src/main/java/io/papermc/paper/math/Position.java
@@ -0,0 +1,191 @@
@@ -0,0 +1,192 @@
+package io.papermc.paper.math;
+
+import org.bukkit.Location;
@ -203,7 +206,7 @@ index 0000000000000000000000000000000000000000..26bc5a0fa67855af87c8fd4cd8229b4d
+import org.bukkit.util.Vector;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * Common interface for {@link FinePosition} and {@link BlockPosition}.
@ -211,6 +214,7 @@ index 0000000000000000000000000000000000000000..26bc5a0fa67855af87c8fd4cd8229b4d
+ * <b>May see breaking changes until Experimental annotation is removed.</b>
+ */
+@ApiStatus.Experimental
+@NullMarked
+public interface Position {
+
+ FinePosition FINE_ZERO = new FinePositionImpl(0, 0, 0);
@ -287,7 +291,7 @@ index 0000000000000000000000000000000000000000..26bc5a0fa67855af87c8fd4cd8229b4d
+ * @param z z value to offset
+ * @return the offset position
+ */
+ @NotNull Position offset(int x, int y, int z);
+ Position offset(int x, int y, int z);
+
+ /**
+ * Returns a position offset by the specified amounts.
@ -297,7 +301,7 @@ index 0000000000000000000000000000000000000000..26bc5a0fa67855af87c8fd4cd8229b4d
+ * @param z z value to offset
+ * @return the offset position
+ */
+ @NotNull FinePosition offset(double x, double y, double z);
+ FinePosition offset(double x, double y, double z);
+
+ /**
+ * Returns a new position at the center of the block position this represents
@ -305,7 +309,7 @@ index 0000000000000000000000000000000000000000..26bc5a0fa67855af87c8fd4cd8229b4d
+ * @return a new center position
+ */
+ @Contract(value = "-> new", pure = true)
+ default @NotNull FinePosition toCenter() {
+ default FinePosition toCenter() {
+ return new FinePositionImpl(this.blockX() + 0.5, this.blockY() + 0.5, this.blockZ() + 0.5);
+ }
+
@ -316,7 +320,7 @@ index 0000000000000000000000000000000000000000..26bc5a0fa67855af87c8fd4cd8229b4d
+ * @return the block position
+ */
+ @Contract(pure = true)
+ @NotNull BlockPosition toBlock();
+ BlockPosition toBlock();
+
+ /**
+ * Converts this position to a vector
@ -324,7 +328,7 @@ index 0000000000000000000000000000000000000000..26bc5a0fa67855af87c8fd4cd8229b4d
+ * @return a new vector
+ */
+ @Contract(value = "-> new", pure = true)
+ default @NotNull Vector toVector() {
+ default Vector toVector() {
+ return new Vector(this.x(), this.y(), this.z());
+ }
+
@ -335,7 +339,7 @@ index 0000000000000000000000000000000000000000..26bc5a0fa67855af87c8fd4cd8229b4d
+ * @return a new location
+ */
+ @Contract(value = "_ -> new", pure = true)
+ default @NotNull Location toLocation(@NotNull World world) {
+ default Location toLocation(final World world) {
+ return new Location(world, this.x(), this.y(), this.z());
+ }
+
@ -348,7 +352,7 @@ index 0000000000000000000000000000000000000000..26bc5a0fa67855af87c8fd4cd8229b4d
+ * @return a position with those coords
+ */
+ @Contract(value = "_, _, _ -> new", pure = true)
+ static @NotNull BlockPosition block(int x, int y, int z) {
+ static BlockPosition block(final int x, final int y, final int z) {
+ return new BlockPositionImpl(x, y, z);
+ }
+
@ -359,7 +363,7 @@ index 0000000000000000000000000000000000000000..26bc5a0fa67855af87c8fd4cd8229b4d
+ * @return a new position at that location
+ */
+ @Contract(value = "_ -> new", pure = true)
+ static @NotNull BlockPosition block(@NotNull Location location) {
+ static BlockPosition block(final Location location) {
+ return new BlockPositionImpl(location.getBlockX(), location.getBlockY(), location.getBlockZ());
+ }
+
@ -372,7 +376,7 @@ index 0000000000000000000000000000000000000000..26bc5a0fa67855af87c8fd4cd8229b4d
+ * @return a position with those coords
+ */
+ @Contract(value = "_, _, _ -> new", pure = true)
+ static @NotNull FinePosition fine(double x, double y, double z) {
+ static FinePosition fine(final double x, final double y, final double z) {
+ return new FinePositionImpl(x, y, z);
+ }
+
@ -383,7 +387,7 @@ index 0000000000000000000000000000000000000000..26bc5a0fa67855af87c8fd4cd8229b4d
+ * @return a new position at that location
+ */
+ @Contract(value = "_ -> new", pure = true)
+ static @NotNull FinePosition fine(@NotNull Location location) {
+ static FinePosition fine(final Location location) {
+ return new FinePositionImpl(location.getX(), location.getY(), location.getZ());
+ }
+}

View file

@ -63,10 +63,10 @@ index 0000000000000000000000000000000000000000..023cc52a9e28e1238c7452c0f3f577f2
+}
diff --git a/src/main/java/io/papermc/paper/ServerBuildInfo.java b/src/main/java/io/papermc/paper/ServerBuildInfo.java
new file mode 100644
index 0000000000000000000000000000000000000000..9df9d09aa477d4cd3c496ba0933c816df1ef0964
index 0000000000000000000000000000000000000000..652ff54e7c50412503725d628bfe72ed03059790
--- /dev/null
+++ b/src/main/java/io/papermc/paper/ServerBuildInfo.java
@@ -0,0 +1,121 @@
@@ -0,0 +1,122 @@
+package io.papermc.paper;
+
+import java.time.Instant;
@ -75,11 +75,12 @@ index 0000000000000000000000000000000000000000..9df9d09aa477d4cd3c496ba0933c816d
+import net.kyori.adventure.key.Key;
+import net.kyori.adventure.util.Services;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * Information about the current server build.
+ */
+@NullMarked
+@ApiStatus.NonExtendable
+public interface ServerBuildInfo {
+ /**
@ -92,7 +93,7 @@ index 0000000000000000000000000000000000000000..9df9d09aa477d4cd3c496ba0933c816d
+ *
+ * @return the {@code ServerBuildInfo}
+ */
+ static @NotNull ServerBuildInfo buildInfo() {
+ static ServerBuildInfo buildInfo() {
+ //<editor-fold defaultstate="collapsed" desc="Holder">
+ final class Holder {
+ static final Optional<ServerBuildInfo> INSTANCE = Services.service(ServerBuildInfo.class);
@ -106,7 +107,7 @@ index 0000000000000000000000000000000000000000..9df9d09aa477d4cd3c496ba0933c816d
+ *
+ * @return the brand id of the server (e.g. "papermc:paper")
+ */
+ @NotNull Key brandId();
+ Key brandId();
+
+ /**
+ * Checks if the current server supports the specified brand.
@ -115,56 +116,56 @@ index 0000000000000000000000000000000000000000..9df9d09aa477d4cd3c496ba0933c816d
+ * @return {@code true} if the server supports the specified brand
+ */
+ @ApiStatus.Experimental
+ boolean isBrandCompatible(final @NotNull Key brandId);
+ boolean isBrandCompatible(final Key brandId);
+
+ /**
+ * Gets the brand name of the server.
+ *
+ * @return the brand name of the server (e.g. "Paper")
+ */
+ @NotNull String brandName();
+ String brandName();
+
+ /**
+ * Gets the Minecraft version id.
+ *
+ * @return the Minecraft version id (e.g. "1.20.4", "1.20.2-pre2", "23w31a")
+ */
+ @NotNull String minecraftVersionId();
+ String minecraftVersionId();
+
+ /**
+ * Gets the Minecraft version name.
+ *
+ * @return the Minecraft version name (e.g. "1.20.4", "1.20.2 Pre-release 2", "23w31a")
+ */
+ @NotNull String minecraftVersionName();
+ String minecraftVersionName();
+
+ /**
+ * Gets the build number.
+ *
+ * @return the build number
+ */
+ @NotNull OptionalInt buildNumber();
+ OptionalInt buildNumber();
+
+ /**
+ * Gets the build time.
+ *
+ * @return the build time
+ */
+ @NotNull Instant buildTime();
+ Instant buildTime();
+
+ /**
+ * Gets the git commit branch.
+ *
+ * @return the git commit branch
+ */
+ @NotNull Optional<String> gitBranch();
+ Optional<String> gitBranch();
+
+ /**
+ * Gets the git commit hash.
+ *
+ * @return the git commit hash
+ */
+ @NotNull Optional<String> gitCommit();
+ Optional<String> gitCommit();
+
+ /**
+ * Creates a string representation of the server build information.
@ -172,7 +173,7 @@ index 0000000000000000000000000000000000000000..9df9d09aa477d4cd3c496ba0933c816d
+ * @param representation the type of representation
+ * @return a string
+ */
+ @NotNull String asString(final @NotNull StringRepresentation representation);
+ String asString(final StringRepresentation representation);
+
+ /**
+ * String representation types.
@ -190,10 +191,10 @@ index 0000000000000000000000000000000000000000..9df9d09aa477d4cd3c496ba0933c816d
+}
diff --git a/src/main/java/io/papermc/paper/util/JarManifests.java b/src/main/java/io/papermc/paper/util/JarManifests.java
new file mode 100644
index 0000000000000000000000000000000000000000..909617079db61b675cc7b60b44ef96b306076343
index 0000000000000000000000000000000000000000..7915a70d676b1205dcae39259f670af258a1ab9b
--- /dev/null
+++ b/src/main/java/io/papermc/paper/util/JarManifests.java
@@ -0,0 +1,37 @@
@@ -0,0 +1,38 @@
+package io.papermc.paper.util;
+
+import java.io.IOException;
@ -204,9 +205,10 @@ index 0000000000000000000000000000000000000000..909617079db61b675cc7b60b44ef96b3
+import java.util.WeakHashMap;
+import java.util.jar.Manifest;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jspecify.annotations.NullMarked;
+import org.jspecify.annotations.Nullable;
+
+@NullMarked
+@ApiStatus.Internal
+public final class JarManifests {
+ private JarManifests() {
@ -214,7 +216,7 @@ index 0000000000000000000000000000000000000000..909617079db61b675cc7b60b44ef96b3
+
+ private static final Map<ClassLoader, Manifest> MANIFESTS = Collections.synchronizedMap(new WeakHashMap<>());
+
+ public static @Nullable Manifest manifest(final @NotNull Class<?> clazz) {
+ public static @Nullable Manifest manifest(final Class<?> clazz) {
+ return MANIFESTS.computeIfAbsent(clazz.getClassLoader(), classLoader -> {
+ final String classLocation = "/" + clazz.getName().replace(".", "/") + ".class";
+ final URL resource = clazz.getResource(classLocation);

View file

@ -12,14 +12,13 @@ Provides methods to determine players looted state for an object
diff --git a/src/main/java/com/destroystokyo/paper/loottable/LootableBlockInventory.java b/src/main/java/com/destroystokyo/paper/loottable/LootableBlockInventory.java
new file mode 100644
index 0000000000000000000000000000000000000000..08e82b9de34c5ce8c5e83631b1229e90e5aa9694
index 0000000000000000000000000000000000000000..6a03252d66a3e13c1960568ea23f6dcc673f34af
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/loottable/LootableBlockInventory.java
@@ -0,0 +1,18 @@
@@ -0,0 +1,17 @@
+package com.destroystokyo.paper.loottable;
+
+import org.bukkit.block.Block;
+import org.jetbrains.annotations.NotNull;
+import org.jspecify.annotations.NullMarked;
+
+/**
@ -36,14 +35,13 @@ index 0000000000000000000000000000000000000000..08e82b9de34c5ce8c5e83631b1229e90
+}
diff --git a/src/main/java/com/destroystokyo/paper/loottable/LootableEntityInventory.java b/src/main/java/com/destroystokyo/paper/loottable/LootableEntityInventory.java
new file mode 100644
index 0000000000000000000000000000000000000000..a1e1a0256010f293e7dfa63c6622e9125eb4cc73
index 0000000000000000000000000000000000000000..31ca54dea65dc0363a0ff7991ba5be3b06533876
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/loottable/LootableEntityInventory.java
@@ -0,0 +1,18 @@
@@ -0,0 +1,17 @@
+package com.destroystokyo.paper.loottable;
+
+import org.bukkit.entity.Entity;
+import org.jetbrains.annotations.NotNull;
+import org.jspecify.annotations.NullMarked;
+
+/**
@ -60,17 +58,17 @@ index 0000000000000000000000000000000000000000..a1e1a0256010f293e7dfa63c6622e912
+}
diff --git a/src/main/java/com/destroystokyo/paper/loottable/LootableInventory.java b/src/main/java/com/destroystokyo/paper/loottable/LootableInventory.java
new file mode 100644
index 0000000000000000000000000000000000000000..b085e1217838012e4f4c6bcce100d8282190cdbc
index 0000000000000000000000000000000000000000..9fb621200fb3969e8e440341e906acc3547b4039
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/loottable/LootableInventory.java
@@ -0,0 +1,129 @@
@@ -0,0 +1,128 @@
+package com.destroystokyo.paper.loottable;
+
+import java.util.UUID;
+import org.bukkit.entity.Player;
+import org.bukkit.loot.Lootable;
+import org.jetbrains.annotations.Nullable;
+import org.jspecify.annotations.NullMarked;
+import org.jspecify.annotations.Nullable;
+
+/**
+ * Represents an Inventory that contains a Loot Table associated to it that will
@ -141,8 +139,7 @@ index 0000000000000000000000000000000000000000..b085e1217838012e4f4c6bcce100d828
+ * @param player The player to check
+ * @return Timestamp last looted, or null if player has not looted this object
+ */
+ @Nullable
+ Long getLastLooted(UUID player);
+ @Nullable Long getLastLooted(UUID player);
+
+ /**
+ * Change the state of whether a player has looted this block

View file

@ -6,41 +6,44 @@ Subject: [PATCH] Misc Utils
diff --git a/src/main/java/com/destroystokyo/paper/utils/CachedSizeConcurrentLinkedQueue.java b/src/main/java/com/destroystokyo/paper/utils/CachedSizeConcurrentLinkedQueue.java
new file mode 100644
index 0000000000000000000000000000000000000000..5bb677ce585b856b3d3e589e29786a29619c56a7
index 0000000000000000000000000000000000000000..ebaa12ecacd169f00e184fed95720d047eda8b9d
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/utils/CachedSizeConcurrentLinkedQueue.java
@@ -0,0 +1,34 @@
@@ -0,0 +1,37 @@
+package com.destroystokyo.paper.utils;
+
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.atomic.LongAdder;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.annotations.ApiStatus;
+import org.jspecify.annotations.NullMarked;
+import org.jspecify.annotations.Nullable;
+
+@NullMarked
+@ApiStatus.Internal
+public class CachedSizeConcurrentLinkedQueue<E> extends ConcurrentLinkedQueue<E> {
+
+ private final LongAdder cachedSize = new LongAdder();
+
+ @Override
+ public boolean add(@NotNull E e) {
+ boolean result = super.add(e);
+ public boolean add(final E e) {
+ final boolean result = super.add(e);
+ if (result) {
+ cachedSize.increment();
+ this.cachedSize.increment();
+ }
+ return result;
+ }
+
+ @Nullable
+ @Override
+ public E poll() {
+ E result = super.poll();
+ public @Nullable E poll() {
+ final E result = super.poll();
+ if (result != null) {
+ cachedSize.decrement();
+ this.cachedSize.decrement();
+ }
+ return result;
+ }
+
+ @Override
+ public int size() {
+ return cachedSize.intValue();
+ return this.cachedSize.intValue();
+ }
+}

View file

@ -6,10 +6,10 @@ Subject: [PATCH] PlayerAttemptPickupItemEvent
diff --git a/src/main/java/org/bukkit/event/player/PlayerAttemptPickupItemEvent.java b/src/main/java/org/bukkit/event/player/PlayerAttemptPickupItemEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..13e4b1309ea9965a07fc8b276d5a7e606e205824
index 0000000000000000000000000000000000000000..eff29875f6282d8bd04a33cb572c90090742d3b2
--- /dev/null
+++ b/src/main/java/org/bukkit/event/player/PlayerAttemptPickupItemEvent.java
@@ -0,0 +1,96 @@
@@ -0,0 +1,94 @@
+package org.bukkit.event.player;
+
+import org.bukkit.entity.Item;
@ -17,16 +17,17 @@ index 0000000000000000000000000000000000000000..13e4b1309ea9965a07fc8b276d5a7e60
+import org.bukkit.event.Cancellable;
+import org.bukkit.event.HandlerList;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * Thrown when a player attempts to pick an item up from the ground
+ */
+@NullMarked
+public class PlayerAttemptPickupItemEvent extends PlayerEvent implements Cancellable {
+
+ private static final HandlerList HANDLER_LIST = new HandlerList();
+
+ @NotNull private final Item item;
+ private final Item item;
+ private final int remaining;
+ private boolean flyAtPlayer = true;
+
@ -34,12 +35,12 @@ index 0000000000000000000000000000000000000000..13e4b1309ea9965a07fc8b276d5a7e60
+
+ @Deprecated // Remove in 1.13 // Remove in 1.14?
+ @ApiStatus.Internal
+ public PlayerAttemptPickupItemEvent(@NotNull final Player player, @NotNull final Item item) {
+ public PlayerAttemptPickupItemEvent(final Player player, final Item item) {
+ this(player, item, 0);
+ }
+
+ @ApiStatus.Internal
+ public PlayerAttemptPickupItemEvent(@NotNull final Player player, @NotNull final Item item, final int remaining) {
+ public PlayerAttemptPickupItemEvent(final Player player, final Item item, final int remaining) {
+ super(player);
+ this.item = item;
+ this.remaining = remaining;
@ -50,7 +51,6 @@ index 0000000000000000000000000000000000000000..13e4b1309ea9965a07fc8b276d5a7e60
+ *
+ * @return Item
+ */
+ @NotNull
+ public Item getItem() {
+ return this.item;
+ }
@ -95,13 +95,11 @@ index 0000000000000000000000000000000000000000..13e4b1309ea9965a07fc8b276d5a7e60
+ this.flyAtPlayer = !cancel;
+ }
+
+ @NotNull
+ @Override
+ public HandlerList getHandlers() {
+ return HANDLER_LIST;
+ }
+
+ @NotNull
+ public static HandlerList getHandlerList() {
+ return HANDLER_LIST;
+ }

View file

@ -6,10 +6,10 @@ Subject: [PATCH] Add UnknownCommandEvent
diff --git a/src/main/java/org/bukkit/event/command/UnknownCommandEvent.java b/src/main/java/org/bukkit/event/command/UnknownCommandEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..9bdeeecdb6021d61fd9141270011e56b06a58a76
index 0000000000000000000000000000000000000000..58fe8f60ad5e7ca0ffddebb7ba5748bbfb129ddd
--- /dev/null
+++ b/src/main/java/org/bukkit/event/command/UnknownCommandEvent.java
@@ -0,0 +1,110 @@
@@ -0,0 +1,105 @@
+package org.bukkit.event.command;
+
+import net.kyori.adventure.text.Component;
@ -19,22 +19,23 @@ index 0000000000000000000000000000000000000000..9bdeeecdb6021d61fd9141270011e56b
+import org.bukkit.event.HandlerList;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jspecify.annotations.NullMarked;
+import org.jspecify.annotations.Nullable;
+
+/**
+ * Thrown when a player executes a command that is not defined
+ */
+@NullMarked
+public class UnknownCommandEvent extends Event {
+
+ private static final HandlerList HANDLER_LIST = new HandlerList();
+
+ @NotNull private final CommandSender sender;
+ @NotNull private final String commandLine;
+ @Nullable private Component message;
+ private final CommandSender sender;
+ private final String commandLine;
+ private @Nullable Component message;
+
+ @ApiStatus.Internal
+ public UnknownCommandEvent(@NotNull final CommandSender sender, @NotNull final String commandLine, @Nullable final Component message) {
+ public UnknownCommandEvent(final CommandSender sender, final String commandLine, final @Nullable Component message) {
+ super(false);
+ this.sender = sender;
+ this.commandLine = commandLine;
@ -46,7 +47,6 @@ index 0000000000000000000000000000000000000000..9bdeeecdb6021d61fd9141270011e56b
+ *
+ * @return Sender of the command
+ */
+ @NotNull
+ public CommandSender getSender() {
+ return this.sender;
+ }
@ -56,7 +56,6 @@ index 0000000000000000000000000000000000000000..9bdeeecdb6021d61fd9141270011e56b
+ *
+ * @return Command sent
+ */
+ @NotNull
+ public String getCommandLine() {
+ return this.commandLine;
+ }
@ -67,9 +66,8 @@ index 0000000000000000000000000000000000000000..9bdeeecdb6021d61fd9141270011e56b
+ * @return Unknown command message
+ * @deprecated use {@link #message()}
+ */
+ @Nullable
+ @Deprecated
+ public String getMessage() {
+ public @Nullable String getMessage() {
+ return this.message == null ? null : LegacyComponentSerializer.legacySection().serialize(this.message);
+ }
+
@ -91,9 +89,8 @@ index 0000000000000000000000000000000000000000..9bdeeecdb6021d61fd9141270011e56b
+ *
+ * @return Unknown command message
+ */
+ @Nullable
+ @Contract(pure = true)
+ public Component message() {
+ public @Nullable Component message() {
+ return this.message;
+ }
+
@ -108,13 +105,11 @@ index 0000000000000000000000000000000000000000..9bdeeecdb6021d61fd9141270011e56b
+ this.message = message;
+ }
+
+ @NotNull
+ @Override
+ public HandlerList getHandlers() {
+ return HANDLER_LIST;
+ }
+
+ @NotNull
+ public static HandlerList getHandlerList() {
+ return HANDLER_LIST;
+ }

View file

@ -7,29 +7,30 @@ Provides basic elements of a PlayerProfile to be used by future API/events
diff --git a/src/main/java/com/destroystokyo/paper/profile/PlayerProfile.java b/src/main/java/com/destroystokyo/paper/profile/PlayerProfile.java
new file mode 100644
index 0000000000000000000000000000000000000000..b4f9ffbebab8eef99dbd81c816c16c274a1ec4cd
index 0000000000000000000000000000000000000000..324c1cba46c9eef95cc22ffa231b04f9298a5e00
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/profile/PlayerProfile.java
@@ -0,0 +1,234 @@
@@ -0,0 +1,246 @@
+package com.destroystokyo.paper.profile;
+
+import java.util.Collection;
+import java.util.Set;
+import java.util.UUID;
+
+import java.util.concurrent.CompletableFuture;
+import org.bukkit.profile.PlayerTextures;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jspecify.annotations.NullMarked;
+import org.jspecify.annotations.Nullable;
+
+/**
+ * Represents a players profile for the game, such as UUID, Name, and textures.
+ */
+@NullMarked
+public interface PlayerProfile extends org.bukkit.profile.PlayerProfile {
+
+ /**
+ * @return The players name, if set
+ */
+ @Override
+ @Nullable
+ String getName();
+
@ -39,14 +40,14 @@ index 0000000000000000000000000000000000000000..b4f9ffbebab8eef99dbd81c816c16c27
+ * @param name The new Name
+ * @return The previous Name
+ */
+ @NotNull
+ @Deprecated(forRemoval = true, since = "1.18.1")
+ String setName(@Nullable String name);
+
+ /**
+ * @return The players unique identifier, if set
+ */
+ @Nullable UUID getId();
+ @Nullable
+ UUID getId();
+
+ /**
+ * Sets this profiles UUID
@ -54,8 +55,8 @@ index 0000000000000000000000000000000000000000..b4f9ffbebab8eef99dbd81c816c16c27
+ * @param uuid The new UUID
+ * @return The previous UUID
+ */
+ @Nullable
+ @Deprecated(forRemoval = true, since = "1.18.1")
+ @Nullable
+ UUID setId(@Nullable UUID uuid);
+
+ /**
@ -65,7 +66,7 @@ index 0000000000000000000000000000000000000000..b4f9ffbebab8eef99dbd81c816c16c27
+ *
+ * @return the textures, not <code>null</code>
+ */
+ @NotNull
+ @Override
+ PlayerTextures getTextures();
+
+ /**
@ -74,16 +75,18 @@ index 0000000000000000000000000000000000000000..b4f9ffbebab8eef99dbd81c816c16c27
+ * @param textures the textures to copy, or <code>null</code> to clear the
+ * textures
+ */
+ @Override
+ void setTextures(@Nullable PlayerTextures textures);
+
+ /**
+ * @return A Mutable set of this players properties, such as textures.
+ * Values specified here are subject to implementation details.
+ */
+ @NotNull Set<ProfileProperty> getProperties();
+ Set<ProfileProperty> getProperties();
+
+ /**
+ * Check if the Profile has the specified property
+ *
+ * @param property Property name to check
+ * @return If the property is set
+ */
@ -95,17 +98,19 @@ index 0000000000000000000000000000000000000000..b4f9ffbebab8eef99dbd81c816c16c27
+ * @param property Property to set.
+ * @throws IllegalArgumentException if setting the property results in more than 16 properties
+ */
+ void setProperty(@NotNull ProfileProperty property);
+ void setProperty(ProfileProperty property);
+
+ /**
+ * Sets multiple properties. If any of the set properties already exist, it will be replaced
+ *
+ * @param properties The properties to set
+ * @throws IllegalArgumentException if the number of properties exceeds 16
+ */
+ void setProperties(@NotNull Collection<ProfileProperty> properties);
+ void setProperties(Collection<ProfileProperty> properties);
+
+ /**
+ * Removes a specific property from this profile
+ *
+ * @param property The property to remove
+ * @return If a property was removed
+ */
@ -113,22 +118,24 @@ index 0000000000000000000000000000000000000000..b4f9ffbebab8eef99dbd81c816c16c27
+
+ /**
+ * Removes a specific property from this profile
+ *
+ * @param property The property to remove
+ * @return If a property was removed
+ */
+ default boolean removeProperty(@NotNull ProfileProperty property) {
+ return removeProperty(property.getName());
+ default boolean removeProperty(final ProfileProperty property) {
+ return this.removeProperty(property.getName());
+ }
+
+ /**
+ * Removes all properties in the collection
+ *
+ * @param properties The properties to remove
+ * @return If any property was removed
+ */
+ default boolean removeProperties(@NotNull Collection<ProfileProperty> properties) {
+ default boolean removeProperties(final Collection<ProfileProperty> properties) {
+ boolean removed = false;
+ for (ProfileProperty property : properties) {
+ if (removeProperty(property)) {
+ for (final ProfileProperty property : properties) {
+ if (this.removeProperty(property)) {
+ removed = true;
+ }
+ }
@ -143,6 +150,7 @@ index 0000000000000000000000000000000000000000..b4f9ffbebab8eef99dbd81c816c16c27
+ /**
+ * @return If the profile is now complete (has UUID and Name)
+ */
+ @Override
+ boolean isComplete();
+
+ /**
@ -175,21 +183,23 @@ index 0000000000000000000000000000000000000000..b4f9ffbebab8eef99dbd81c816c16c27
+ /**
+ * If this profile is not complete, then make the API call to complete it.
+ * This is a blocking operation and should be done asynchronously.
+ *
+ * <p>
+ * This will also complete textures. If you do not want to load textures, use {{@link #complete(boolean)}}
+ *
+ * @return If the profile is now complete (has UUID and Name) (if you get rate limited, this operation may fail)
+ */
+ default boolean complete() {
+ return complete(true);
+ return this.complete(true);
+ }
+
+ /**
+ * If this profile is not complete, then make the API call to complete it.
+ * This is a blocking operation and should be done asynchronously.
+ *
+ * <p>
+ * Optionally will also fill textures.
+ *
+ * <p>
+ * Online mode will be automatically determined
+ *
+ * @param textures controls if we should fill the profile with texture properties
+ * @return If the profile is now complete (has UUID and Name) (if you get rate limited, this operation may fail)
+ */
@ -198,8 +208,9 @@ index 0000000000000000000000000000000000000000..b4f9ffbebab8eef99dbd81c816c16c27
+ /**
+ * If this profile is not complete, then make the API call to complete it.
+ * This is a blocking operation and should be done asynchronously.
+ *
+ * <p>
+ * Optionally will also fill textures.
+ *
+ * @param textures controls if we should fill the profile with texture properties
+ * @param onlineMode Treat this server as online mode or not
+ * @return If the profile is now complete (has UUID and Name) (if you get rate limited, this operation may fail)
@ -235,43 +246,45 @@ index 0000000000000000000000000000000000000000..b4f9ffbebab8eef99dbd81c816c16c27
+ * </pre>
+ */
+ @Override
+ @NotNull CompletableFuture<PlayerProfile> update();
+ CompletableFuture<PlayerProfile> update();
+
+ /**
+ * Whether this Profile has textures associated to it
+ *
+ * @return If it has a textures property
+ */
+ default boolean hasTextures() {
+ return hasProperty("textures");
+ return this.hasProperty("textures");
+ }
+}
diff --git a/src/main/java/com/destroystokyo/paper/profile/ProfileProperty.java b/src/main/java/com/destroystokyo/paper/profile/ProfileProperty.java
new file mode 100644
index 0000000000000000000000000000000000000000..8f6240484d12f01bb555972feb0937bc74399a64
index 0000000000000000000000000000000000000000..35341d8f1ac2d80f339084ef80d099a545027554
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/profile/ProfileProperty.java
@@ -0,0 +1,75 @@
@@ -0,0 +1,73 @@
+package com.destroystokyo.paper.profile;
+
+import com.google.common.base.Preconditions;
+
+import java.util.Objects;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jspecify.annotations.NullMarked;
+import org.jspecify.annotations.Nullable;
+
+/**
+ * Represents a property on a {@link PlayerProfile}
+ */
+@NullMarked
+public final class ProfileProperty {
+
+ private final String name;
+ private final String value;
+ private final String signature;
+ private final @Nullable String signature;
+
+ public ProfileProperty(@NotNull String name, @NotNull String value) {
+ public ProfileProperty(final String name, final String value) {
+ this(name, value, null);
+ }
+
+ public ProfileProperty(@NotNull String name, @NotNull String value, @Nullable String signature) {
+ public ProfileProperty(final String name, final String value, final @Nullable String signature) {
+ this.name = Preconditions.checkNotNull(name, "ProfileProperty name can not be null");
+ this.value = Preconditions.checkNotNull(value, "ProfileProperty value can not be null");
+ this.signature = signature;
@ -283,25 +296,22 @@ index 0000000000000000000000000000000000000000..8f6240484d12f01bb555972feb0937bc
+ /**
+ * @return The property name, ie "textures"
+ */
+ @NotNull
+ public String getName() {
+ return name;
+ return this.name;
+ }
+
+ /**
+ * @return The property value, likely to be base64 encoded
+ */
+ @NotNull
+ public String getValue() {
+ return value;
+ return this.value;
+ }
+
+ /**
+ * @return A signature from Mojang for signed properties
+ */
+ @Nullable
+ public String getSignature() {
+ return signature;
+ public @Nullable String getSignature() {
+ return this.signature;
+ }
+
+ /**
@ -312,18 +322,18 @@ index 0000000000000000000000000000000000000000..8f6240484d12f01bb555972feb0937bc
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ public boolean equals(final @Nullable Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ ProfileProperty that = (ProfileProperty) o;
+ return Objects.equals(name, that.name) &&
+ Objects.equals(value, that.value) &&
+ Objects.equals(signature, that.signature);
+ if (o == null || this.getClass() != o.getClass()) return false;
+ final ProfileProperty that = (ProfileProperty) o;
+ return Objects.equals(this.name, that.name) &&
+ Objects.equals(this.value, that.value) &&
+ Objects.equals(this.signature, that.signature);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(name);
+ return Objects.hash(this.name);
+ }
+}
diff --git a/src/main/java/org/bukkit/Bukkit.java b/src/main/java/org/bukkit/Bukkit.java

View file

@ -8,14 +8,16 @@ and access their saddle state separately from an interface shared with Armor.
diff --git a/src/main/java/org/bukkit/inventory/ArmoredHorseInventory.java b/src/main/java/org/bukkit/inventory/ArmoredHorseInventory.java
new file mode 100644
index 0000000000000000000000000000000000000000..163ffe8ff76ded6265d865901d5110fb6a56950d
index 0000000000000000000000000000000000000000..994f34ac2062c092c2b4e5ff364067482d19588c
--- /dev/null
+++ b/src/main/java/org/bukkit/inventory/ArmoredHorseInventory.java
@@ -0,0 +1,21 @@
@@ -0,0 +1,22 @@
+package org.bukkit.inventory;
+
+import org.jetbrains.annotations.Nullable;
+import org.jspecify.annotations.NullMarked;
+import org.jspecify.annotations.Nullable;
+
+@NullMarked
+public interface ArmoredHorseInventory extends AbstractHorseInventory {
+
+ /**
@ -23,8 +25,7 @@ index 0000000000000000000000000000000000000000..163ffe8ff76ded6265d865901d5110fb
+ *
+ * @return the armor item
+ */
+ @Nullable
+ ItemStack getArmor();
+ @Nullable ItemStack getArmor();
+
+ /**
+ * Sets the item in the horse's armor slot.
@ -74,10 +75,14 @@ index 2fa2c9d07ecbafaf2396d913af90f1f4d432b238..5ac1afb8a213fa0fe344db4730ecbc5d
* Gets the item in the llama's decor slot.
diff --git a/src/main/java/org/bukkit/inventory/SaddledHorseInventory.java b/src/main/java/org/bukkit/inventory/SaddledHorseInventory.java
new file mode 100644
index 0000000000000000000000000000000000000000..7944f26a3e2a92601c3be0e55c00c39cc16cf177
index 0000000000000000000000000000000000000000..0f508a8df87f1e23764152d00e02a3da5131f034
--- /dev/null
+++ b/src/main/java/org/bukkit/inventory/SaddledHorseInventory.java
@@ -0,0 +1,3 @@
@@ -0,0 +1,7 @@
+package org.bukkit.inventory;
+
+public interface SaddledHorseInventory extends AbstractHorseInventory {}
+import org.jspecify.annotations.NullMarked;
+
+@NullMarked
+public interface SaddledHorseInventory extends AbstractHorseInventory {
+}

View file

@ -9,10 +9,10 @@ Allows you to do dynamic whitelisting and change of kick message
diff --git a/src/main/java/com/destroystokyo/paper/event/profile/ProfileWhitelistVerifyEvent.java b/src/main/java/com/destroystokyo/paper/event/profile/ProfileWhitelistVerifyEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..31884c55d45931a313292df552b604d929a22586
index 0000000000000000000000000000000000000000..901efb61fdc02b3228cc25649926d691c4617512
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/event/profile/ProfileWhitelistVerifyEvent.java
@@ -0,0 +1,144 @@
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2017 - Daniel Ennis (Aikar) - MIT License
+ *
@ -44,6 +44,7 @@ index 0000000000000000000000000000000000000000..31884c55d45931a313292df552b604d9
+import org.bukkit.event.Event;
+import org.bukkit.event.HandlerList;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.Contract;
+import org.jspecify.annotations.NullMarked;
+import org.jspecify.annotations.Nullable;
+
@ -100,6 +101,7 @@ index 0000000000000000000000000000000000000000..31884c55d45931a313292df552b604d9
+ /**
+ * @return the currently planned message to send to the user if they are not whitelisted
+ */
+ @Contract(pure = true)
+ public @Nullable Component kickMessage() {
+ return this.kickMessage;
+ }

View file

@ -356,15 +356,12 @@ index 0000000000000000000000000000000000000000..0482ecf5b84ba8e0260679049f384f34
+}
diff --git a/src/main/java/io/papermc/paper/util/TransformingRandomAccessList.java b/src/main/java/io/papermc/paper/util/TransformingRandomAccessList.java
new file mode 100644
index 0000000000000000000000000000000000000000..6f560a51277ccbd46a9142cfa057d276118c1c7b
index 0000000000000000000000000000000000000000..488250fdcdc93ca1aba5042f63fb6286db518014
--- /dev/null
+++ b/src/main/java/io/papermc/paper/util/TransformingRandomAccessList.java
@@ -0,0 +1,169 @@
@@ -0,0 +1,172 @@
+package io.papermc.paper.util;
+
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.AbstractList;
+import java.util.Iterator;
+import java.util.List;
@ -372,6 +369,8 @@ index 0000000000000000000000000000000000000000..6f560a51277ccbd46a9142cfa057d276
+import java.util.RandomAccess;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import org.jetbrains.annotations.ApiStatus;
+import org.jspecify.annotations.NullMarked;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
@ -381,7 +380,10 @@ index 0000000000000000000000000000000000000000..6f560a51277ccbd46a9142cfa057d276
+ * @param <F> backing list element type
+ * @param <T> transformed list element type
+ */
+@NullMarked
+@ApiStatus.Internal
+public final class TransformingRandomAccessList<F, T> extends AbstractList<T> implements RandomAccess {
+
+ final List<F> fromList;
+ final Function<? super F, ? extends T> toFunction;
+ final Function<? super T, ? extends F> fromFunction;
@ -389,14 +391,14 @@ index 0000000000000000000000000000000000000000..6f560a51277ccbd46a9142cfa057d276
+ /**
+ * Create a new {@link TransformingRandomAccessList}.
+ *
+ * @param fromList backing list
+ * @param toFunction function mapping backing list element type to transformed list element type
+ * @param fromList backing list
+ * @param toFunction function mapping backing list element type to transformed list element type
+ * @param fromFunction function mapping transformed list element type to backing list element type
+ */
+ public TransformingRandomAccessList(
+ final @NonNull List<F> fromList,
+ final @NonNull Function<? super F, ? extends T> toFunction,
+ final @NonNull Function<? super T, ? extends F> fromFunction
+ final List<F> fromList,
+ final Function<? super F, ? extends T> toFunction,
+ final Function<? super T, ? extends F> fromFunction
+ ) {
+ this.fromList = checkNotNull(fromList);
+ this.toFunction = checkNotNull(toFunction);
@ -409,25 +411,25 @@ index 0000000000000000000000000000000000000000..6f560a51277ccbd46a9142cfa057d276
+ }
+
+ @Override
+ public T get(int index) {
+ public T get(final int index) {
+ return this.toFunction.apply(this.fromList.get(index));
+ }
+
+ @Override
+ public @NotNull Iterator<T> iterator() {
+ public Iterator<T> iterator() {
+ return this.listIterator();
+ }
+
+ @Override
+ public @NotNull ListIterator<T> listIterator(int index) {
+ return new TransformedListIterator<F, T>(this.fromList.listIterator(index)) {
+ public ListIterator<T> listIterator(final int index) {
+ return new TransformedListIterator<>(this.fromList.listIterator(index)) {
+ @Override
+ T transform(F from) {
+ T transform(final F from) {
+ return TransformingRandomAccessList.this.toFunction.apply(from);
+ }
+
+ @Override
+ F transformBack(T from) {
+ F transformBack(final T from) {
+ return TransformingRandomAccessList.this.fromFunction.apply(from);
+ }
+ };
@ -439,13 +441,13 @@ index 0000000000000000000000000000000000000000..6f560a51277ccbd46a9142cfa057d276
+ }
+
+ @Override
+ public boolean removeIf(Predicate<? super T> filter) {
+ public boolean removeIf(final Predicate<? super T> filter) {
+ checkNotNull(filter);
+ return this.fromList.removeIf(element -> filter.test(this.toFunction.apply(element)));
+ }
+
+ @Override
+ public T remove(int index) {
+ public T remove(final int index) {
+ return this.toFunction.apply(this.fromList.remove(index));
+ }
+
@ -455,19 +457,20 @@ index 0000000000000000000000000000000000000000..6f560a51277ccbd46a9142cfa057d276
+ }
+
+ @Override
+ public T set(int i, T t) {
+ public T set(final int i, final T t) {
+ return this.toFunction.apply(this.fromList.set(i, this.fromFunction.apply(t)));
+ }
+
+ @Override
+ public void add(int i, T t) {
+ public void add(final int i, final T t) {
+ this.fromList.add(i, this.fromFunction.apply(t));
+ }
+
+ static abstract class TransformedListIterator<F, T> implements ListIterator<T>, Iterator<T> {
+ abstract static class TransformedListIterator<F, T> implements ListIterator<T>, Iterator<T> {
+
+ final Iterator<F> backingIterator;
+
+ TransformedListIterator(ListIterator<F> backingIterator) {
+ TransformedListIterator(final ListIterator<F> backingIterator) {
+ this.backingIterator = checkNotNull((Iterator<F>) backingIterator);
+ }
+
@ -475,7 +478,7 @@ index 0000000000000000000000000000000000000000..6f560a51277ccbd46a9142cfa057d276
+ return cast(this.backingIterator);
+ }
+
+ static <A> ListIterator<A> cast(Iterator<A> iterator) {
+ static <A> ListIterator<A> cast(final Iterator<A> iterator) {
+ return (ListIterator<A>) iterator;
+ }
+
@ -500,12 +503,12 @@ index 0000000000000000000000000000000000000000..6f560a51277ccbd46a9142cfa057d276
+ }
+
+ @Override
+ public void set(T element) {
+ public void set(final T element) {
+ this.backingIterator().set(this.transformBack(element));
+ }
+
+ @Override
+ public void add(T element) {
+ public void add(final T element) {
+ this.backingIterator().add(this.transformBack(element));
+ }
+

View file

@ -12,18 +12,19 @@ Co-authored-by: SoSeDiK <mrsosedik@gmail.com>
diff --git a/src/main/java/io/papermc/paper/math/Rotations.java b/src/main/java/io/papermc/paper/math/Rotations.java
new file mode 100644
index 0000000000000000000000000000000000000000..0ac1618113699ac50b9c35294bf23fb9fb7cfbad
index 0000000000000000000000000000000000000000..2a8e1aa29ab87c69a02f72615fbf1374714f54cf
--- /dev/null
+++ b/src/main/java/io/papermc/paper/math/Rotations.java
@@ -0,0 +1,100 @@
@@ -0,0 +1,101 @@
+package io.papermc.paper.math;
+
+import org.jetbrains.annotations.NotNull;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * Rotations is an immutable object that stores rotations
+ * in degrees on each axis (X, Y, Z).
+ */
+@NullMarked
+public interface Rotations {
+
+ /**
@ -39,7 +40,7 @@ index 0000000000000000000000000000000000000000..0ac1618113699ac50b9c35294bf23fb9
+ * @param z the angle for the Z axis in degrees
+ * @return Rotations instance holding the provided rotations
+ */
+ static @NotNull Rotations ofDegrees(double x, double y, double z) {
+ static Rotations ofDegrees(final double x, final double y, final double z) {
+ return new RotationsImpl(x, y, z);
+ }
+
@ -71,7 +72,7 @@ index 0000000000000000000000000000000000000000..0ac1618113699ac50b9c35294bf23fb9
+ * @param x the angle in degrees
+ * @return the resultant Rotations
+ */
+ @NotNull Rotations withX(double x);
+ Rotations withX(double x);
+
+ /**
+ * Returns a new Rotations instance which is the result
@ -80,7 +81,7 @@ index 0000000000000000000000000000000000000000..0ac1618113699ac50b9c35294bf23fb9
+ * @param y the angle in degrees
+ * @return the resultant Rotations
+ */
+ @NotNull Rotations withY(double y);
+ Rotations withY(double y);
+
+ /**
+ * Returns a new Rotations instance which is the result
@ -89,7 +90,7 @@ index 0000000000000000000000000000000000000000..0ac1618113699ac50b9c35294bf23fb9
+ * @param z the angle in degrees
+ * @return the resultant Rotations
+ */
+ @NotNull Rotations withZ(double z);
+ Rotations withZ(double z);
+
+ /**
+ * Returns a new Rotations instance which is the result of adding
@ -100,7 +101,7 @@ index 0000000000000000000000000000000000000000..0ac1618113699ac50b9c35294bf23fb9
+ * @param z the angle to add to the Z axis in degrees
+ * @return the resultant Rotations
+ */
+ @NotNull Rotations add(double x, double y, double z);
+ Rotations add(double x, double y, double z);
+
+ /**
+ * Returns a new Rotations instance which is the result of subtracting
@ -111,40 +112,41 @@ index 0000000000000000000000000000000000000000..0ac1618113699ac50b9c35294bf23fb9
+ * @param z the angle to subtract from the Z axis in degrees
+ * @return the resultant Rotations
+ */
+ default @NotNull Rotations subtract(double x, double y, double z) {
+ return add(-x, -y, -z);
+ default Rotations subtract(final double x, final double y, final double z) {
+ return this.add(-x, -y, -z);
+ }
+
+}
diff --git a/src/main/java/io/papermc/paper/math/RotationsImpl.java b/src/main/java/io/papermc/paper/math/RotationsImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..53359ab4a6659bce895deef6a21cde848d3cadcd
index 0000000000000000000000000000000000000000..35b493b870240f2cb142ea0c3bc2a5b2a89af25b
--- /dev/null
+++ b/src/main/java/io/papermc/paper/math/RotationsImpl.java
@@ -0,0 +1,27 @@
@@ -0,0 +1,28 @@
+package io.papermc.paper.math;
+
+import org.jetbrains.annotations.NotNull;
+import org.jspecify.annotations.NullMarked;
+
+@NullMarked
+record RotationsImpl(double x, double y, double z) implements Rotations {
+
+ @Override
+ public @NotNull RotationsImpl withX(double x) {
+ return new RotationsImpl(x, y, z);
+ public RotationsImpl withX(final double x) {
+ return new RotationsImpl(x, this.y, this.z);
+ }
+
+ @Override
+ public @NotNull RotationsImpl withY(double y) {
+ return new RotationsImpl(x, y, z);
+ public RotationsImpl withY(final double y) {
+ return new RotationsImpl(this.x, y, this.z);
+ }
+
+ @Override
+ public @NotNull RotationsImpl withZ(double z) {
+ return new RotationsImpl(x, y, z);
+ public RotationsImpl withZ(final double z) {
+ return new RotationsImpl(this.x, this.y, z);
+ }
+
+ @Override
+ public @NotNull RotationsImpl add(double x, double y, double z) {
+ public RotationsImpl add(final double x, final double y, final double z) {
+ return new RotationsImpl(this.x + x, this.y + y, this.z + z);
+ }
+

View file

@ -6,17 +6,15 @@ Subject: [PATCH] Add villager reputation API
diff --git a/src/main/java/com/destroystokyo/paper/entity/villager/Reputation.java b/src/main/java/com/destroystokyo/paper/entity/villager/Reputation.java
new file mode 100644
index 0000000000000000000000000000000000000000..f16294ef3148f8671389fa097e3e369a046e48cf
index 0000000000000000000000000000000000000000..cbbf70507c2df922e75b686c36500f6f85f92db6
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/entity/villager/Reputation.java
@@ -0,0 +1,58 @@
@@ -0,0 +1,56 @@
+package com.destroystokyo.paper.entity.villager;
+
+import com.google.common.base.Preconditions;
+
+import java.util.EnumMap;
+import java.util.Map;
+import org.jetbrains.annotations.NotNull;
+import org.jspecify.annotations.NullMarked;
+
+/**
@ -31,7 +29,7 @@ index 0000000000000000000000000000000000000000..f16294ef3148f8671389fa097e3e369a
+ this(new EnumMap<>(ReputationType.class));
+ }
+
+ public Reputation(Map<ReputationType, Integer> reputation) {
+ public Reputation(final Map<ReputationType, Integer> reputation) {
+ Preconditions.checkNotNull(reputation, "reputation cannot be null");
+ this.reputation = reputation;
+ }
@ -42,7 +40,7 @@ index 0000000000000000000000000000000000000000..f16294ef3148f8671389fa097e3e369a
+ * @param type The {@link ReputationType type} of reputation to get.
+ * @return The value of the {@link ReputationType type}.
+ */
+ public int getReputation(ReputationType type) {
+ public int getReputation(final ReputationType type) {
+ Preconditions.checkNotNull(type, "the reputation type cannot be null");
+ return this.reputation.getOrDefault(type, 0);
+ }
@ -53,7 +51,7 @@ index 0000000000000000000000000000000000000000..f16294ef3148f8671389fa097e3e369a
+ * @param type The {@link ReputationType type} of reputation to set.
+ * @param value The value of the {@link ReputationType type}.
+ */
+ public void setReputation(ReputationType type, int value) {
+ public void setReputation(final ReputationType type, final int value) {
+ Preconditions.checkNotNull(type, "the reputation type cannot be null");
+ this.reputation.put(type, value);
+ }
@ -64,7 +62,7 @@ index 0000000000000000000000000000000000000000..f16294ef3148f8671389fa097e3e369a
+ * @param type The {@link ReputationType type} to check
+ * @return If there is a value for this {@link ReputationType type} set.
+ */
+ public boolean hasReputationSet(ReputationType type) {
+ public boolean hasReputationSet(final ReputationType type) {
+ return this.reputation.containsKey(type);
+ }
+}

View file

@ -6,17 +6,17 @@ Subject: [PATCH] Add moon phase API
diff --git a/src/main/java/io/papermc/paper/world/MoonPhase.java b/src/main/java/io/papermc/paper/world/MoonPhase.java
new file mode 100644
index 0000000000000000000000000000000000000000..df05153397b42930cd53d37b30824c7e5f008f7e
index 0000000000000000000000000000000000000000..0b24e1a92bba4fed0ca2d4336a3b8351a800f93a
--- /dev/null
+++ b/src/main/java/io/papermc/paper/world/MoonPhase.java
@@ -0,0 +1,36 @@
@@ -0,0 +1,35 @@
+package io.papermc.paper.world;
+
+import org.jetbrains.annotations.NotNull;
+
+import java.util.HashMap;
+import java.util.Map;
+import org.jspecify.annotations.NullMarked;
+
+@NullMarked
+public enum MoonPhase {
+ FULL_MOON(0L),
+ WANING_GIBBOUS(1L),
@ -29,20 +29,19 @@ index 0000000000000000000000000000000000000000..df05153397b42930cd53d37b30824c7e
+
+ private final long day;
+
+ MoonPhase(long day) {
+ MoonPhase(final long day) {
+ this.day = day;
+ }
+
+ private static final Map<Long, MoonPhase> BY_DAY = new HashMap<>();
+
+ static {
+ for (MoonPhase phase : values()) {
+ for (final MoonPhase phase : values()) {
+ BY_DAY.put(phase.day, phase);
+ }
+ }
+
+ @NotNull
+ public static MoonPhase getPhase(long day) {
+ public static MoonPhase getPhase(final long day) {
+ return BY_DAY.get(day % 8L);
+ }
+}

View file

@ -96,23 +96,24 @@ index 0000000000000000000000000000000000000000..f29e76a6b66ddfec12ddf8db6dcb2df6
+}
diff --git a/src/main/java/io/papermc/paper/registry/RegistryAccess.java b/src/main/java/io/papermc/paper/registry/RegistryAccess.java
new file mode 100644
index 0000000000000000000000000000000000000000..86ab67ff5023bf6adea80b02648b6f67476e30e5
index 0000000000000000000000000000000000000000..00a3a4a196808b4b5c84ecccbfb1ea0e3825146a
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/RegistryAccess.java
@@ -0,0 +1,49 @@
@@ -0,0 +1,50 @@
+package io.papermc.paper.registry;
+
+import org.bukkit.Keyed;
+import org.bukkit.Registry;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jspecify.annotations.NullMarked;
+import org.jspecify.annotations.Nullable;
+
+/**
+ * Used for accessing different {@link Registry} instances
+ * by a {@link RegistryKey}. Get the main instance of {@link RegistryAccess}
+ * with {@link RegistryAccess#registryAccess()}.
+ */
+@NullMarked
+@ApiStatus.NonExtendable
+public interface RegistryAccess {
+
@ -121,7 +122,7 @@ index 0000000000000000000000000000000000000000..86ab67ff5023bf6adea80b02648b6f67
+ *
+ * @return the RegistryAccess instance
+ */
+ static @NotNull RegistryAccess registryAccess() {
+ static RegistryAccess registryAccess() {
+ return RegistryAccessHolder.INSTANCE.orElseThrow(() -> new IllegalStateException("No RegistryAccess implementation found"));
+ }
+
@ -134,7 +135,7 @@ index 0000000000000000000000000000000000000000..86ab67ff5023bf6adea80b02648b6f67
+ * @deprecated use {@link #getRegistry(RegistryKey)} with keys from {@link RegistryKey}
+ */
+ @Deprecated(since = "1.20.6", forRemoval = true)
+ <T extends Keyed> @Nullable Registry<T> getRegistry(@NotNull Class<T> type);
+ <T extends Keyed> @Nullable Registry<T> getRegistry(Class<T> type);
+
+ /**
+ * Gets the registry with the specified key.
@ -147,7 +148,7 @@ index 0000000000000000000000000000000000000000..86ab67ff5023bf6adea80b02648b6f67
+ */
+ // Future note: We should have no trouble removing this generic qualifier when
+ // registry types no longer have to be "keyed" as it shouldn't break ABI or API.
+ <T extends Keyed> @NotNull Registry<T> getRegistry(@NotNull RegistryKey<T> registryKey);
+ <T extends Keyed> Registry<T> getRegistry(RegistryKey<T> registryKey);
+}
diff --git a/src/main/java/io/papermc/paper/registry/RegistryAccessHolder.java b/src/main/java/io/papermc/paper/registry/RegistryAccessHolder.java
new file mode 100644
@ -168,16 +169,16 @@ index 0000000000000000000000000000000000000000..b89e19c070f97c9662f1e16309446494
+ }
+}
diff --git a/src/main/java/io/papermc/paper/registry/RegistryKeyImpl.java b/src/main/java/io/papermc/paper/registry/RegistryKeyImpl.java
index 791813220b2504214b1adecc69093cd600fb0f8c..47fe5b0d5d031110c27210a0a256c260b35d9ba1 100644
index 80e3e64f47ac55a4978c9e5b430e2f2d1c871d1b..ac68dfce754dc7e014bb31bba32d9b246ffd411c 100644
--- a/src/main/java/io/papermc/paper/registry/RegistryKeyImpl.java
+++ b/src/main/java/io/papermc/paper/registry/RegistryKeyImpl.java
@@ -10,6 +10,17 @@ record RegistryKeyImpl<T>(@NotNull Key key) implements RegistryKey<T> {
@@ -12,6 +12,17 @@ record RegistryKeyImpl<T>(Key key) implements RegistryKey<T> {
static final Set<RegistryKey<?>> REGISTRY_KEYS = Sets.newIdentityHashSet();
+ // override equals and hashCode to this can be used to simulate an "identity" hashmap
+ @Override
+ public boolean equals(final Object obj) {
+ public boolean equals(final @Nullable Object obj) {
+ return obj == this;
+ }
+

View file

@ -7,23 +7,24 @@ Co-authored-by: Jake Potrebic <jake.m.potrebic@gmail.com>
diff --git a/src/main/java/io/papermc/paper/datapack/Datapack.java b/src/main/java/io/papermc/paper/datapack/Datapack.java
new file mode 100644
index 0000000000000000000000000000000000000000..233a31afa9673c9cb8d9eb52551425ff15f79661
index 0000000000000000000000000000000000000000..95039ec90f81993cb2e36f82b7d13e9e7a30220e
--- /dev/null
+++ b/src/main/java/io/papermc/paper/datapack/Datapack.java
@@ -0,0 +1,98 @@
@@ -0,0 +1,99 @@
+package io.papermc.paper.datapack;
+
+import java.util.Set;
+import net.kyori.adventure.text.Component;
+import org.bukkit.FeatureFlag;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.Unmodifiable;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * This is a snapshot of a datapack on the server. It
+ * won't be updated as datapacks are updated.
+ */
+@NullMarked
+public interface Datapack {
+
+ /**
@ -32,21 +33,21 @@ index 0000000000000000000000000000000000000000..233a31afa9673c9cb8d9eb52551425ff
+ * @return the name of the pack
+ */
+ @Contract(pure = true)
+ @NonNull String getName();
+ String getName();
+
+ /**
+ * Gets the title component of this datapack.
+ *
+ * @return the title
+ */
+ @NonNull Component getTitle();
+ Component getTitle();
+
+ /**
+ * Gets the description component of this datapack.
+ *
+ * @return the description
+ */
+ @NonNull Component getDescription();
+ Component getDescription();
+
+ /**
+ * Gets if this datapack is required to be enabled.
@ -60,14 +61,14 @@ index 0000000000000000000000000000000000000000..233a31afa9673c9cb8d9eb52551425ff
+ *
+ * @return the compatibility of the pack
+ */
+ @NonNull Compatibility getCompatibility();
+ Compatibility getCompatibility();
+
+ /**
+ * Gets the set of required features for this datapack.
+ *
+ * @return the set of required features
+ */
+ @NonNull @Unmodifiable Set<FeatureFlag> getRequiredFeatures();
+ @Unmodifiable Set<FeatureFlag> getRequiredFeatures();
+
+ /**
+ * Gets the enabled state of this pack.
@ -91,7 +92,7 @@ index 0000000000000000000000000000000000000000..233a31afa9673c9cb8d9eb52551425ff
+ *
+ * @return the pack source
+ */
+ @NonNull DatapackSource getSource();
+ DatapackSource getSource();
+
+ /**
+ * Computes the component vanilla Minecraft uses
@ -101,7 +102,7 @@ index 0000000000000000000000000000000000000000..233a31afa9673c9cb8d9eb52551425ff
+ * @return a new component
+ */
+ @Contract(pure = true, value = "-> new")
+ @NonNull Component computeDisplayName();
+ Component computeDisplayName();
+
+ enum Compatibility {
+ TOO_OLD,
@ -111,18 +112,18 @@ index 0000000000000000000000000000000000000000..233a31afa9673c9cb8d9eb52551425ff
+}
diff --git a/src/main/java/io/papermc/paper/datapack/DatapackManager.java b/src/main/java/io/papermc/paper/datapack/DatapackManager.java
new file mode 100644
index 0000000000000000000000000000000000000000..bbb81a8058a67fd554c781dbb4908434ad339655
index 0000000000000000000000000000000000000000..4f758a781612bb14c8f2ee41b2b6f40a074e6359
--- /dev/null
+++ b/src/main/java/io/papermc/paper/datapack/DatapackManager.java
@@ -0,0 +1,43 @@
+package io.papermc.paper.datapack;
+
+import org.checkerframework.checker.nullness.qual.NonNull;
+
+import java.util.Collection;
+import org.checkerframework.checker.nullness.qual.Nullable;
+import org.jetbrains.annotations.Unmodifiable;
+import org.jspecify.annotations.NullMarked;
+import org.jspecify.annotations.Nullable;
+
+@NullMarked
+public interface DatapackManager {
+
+ /**
@ -140,7 +141,7 @@ index 0000000000000000000000000000000000000000..bbb81a8058a67fd554c781dbb4908434
+ * @param name the name/id of the datapack
+ * @return the datapack, or null if not found
+ */
+ @Nullable Datapack getPack(@NonNull String name);
+ @Nullable Datapack getPack(String name);
+
+ /**
+ * Gets the available datapacks. May require calling {@link #refreshPacks()} before
@ -148,7 +149,7 @@ index 0000000000000000000000000000000000000000..bbb81a8058a67fd554c781dbb4908434
+ *
+ * @return all the packs known to the server
+ */
+ @NonNull @Unmodifiable Collection<Datapack> getPacks();
+ @Unmodifiable Collection<Datapack> getPacks();
+
+ /**
+ * Gets the enabled datapacks. May require calling {@link #refreshPacks()} before
@ -156,19 +157,22 @@ index 0000000000000000000000000000000000000000..bbb81a8058a67fd554c781dbb4908434
+ *
+ * @return all the packs which are currently enabled
+ */
+ @NonNull @Unmodifiable Collection<Datapack> getEnabledPacks();
+ @Unmodifiable Collection<Datapack> getEnabledPacks();
+}
diff --git a/src/main/java/io/papermc/paper/datapack/DatapackSource.java b/src/main/java/io/papermc/paper/datapack/DatapackSource.java
new file mode 100644
index 0000000000000000000000000000000000000000..1679cbd78920005475343092857e13906ab73f82
index 0000000000000000000000000000000000000000..b9b81cd974c2df501fef55bd8d9b78406c073038
--- /dev/null
+++ b/src/main/java/io/papermc/paper/datapack/DatapackSource.java
@@ -0,0 +1,17 @@
@@ -0,0 +1,20 @@
+package io.papermc.paper.datapack;
+
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * Source of a datapack.
+ */
+@NullMarked
+public sealed interface DatapackSource permits DatapackSourceImpl {
+
+ DatapackSource DEFAULT = create("default");
@ -183,15 +187,17 @@ index 0000000000000000000000000000000000000000..1679cbd78920005475343092857e1390
+}
diff --git a/src/main/java/io/papermc/paper/datapack/DatapackSourceImpl.java b/src/main/java/io/papermc/paper/datapack/DatapackSourceImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..3eb4d1df8187fdeab74948d261d9c8e03e55605c
index 0000000000000000000000000000000000000000..0dfd101f01d16cc38f21831ca873633453dc6c9e
--- /dev/null
+++ b/src/main/java/io/papermc/paper/datapack/DatapackSourceImpl.java
@@ -0,0 +1,12 @@
@@ -0,0 +1,14 @@
+package io.papermc.paper.datapack;
+
+import org.jetbrains.annotations.ApiStatus;
+import org.jspecify.annotations.NullMarked;
+
+@ApiStatus.Internal
+@NullMarked
+record DatapackSourceImpl(String name) implements DatapackSource {
+
+ @Override

View file

@ -16,19 +16,20 @@ Co-authored-by: TotalledZebra <Holappa57@gmail.com>
diff --git a/src/main/java/io/papermc/paper/entity/SchoolableFish.java b/src/main/java/io/papermc/paper/entity/SchoolableFish.java
new file mode 100644
index 0000000000000000000000000000000000000000..39ad7d283609d7e427a2ab35b6fad839e032fe92
index 0000000000000000000000000000000000000000..a16329a8d08c74ce58960ff9dcc0af098b8543e9
--- /dev/null
+++ b/src/main/java/io/papermc/paper/entity/SchoolableFish.java
@@ -0,0 +1,47 @@
+package io.papermc.paper.entity;
+
+import org.bukkit.entity.Fish;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jspecify.annotations.NullMarked;
+import org.jspecify.annotations.Nullable;
+
+/**
+ * Represents a fish that can school with other fish.
+ */
+@NullMarked
+public interface SchoolableFish extends Fish {
+
+ /**
@ -36,7 +37,7 @@ index 0000000000000000000000000000000000000000..39ad7d283609d7e427a2ab35b6fad839
+ *
+ * @param leader fish to follow
+ */
+ void startFollowing(@NotNull SchoolableFish leader);
+ void startFollowing(SchoolableFish leader);
+
+ /**
+ * Causes the fish to stop following their current
@ -63,8 +64,7 @@ index 0000000000000000000000000000000000000000..39ad7d283609d7e427a2ab35b6fad839
+ *
+ * @return following fish
+ */
+ @Nullable
+ SchoolableFish getSchoolLeader();
+ @Nullable SchoolableFish getSchoolLeader();
+
+}
diff --git a/src/main/java/org/bukkit/entity/AbstractHorse.java b/src/main/java/org/bukkit/entity/AbstractHorse.java

View file

@ -6,16 +6,17 @@ Subject: [PATCH] More CommandBlock API
diff --git a/src/main/java/io/papermc/paper/command/CommandBlockHolder.java b/src/main/java/io/papermc/paper/command/CommandBlockHolder.java
new file mode 100644
index 0000000000000000000000000000000000000000..cbca0e890a8e79c98555c94fb6487e7d181901aa
index 0000000000000000000000000000000000000000..8dc061e38a1728ec4d9dddcb3564ed1b1da746c8
--- /dev/null
+++ b/src/main/java/io/papermc/paper/command/CommandBlockHolder.java
@@ -0,0 +1,58 @@
@@ -0,0 +1,57 @@
+package io.papermc.paper.command;
+
+import net.kyori.adventure.text.Component;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jspecify.annotations.NullMarked;
+import org.jspecify.annotations.Nullable;
+
+@NullMarked
+public interface CommandBlockHolder {
+
+ /**
@ -25,7 +26,6 @@ index 0000000000000000000000000000000000000000..cbca0e890a8e79c98555c94fb6487e7d
+ *
+ * @return Command that this CommandBlock will run when activated.
+ */
+ @NotNull
+ String getCommand();
+
+ /**
@ -42,7 +42,6 @@ index 0000000000000000000000000000000000000000..cbca0e890a8e79c98555c94fb6487e7d
+ *
+ * @return the last output
+ */
+ @NotNull
+ Component lastOutput();
+
+ /**

View file

@ -7,10 +7,10 @@ Co-authored-by: Jake Potrebic <jake.m.potrebic@gmail.com>
diff --git a/src/main/java/io/papermc/paper/advancement/AdvancementDisplay.java b/src/main/java/io/papermc/paper/advancement/AdvancementDisplay.java
new file mode 100644
index 0000000000000000000000000000000000000000..19202e17f362fdffca472178b102d1336349590c
index 0000000000000000000000000000000000000000..59228f2e66e982feca77d9f962004ceacb648783
--- /dev/null
+++ b/src/main/java/io/papermc/paper/advancement/AdvancementDisplay.java
@@ -0,0 +1,166 @@
@@ -0,0 +1,160 @@
+package io.papermc.paper.advancement;
+
+import net.kyori.adventure.text.Component;
@ -20,8 +20,8 @@ index 0000000000000000000000000000000000000000..19202e17f362fdffca472178b102d133
+import net.kyori.adventure.util.Index;
+import org.bukkit.NamespacedKey;
+import org.bukkit.inventory.ItemStack;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jspecify.annotations.NullMarked;
+import org.jspecify.annotations.Nullable;
+
+/**
+ * Describes the display of an advancement.
@ -29,6 +29,7 @@ index 0000000000000000000000000000000000000000..19202e17f362fdffca472178b102d133
+ * The display is used in the chat, in the toast messages and the advancements
+ * screen.
+ */
+@NullMarked
+public interface AdvancementDisplay {
+
+ /**
@ -39,7 +40,6 @@ index 0000000000000000000000000000000000000000..19202e17f362fdffca472178b102d133
+ *
+ * @return the frame type
+ */
+ @NotNull
+ Frame frame();
+
+ /**
@ -47,7 +47,6 @@ index 0000000000000000000000000000000000000000..19202e17f362fdffca472178b102d133
+ *
+ * @return the title
+ */
+ @NotNull
+ Component title();
+
+ /**
@ -55,7 +54,6 @@ index 0000000000000000000000000000000000000000..19202e17f362fdffca472178b102d133
+ *
+ * @return the description
+ */
+ @NotNull
+ Component description();
+
+ /**
@ -63,7 +61,6 @@ index 0000000000000000000000000000000000000000..19202e17f362fdffca472178b102d133
+ *
+ * @return a copy of the icon
+ */
+ @NotNull
+ ItemStack icon();
+
+ /**
@ -104,8 +101,7 @@ index 0000000000000000000000000000000000000000..19202e17f362fdffca472178b102d133
+ *
+ * @return the background texture path
+ */
+ @Nullable
+ NamespacedKey backgroundPath();
+ @Nullable NamespacedKey backgroundPath();
+
+ /**
+ * Gets the formatted display name for this display. This
@ -115,7 +111,7 @@ index 0000000000000000000000000000000000000000..19202e17f362fdffca472178b102d133
+ * @return the display name
+ * @see org.bukkit.advancement.Advancement#displayName()
+ */
+ @NotNull Component displayName();
+ Component displayName();
+
+ /**
+ * Defines how the {@link #icon()} appears in the advancements screen and
@ -148,7 +144,7 @@ index 0000000000000000000000000000000000000000..19202e17f362fdffca472178b102d133
+ private final String name;
+ private final TextColor color;
+
+ Frame(String name, TextColor color) {
+ Frame(final String name, final TextColor color) {
+ this.name = name;
+ this.color = color;
+ }
@ -158,7 +154,6 @@ index 0000000000000000000000000000000000000000..19202e17f362fdffca472178b102d133
+ *
+ * @return the text color
+ */
+ @NotNull
+ public TextColor color() {
+ return this.color;
+ }
@ -171,7 +166,6 @@ index 0000000000000000000000000000000000000000..19202e17f362fdffca472178b102d133
+ * @return the toast message key
+ */
+ @Override
+ @NotNull
+ public String translationKey() {
+ return "advancements.toast." + this.name;
+ }

View file

@ -6,20 +6,21 @@ Subject: [PATCH] Bucketable API
diff --git a/src/main/java/io/papermc/paper/entity/Bucketable.java b/src/main/java/io/papermc/paper/entity/Bucketable.java
new file mode 100644
index 0000000000000000000000000000000000000000..84e360a16de956834a91142d45e4b5a0fe3d3a92
index 0000000000000000000000000000000000000000..a63c3d0fe50f5808215ed169b81269112f76ec1b
--- /dev/null
+++ b/src/main/java/io/papermc/paper/entity/Bucketable.java
@@ -0,0 +1,42 @@
@@ -0,0 +1,41 @@
+package io.papermc.paper.entity;
+
+import org.bukkit.Sound;
+import org.bukkit.entity.Entity;
+import org.bukkit.inventory.ItemStack;
+import org.jetbrains.annotations.NotNull;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * Represents an entity that can be bucketed.
+ */
+@NullMarked
+public interface Bucketable extends Entity {
+
+ /**
@ -41,7 +42,6 @@ index 0000000000000000000000000000000000000000..84e360a16de956834a91142d45e4b5a0
+ *
+ * @return bucket form
+ */
+ @NotNull
+ ItemStack getBaseBucketItem();
+
+ /**
@ -49,7 +49,6 @@ index 0000000000000000000000000000000000000000..84e360a16de956834a91142d45e4b5a0
+ * is picked up in a bucket.
+ * @return bucket pickup sound
+ */
+ @NotNull
+ Sound getPickupSound();
+}
diff --git a/src/main/java/org/bukkit/entity/Axolotl.java b/src/main/java/org/bukkit/entity/Axolotl.java

View file

@ -6,28 +6,26 @@ Subject: [PATCH] Custom Potion Mixes
diff --git a/src/main/java/io/papermc/paper/potion/PotionMix.java b/src/main/java/io/papermc/paper/potion/PotionMix.java
new file mode 100644
index 0000000000000000000000000000000000000000..3fc922ebf972418b84181cd02e68d8ef0efd739b
index 0000000000000000000000000000000000000000..01b2a7c7a6bf328b3f7c30db3be0bfb8156ebc89
--- /dev/null
+++ b/src/main/java/io/papermc/paper/potion/PotionMix.java
@@ -0,0 +1,105 @@
@@ -0,0 +1,103 @@
+package io.papermc.paper.potion;
+
+import java.util.Objects;
+import java.util.function.Predicate;
+import org.bukkit.Keyed;
+import org.bukkit.NamespacedKey;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.RecipeChoice;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Objects;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * Represents a potion mix made in a Brewing Stand.
+ */
+@ApiStatus.NonExtendable
+public class PotionMix implements Keyed {
+@NullMarked
+public final class PotionMix implements Keyed {
+
+ private final NamespacedKey key;
+ private final ItemStack result;
@ -42,7 +40,7 @@ index 0000000000000000000000000000000000000000..3fc922ebf972418b84181cd02e68d8ef
+ * @param input the input placed into the bottom 3 slots
+ * @param ingredient the ingredient placed into the top slot
+ */
+ public PotionMix(final @NotNull NamespacedKey key, final @NotNull ItemStack result, final @NotNull RecipeChoice input, final @NotNull RecipeChoice ingredient) {
+ public PotionMix(final NamespacedKey key, final ItemStack result, final RecipeChoice input, final RecipeChoice ingredient) {
+ this.key = key;
+ this.result = result;
+ this.input = input;
@ -57,12 +55,12 @@ index 0000000000000000000000000000000000000000..3fc922ebf972418b84181cd02e68d8ef
+ * @return a new RecipeChoice
+ */
+ @Contract(value = "_ -> new", pure = true)
+ public static @NotNull RecipeChoice createPredicateChoice(final @NotNull Predicate<? super ItemStack> stackPredicate) {
+ public static RecipeChoice createPredicateChoice(final Predicate<? super ItemStack> stackPredicate) {
+ return new PredicateRecipeChoice(stackPredicate);
+ }
+
+ @Override
+ public @NotNull NamespacedKey getKey() {
+ public NamespacedKey getKey() {
+ return this.key;
+ }
+
@ -71,7 +69,7 @@ index 0000000000000000000000000000000000000000..3fc922ebf972418b84181cd02e68d8ef
+ *
+ * @return the result itemstack
+ */
+ public @NotNull ItemStack getResult() {
+ public ItemStack getResult() {
+ return this.result;
+ }
+
@ -80,7 +78,7 @@ index 0000000000000000000000000000000000000000..3fc922ebf972418b84181cd02e68d8ef
+ *
+ * @return the bottom 3 slot ingredients
+ */
+ public @NotNull RecipeChoice getInput() {
+ public RecipeChoice getInput() {
+ return this.input;
+ }
+
@ -89,7 +87,7 @@ index 0000000000000000000000000000000000000000..3fc922ebf972418b84181cd02e68d8ef
+ *
+ * @return the top slot input
+ */
+ public @NotNull RecipeChoice getIngredient() {
+ public RecipeChoice getIngredient() {
+ return this.ingredient;
+ }
+
@ -117,21 +115,20 @@ index 0000000000000000000000000000000000000000..3fc922ebf972418b84181cd02e68d8ef
+}
diff --git a/src/main/java/io/papermc/paper/potion/PredicateRecipeChoice.java b/src/main/java/io/papermc/paper/potion/PredicateRecipeChoice.java
new file mode 100644
index 0000000000000000000000000000000000000000..3ede1e8f7bf0436fdc5bf395c0f9eaf11c252453
index 0000000000000000000000000000000000000000..c252b432a9df5fd7da71a5eecba37a8844820700
--- /dev/null
+++ b/src/main/java/io/papermc/paper/potion/PredicateRecipeChoice.java
@@ -0,0 +1,33 @@
@@ -0,0 +1,32 @@
+package io.papermc.paper.potion;
+
+import java.util.function.Predicate;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.RecipeChoice;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.framework.qual.DefaultQualifier;
+import org.jetbrains.annotations.ApiStatus;
+import org.jspecify.annotations.NullMarked;
+
+@ApiStatus.Internal
+@DefaultQualifier(NonNull.class)
+@NullMarked
+record PredicateRecipeChoice(Predicate<? super ItemStack> itemStackPredicate) implements RecipeChoice, Cloneable {
+
+ @Override
@ -155,7 +152,7 @@ index 0000000000000000000000000000000000000000..3ede1e8f7bf0436fdc5bf395c0f9eaf1
+ }
+}
diff --git a/src/main/java/org/bukkit/Bukkit.java b/src/main/java/org/bukkit/Bukkit.java
index aa1795b9640a5e39cc5063dd3c389f6d5815ed36..30cb0f2c4d53a7ad473812810c4f50173b7d7391 100644
index e2d3e42b403dce454988c3ae3e44bcd89337b1cf..22021582b2f490ea2db87f2d3fe8a99b44d4f457 100644
--- a/src/main/java/org/bukkit/Bukkit.java
+++ b/src/main/java/org/bukkit/Bukkit.java
@@ -2641,6 +2641,15 @@ public final class Bukkit {
@ -175,7 +172,7 @@ index aa1795b9640a5e39cc5063dd3c389f6d5815ed36..30cb0f2c4d53a7ad473812810c4f5017
@NotNull
diff --git a/src/main/java/org/bukkit/Server.java b/src/main/java/org/bukkit/Server.java
index 65060c06c1e5521656bd88547b8d0df5975c1d29..7c33c9489e0f3c4429e9bc30d87a3f4c29ca010f 100644
index 5dd7ce5c008c852dbeb0474a70e9357230406318..178e91f3ad918c1a5600d6e9a14a21d478f7e1df 100644
--- a/src/main/java/org/bukkit/Server.java
+++ b/src/main/java/org/bukkit/Server.java
@@ -2304,5 +2304,12 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi

View file

@ -6,19 +6,20 @@ Subject: [PATCH] Add EntityDyeEvent and CollarColorable interface
diff --git a/src/main/java/io/papermc/paper/entity/CollarColorable.java b/src/main/java/io/papermc/paper/entity/CollarColorable.java
new file mode 100644
index 0000000000000000000000000000000000000000..bb68e6a2528eee81eb3f26f22b9c35508f1e69c1
index 0000000000000000000000000000000000000000..205b561c76386c02c827694b17ea50175a3c84ff
--- /dev/null
+++ b/src/main/java/io/papermc/paper/entity/CollarColorable.java
@@ -0,0 +1,25 @@
@@ -0,0 +1,26 @@
+package io.papermc.paper.entity;
+
+import org.bukkit.DyeColor;
+import org.bukkit.entity.LivingEntity;
+import org.jetbrains.annotations.NotNull;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * Entities that can have their collars colored.
+ */
+@NullMarked
+public interface CollarColorable extends LivingEntity {
+
+ /**
@ -26,14 +27,14 @@ index 0000000000000000000000000000000000000000..bb68e6a2528eee81eb3f26f22b9c3550
+ *
+ * @return the color of the collar
+ */
+ @NotNull DyeColor getCollarColor();
+ DyeColor getCollarColor();
+
+ /**
+ * Set the collar color of this entity
+ *
+ * @param color the color to apply
+ */
+ void setCollarColor(@NotNull DyeColor color);
+ void setCollarColor(DyeColor color);
+}
diff --git a/src/main/java/io/papermc/paper/event/entity/EntityDyeEvent.java b/src/main/java/io/papermc/paper/event/entity/EntityDyeEvent.java
new file mode 100644

View file

@ -6,17 +6,18 @@ Subject: [PATCH] Add Moving Piston API
diff --git a/src/main/java/io/papermc/paper/block/MovingPiston.java b/src/main/java/io/papermc/paper/block/MovingPiston.java
new file mode 100644
index 0000000000000000000000000000000000000000..ee7c85abf66dc11920bb29f9ce3b407121a665ee
index 0000000000000000000000000000000000000000..962b92d487b3c442d2fe8b665ec3b2d375aaa9aa
--- /dev/null
+++ b/src/main/java/io/papermc/paper/block/MovingPiston.java
@@ -0,0 +1,43 @@
@@ -0,0 +1,42 @@
+package io.papermc.paper.block;
+
+import org.bukkit.block.BlockFace;
+import org.bukkit.block.TileState;
+import org.bukkit.block.data.BlockData;
+import org.jetbrains.annotations.NotNull;
+import org.jspecify.annotations.NullMarked;
+
+@NullMarked
+public interface MovingPiston extends TileState {
+
+ /**
@ -24,7 +25,6 @@ index 0000000000000000000000000000000000000000..ee7c85abf66dc11920bb29f9ce3b4071
+ *
+ * @return the pushed block
+ */
+ @NotNull
+ BlockData getMovingBlock();
+
+ /**
@ -33,7 +33,6 @@ index 0000000000000000000000000000000000000000..ee7c85abf66dc11920bb29f9ce3b4071
+ *
+ * @return the direction
+ */
+ @NotNull
+ BlockFace getDirection();
+
+ /**

View file

@ -6,46 +6,50 @@ Subject: [PATCH] Add Tick TemporalUnit
diff --git a/src/main/java/io/papermc/paper/util/Tick.java b/src/main/java/io/papermc/paper/util/Tick.java
new file mode 100644
index 0000000000000000000000000000000000000000..10430f02e1d1e654383154c04473f07469bc7fee
index 0000000000000000000000000000000000000000..1b264819c6faf2a4390d76350deb8e93804c6772
--- /dev/null
+++ b/src/main/java/io/papermc/paper/util/Tick.java
@@ -0,0 +1,95 @@
@@ -0,0 +1,101 @@
+package io.papermc.paper.util;
+
+import net.kyori.adventure.util.Ticks;
+import org.jetbrains.annotations.NotNull;
+
+import java.time.Duration;
+import java.time.temporal.ChronoUnit;
+import java.time.temporal.Temporal;
+import java.time.temporal.TemporalUnit;
+import java.util.Objects;
+import net.kyori.adventure.util.Ticks;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * A TemporalUnit that represents the target length of one server tick. This is defined
+ * as 50 milliseconds. Note that this class is not for measuring the length that a tick
+ * took, rather it is used for simple conversion between times and ticks.
+ *
+ * @see #tick()
+ */
+@NullMarked
+public final class Tick implements TemporalUnit {
+
+ private static final Tick INSTANCE = new Tick(Ticks.SINGLE_TICK_DURATION_MS);
+
+ private final long milliseconds;
+
+ /**
+ * Gets the instance of the tick temporal unit.
+ *
+ * @return the tick instance
+ */
+ public static @NotNull Tick tick() {
+ public static Tick tick() {
+ return INSTANCE;
+ }
+
+ /**
+ * Creates a new tick.
+ *
+ * @param length the length of the tick in milliseconds
+ * @see #tick()
+ */
+ private Tick(long length) {
+ private Tick(final long length) {
+ this.milliseconds = length;
+ }
+
@ -53,27 +57,29 @@ index 0000000000000000000000000000000000000000..10430f02e1d1e654383154c04473f074
+ * Creates a duration from an amount of ticks. This is shorthand for
+ * {@link Duration#of(long, TemporalUnit)} called with the amount of ticks and
+ * {@link #tick()}.
+ *
+ * @param ticks the amount of ticks
+ * @return the duration
+ */
+ public static @NotNull Duration of(long ticks) {
+ public static Duration of(final long ticks) {
+ return Duration.of(ticks, INSTANCE);
+ }
+
+ /**
+ * Gets the number of whole ticks that occur in the provided duration. Note that this
+ * method returns an {@code int} as this is the unit that Minecraft stores ticks in.
+ *
+ * @param duration the duration
+ * @return the number of whole ticks in this duration
+ * @throws ArithmeticException if the duration is zero or an overflow occurs
+ */
+ public int fromDuration(@NotNull Duration duration) {
+ public int fromDuration(final Duration duration) {
+ Objects.requireNonNull(duration, "duration cannot be null");
+ return Math.toIntExact(Math.floorDiv(duration.toMillis(), this.milliseconds));
+ }
+
+ @Override
+ public @NotNull Duration getDuration() {
+ public Duration getDuration() {
+ return Duration.ofMillis(this.milliseconds);
+ }
+
@ -96,12 +102,12 @@ index 0000000000000000000000000000000000000000..10430f02e1d1e654383154c04473f074
+
+ @SuppressWarnings("unchecked") // following ChronoUnit#addTo
+ @Override
+ public <R extends Temporal> @NotNull R addTo(@NotNull R temporal, long amount) {
+ return (R) temporal.plus(getDuration().multipliedBy(amount));
+ public <R extends Temporal> R addTo(final R temporal, final long amount) {
+ return (R) temporal.plus(this.getDuration().multipliedBy(amount));
+ }
+
+ @Override
+ public long between(@NotNull Temporal start, @NotNull Temporal end) {
+ public long between(final Temporal start, final Temporal end) {
+ return start.until(end, ChronoUnit.MILLIS) / this.milliseconds;
+ }
+}

View file

@ -6,7 +6,7 @@ Subject: [PATCH] Friction API
diff --git a/src/main/java/io/papermc/paper/entity/Frictional.java b/src/main/java/io/papermc/paper/entity/Frictional.java
new file mode 100644
index 0000000000000000000000000000000000000000..6eb88c664d873506372ad14075bfcbe42958126f
index 0000000000000000000000000000000000000000..315558d8a592f7238f45fd1e68b70476e721be92
--- /dev/null
+++ b/src/main/java/io/papermc/paper/entity/Frictional.java
@@ -0,0 +1,35 @@
@ -14,11 +14,12 @@ index 0000000000000000000000000000000000000000..6eb88c664d873506372ad14075bfcbe4
+
+import net.kyori.adventure.util.TriState;
+import org.bukkit.entity.Entity;
+import org.jetbrains.annotations.NotNull;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * Represents an {@link Entity} that can experience friction with the air and ground.
+ */
+@NullMarked
+public interface Frictional {
+
+ /**
@ -29,7 +30,6 @@ index 0000000000000000000000000000000000000000..6eb88c664d873506372ad14075bfcbe4
+ *
+ * @return the entity's friction state
+ */
+ @NotNull
+ TriState getFrictionState();
+
+ /**
@ -42,7 +42,7 @@ index 0000000000000000000000000000000000000000..6eb88c664d873506372ad14075bfcbe4
+ *
+ * @param state the new friction state to set for the entity
+ */
+ void setFrictionState(@NotNull TriState state);
+ void setFrictionState(TriState state);
+
+}
diff --git a/src/main/java/org/bukkit/entity/Item.java b/src/main/java/org/bukkit/entity/Item.java

View file

@ -6,19 +6,20 @@ Subject: [PATCH] Add Shearable API
diff --git a/src/main/java/io/papermc/paper/entity/Shearable.java b/src/main/java/io/papermc/paper/entity/Shearable.java
new file mode 100644
index 0000000000000000000000000000000000000000..0d5793790ab6a47525ad33033517361205756338
index 0000000000000000000000000000000000000000..3991f2aa530c588f52e1f596d3b03743e8a8ecc4
--- /dev/null
+++ b/src/main/java/io/papermc/paper/entity/Shearable.java
@@ -0,0 +1,43 @@
@@ -0,0 +1,44 @@
+package io.papermc.paper.entity;
+
+import net.kyori.adventure.sound.Sound;
+import org.bukkit.entity.Entity;
+import org.jetbrains.annotations.NotNull;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * Represents an entity that can be sheared.
+ */
+@NullMarked
+public interface Shearable extends Entity {
+
+ /**
@ -44,7 +45,7 @@ index 0000000000000000000000000000000000000000..0d5793790ab6a47525ad330335173612
+ *
+ * @param source Sound source to play any sound effects on
+ */
+ void shear(@NotNull Sound.Source source);
+ void shear(Sound.Source source);
+
+ /**
+ * Gets if the entity would be able to be sheared or not naturally using shears.

View file

@ -8,22 +8,20 @@ meant for managing resources across reloads and from points in the
PluginBootstrap.
diff --git a/src/main/java/io/papermc/paper/plugin/bootstrap/BootstrapContext.java b/src/main/java/io/papermc/paper/plugin/bootstrap/BootstrapContext.java
index 70d5f9802f90605a5120ff2a000a2e9395f0aecc..c319f3bbfabdd39ee571c59d7bfc144c58232abc 100644
index 4c47414fc08e1183b1e59369bacc4d7f7042f262..577a9d5aeae55a3b8452b6d873b51b30384c1fea 100644
--- a/src/main/java/io/papermc/paper/plugin/bootstrap/BootstrapContext.java
+++ b/src/main/java/io/papermc/paper/plugin/bootstrap/BootstrapContext.java
@@ -1,6 +1,9 @@
@@ -1,5 +1,7 @@
package io.papermc.paper.plugin.bootstrap;
+import io.papermc.paper.plugin.lifecycle.event.LifecycleEventManager;
+import io.papermc.paper.plugin.lifecycle.event.LifecycleEventOwner;
import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
import org.jspecify.annotations.NullMarked;
/**
* Represents the context provided to a {@link PluginBootstrap} during both the bootstrapping and plugin
@@ -10,5 +13,13 @@ import org.jetbrains.annotations.ApiStatus;
*/
@@ -12,5 +14,13 @@ import org.jspecify.annotations.NullMarked;
@ApiStatus.Experimental
@NullMarked
@ApiStatus.NonExtendable
-public interface BootstrapContext extends PluginProviderContext {
+public interface BootstrapContext extends PluginProviderContext, LifecycleEventOwner {
@ -34,7 +32,7 @@ index 70d5f9802f90605a5120ff2a000a2e9395f0aecc..c319f3bbfabdd39ee571c59d7bfc144c
+ *
+ * @return the lifecycle event manager
+ */
+ @NotNull LifecycleEventManager<BootstrapContext> getLifecycleManager();
+ LifecycleEventManager<BootstrapContext> getLifecycleManager();
}
diff --git a/src/main/java/io/papermc/paper/plugin/lifecycle/event/LifecycleEvent.java b/src/main/java/io/papermc/paper/plugin/lifecycle/event/LifecycleEvent.java
new file mode 100644
@ -61,17 +59,17 @@ index 0000000000000000000000000000000000000000..0b8eafd3e79494d4a750cd9182387fba
+}
diff --git a/src/main/java/io/papermc/paper/plugin/lifecycle/event/LifecycleEventManager.java b/src/main/java/io/papermc/paper/plugin/lifecycle/event/LifecycleEventManager.java
new file mode 100644
index 0000000000000000000000000000000000000000..3626ce3da17f20ec44f0c15baa13f40e1dc2bc9c
index 0000000000000000000000000000000000000000..e05cdb7ab166f92e270ea1b85e75f465878d05f2
--- /dev/null
+++ b/src/main/java/io/papermc/paper/plugin/lifecycle/event/LifecycleEventManager.java
@@ -0,0 +1,52 @@
@@ -0,0 +1,53 @@
+package io.papermc.paper.plugin.lifecycle.event;
+
+import io.papermc.paper.plugin.lifecycle.event.handler.LifecycleEventHandler;
+import io.papermc.paper.plugin.lifecycle.event.handler.configuration.LifecycleEventHandlerConfiguration;
+import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEventType;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * Manages a plugin's lifecycle events. Can be obtained
@ -80,6 +78,7 @@ index 0000000000000000000000000000000000000000..3626ce3da17f20ec44f0c15baa13f40e
+ * @param <O> the owning type, {@link org.bukkit.plugin.Plugin} or {@link io.papermc.paper.plugin.bootstrap.BootstrapContext}
+ */
+@ApiStatus.Experimental
+@NullMarked
+@ApiStatus.NonExtendable
+public interface LifecycleEventManager<O extends LifecycleEventOwner> {
+
@ -102,7 +101,7 @@ index 0000000000000000000000000000000000000000..3626ce3da17f20ec44f0c15baa13f40e
+ * @param eventHandler the handler for that event
+ * @param <E> the type of the event object
+ */
+ default <E extends LifecycleEvent> void registerEventHandler(final @NotNull LifecycleEventType<? super O, ? extends E, ?> eventType, final @NotNull LifecycleEventHandler<? super E> eventHandler) {
+ default <E extends LifecycleEvent> void registerEventHandler(final LifecycleEventType<? super O, ? extends E, ?> eventType, final LifecycleEventHandler<? super E> eventHandler) {
+ this.registerEventHandler(eventType.newHandler(eventHandler));
+ }
+
@ -115,19 +114,19 @@ index 0000000000000000000000000000000000000000..3626ce3da17f20ec44f0c15baa13f40e
+ *
+ * @param handlerConfiguration the handler configuration to register
+ */
+ void registerEventHandler(@NotNull LifecycleEventHandlerConfiguration<? super O> handlerConfiguration);
+ void registerEventHandler(LifecycleEventHandlerConfiguration<? super O> handlerConfiguration);
+}
diff --git a/src/main/java/io/papermc/paper/plugin/lifecycle/event/LifecycleEventOwner.java b/src/main/java/io/papermc/paper/plugin/lifecycle/event/LifecycleEventOwner.java
new file mode 100644
index 0000000000000000000000000000000000000000..1160474f94476b580426cec29756c4699e163bf7
index 0000000000000000000000000000000000000000..ce5891eb110464a1c0cd7416712110851d010a1b
--- /dev/null
+++ b/src/main/java/io/papermc/paper/plugin/lifecycle/event/LifecycleEventOwner.java
@@ -0,0 +1,24 @@
@@ -0,0 +1,25 @@
+package io.papermc.paper.plugin.lifecycle.event;
+
+import io.papermc.paper.plugin.configuration.PluginMeta;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * Implemented by types that are considered owners
@ -137,6 +136,7 @@ index 0000000000000000000000000000000000000000..1160474f94476b580426cec29756c469
+ * event handlers.
+ */
+@ApiStatus.Experimental
+@NullMarked
+@ApiStatus.NonExtendable
+public interface LifecycleEventOwner {
+
@ -145,19 +145,19 @@ index 0000000000000000000000000000000000000000..1160474f94476b580426cec29756c469
+ *
+ * @return the plugin meta
+ */
+ @NotNull PluginMeta getPluginMeta();
+ PluginMeta getPluginMeta();
+}
diff --git a/src/main/java/io/papermc/paper/plugin/lifecycle/event/handler/LifecycleEventHandler.java b/src/main/java/io/papermc/paper/plugin/lifecycle/event/handler/LifecycleEventHandler.java
new file mode 100644
index 0000000000000000000000000000000000000000..8239ba3c0147c0e8e8d28987d3f543a67641892a
index 0000000000000000000000000000000000000000..3093ef23dd92f86240854065f7a7bb6c11ecf4fe
--- /dev/null
+++ b/src/main/java/io/papermc/paper/plugin/lifecycle/event/handler/LifecycleEventHandler.java
@@ -0,0 +1,18 @@
@@ -0,0 +1,19 @@
+package io.papermc.paper.plugin.lifecycle.event.handler;
+
+import io.papermc.paper.plugin.lifecycle.event.LifecycleEvent;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * A handler for a specific event. Can be implemented
@ -166,22 +166,24 @@ index 0000000000000000000000000000000000000000..8239ba3c0147c0e8e8d28987d3f543a6
+ * @param <E> the event
+ */
+@ApiStatus.Experimental
+@NullMarked
+@FunctionalInterface
+public interface LifecycleEventHandler<E extends LifecycleEvent> {
+
+ void run(@NotNull E event);
+ void run(E event);
+}
diff --git a/src/main/java/io/papermc/paper/plugin/lifecycle/event/handler/configuration/LifecycleEventHandlerConfiguration.java b/src/main/java/io/papermc/paper/plugin/lifecycle/event/handler/configuration/LifecycleEventHandlerConfiguration.java
new file mode 100644
index 0000000000000000000000000000000000000000..0831794fad1f6eb8960225909d40f4a3b20a2a3b
index 0000000000000000000000000000000000000000..9b9f4655f222597b4e00519cfe128147bc438367
--- /dev/null
+++ b/src/main/java/io/papermc/paper/plugin/lifecycle/event/handler/configuration/LifecycleEventHandlerConfiguration.java
@@ -0,0 +1,18 @@
@@ -0,0 +1,20 @@
+package io.papermc.paper.plugin.lifecycle.event.handler.configuration;
+
+import io.papermc.paper.plugin.lifecycle.event.LifecycleEventOwner;
+import io.papermc.paper.plugin.lifecycle.event.handler.LifecycleEventHandler;
+import org.jetbrains.annotations.ApiStatus;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * Base type for constructing configured event handlers for
@ -192,20 +194,22 @@ index 0000000000000000000000000000000000000000..0831794fad1f6eb8960225909d40f4a3
+ */
+@SuppressWarnings("unused")
+@ApiStatus.Experimental
+@NullMarked
+@ApiStatus.NonExtendable
+public interface LifecycleEventHandlerConfiguration<O extends LifecycleEventOwner> {
+}
diff --git a/src/main/java/io/papermc/paper/plugin/lifecycle/event/handler/configuration/MonitorLifecycleEventHandlerConfiguration.java b/src/main/java/io/papermc/paper/plugin/lifecycle/event/handler/configuration/MonitorLifecycleEventHandlerConfiguration.java
new file mode 100644
index 0000000000000000000000000000000000000000..d307ede51a66279f2eeef4e5b41c71779503f0d4
index 0000000000000000000000000000000000000000..a2acc6e3867d6805c68e4c630aca3d14aa958a1d
--- /dev/null
+++ b/src/main/java/io/papermc/paper/plugin/lifecycle/event/handler/configuration/MonitorLifecycleEventHandlerConfiguration.java
@@ -0,0 +1,25 @@
@@ -0,0 +1,27 @@
+package io.papermc.paper.plugin.lifecycle.event.handler.configuration;
+
+import io.papermc.paper.plugin.lifecycle.event.LifecycleEventOwner;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.Contract;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * Handler configuration for event types that allow "monitor" handlers.
@ -213,6 +217,7 @@ index 0000000000000000000000000000000000000000..d307ede51a66279f2eeef4e5b41c7177
+ * @param <O> the required owner type
+ */
+@ApiStatus.Experimental
+@NullMarked
+@ApiStatus.NonExtendable
+public interface MonitorLifecycleEventHandlerConfiguration<O extends LifecycleEventOwner> extends LifecycleEventHandlerConfiguration<O> {
+
@ -228,15 +233,16 @@ index 0000000000000000000000000000000000000000..d307ede51a66279f2eeef4e5b41c7177
+}
diff --git a/src/main/java/io/papermc/paper/plugin/lifecycle/event/handler/configuration/PrioritizedLifecycleEventHandlerConfiguration.java b/src/main/java/io/papermc/paper/plugin/lifecycle/event/handler/configuration/PrioritizedLifecycleEventHandlerConfiguration.java
new file mode 100644
index 0000000000000000000000000000000000000000..1c404df0be359ceac7fb52fec03027c771395e07
index 0000000000000000000000000000000000000000..100e5d169f1f644e54a042c697649f08fff1e6de
--- /dev/null
+++ b/src/main/java/io/papermc/paper/plugin/lifecycle/event/handler/configuration/PrioritizedLifecycleEventHandlerConfiguration.java
@@ -0,0 +1,39 @@
@@ -0,0 +1,41 @@
+package io.papermc.paper.plugin.lifecycle.event.handler.configuration;
+
+import io.papermc.paper.plugin.lifecycle.event.LifecycleEventOwner;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.Contract;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * Handler configuration that allows both "monitor" and prioritized handlers.
@ -245,6 +251,7 @@ index 0000000000000000000000000000000000000000..1c404df0be359ceac7fb52fec03027c7
+ * @param <O> the required owner type
+ */
+@ApiStatus.Experimental
+@NullMarked
+@ApiStatus.NonExtendable
+public interface PrioritizedLifecycleEventHandlerConfiguration<O extends LifecycleEventOwner> extends LifecycleEventHandlerConfiguration<O> {
+
@ -291,16 +298,16 @@ index 0000000000000000000000000000000000000000..fd9c3605a8f5e6bdd31e42f18a45154d
+}
diff --git a/src/main/java/io/papermc/paper/plugin/lifecycle/event/registrar/RegistrarEvent.java b/src/main/java/io/papermc/paper/plugin/lifecycle/event/registrar/RegistrarEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..2e5758d1af6215f33f89b12984a5594df592147f
index 0000000000000000000000000000000000000000..7dca6be092a8b5deca9c45b152a96ffe72fe2533
--- /dev/null
+++ b/src/main/java/io/papermc/paper/plugin/lifecycle/event/registrar/RegistrarEvent.java
@@ -0,0 +1,27 @@
@@ -0,0 +1,28 @@
+package io.papermc.paper.plugin.lifecycle.event.registrar;
+
+import io.papermc.paper.plugin.lifecycle.event.LifecycleEvent;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * A lifecycle event that exposes a {@link Registrar} of some kind
@ -311,6 +318,7 @@ index 0000000000000000000000000000000000000000..2e5758d1af6215f33f89b12984a5594d
+ * @see ReloadableRegistrarEvent
+ */
+@ApiStatus.Experimental
+@NullMarked
+@ApiStatus.NonExtendable
+public interface RegistrarEvent<R extends Registrar> extends LifecycleEvent {
+
@ -320,19 +328,19 @@ index 0000000000000000000000000000000000000000..2e5758d1af6215f33f89b12984a5594d
+ * @return the registrar
+ */
+ @Contract(pure = true)
+ @NotNull R registrar();
+ R registrar();
+}
diff --git a/src/main/java/io/papermc/paper/plugin/lifecycle/event/registrar/ReloadableRegistrarEvent.java b/src/main/java/io/papermc/paper/plugin/lifecycle/event/registrar/ReloadableRegistrarEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..b8b439bdad2e47c7c715fe30e0c1e69aa25374dd
index 0000000000000000000000000000000000000000..9bce1c13c8092238939fbbec6b499d1ca85e5b89
--- /dev/null
+++ b/src/main/java/io/papermc/paper/plugin/lifecycle/event/registrar/ReloadableRegistrarEvent.java
@@ -0,0 +1,38 @@
@@ -0,0 +1,39 @@
+package io.papermc.paper.plugin.lifecycle.event.registrar;
+
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * A lifecycle event that exposes a {@link Registrar} that is
@ -342,6 +350,7 @@ index 0000000000000000000000000000000000000000..b8b439bdad2e47c7c715fe30e0c1e69a
+ * @see RegistrarEvent
+ */
+@ApiStatus.Experimental
+@NullMarked
+@ApiStatus.NonExtendable
+public interface ReloadableRegistrarEvent<R extends Registrar> extends RegistrarEvent<R> {
+
@ -351,7 +360,7 @@ index 0000000000000000000000000000000000000000..b8b439bdad2e47c7c715fe30e0c1e69a
+ * @return the cause
+ */
+ @Contract(pure = true)
+ @NotNull Cause cause();
+ Cause cause();
+
+ @ApiStatus.Experimental
+ enum Cause {
@ -368,10 +377,10 @@ index 0000000000000000000000000000000000000000..b8b439bdad2e47c7c715fe30e0c1e69a
+}
diff --git a/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/LifecycleEventType.java b/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/LifecycleEventType.java
new file mode 100644
index 0000000000000000000000000000000000000000..92ea0374079a228ccc59c00fcf58abff2f6c46fe
index 0000000000000000000000000000000000000000..75d9e20f53735ead4fa4aec478b4b72b85ca5e1e
--- /dev/null
+++ b/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/LifecycleEventType.java
@@ -0,0 +1,73 @@
@@ -0,0 +1,74 @@
+package io.papermc.paper.plugin.lifecycle.event.types;
+
+import io.papermc.paper.plugin.lifecycle.event.LifecycleEvent;
@ -383,7 +392,7 @@ index 0000000000000000000000000000000000000000..92ea0374079a228ccc59c00fcf58abff
+import io.papermc.paper.plugin.lifecycle.event.handler.configuration.PrioritizedLifecycleEventHandlerConfiguration;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * Base type for all types of lifecycle events. Differs from
@ -397,6 +406,7 @@ index 0000000000000000000000000000000000000000..92ea0374079a228ccc59c00fcf58abff
+ * @param <C> the configuration type
+ */
+@ApiStatus.Experimental
+@NullMarked
+@ApiStatus.NonExtendable
+public interface LifecycleEventType<O extends LifecycleEventOwner, E extends LifecycleEvent, C extends LifecycleEventHandlerConfiguration<O>> {
+
@ -406,7 +416,7 @@ index 0000000000000000000000000000000000000000..92ea0374079a228ccc59c00fcf58abff
+ * @return the name
+ */
+ @Contract(pure = true)
+ @NotNull String name();
+ String name();
+
+ /**
+ * Create a configuration for this event with the specified
@ -417,7 +427,7 @@ index 0000000000000000000000000000000000000000..92ea0374079a228ccc59c00fcf58abff
+ * @see LifecycleEventManager#registerEventHandler(LifecycleEventHandlerConfiguration)
+ */
+ @Contract("_ -> new")
+ @NotNull C newHandler(@NotNull LifecycleEventHandler<? super E> handler);
+ C newHandler(LifecycleEventHandler<? super E> handler);
+
+ /**
+ * Lifecycle event type that supports separate registration
@ -447,10 +457,10 @@ index 0000000000000000000000000000000000000000..92ea0374079a228ccc59c00fcf58abff
+}
diff --git a/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/LifecycleEventTypeProvider.java b/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/LifecycleEventTypeProvider.java
new file mode 100644
index 0000000000000000000000000000000000000000..3b17f056d162dd42cae0d33cbbb22fece82fe525
index 0000000000000000000000000000000000000000..e15e09c2a4d3f43db6a0159fa8af6179362ea8d6
--- /dev/null
+++ b/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/LifecycleEventTypeProvider.java
@@ -0,0 +1,22 @@
@@ -0,0 +1,24 @@
+package io.papermc.paper.plugin.lifecycle.event.types;
+
+import io.papermc.paper.plugin.lifecycle.event.LifecycleEvent;
@ -458,8 +468,10 @@ index 0000000000000000000000000000000000000000..3b17f056d162dd42cae0d33cbbb22fec
+import java.util.Optional;
+import java.util.ServiceLoader;
+import org.jetbrains.annotations.ApiStatus;
+import org.jspecify.annotations.NullMarked;
+
+@ApiStatus.Internal
+@NullMarked
+interface LifecycleEventTypeProvider {
+
+ Optional<LifecycleEventTypeProvider> INSTANCE = ServiceLoader.load(LifecycleEventTypeProvider.class)
@ -475,10 +487,10 @@ index 0000000000000000000000000000000000000000..3b17f056d162dd42cae0d33cbbb22fec
+}
diff --git a/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/LifecycleEvents.java b/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/LifecycleEvents.java
new file mode 100644
index 0000000000000000000000000000000000000000..29476a1a82b3526ac03f21011d297d01462d153e
index 0000000000000000000000000000000000000000..f70814de0d6c40b2c1c9921b8abdd1162e1d3995
--- /dev/null
+++ b/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/LifecycleEvents.java
@@ -0,0 +1,52 @@
@@ -0,0 +1,54 @@
+package io.papermc.paper.plugin.lifecycle.event.types;
+
+import io.papermc.paper.plugin.bootstrap.BootstrapContext;
@ -487,6 +499,7 @@ index 0000000000000000000000000000000000000000..29476a1a82b3526ac03f21011d297d01
+import io.papermc.paper.plugin.lifecycle.event.LifecycleEventOwner;
+import org.bukkit.plugin.Plugin;
+import org.jetbrains.annotations.ApiStatus;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * Holds various types of lifecycle events for
@ -494,6 +507,7 @@ index 0000000000000000000000000000000000000000..29476a1a82b3526ac03f21011d297d01
+ * in {@link LifecycleEventManager}.
+ */
+@ApiStatus.Experimental
+@NullMarked
+public final class LifecycleEvents {
+
+ //<editor-fold desc="helper methods" defaultstate="collapsed">

View file

@ -6,20 +6,21 @@ Subject: [PATCH] ItemStack Tooltip API
diff --git a/src/main/java/io/papermc/paper/inventory/tooltip/TooltipContext.java b/src/main/java/io/papermc/paper/inventory/tooltip/TooltipContext.java
new file mode 100644
index 0000000000000000000000000000000000000000..39ac768b3c5148544cb1aaf2c817e661f6856f64
index 0000000000000000000000000000000000000000..7e16f2645e956cbac8d0fc75ba8209f67fd1835c
--- /dev/null
+++ b/src/main/java/io/papermc/paper/inventory/tooltip/TooltipContext.java
@@ -0,0 +1,75 @@
@@ -0,0 +1,76 @@
+package io.papermc.paper.inventory.tooltip;
+
+import org.bukkit.entity.Player;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * Context for computing itemstack tooltips via
+ * {@link org.bukkit.inventory.ItemStack#computeTooltipLines(TooltipContext, Player)}
+ */
+@NullMarked
+public interface TooltipContext {
+
+ /**
@ -31,7 +32,7 @@ index 0000000000000000000000000000000000000000..39ac768b3c5148544cb1aaf2c817e661
+ * @return a new context
+ */
+ @Contract("_, _ -> new")
+ static @NotNull TooltipContext create(final boolean advanced, final boolean creative) {
+ static TooltipContext create(final boolean advanced, final boolean creative) {
+ return new TooltipContextImpl(advanced, creative);
+ }
+
@ -41,7 +42,7 @@ index 0000000000000000000000000000000000000000..39ac768b3c5148544cb1aaf2c817e661
+ * @return a new context
+ */
+ @Contract("-> new")
+ static @NotNull TooltipContext create() {
+ static TooltipContext create() {
+ return new TooltipContextImpl(false, false);
+ }
+
@ -74,7 +75,7 @@ index 0000000000000000000000000000000000000000..39ac768b3c5148544cb1aaf2c817e661
+ * @return a new context
+ */
+ @Contract("-> new")
+ @NotNull TooltipContext asAdvanced();
+ TooltipContext asAdvanced();
+
+ /**
+ * Returns a new context with {@link #isCreative()}
@ -83,27 +84,28 @@ index 0000000000000000000000000000000000000000..39ac768b3c5148544cb1aaf2c817e661
+ * @return a new context
+ */
+ @Contract("-> new")
+ @NotNull TooltipContext asCreative();
+ TooltipContext asCreative();
+}
diff --git a/src/main/java/io/papermc/paper/inventory/tooltip/TooltipContextImpl.java b/src/main/java/io/papermc/paper/inventory/tooltip/TooltipContextImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..c9c0ce750f93ae55e0b2d322a738919474d2d5dd
index 0000000000000000000000000000000000000000..a649b90dfac6000c01579a48234a11383c731439
--- /dev/null
+++ b/src/main/java/io/papermc/paper/inventory/tooltip/TooltipContextImpl.java
@@ -0,0 +1,16 @@
@@ -0,0 +1,17 @@
+package io.papermc.paper.inventory.tooltip;
+
+import org.jetbrains.annotations.NotNull;
+import org.jspecify.annotations.NullMarked;
+
+@NullMarked
+record TooltipContextImpl(boolean isAdvanced, boolean isCreative) implements TooltipContext {
+
+ @Override
+ public @NotNull TooltipContext asCreative() {
+ public TooltipContext asCreative() {
+ return new TooltipContextImpl(this.isAdvanced, true);
+ }
+
+ @Override
+ public @NotNull TooltipContext asAdvanced() {
+ public TooltipContext asAdvanced() {
+ return new TooltipContextImpl(true, this.isCreative);
+ }
+}
@ -119,7 +121,7 @@ index 141d5a964cc299284aecd4d34d57008a32f94247..31217b38e769f97801fa1afefeb223d1
+ @NotNull java.util.List<net.kyori.adventure.text.Component> computeTooltipLines(@NotNull ItemStack itemStack, @NotNull io.papermc.paper.inventory.tooltip.TooltipContext tooltipContext, @Nullable org.bukkit.entity.Player player); // Paper - expose itemstack tooltip lines
}
diff --git a/src/main/java/org/bukkit/inventory/ItemStack.java b/src/main/java/org/bukkit/inventory/ItemStack.java
index 43b8823d4af93febbd60d1b16b406a665373bacd..268baface3b58fba4f7c8a4d3f1b370fa1d26cee 100644
index e6c69a54e0c1dc511fe5769f869dcecb13e04ed3..49390979cc0c68b8e719f2a2ce9e7d193c747959 100644
--- a/src/main/java/org/bukkit/inventory/ItemStack.java
+++ b/src/main/java/org/bukkit/inventory/ItemStack.java
@@ -1124,4 +1124,21 @@ public class ItemStack implements Cloneable, ConfigurationSerializable, Translat

View file

@ -6,22 +6,23 @@ Subject: [PATCH] Add FluidState API
diff --git a/src/main/java/io/papermc/paper/block/fluid/FluidData.java b/src/main/java/io/papermc/paper/block/fluid/FluidData.java
new file mode 100644
index 0000000000000000000000000000000000000000..913acd58547d97cafc1466f6e2b3b4d22cf0b02d
index 0000000000000000000000000000000000000000..0750219fc68261e5c396636967e0b633ae17b72e
--- /dev/null
+++ b/src/main/java/io/papermc/paper/block/fluid/FluidData.java
@@ -0,0 +1,68 @@
@@ -0,0 +1,69 @@
+package io.papermc.paper.block.fluid;
+
+import org.bukkit.Fluid;
+import org.bukkit.Location;
+import org.bukkit.util.Vector;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Range;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * A representation of a fluid in a specific state of data.
+ * This type is not linked to a specific location and hence mostly resembles a {@link org.bukkit.block.data.BlockData}.
+ */
+@NullMarked
+public interface FluidData extends Cloneable {
+
+ /**
@ -29,14 +30,14 @@ index 0000000000000000000000000000000000000000..913acd58547d97cafc1466f6e2b3b4d2
+ *
+ * @return the fluid type
+ */
+ @NotNull Fluid getFluidType();
+ Fluid getFluidType();
+
+ /**
+ * Returns a copy of this FluidData.
+ *
+ * @return a copy of the fluid data
+ */
+ @NotNull FluidData clone();
+ FluidData clone();
+
+ /**
+ * Computes the direction of the flow of the liquid at the given location as a vector.
@ -48,7 +49,7 @@ index 0000000000000000000000000000000000000000..913acd58547d97cafc1466f6e2b3b4d2
+ * @param location - the location to check the liquid flow
+ * @return the flow direction vector at the given location
+ */
+ @NotNull Vector computeFlowDirection(@NotNull Location location);
+ Vector computeFlowDirection(Location location);
+
+ /**
+ * Returns the level of liquid this fluid data holds.
@ -69,7 +70,7 @@ index 0000000000000000000000000000000000000000..913acd58547d97cafc1466f6e2b3b4d2
+ * @return the height as a float value
+ */
+ @Range(from = 0, to = 1)
+ float computeHeight(@NotNull Location location);
+ float computeHeight(Location location);
+
+ /**
+ * Returns whether this fluid is a source block

View file

@ -18,19 +18,20 @@ index 0000000000000000000000000000000000000000..486da6ebe0137bb3280e8b33c8e35e30
+}
diff --git a/src/main/java/io/papermc/paper/scoreboard/numbers/FixedFormat.java b/src/main/java/io/papermc/paper/scoreboard/numbers/FixedFormat.java
new file mode 100644
index 0000000000000000000000000000000000000000..66e0569789d523076cb571fb32be78ecff74305b
index 0000000000000000000000000000000000000000..3ef4595b692a13566c5c738050b83b0462094e9b
--- /dev/null
+++ b/src/main/java/io/papermc/paper/scoreboard/numbers/FixedFormat.java
@@ -0,0 +1,19 @@
@@ -0,0 +1,20 @@
+package io.papermc.paper.scoreboard.numbers;
+
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.ComponentLike;
+import org.jetbrains.annotations.NotNull;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * A scoreboard number format that replaces the score number with a chat component.
+ */
+@NullMarked
+public interface FixedFormat extends NumberFormat, ComponentLike {
+
+ /**
@ -38,43 +39,45 @@ index 0000000000000000000000000000000000000000..66e0569789d523076cb571fb32be78ec
+ *
+ * @return the chat component
+ */
+ @NotNull Component component();
+ Component component();
+
+}
diff --git a/src/main/java/io/papermc/paper/scoreboard/numbers/FixedFormatImpl.java b/src/main/java/io/papermc/paper/scoreboard/numbers/FixedFormatImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..969bbfcdb68ffb5a207207e20e4d79621900c0f5
index 0000000000000000000000000000000000000000..be47bf438805f9ab84b241e564281ea9c287aa6e
--- /dev/null
+++ b/src/main/java/io/papermc/paper/scoreboard/numbers/FixedFormatImpl.java
@@ -0,0 +1,12 @@
@@ -0,0 +1,13 @@
+package io.papermc.paper.scoreboard.numbers;
+
+import net.kyori.adventure.text.Component;
+import org.jetbrains.annotations.NotNull;
+import org.jspecify.annotations.NullMarked;
+
+record FixedFormatImpl(@NotNull Component component) implements FixedFormat {
+@NullMarked
+record FixedFormatImpl(Component component) implements FixedFormat {
+
+ @Override
+ public @NotNull Component asComponent() {
+ public Component asComponent() {
+ return this.component();
+ }
+}
diff --git a/src/main/java/io/papermc/paper/scoreboard/numbers/NumberFormat.java b/src/main/java/io/papermc/paper/scoreboard/numbers/NumberFormat.java
new file mode 100644
index 0000000000000000000000000000000000000000..eadf637f5fc582a2af5db71274ac1f01b2a28913
index 0000000000000000000000000000000000000000..7c093e4e9a2a67021da9025631a3c6fa7ac3ef35
--- /dev/null
+++ b/src/main/java/io/papermc/paper/scoreboard/numbers/NumberFormat.java
@@ -0,0 +1,60 @@
@@ -0,0 +1,61 @@
+package io.papermc.paper.scoreboard.numbers;
+
+import net.kyori.adventure.text.ComponentLike;
+import net.kyori.adventure.text.format.Style;
+import net.kyori.adventure.text.format.StyleBuilderApplicable;
+import org.jetbrains.annotations.NotNull;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * Describes a scoreboard number format that applies custom formatting to scoreboard scores.
+ */
+@NullMarked
+public interface NumberFormat {
+
+ /**
@ -82,7 +85,7 @@ index 0000000000000000000000000000000000000000..eadf637f5fc582a2af5db71274ac1f01
+ *
+ * @return a blank number format
+ */
+ static @NotNull NumberFormat blank() {
+ static NumberFormat blank() {
+ return BlankFormatImpl.INSTANCE;
+ }
+
@ -91,7 +94,7 @@ index 0000000000000000000000000000000000000000..eadf637f5fc582a2af5db71274ac1f01
+ *
+ * @return an un-styled number format
+ */
+ static @NotNull StyledFormat noStyle() {
+ static StyledFormat noStyle() {
+ return StyledFormatImpl.NO_STYLE;
+ }
+
@ -101,7 +104,7 @@ index 0000000000000000000000000000000000000000..eadf637f5fc582a2af5db71274ac1f01
+ * @param style the style to apply on the number
+ * @return a styled number format
+ */
+ static @NotNull StyledFormat styled(final @NotNull Style style) {
+ static StyledFormat styled(final Style style) {
+ return new StyledFormatImpl(style);
+ }
+
@ -111,7 +114,7 @@ index 0000000000000000000000000000000000000000..eadf637f5fc582a2af5db71274ac1f01
+ * @param styleBuilderApplicables the style to apply on the number
+ * @return a styled number format
+ */
+ static @NotNull StyledFormat styled(final @NotNull StyleBuilderApplicable @NotNull... styleBuilderApplicables) {
+ static StyledFormat styled(final StyleBuilderApplicable... styleBuilderApplicables) {
+ return styled(Style.style(styleBuilderApplicables));
+ }
+
@ -121,25 +124,26 @@ index 0000000000000000000000000000000000000000..eadf637f5fc582a2af5db71274ac1f01
+ * @param component the component to replace the number with
+ * @return a fixed number format
+ */
+ static @NotNull FixedFormat fixed(final @NotNull ComponentLike component) {
+ static FixedFormat fixed(final ComponentLike component) {
+ return new FixedFormatImpl(component.asComponent());
+ }
+}
diff --git a/src/main/java/io/papermc/paper/scoreboard/numbers/StyledFormat.java b/src/main/java/io/papermc/paper/scoreboard/numbers/StyledFormat.java
new file mode 100644
index 0000000000000000000000000000000000000000..fe844677d689c3afe5ff2b706d562724e4121137
index 0000000000000000000000000000000000000000..cfb14bb1b338727a5d9eeaa7a73c40540b04dbed
--- /dev/null
+++ b/src/main/java/io/papermc/paper/scoreboard/numbers/StyledFormat.java
@@ -0,0 +1,19 @@
@@ -0,0 +1,20 @@
+package io.papermc.paper.scoreboard.numbers;
+
+import net.kyori.adventure.text.format.Style;
+import net.kyori.adventure.text.format.StyleBuilderApplicable;
+import org.jetbrains.annotations.NotNull;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * A scoreboard number format that applies a custom formatting to the score number.
+ */
+@NullMarked
+public interface StyledFormat extends NumberFormat, StyleBuilderApplicable {
+
+ /**
@ -147,25 +151,26 @@ index 0000000000000000000000000000000000000000..fe844677d689c3afe5ff2b706d562724
+ *
+ * @return the style to apply
+ */
+ @NotNull Style style();
+ Style style();
+
+}
diff --git a/src/main/java/io/papermc/paper/scoreboard/numbers/StyledFormatImpl.java b/src/main/java/io/papermc/paper/scoreboard/numbers/StyledFormatImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..0421e6c7cb32a912cf4aa281623c4311d5d1a34f
index 0000000000000000000000000000000000000000..e288beb1596f2d4e7e602364955da4e8bc0de21c
--- /dev/null
+++ b/src/main/java/io/papermc/paper/scoreboard/numbers/StyledFormatImpl.java
@@ -0,0 +1,13 @@
@@ -0,0 +1,14 @@
+package io.papermc.paper.scoreboard.numbers;
+
+import net.kyori.adventure.text.format.Style;
+import org.jetbrains.annotations.NotNull;
+import org.jspecify.annotations.NullMarked;
+
+record StyledFormatImpl(@NotNull Style style) implements StyledFormat {
+@NullMarked
+record StyledFormatImpl(Style style) implements StyledFormat {
+ static final StyledFormat NO_STYLE = new StyledFormatImpl(Style.empty());
+
+ @Override
+ public void styleApply(final Style.@NotNull Builder style) {
+ public void styleApply(final Style.Builder style) {
+ style.merge(this.style);
+ }
+}

View file

@ -6,21 +6,22 @@ Subject: [PATCH] improve BanList types
diff --git a/src/main/java/io/papermc/paper/ban/BanListType.java b/src/main/java/io/papermc/paper/ban/BanListType.java
new file mode 100644
index 0000000000000000000000000000000000000000..2980abf2f41cb14f0ee5c829c365f8e304130618
index 0000000000000000000000000000000000000000..fdd5cfdc8c2c2ba97a3ac3db57be5a0e13dc327d
--- /dev/null
+++ b/src/main/java/io/papermc/paper/ban/BanListType.java
@@ -0,0 +1,29 @@
@@ -0,0 +1,30 @@
+package io.papermc.paper.ban;
+
+import org.bukkit.BanList;
+import org.bukkit.ban.IpBanList;
+import org.bukkit.ban.ProfileBanList;
+import org.jetbrains.annotations.NotNull;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * Represents a ban-type that a {@link BanList} may track.
+ * It enforces the correct return value at compile time.
+ */
+@NullMarked
+public interface BanListType<T> {
+
+ /**
@ -37,21 +38,22 @@ index 0000000000000000000000000000000000000000..2980abf2f41cb14f0ee5c829c365f8e3
+ *
+ * @return the type class
+ */
+ @NotNull Class<T> typeClass();
+ Class<T> typeClass();
+}
diff --git a/src/main/java/io/papermc/paper/ban/BanListTypeImpl.java b/src/main/java/io/papermc/paper/ban/BanListTypeImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..1159e7cd29fbf11f3fa1448fcf9d0768e1bcb0a3
index 0000000000000000000000000000000000000000..26d4df3cbf8d31790218095bb7fb1a1762e2b322
--- /dev/null
+++ b/src/main/java/io/papermc/paper/ban/BanListTypeImpl.java
@@ -0,0 +1,8 @@
@@ -0,0 +1,9 @@
+package io.papermc.paper.ban;
+
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
+import org.jspecify.annotations.NullMarked;
+
+@ApiStatus.Internal
+record BanListTypeImpl<T>(@NotNull Class<T> typeClass) implements BanListType<T> {
+@NullMarked
+record BanListTypeImpl<T>(Class<T> typeClass) implements BanListType<T> {
+}
diff --git a/src/main/java/org/bukkit/BanList.java b/src/main/java/org/bukkit/BanList.java
index a77c0411a68a9bad33ddfb335b7a996a843e478c..739d9d3ec789e58c10c8d818a9ca59ce447600d5 100644

View file

@ -15,20 +15,21 @@ Co-authored-by: Yannick Lamprecht <yannicklamprecht@live.de>
diff --git a/src/main/java/io/papermc/paper/potion/SuspiciousEffectEntry.java b/src/main/java/io/papermc/paper/potion/SuspiciousEffectEntry.java
new file mode 100644
index 0000000000000000000000000000000000000000..6a96e339ff1466df5743b5d42a31ce6a67e48f16
index 0000000000000000000000000000000000000000..6311ede3e3812da2ccdbcb19522facd7b2e3bd5c
--- /dev/null
+++ b/src/main/java/io/papermc/paper/potion/SuspiciousEffectEntry.java
@@ -0,0 +1,38 @@
@@ -0,0 +1,39 @@
+package io.papermc.paper.potion;
+
+import org.bukkit.potion.PotionEffect;
+import org.bukkit.potion.PotionEffectType;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * Represents a {@link PotionEffectType} paired with a duration.
+ */
+@NullMarked
+public sealed interface SuspiciousEffectEntry permits SuspiciousEffectEntryImpl {
+
+ /**
@ -36,7 +37,7 @@ index 0000000000000000000000000000000000000000..6a96e339ff1466df5743b5d42a31ce6a
+ *
+ * @return effect type
+ */
+ @NotNull PotionEffectType effect();
+ PotionEffectType effect();
+
+ /**
+ * Gets the duration for this effect instance.
@ -53,22 +54,25 @@ index 0000000000000000000000000000000000000000..6a96e339ff1466df5743b5d42a31ce6a
+ * @return new instance of an entry
+ */
+ @Contract(value = "_, _ -> new", pure = true)
+ static @NotNull SuspiciousEffectEntry create(final @NotNull PotionEffectType effectType, final int duration) {
+ static SuspiciousEffectEntry create(final PotionEffectType effectType, final int duration) {
+ return new SuspiciousEffectEntryImpl(effectType, duration);
+ }
+}
diff --git a/src/main/java/io/papermc/paper/potion/SuspiciousEffectEntryImpl.java b/src/main/java/io/papermc/paper/potion/SuspiciousEffectEntryImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..e5002ccaef9ea7a9db94296ad0d66cdae050cdd1
index 0000000000000000000000000000000000000000..a91d37ee450944ea779bd6b61d416467948bb55f
--- /dev/null
+++ b/src/main/java/io/papermc/paper/potion/SuspiciousEffectEntryImpl.java
@@ -0,0 +1,7 @@
@@ -0,0 +1,10 @@
+package io.papermc.paper.potion;
+
+import org.bukkit.potion.PotionEffectType;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.ApiStatus;
+import org.jspecify.annotations.NullMarked;
+
+record SuspiciousEffectEntryImpl(@NotNull PotionEffectType effect, int duration) implements SuspiciousEffectEntry {
+@ApiStatus.Internal
+@NullMarked
+record SuspiciousEffectEntryImpl(PotionEffectType effect, int duration) implements SuspiciousEffectEntry {
+}
diff --git a/src/main/java/org/bukkit/entity/MushroomCow.java b/src/main/java/org/bukkit/entity/MushroomCow.java
index 86c0043ef4e1288b6fe2f68a9b6d01c3de2c3454..3677f19ef1c05b76d946b1b2b491a6c3cec76140 100644

View file

@ -502,24 +502,25 @@ index 0000000000000000000000000000000000000000..9df87708206e26167a2c4934deff7fc6
+}
diff --git a/src/main/java/io/papermc/paper/command/brigadier/BasicCommand.java b/src/main/java/io/papermc/paper/command/brigadier/BasicCommand.java
new file mode 100644
index 0000000000000000000000000000000000000000..99a9a5ecc5010bcd30351f8844cb5d824c252523
index 0000000000000000000000000000000000000000..c89d6c4c38e2390cb11ffba182f8741d3726cfd1
--- /dev/null
+++ b/src/main/java/io/papermc/paper/command/brigadier/BasicCommand.java
@@ -0,0 +1,60 @@
@@ -0,0 +1,62 @@
+package io.papermc.paper.command.brigadier;
+
+import java.util.Collection;
+import java.util.Collections;
+import org.bukkit.command.CommandSender;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jspecify.annotations.NullMarked;
+import org.jspecify.annotations.Nullable;
+
+/**
+ * Implementing this interface allows for easily creating "Bukkit-style" {@code String[] args} commands.
+ * The implementation handles converting the command to a representation compatible with Brigadier on registration, usually in the form of {@literal /commandlabel <greedy_string>}.
+ */
+@ApiStatus.Experimental
+@NullMarked
+@FunctionalInterface
+public interface BasicCommand {
+
@ -530,7 +531,7 @@ index 0000000000000000000000000000000000000000..99a9a5ecc5010bcd30351f8844cb5d82
+ * @param args the arguments of the command ignoring repeated spaces
+ */
+ @ApiStatus.OverrideOnly
+ void execute(@NotNull CommandSourceStack commandSourceStack, @NotNull String[] args);
+ void execute(CommandSourceStack commandSourceStack, String[] args);
+
+ /**
+ * Suggests possible completions for the given command {@link CommandSourceStack} and arguments.
@ -540,7 +541,7 @@ index 0000000000000000000000000000000000000000..99a9a5ecc5010bcd30351f8844cb5d82
+ * @return a collection of suggestions
+ */
+ @ApiStatus.OverrideOnly
+ default @NotNull Collection<String> suggest(final @NotNull CommandSourceStack commandSourceStack, final @NotNull String[] args) {
+ default Collection<String> suggest(final CommandSourceStack commandSourceStack, final String[] args) {
+ return Collections.emptyList();
+ }
+
@ -552,8 +553,9 @@ index 0000000000000000000000000000000000000000..99a9a5ecc5010bcd30351f8844cb5d82
+ * @see #permission()
+ */
+ @ApiStatus.OverrideOnly
+ default boolean canUse(final @NotNull CommandSender sender) {
+ return this.permission() == null || sender.hasPermission(this.permission());
+ default boolean canUse(final CommandSender sender) {
+ final String permission = this.permission();
+ return permission == null || sender.hasPermission(permission);
+ }
+
+ /**
@ -588,18 +590,18 @@ index 0000000000000000000000000000000000000000..7e24babf746de474c8deec4b147e2203
+}
diff --git a/src/main/java/io/papermc/paper/command/brigadier/CommandSourceStack.java b/src/main/java/io/papermc/paper/command/brigadier/CommandSourceStack.java
new file mode 100644
index 0000000000000000000000000000000000000000..54288dbe7185b875a74184f002ee4de4405e91b1
index 0000000000000000000000000000000000000000..ac6f5b754a15e85ce09de4ed4cdee2044b45022c
--- /dev/null
+++ b/src/main/java/io/papermc/paper/command/brigadier/CommandSourceStack.java
@@ -0,0 +1,50 @@
@@ -0,0 +1,51 @@
+package io.papermc.paper.command.brigadier;
+
+import org.bukkit.Location;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Entity;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jspecify.annotations.NullMarked;
+import org.jspecify.annotations.Nullable;
+
+/**
+ * The command source type for Brigadier commands registered using Paper API.
@ -613,8 +615,9 @@ index 0000000000000000000000000000000000000000..54288dbe7185b875a74184f002ee4de4
+ * exist yet, or no specific sender is available. Methods on such a {@link CommandSender}
+ * will either have no effect or throw an {@link UnsupportedOperationException}.</p>
+ */
+@ApiStatus.NonExtendable
+@ApiStatus.Experimental
+@NullMarked
+@ApiStatus.NonExtendable
+public interface CommandSourceStack {
+
+ /**
@ -622,7 +625,7 @@ index 0000000000000000000000000000000000000000..54288dbe7185b875a74184f002ee4de4
+ *
+ * @return a cloned location instance.
+ */
+ @NotNull Location getLocation();
+ Location getLocation();
+
+ /**
+ * Gets the command sender that executed this command.
@ -631,7 +634,7 @@ index 0000000000000000000000000000000000000000..54288dbe7185b875a74184f002ee4de4
+ *
+ * @return the command sender instance
+ */
+ @NotNull CommandSender getSender();
+ CommandSender getSender();
+
+ /**
+ * Gets the entity that executes this command.
@ -644,10 +647,10 @@ index 0000000000000000000000000000000000000000..54288dbe7185b875a74184f002ee4de4
+}
diff --git a/src/main/java/io/papermc/paper/command/brigadier/Commands.java b/src/main/java/io/papermc/paper/command/brigadier/Commands.java
new file mode 100644
index 0000000000000000000000000000000000000000..ce60b618de10da7638f5aefa974aebe02600465c
index 0000000000000000000000000000000000000000..e32559772a39af781d89de101b3f7483a339e317
--- /dev/null
+++ b/src/main/java/io/papermc/paper/command/brigadier/Commands.java
@@ -0,0 +1,266 @@
@@ -0,0 +1,267 @@
+package io.papermc.paper.command.brigadier;
+
+import com.mojang.brigadier.CommandDispatcher;
@ -664,9 +667,9 @@ index 0000000000000000000000000000000000000000..ce60b618de10da7638f5aefa974aebe0
+import java.util.Collections;
+import java.util.Set;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.annotations.Unmodifiable;
+import org.jspecify.annotations.NullMarked;
+import org.jspecify.annotations.Nullable;
+
+/**
+ * The registrar for custom commands. Supports Brigadier commands and {@link BasicCommand}.
@ -709,6 +712,7 @@ index 0000000000000000000000000000000000000000..ce60b618de10da7638f5aefa974aebe0
+ * @see io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents#COMMANDS
+ */
+@ApiStatus.Experimental
+@NullMarked
+@ApiStatus.NonExtendable
+public interface Commands extends Registrar {
+
@ -718,7 +722,7 @@ index 0000000000000000000000000000000000000000..ce60b618de10da7638f5aefa974aebe0
+ * @param literal literal name
+ * @return a new builder instance
+ */
+ static @NotNull LiteralArgumentBuilder<CommandSourceStack> literal(final @NotNull String literal) {
+ static LiteralArgumentBuilder<CommandSourceStack> literal(final String literal) {
+ return LiteralArgumentBuilder.literal(literal);
+ }
+
@ -730,7 +734,7 @@ index 0000000000000000000000000000000000000000..ce60b618de10da7638f5aefa974aebe0
+ * @param <T> the generic type of the argument value
+ * @return a new required argument builder
+ */
+ static <T> @NotNull RequiredArgumentBuilder<CommandSourceStack, T> argument(final @NotNull String name, final @NotNull ArgumentType<T> argumentType) {
+ static <T> RequiredArgumentBuilder<CommandSourceStack, T> argument(final String name, final ArgumentType<T> argumentType) {
+ return RequiredArgumentBuilder.argument(name, argumentType);
+ }
+
@ -754,7 +758,7 @@ index 0000000000000000000000000000000000000000..ce60b618de10da7638f5aefa974aebe0
+ * @return the dispatcher instance
+ */
+ @ApiStatus.Experimental
+ @NotNull CommandDispatcher<CommandSourceStack> getDispatcher();
+ CommandDispatcher<CommandSourceStack> getDispatcher();
+
+ /**
+ * Registers a command for the current plugin context.
@ -768,7 +772,7 @@ index 0000000000000000000000000000000000000000..ce60b618de10da7638f5aefa974aebe0
+ * @param node the built literal command node
+ * @return successfully registered root command labels (including aliases and namespaced variants)
+ */
+ default @Unmodifiable @NotNull Set<String> register(final @NotNull LiteralCommandNode<CommandSourceStack> node) {
+ default @Unmodifiable Set<String> register(final LiteralCommandNode<CommandSourceStack> node) {
+ return this.register(node, null, Collections.emptyList());
+ }
+
@ -785,7 +789,7 @@ index 0000000000000000000000000000000000000000..ce60b618de10da7638f5aefa974aebe0
+ * @param description the help description for the root literal node
+ * @return successfully registered root command labels (including aliases and namespaced variants)
+ */
+ default @Unmodifiable @NotNull Set<String> register(final @NotNull LiteralCommandNode<CommandSourceStack> node, final @Nullable String description) {
+ default @Unmodifiable Set<String> register(final LiteralCommandNode<CommandSourceStack> node, final @Nullable String description) {
+ return this.register(node, description, Collections.emptyList());
+ }
+
@ -802,7 +806,7 @@ index 0000000000000000000000000000000000000000..ce60b618de10da7638f5aefa974aebe0
+ * @param aliases a collection of aliases to register the literal node's command to
+ * @return successfully registered root command labels (including aliases and namespaced variants)
+ */
+ default @Unmodifiable @NotNull Set<String> register(final @NotNull LiteralCommandNode<CommandSourceStack> node, final @NotNull Collection<String> aliases) {
+ default @Unmodifiable Set<String> register(final LiteralCommandNode<CommandSourceStack> node, final Collection<String> aliases) {
+ return this.register(node, null, aliases);
+ }
+
@ -820,7 +824,7 @@ index 0000000000000000000000000000000000000000..ce60b618de10da7638f5aefa974aebe0
+ * @param aliases a collection of aliases to register the literal node's command to
+ * @return successfully registered root command labels (including aliases and namespaced variants)
+ */
+ @Unmodifiable @NotNull Set<String> register(@NotNull LiteralCommandNode<CommandSourceStack> node, @Nullable String description, @NotNull Collection<String> aliases);
+ @Unmodifiable Set<String> register(LiteralCommandNode<CommandSourceStack> node, @Nullable String description, Collection<String> aliases);
+
+ /**
+ * Registers a command for a plugin.
@ -837,7 +841,7 @@ index 0000000000000000000000000000000000000000..ce60b618de10da7638f5aefa974aebe0
+ * @param aliases a collection of aliases to register the literal node's command to
+ * @return successfully registered root command labels (including aliases and namespaced variants)
+ */
+ @Unmodifiable @NotNull Set<String> register(@NotNull PluginMeta pluginMeta, @NotNull LiteralCommandNode<CommandSourceStack> node, @Nullable String description, @NotNull Collection<String> aliases);
+ @Unmodifiable Set<String> register(PluginMeta pluginMeta, LiteralCommandNode<CommandSourceStack> node, @Nullable String description, Collection<String> aliases);
+
+ /**
+ * This allows configuring the registration of your command, which is not intended for public use.
@ -854,7 +858,7 @@ index 0000000000000000000000000000000000000000..ce60b618de10da7638f5aefa974aebe0
+ * See {@link CommandRegistrationFlag} for a more indepth explanation of this method's use-case.
+ */
+ @ApiStatus.Internal
+ @Unmodifiable @NotNull Set<String> registerWithFlags(@NotNull PluginMeta pluginMeta, @NotNull LiteralCommandNode<CommandSourceStack> node, @Nullable String description, @NotNull Collection<String> aliases, @NotNull Set<CommandRegistrationFlag> flags);
+ @Unmodifiable Set<String> registerWithFlags(PluginMeta pluginMeta, LiteralCommandNode<CommandSourceStack> node, @Nullable String description, Collection<String> aliases, Set<CommandRegistrationFlag> flags);
+
+ /**
+ * Registers a command under the same logic as {@link Commands#register(LiteralCommandNode, String, Collection)}.
@ -863,7 +867,7 @@ index 0000000000000000000000000000000000000000..ce60b618de10da7638f5aefa974aebe0
+ * @param basicCommand the basic command instance to register
+ * @return successfully registered root command labels (including aliases and namespaced variants)
+ */
+ default @Unmodifiable @NotNull Set<String> register(final @NotNull String label, final @NotNull BasicCommand basicCommand) {
+ default @Unmodifiable Set<String> register(final String label, final BasicCommand basicCommand) {
+ return this.register(label, null, Collections.emptyList(), basicCommand);
+ }
+
@ -875,7 +879,7 @@ index 0000000000000000000000000000000000000000..ce60b618de10da7638f5aefa974aebe0
+ * @param basicCommand the basic command instance to register
+ * @return successfully registered root command labels (including aliases and namespaced variants)
+ */
+ default @Unmodifiable @NotNull Set<String> register(final @NotNull String label, final @Nullable String description, final @NotNull BasicCommand basicCommand) {
+ default @Unmodifiable Set<String> register(final String label, final @Nullable String description, final BasicCommand basicCommand) {
+ return this.register(label, description, Collections.emptyList(), basicCommand);
+ }
+
@ -887,7 +891,7 @@ index 0000000000000000000000000000000000000000..ce60b618de10da7638f5aefa974aebe0
+ * @param basicCommand the basic command instance to register
+ * @return successfully registered root command labels (including aliases and namespaced variants)
+ */
+ default @Unmodifiable @NotNull Set<String> register(final @NotNull String label, final @NotNull Collection<String> aliases, final @NotNull BasicCommand basicCommand) {
+ default @Unmodifiable Set<String> register(final String label, final Collection<String> aliases, final BasicCommand basicCommand) {
+ return this.register(label, null, aliases, basicCommand);
+ }
+
@ -900,7 +904,7 @@ index 0000000000000000000000000000000000000000..ce60b618de10da7638f5aefa974aebe0
+ * @param basicCommand the basic command instance to register
+ * @return successfully registered root command labels (including aliases and namespaced variants)
+ */
+ @Unmodifiable @NotNull Set<String> register(@NotNull String label, @Nullable String description, @NotNull Collection<String> aliases, @NotNull BasicCommand basicCommand);
+ @Unmodifiable Set<String> register(String label, @Nullable String description, Collection<String> aliases, BasicCommand basicCommand);
+
+ /**
+ * Registers a command under the same logic as {@link Commands#register(PluginMeta, LiteralCommandNode, String, Collection)}.
@ -912,26 +916,27 @@ index 0000000000000000000000000000000000000000..ce60b618de10da7638f5aefa974aebe0
+ * @param basicCommand the basic command instance to register
+ * @return successfully registered root command labels (including aliases and namespaced variants)
+ */
+ @Unmodifiable @NotNull Set<String> register(@NotNull PluginMeta pluginMeta, @NotNull String label, @Nullable String description, @NotNull Collection<String> aliases, @NotNull BasicCommand basicCommand);
+ @Unmodifiable Set<String> register(PluginMeta pluginMeta, String label, @Nullable String description, Collection<String> aliases, BasicCommand basicCommand);
+}
diff --git a/src/main/java/io/papermc/paper/command/brigadier/MessageComponentSerializer.java b/src/main/java/io/papermc/paper/command/brigadier/MessageComponentSerializer.java
new file mode 100644
index 0000000000000000000000000000000000000000..57061a3dd738416c2045e641b6080dc3f096de1a
index 0000000000000000000000000000000000000000..19f3dc12426be09613a13b5889f77627a81305f4
--- /dev/null
+++ b/src/main/java/io/papermc/paper/command/brigadier/MessageComponentSerializer.java
@@ -0,0 +1,24 @@
@@ -0,0 +1,25 @@
+package io.papermc.paper.command.brigadier;
+
+import com.mojang.brigadier.Message;
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.serializer.ComponentSerializer;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * A component serializer for converting between {@link Message} and {@link Component}.
+ */
+@ApiStatus.Experimental
+@NullMarked
+@ApiStatus.NonExtendable
+public interface MessageComponentSerializer extends ComponentSerializer<Component, Component, Message> {
+
@ -940,7 +945,7 @@ index 0000000000000000000000000000000000000000..57061a3dd738416c2045e641b6080dc3
+ *
+ * @return serializer instance
+ */
+ static @NotNull MessageComponentSerializer message() {
+ static MessageComponentSerializer message() {
+ return MessageComponentSerializerHolder.PROVIDER.orElseThrow();
+ }
+}
@ -964,10 +969,10 @@ index 0000000000000000000000000000000000000000..2db12952461c92a64505d6646f6f49f8
+}
diff --git a/src/main/java/io/papermc/paper/command/brigadier/argument/ArgumentTypes.java b/src/main/java/io/papermc/paper/command/brigadier/argument/ArgumentTypes.java
new file mode 100644
index 0000000000000000000000000000000000000000..f102176791e57d23a148ba79bfc949f4ef470c70
index 0000000000000000000000000000000000000000..9abb9ff33672036bb548c688c5661dc8f237aae2
--- /dev/null
+++ b/src/main/java/io/papermc/paper/command/brigadier/argument/ArgumentTypes.java
@@ -0,0 +1,370 @@
@@ -0,0 +1,371 @@
+package io.papermc.paper.command.brigadier.argument;
+
+import com.mojang.brigadier.arguments.ArgumentType;
@ -998,7 +1003,7 @@ index 0000000000000000000000000000000000000000..f102176791e57d23a148ba79bfc949f4
+import org.bukkit.scoreboard.Criteria;
+import org.bukkit.scoreboard.DisplaySlot;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
+import org.jspecify.annotations.NullMarked;
+
+import static io.papermc.paper.command.brigadier.argument.VanillaArgumentProvider.provider;
+
@ -1011,6 +1016,7 @@ index 0000000000000000000000000000000000000000..f102176791e57d23a148ba79bfc949f4
+ * <p>{@link CustomArgumentType} is provided for customizing parsing or result types server-side, while sending the vanilla argument type to the client.</p>
+ */
+@ApiStatus.Experimental
+@NullMarked
+public final class ArgumentTypes {
+
+ /**
@ -1019,7 +1025,7 @@ index 0000000000000000000000000000000000000000..f102176791e57d23a148ba79bfc949f4
+ *
+ * @return argument that takes one entity
+ */
+ public static @NotNull ArgumentType<EntitySelectorArgumentResolver> entity() {
+ public static ArgumentType<EntitySelectorArgumentResolver> entity() {
+ return provider().entity();
+ }
+
@ -1029,7 +1035,7 @@ index 0000000000000000000000000000000000000000..f102176791e57d23a148ba79bfc949f4
+ *
+ * @return argument that takes multiple entities
+ */
+ public static @NotNull ArgumentType<EntitySelectorArgumentResolver> entities() {
+ public static ArgumentType<EntitySelectorArgumentResolver> entities() {
+ return provider().entities();
+ }
+
@ -1039,7 +1045,7 @@ index 0000000000000000000000000000000000000000..f102176791e57d23a148ba79bfc949f4
+ *
+ * @return argument that takes one player
+ */
+ public static @NotNull ArgumentType<PlayerSelectorArgumentResolver> player() {
+ public static ArgumentType<PlayerSelectorArgumentResolver> player() {
+ return provider().player();
+ }
+
@ -1049,7 +1055,7 @@ index 0000000000000000000000000000000000000000..f102176791e57d23a148ba79bfc949f4
+ *
+ * @return argument that takes multiple players
+ */
+ public static @NotNull ArgumentType<PlayerSelectorArgumentResolver> players() {
+ public static ArgumentType<PlayerSelectorArgumentResolver> players() {
+ return provider().players();
+ }
+
@ -1059,7 +1065,7 @@ index 0000000000000000000000000000000000000000..f102176791e57d23a148ba79bfc949f4
+ *
+ * @return player profile argument
+ */
+ public static @NotNull ArgumentType<PlayerProfileListResolver> playerProfiles() {
+ public static ArgumentType<PlayerProfileListResolver> playerProfiles() {
+ return provider().playerProfiles();
+ }
+
@ -1068,7 +1074,7 @@ index 0000000000000000000000000000000000000000..f102176791e57d23a148ba79bfc949f4
+ *
+ * @return block position argument
+ */
+ public static @NotNull ArgumentType<BlockPositionResolver> blockPosition() {
+ public static ArgumentType<BlockPositionResolver> blockPosition() {
+ return provider().blockPosition();
+ }
+
@ -1078,7 +1084,7 @@ index 0000000000000000000000000000000000000000..f102176791e57d23a148ba79bfc949f4
+ * @return fine position argument
+ * @see #finePosition(boolean) to center whole numbers
+ */
+ public static @NotNull ArgumentType<FinePositionResolver> finePosition() {
+ public static ArgumentType<FinePositionResolver> finePosition() {
+ return finePosition(false);
+ }
+
@ -1088,7 +1094,7 @@ index 0000000000000000000000000000000000000000..f102176791e57d23a148ba79bfc949f4
+ * @param centerIntegers if whole numbers should be centered (+0.5)
+ * @return fine position argument
+ */
+ public static @NotNull ArgumentType<FinePositionResolver> finePosition(final boolean centerIntegers) {
+ public static ArgumentType<FinePositionResolver> finePosition(final boolean centerIntegers) {
+ return provider().finePosition(centerIntegers);
+ }
+
@ -1098,7 +1104,7 @@ index 0000000000000000000000000000000000000000..f102176791e57d23a148ba79bfc949f4
+ *
+ * @return argument
+ */
+ public static @NotNull ArgumentType<BlockState> blockState() {
+ public static ArgumentType<BlockState> blockState() {
+ return provider().blockState();
+ }
+
@ -1108,7 +1114,7 @@ index 0000000000000000000000000000000000000000..f102176791e57d23a148ba79bfc949f4
+ *
+ * @return argument
+ */
+ public static @NotNull ArgumentType<ItemStack> itemStack() {
+ public static ArgumentType<ItemStack> itemStack() {
+ return provider().itemStack();
+ }
+
@ -1117,7 +1123,7 @@ index 0000000000000000000000000000000000000000..f102176791e57d23a148ba79bfc949f4
+ *
+ * @return argument
+ */
+ public static @NotNull ArgumentType<ItemStackPredicate> itemPredicate() {
+ public static ArgumentType<ItemStackPredicate> itemPredicate() {
+ return provider().itemStackPredicate();
+ }
+
@ -1126,7 +1132,7 @@ index 0000000000000000000000000000000000000000..f102176791e57d23a148ba79bfc949f4
+ *
+ * @return argument
+ */
+ public static @NotNull ArgumentType<NamedTextColor> namedColor() {
+ public static ArgumentType<NamedTextColor> namedColor() {
+ return provider().namedColor();
+ }
+
@ -1135,7 +1141,7 @@ index 0000000000000000000000000000000000000000..f102176791e57d23a148ba79bfc949f4
+ *
+ * @return argument
+ */
+ public static @NotNull ArgumentType<Component> component() {
+ public static ArgumentType<Component> component() {
+ return provider().component();
+ }
+
@ -1144,7 +1150,7 @@ index 0000000000000000000000000000000000000000..f102176791e57d23a148ba79bfc949f4
+ *
+ * @return argument
+ */
+ public static @NotNull ArgumentType<Style> style() {
+ public static ArgumentType<Style> style() {
+ return provider().style();
+ }
+
@ -1155,7 +1161,7 @@ index 0000000000000000000000000000000000000000..f102176791e57d23a148ba79bfc949f4
+ *
+ * @return argument
+ */
+ public static @NotNull ArgumentType<SignedMessageResolver> signedMessage() {
+ public static ArgumentType<SignedMessageResolver> signedMessage() {
+ return provider().signedMessage();
+ }
+
@ -1164,7 +1170,7 @@ index 0000000000000000000000000000000000000000..f102176791e57d23a148ba79bfc949f4
+ *
+ * @return argument
+ */
+ public static @NotNull ArgumentType<DisplaySlot> scoreboardDisplaySlot() {
+ public static ArgumentType<DisplaySlot> scoreboardDisplaySlot() {
+ return provider().scoreboardDisplaySlot();
+ }
+
@ -1173,7 +1179,7 @@ index 0000000000000000000000000000000000000000..f102176791e57d23a148ba79bfc949f4
+ *
+ * @return argument
+ */
+ public static @NotNull ArgumentType<NamespacedKey> namespacedKey() {
+ public static ArgumentType<NamespacedKey> namespacedKey() {
+ return provider().namespacedKey();
+ }
+
@ -1183,7 +1189,7 @@ index 0000000000000000000000000000000000000000..f102176791e57d23a148ba79bfc949f4
+ * @return argument
+ */
+ // include both key types as we are slowly moving to use adventure's key
+ public static @NotNull ArgumentType<Key> key() {
+ public static ArgumentType<Key> key() {
+ return provider().key();
+ }
+
@ -1192,7 +1198,7 @@ index 0000000000000000000000000000000000000000..f102176791e57d23a148ba79bfc949f4
+ *
+ * @return argument
+ */
+ public static @NotNull ArgumentType<IntegerRangeProvider> integerRange() {
+ public static ArgumentType<IntegerRangeProvider> integerRange() {
+ return provider().integerRange();
+ }
+
@ -1201,7 +1207,7 @@ index 0000000000000000000000000000000000000000..f102176791e57d23a148ba79bfc949f4
+ *
+ * @return argument
+ */
+ public static @NotNull ArgumentType<DoubleRangeProvider> doubleRange() {
+ public static ArgumentType<DoubleRangeProvider> doubleRange() {
+ return provider().doubleRange();
+ }
+
@ -1210,7 +1216,7 @@ index 0000000000000000000000000000000000000000..f102176791e57d23a148ba79bfc949f4
+ *
+ * @return argument
+ */
+ public static @NotNull ArgumentType<World> world() {
+ public static ArgumentType<World> world() {
+ return provider().world();
+ }
+
@ -1219,16 +1225,16 @@ index 0000000000000000000000000000000000000000..f102176791e57d23a148ba79bfc949f4
+ *
+ * @return argument
+ */
+ public static @NotNull ArgumentType<GameMode> gameMode() {
+ public static ArgumentType<GameMode> gameMode() {
+ return provider().gameMode();
+ }
+
+ /**
+ * A argument for getting a heightmap type.
+ * An argument for getting a heightmap type.
+ *
+ * @return argument
+ */
+ public static @NotNull ArgumentType<HeightMap> heightMap() {
+ public static ArgumentType<HeightMap> heightMap() {
+ return provider().heightMap();
+ }
+
@ -1237,7 +1243,7 @@ index 0000000000000000000000000000000000000000..f102176791e57d23a148ba79bfc949f4
+ *
+ * @return argument
+ */
+ public static @NotNull ArgumentType<UUID> uuid() {
+ public static ArgumentType<UUID> uuid() {
+ return provider().uuid();
+ }
+
@ -1246,7 +1252,7 @@ index 0000000000000000000000000000000000000000..f102176791e57d23a148ba79bfc949f4
+ *
+ * @return argument
+ */
+ public static @NotNull ArgumentType<Criteria> objectiveCriteria() {
+ public static ArgumentType<Criteria> objectiveCriteria() {
+ return provider().objectiveCriteria();
+ }
+
@ -1255,7 +1261,7 @@ index 0000000000000000000000000000000000000000..f102176791e57d23a148ba79bfc949f4
+ *
+ * @return argument
+ */
+ public static @NotNull ArgumentType<LookAnchor> entityAnchor() {
+ public static ArgumentType<LookAnchor> entityAnchor() {
+ return provider().entityAnchor();
+ }
+
@ -1271,7 +1277,7 @@ index 0000000000000000000000000000000000000000..f102176791e57d23a148ba79bfc949f4
+ *
+ * @return argument
+ */
+ public static @NotNull ArgumentType<Integer> time() {
+ public static ArgumentType<Integer> time() {
+ return time(0);
+ }
+
@ -1288,7 +1294,7 @@ index 0000000000000000000000000000000000000000..f102176791e57d23a148ba79bfc949f4
+ * @param mintime The minimum time required for this argument.
+ * @return argument
+ */
+ public static @NotNull ArgumentType<Integer> time(final int mintime) {
+ public static ArgumentType<Integer> time(final int mintime) {
+ return provider().time(mintime);
+ }
+
@ -1298,7 +1304,7 @@ index 0000000000000000000000000000000000000000..f102176791e57d23a148ba79bfc949f4
+ * @return argument
+ * @see Mirror
+ */
+ public static @NotNull ArgumentType<Mirror> templateMirror() {
+ public static ArgumentType<Mirror> templateMirror() {
+ return provider().templateMirror();
+ }
+
@ -1308,7 +1314,7 @@ index 0000000000000000000000000000000000000000..f102176791e57d23a148ba79bfc949f4
+ * @return argument
+ * @see StructureRotation
+ */
+ public static @NotNull ArgumentType<StructureRotation> templateRotation() {
+ public static ArgumentType<StructureRotation> templateRotation() {
+ return provider().templateRotation();
+ }
+
@ -1319,7 +1325,7 @@ index 0000000000000000000000000000000000000000..f102176791e57d23a148ba79bfc949f4
+ * @return argument
+ * @param <T> the registry value type
+ */
+ public static <T> @NotNull ArgumentType<T> resource(final @NotNull RegistryKey<T> registryKey) {
+ public static <T> ArgumentType<T> resource(final RegistryKey<T> registryKey) {
+ return provider().resource(registryKey);
+ }
+
@ -1331,7 +1337,7 @@ index 0000000000000000000000000000000000000000..f102176791e57d23a148ba79bfc949f4
+ * @param <T> the registry value type
+ * @see RegistryArgumentExtractor#getTypedKey(com.mojang.brigadier.context.CommandContext, RegistryKey, String)
+ */
+ public static <T> @NotNull ArgumentType<TypedKey<T>> resourceKey(final @NotNull RegistryKey<T> registryKey) {
+ public static <T> ArgumentType<TypedKey<T>> resourceKey(final RegistryKey<T> registryKey) {
+ return provider().resourceKey(registryKey);
+ }
+
@ -1340,10 +1346,10 @@ index 0000000000000000000000000000000000000000..f102176791e57d23a148ba79bfc949f4
+}
diff --git a/src/main/java/io/papermc/paper/command/brigadier/argument/CustomArgumentType.java b/src/main/java/io/papermc/paper/command/brigadier/argument/CustomArgumentType.java
new file mode 100644
index 0000000000000000000000000000000000000000..02acac7f9186677d19c0a62095cc3012bc112961
index 0000000000000000000000000000000000000000..91d40ef0bdbdee3609e33577782c5cce29deda6a
--- /dev/null
+++ b/src/main/java/io/papermc/paper/command/brigadier/argument/CustomArgumentType.java
@@ -0,0 +1,106 @@
@@ -0,0 +1,107 @@
+package io.papermc.paper.command.brigadier.argument;
+
+import com.mojang.brigadier.StringReader;
@ -1355,7 +1361,7 @@ index 0000000000000000000000000000000000000000..02acac7f9186677d19c0a62095cc3012
+import java.util.Collection;
+import java.util.concurrent.CompletableFuture;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * An argument type that wraps around a native-to-vanilla argument type.
@ -1370,6 +1376,7 @@ index 0000000000000000000000000000000000000000..02acac7f9186677d19c0a62095cc3012
+ * @param <N> type with an argument native to vanilla Minecraft (from {@link ArgumentTypes})
+ */
+@ApiStatus.Experimental
+@NullMarked
+public interface CustomArgumentType<T, N> extends ArgumentType<T> {
+
+ /**
@ -1384,7 +1391,7 @@ index 0000000000000000000000000000000000000000..02acac7f9186677d19c0a62095cc3012
+ * @throws CommandSyntaxException if an error occurs while parsing
+ */
+ @Override
+ @NotNull T parse(final @NotNull StringReader reader) throws CommandSyntaxException;
+ T parse(final StringReader reader) throws CommandSyntaxException;
+
+ /**
+ * Gets the native type that this argument uses,
@ -1392,7 +1399,7 @@ index 0000000000000000000000000000000000000000..02acac7f9186677d19c0a62095cc3012
+ *
+ * @return native argument type
+ */
+ @NotNull ArgumentType<N> getNativeType();
+ ArgumentType<N> getNativeType();
+
+ /**
+ * Cannot be controlled by the server.
@ -1403,7 +1410,7 @@ index 0000000000000000000000000000000000000000..02acac7f9186677d19c0a62095cc3012
+ */
+ @Override
+ @ApiStatus.NonExtendable
+ default @NotNull Collection<String> getExamples() {
+ default Collection<String> getExamples() {
+ return this.getNativeType().getExamples();
+ }
+
@ -1416,7 +1423,7 @@ index 0000000000000000000000000000000000000000..02acac7f9186677d19c0a62095cc3012
+ * @param <S> context type
+ */
+ @Override
+ default <S> @NotNull CompletableFuture<Suggestions> listSuggestions(final @NotNull CommandContext<S> context, final @NotNull SuggestionsBuilder builder) {
+ default <S> CompletableFuture<Suggestions> listSuggestions(final CommandContext<S> context, final SuggestionsBuilder builder) {
+ return ArgumentType.super.listSuggestions(context, builder);
+ }
+
@ -1436,7 +1443,7 @@ index 0000000000000000000000000000000000000000..02acac7f9186677d19c0a62095cc3012
+
+ @ApiStatus.NonExtendable
+ @Override
+ default @NotNull T parse(final @NotNull StringReader reader) throws CommandSyntaxException {
+ default T parse(final StringReader reader) throws CommandSyntaxException {
+ return this.convert(this.getNativeType().parse(reader));
+ }
+
@ -1447,25 +1454,26 @@ index 0000000000000000000000000000000000000000..02acac7f9186677d19c0a62095cc3012
+ * @return converted value
+ * @throws CommandSyntaxException if an exception occurs while parsing
+ */
+ @NotNull T convert(@NotNull N nativeType) throws CommandSyntaxException;
+ T convert(N nativeType) throws CommandSyntaxException;
+ }
+}
diff --git a/src/main/java/io/papermc/paper/command/brigadier/argument/RegistryArgumentExtractor.java b/src/main/java/io/papermc/paper/command/brigadier/argument/RegistryArgumentExtractor.java
new file mode 100644
index 0000000000000000000000000000000000000000..475266144edf0f7dc4e7939abaf9e1705c4e6461
index 0000000000000000000000000000000000000000..c6123342df9610a97752030955a43df18e8d0cbd
--- /dev/null
+++ b/src/main/java/io/papermc/paper/command/brigadier/argument/RegistryArgumentExtractor.java
@@ -0,0 +1,35 @@
@@ -0,0 +1,36 @@
+package io.papermc.paper.command.brigadier.argument;
+
+import com.mojang.brigadier.context.CommandContext;
+import io.papermc.paper.registry.RegistryKey;
+import io.papermc.paper.registry.TypedKey;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * Utilities for extracting registry-related arguments from a {@link CommandContext}.
+ */
+@NullMarked
+public final class RegistryArgumentExtractor {
+
+ /**
@ -1480,7 +1488,7 @@ index 0000000000000000000000000000000000000000..475266144edf0f7dc4e7939abaf9e170
+ * @throws IllegalArgumentException if the registry key doesn't match the typed key
+ */
+ @SuppressWarnings("unchecked")
+ public static <T, S> @NonNull TypedKey<T> getTypedKey(final @NonNull CommandContext<S> context, final @NonNull RegistryKey<T> registryKey, final @NonNull String name) {
+ public static <T, S> TypedKey<T> getTypedKey(final CommandContext<S> context, final RegistryKey<T> registryKey, final String name) {
+ final TypedKey<T> typedKey = context.getArgument(name, TypedKey.class);
+ if (typedKey.registryKey().equals(registryKey)) {
+ return typedKey;
@ -1493,10 +1501,10 @@ index 0000000000000000000000000000000000000000..475266144edf0f7dc4e7939abaf9e170
+}
diff --git a/src/main/java/io/papermc/paper/command/brigadier/argument/SignedMessageResolver.java b/src/main/java/io/papermc/paper/command/brigadier/argument/SignedMessageResolver.java
new file mode 100644
index 0000000000000000000000000000000000000000..159b691e7a1a7066f3e706e80d75ca8f87a3a964
index 0000000000000000000000000000000000000000..2f2c1729c5d9f1a6b6171c0ed5326b9b631f3c20
--- /dev/null
+++ b/src/main/java/io/papermc/paper/command/brigadier/argument/SignedMessageResolver.java
@@ -0,0 +1,41 @@
@@ -0,0 +1,42 @@
+package io.papermc.paper.command.brigadier.argument;
+
+import com.mojang.brigadier.context.CommandContext;
@ -1505,7 +1513,7 @@ index 0000000000000000000000000000000000000000..159b691e7a1a7066f3e706e80d75ca8f
+import java.util.concurrent.CompletableFuture;
+import net.kyori.adventure.chat.SignedMessage;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * A resolver for a {@link SignedMessage}
@ -1513,6 +1521,7 @@ index 0000000000000000000000000000000000000000..159b691e7a1a7066f3e706e80d75ca8f
+ * @see ArgumentTypes#signedMessage()
+ */
+@ApiStatus.Experimental
+@NullMarked
+@ApiStatus.NonExtendable
+public interface SignedMessageResolver {
+
@ -1521,7 +1530,7 @@ index 0000000000000000000000000000000000000000..159b691e7a1a7066f3e706e80d75ca8f
+ *
+ * @return string content
+ */
+ @NotNull String content();
+ String content();
+
+ /**
+ * Resolves this signed message. This will the {@link CommandContext}
@ -1535,15 +1544,15 @@ index 0000000000000000000000000000000000000000..159b691e7a1a7066f3e706e80d75ca8f
+ * @return a completable future for the {@link SignedMessage}
+ * @throws CommandSyntaxException syntax exception
+ */
+ @NotNull CompletableFuture<SignedMessage> resolveSignedMessage(@NotNull String argumentName, @NotNull CommandContext<CommandSourceStack> context) throws CommandSyntaxException;
+ CompletableFuture<SignedMessage> resolveSignedMessage(String argumentName, CommandContext<CommandSourceStack> context) throws CommandSyntaxException;
+
+}
diff --git a/src/main/java/io/papermc/paper/command/brigadier/argument/VanillaArgumentProvider.java b/src/main/java/io/papermc/paper/command/brigadier/argument/VanillaArgumentProvider.java
new file mode 100644
index 0000000000000000000000000000000000000000..a36a36a46b5d71489e1b3e183d48e6e83721ebdf
index 0000000000000000000000000000000000000000..4f640bd3e536fb79db54dcedd5807e7de402acef
--- /dev/null
+++ b/src/main/java/io/papermc/paper/command/brigadier/argument/VanillaArgumentProvider.java
@@ -0,0 +1,107 @@
@@ -0,0 +1,106 @@
+package io.papermc.paper.command.brigadier.argument;
+
+import com.mojang.brigadier.arguments.ArgumentType;
@ -1575,12 +1584,11 @@ index 0000000000000000000000000000000000000000..a36a36a46b5d71489e1b3e183d48e6e8
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.scoreboard.Criteria;
+import org.bukkit.scoreboard.DisplaySlot;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.framework.qual.DefaultQualifier;
+import org.jetbrains.annotations.ApiStatus;
+import org.jspecify.annotations.NullMarked;
+
+@ApiStatus.Internal
+@DefaultQualifier(NonNull.class)
+@NullMarked
+interface VanillaArgumentProvider {
+
+ Optional<VanillaArgumentProvider> PROVIDER = ServiceLoader.load(VanillaArgumentProvider.class)
@ -1714,7 +1722,7 @@ index 0000000000000000000000000000000000000000..06ffff68d2652ef8eb40aa723803c24e
+}
diff --git a/src/main/java/io/papermc/paper/command/brigadier/argument/range/RangeProvider.java b/src/main/java/io/papermc/paper/command/brigadier/argument/range/RangeProvider.java
new file mode 100644
index 0000000000000000000000000000000000000000..651dbf15c155f8d8fef25785300d44752c388b37
index 0000000000000000000000000000000000000000..af1c01ab0d2146225c54a7766788007314f59328
--- /dev/null
+++ b/src/main/java/io/papermc/paper/command/brigadier/argument/range/RangeProvider.java
@@ -0,0 +1,22 @@
@ -1722,7 +1730,7 @@ index 0000000000000000000000000000000000000000..651dbf15c155f8d8fef25785300d4475
+
+import com.google.common.collect.Range;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * A provider for a range of numbers
@ -1731,27 +1739,27 @@ index 0000000000000000000000000000000000000000..651dbf15c155f8d8fef25785300d4475
+ * @see io.papermc.paper.command.brigadier.argument.ArgumentTypes
+ */
+@ApiStatus.Experimental
+@NullMarked
+public sealed interface RangeProvider<T extends Comparable<?>> permits DoubleRangeProvider, IntegerRangeProvider {
+
+ /**
+ * Provides the given range.
+ * @return range
+ */
+ @NotNull
+ Range<T> range();
+}
diff --git a/src/main/java/io/papermc/paper/command/brigadier/argument/resolvers/ArgumentResolver.java b/src/main/java/io/papermc/paper/command/brigadier/argument/resolvers/ArgumentResolver.java
new file mode 100644
index 0000000000000000000000000000000000000000..dea24d91999f78b77fe85221130d87a54edf004a
index 0000000000000000000000000000000000000000..60439269d8b1535c779ae8bd008c8f28cc7e4133
--- /dev/null
+++ b/src/main/java/io/papermc/paper/command/brigadier/argument/resolvers/ArgumentResolver.java
@@ -0,0 +1,26 @@
@@ -0,0 +1,27 @@
+package io.papermc.paper.command.brigadier.argument.resolvers;
+
+import com.mojang.brigadier.exceptions.CommandSyntaxException;
+import io.papermc.paper.command.brigadier.CommandSourceStack;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * An {@link ArgumentResolver} is capable of resolving
@ -1761,6 +1769,7 @@ index 0000000000000000000000000000000000000000..dea24d91999f78b77fe85221130d87a5
+ * @see io.papermc.paper.command.brigadier.argument.ArgumentTypes
+ */
+@ApiStatus.Experimental
+@NullMarked
+@ApiStatus.NonExtendable
+public interface ArgumentResolver<T> {
+
@ -1770,7 +1779,7 @@ index 0000000000000000000000000000000000000000..dea24d91999f78b77fe85221130d87a5
+ * @param sourceStack source stack
+ * @return resolved
+ */
+ @NotNull T resolve(@NotNull CommandSourceStack sourceStack) throws CommandSyntaxException;
+ T resolve(CommandSourceStack sourceStack) throws CommandSyntaxException;
+}
diff --git a/src/main/java/io/papermc/paper/command/brigadier/argument/resolvers/BlockPositionResolver.java b/src/main/java/io/papermc/paper/command/brigadier/argument/resolvers/BlockPositionResolver.java
new file mode 100644
@ -1914,7 +1923,7 @@ index 0000000000000000000000000000000000000000..906ce6eff30ebd9ec3010ce03b471418
+public interface SelectorArgumentResolver<T> extends ArgumentResolver<T> {
+}
diff --git a/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/LifecycleEvents.java b/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/LifecycleEvents.java
index 29476a1a82b3526ac03f21011d297d01462d153e..dae4935d8e9f46c52f700927a43a9202dac13df6 100644
index f70814de0d6c40b2c1c9921b8abdd1162e1d3995..ab6b262cf0d2d17962ed012b2ea7b8f1db8bc576 100644
--- a/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/LifecycleEvents.java
+++ b/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/LifecycleEvents.java
@@ -1,9 +1,11 @@
@ -1928,9 +1937,9 @@ index 29476a1a82b3526ac03f21011d297d01462d153e..dae4935d8e9f46c52f700927a43a9202
+import io.papermc.paper.plugin.lifecycle.event.registrar.ReloadableRegistrarEvent;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.ApiStatus;
@@ -15,6 +17,13 @@ import org.jetbrains.annotations.ApiStatus;
@ApiStatus.Experimental
import org.jspecify.annotations.NullMarked;
@@ -17,6 +19,13 @@ import org.jspecify.annotations.NullMarked;
@NullMarked
public final class LifecycleEvents {
+ /**

View file

@ -57,18 +57,17 @@ index 1b7b07715067014bf3d35002ae1655793248b426..5bf55b40fbf6ec708f37d90bd0853fe7
}
diff --git a/src/main/java/org/bukkit/inventory/EmptyRecipeChoice.java b/src/main/java/org/bukkit/inventory/EmptyRecipeChoice.java
new file mode 100644
index 0000000000000000000000000000000000000000..ed0ab6163f47ec843ba4f7ea4a98bb2fa315eaa1
index 0000000000000000000000000000000000000000..a95ecc97fd6507103f9264a686ec1c061fa725b1
--- /dev/null
+++ b/src/main/java/org/bukkit/inventory/EmptyRecipeChoice.java
@@ -0,0 +1,33 @@
@@ -0,0 +1,32 @@
+package org.bukkit.inventory;
+
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.framework.qual.DefaultQualifier;
+import org.jetbrains.annotations.ApiStatus;
+import org.jspecify.annotations.NullMarked;
+
+@ApiStatus.Internal
+@DefaultQualifier(NonNull.class)
+@NullMarked
+record EmptyRecipeChoice() implements RecipeChoice {
+
+ static final RecipeChoice INSTANCE = new EmptyRecipeChoice();

View file

@ -6,29 +6,31 @@ Subject: [PATCH] Registry Modification API
diff --git a/src/main/java/io/papermc/paper/registry/RegistryBuilder.java b/src/main/java/io/papermc/paper/registry/RegistryBuilder.java
new file mode 100644
index 0000000000000000000000000000000000000000..6edf300c1d81c1001756141c9efd022ba0e372cd
index 0000000000000000000000000000000000000000..379e2638aca243bacac777cf59982f9d0e601f3e
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/RegistryBuilder.java
@@ -0,0 +1,13 @@
@@ -0,0 +1,15 @@
+package io.papermc.paper.registry;
+
+import org.jetbrains.annotations.ApiStatus;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * To be implemented by any type used for modifying registries.
+ *
+ * @param <T> registry value type
+ */
+@ApiStatus.NonExtendable
+@ApiStatus.Experimental
+@NullMarked
+@ApiStatus.NonExtendable
+public interface RegistryBuilder<T> {
+}
diff --git a/src/main/java/io/papermc/paper/registry/event/RegistryEntryAddEvent.java b/src/main/java/io/papermc/paper/registry/event/RegistryEntryAddEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..a5d7385eae9dfb88b52aed0e42c09a10ef807385
index 0000000000000000000000000000000000000000..56468b311e40a6d1aa03c6d31328952b92e95027
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/event/RegistryEntryAddEvent.java
@@ -0,0 +1,47 @@
@@ -0,0 +1,48 @@
+package io.papermc.paper.registry.event;
+
+import io.papermc.paper.registry.RegistryBuilder;
@ -36,8 +38,8 @@ index 0000000000000000000000000000000000000000..a5d7385eae9dfb88b52aed0e42c09a10
+import io.papermc.paper.registry.tag.Tag;
+import io.papermc.paper.registry.tag.TagKey;
+import org.bukkit.Keyed;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.jetbrains.annotations.ApiStatus;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * Event object for {@link RegistryEventProvider#entryAdd()}. This
@ -48,6 +50,7 @@ index 0000000000000000000000000000000000000000..a5d7385eae9dfb88b52aed0e42c09a10
+ * @param <B> registry entry builder type
+ */
+@ApiStatus.Experimental
+@NullMarked
+@ApiStatus.NonExtendable
+public interface RegistryEntryAddEvent<T, B extends RegistryBuilder<T>> extends RegistryEvent<T> {
+
@ -56,14 +59,14 @@ index 0000000000000000000000000000000000000000..a5d7385eae9dfb88b52aed0e42c09a10
+ *
+ * @return the object builder
+ */
+ @NonNull B builder();
+ B builder();
+
+ /**
+ * Gets the key for this entry in the registry.
+ *
+ * @return the key
+ */
+ @NonNull TypedKey<T> key();
+ TypedKey<T> key();
+
+ /**
+ * Gets or creates a tag for the given tag key. This tag
@ -74,20 +77,20 @@ index 0000000000000000000000000000000000000000..a5d7385eae9dfb88b52aed0e42c09a10
+ * @return the tag
+ * @param <V> the tag value type
+ */
+ @NonNull <V extends Keyed> Tag<V> getOrCreateTag(@NonNull TagKey<V> tagKey);
+ <V extends Keyed> Tag<V> getOrCreateTag(TagKey<V> tagKey);
+}
diff --git a/src/main/java/io/papermc/paper/registry/event/RegistryEvent.java b/src/main/java/io/papermc/paper/registry/event/RegistryEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..498c5920926158e86c2aec2bd2129d5e8b8613a3
index 0000000000000000000000000000000000000000..3a8643c06d751bf3e4bff4d95b62c675ecdf0f16
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/event/RegistryEvent.java
@@ -0,0 +1,23 @@
@@ -0,0 +1,24 @@
+package io.papermc.paper.registry.event;
+
+import io.papermc.paper.plugin.lifecycle.event.LifecycleEvent;
+import io.papermc.paper.registry.RegistryKey;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.jetbrains.annotations.ApiStatus;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * Base type for all registry events.
@ -95,6 +98,7 @@ index 0000000000000000000000000000000000000000..498c5920926158e86c2aec2bd2129d5e
+ * @param <T> registry entry type
+ */
+@ApiStatus.Experimental
+@NullMarked
+@ApiStatus.NonExtendable
+public interface RegistryEvent<T> extends LifecycleEvent {
+
@ -103,26 +107,24 @@ index 0000000000000000000000000000000000000000..498c5920926158e86c2aec2bd2129d5e
+ *
+ * @return the registry key
+ */
+ @NonNull RegistryKey<T> registryKey();
+ RegistryKey<T> registryKey();
+}
diff --git a/src/main/java/io/papermc/paper/registry/event/RegistryEventProvider.java b/src/main/java/io/papermc/paper/registry/event/RegistryEventProvider.java
new file mode 100644
index 0000000000000000000000000000000000000000..477ed0fd5acc923d429980529876f0dd7dd3a52a
index 0000000000000000000000000000000000000000..8518e93829318cbfe0eb70f558cb86a7b5742514
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/event/RegistryEventProvider.java
@@ -0,0 +1,58 @@
@@ -0,0 +1,57 @@
+package io.papermc.paper.registry.event;
+
+import io.papermc.paper.plugin.bootstrap.BootstrapContext;
+import io.papermc.paper.plugin.lifecycle.event.handler.LifecycleEventHandler;
+import io.papermc.paper.plugin.lifecycle.event.handler.configuration.LifecycleEventHandlerConfiguration;
+import io.papermc.paper.plugin.lifecycle.event.handler.configuration.PrioritizedLifecycleEventHandlerConfiguration;
+import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEventType;
+import io.papermc.paper.registry.RegistryBuilder;
+import io.papermc.paper.registry.RegistryKey;
+import io.papermc.paper.registry.event.type.RegistryEntryAddEventType;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.jetbrains.annotations.ApiStatus;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * Provider for registry events for a specific registry.
@ -137,6 +139,7 @@ index 0000000000000000000000000000000000000000..477ed0fd5acc923d429980529876f0dd
+ * @param <B> registry entry builder type
+ */
+@ApiStatus.Experimental
+@NullMarked
+@ApiStatus.NonExtendable
+public interface RegistryEventProvider<T, B extends RegistryBuilder<T>> {
+
@ -149,7 +152,7 @@ index 0000000000000000000000000000000000000000..477ed0fd5acc923d429980529876f0dd
+ *
+ * @return the registry entry add event type
+ */
+ @NonNull RegistryEntryAddEventType<T, B> entryAdd();
+ RegistryEntryAddEventType<T, B> entryAdd();
+
+ /**
+ * Gets the event type for {@link RegistryFreezeEvent} which is fired just before
@ -160,21 +163,21 @@ index 0000000000000000000000000000000000000000..477ed0fd5acc923d429980529876f0dd
+ *
+ * @return the registry freeze event type
+ */
+ LifecycleEventType.@NonNull Prioritizable<BootstrapContext, RegistryFreezeEvent<T, B>> freeze();
+ LifecycleEventType.Prioritizable<BootstrapContext, RegistryFreezeEvent<T, B>> freeze();
+
+ /**
+ * Gets the registry key associated with this event type provider.
+ *
+ * @return the registry key
+ */
+ @NonNull RegistryKey<T> registryKey();
+ RegistryKey<T> registryKey();
+}
diff --git a/src/main/java/io/papermc/paper/registry/event/RegistryEventProviderImpl.java b/src/main/java/io/papermc/paper/registry/event/RegistryEventProviderImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..cfe47c8bd0888db6d00867acfefc8db42ef314aa
index 0000000000000000000000000000000000000000..8d9afd49077090d30f13b217b71100c73137d120
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/event/RegistryEventProviderImpl.java
@@ -0,0 +1,30 @@
@@ -0,0 +1,29 @@
+package io.papermc.paper.registry.event;
+
+import io.papermc.paper.plugin.bootstrap.BootstrapContext;
@ -182,12 +185,11 @@ index 0000000000000000000000000000000000000000..cfe47c8bd0888db6d00867acfefc8db4
+import io.papermc.paper.registry.RegistryBuilder;
+import io.papermc.paper.registry.RegistryKey;
+import io.papermc.paper.registry.event.type.RegistryEntryAddEventType;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.framework.qual.DefaultQualifier;
+import org.jetbrains.annotations.ApiStatus;
+import org.jspecify.annotations.NullMarked;
+
+@ApiStatus.Internal
+@DefaultQualifier(NonNull.class)
+@NullMarked
+record RegistryEventProviderImpl<T, B extends RegistryBuilder<T>>(RegistryKey<T> registryKey) implements RegistryEventProvider<T, B> {
+
+ static <T, B extends RegistryBuilder<T>> RegistryEventProvider<T, B> create(final RegistryKey<T> registryKey) {
@ -237,19 +239,21 @@ index 0000000000000000000000000000000000000000..d807bd2f42c98e37a96cf110ad77820d
+}
diff --git a/src/main/java/io/papermc/paper/registry/event/RegistryEvents.java b/src/main/java/io/papermc/paper/registry/event/RegistryEvents.java
new file mode 100644
index 0000000000000000000000000000000000000000..1f89945be2ed68f52a544f41f7a151b8fdfe113e
index 0000000000000000000000000000000000000000..91ae9c0d3ec55ce417d4b447bf3d1b0d0c174b5e
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/event/RegistryEvents.java
@@ -0,0 +1,14 @@
@@ -0,0 +1,16 @@
+package io.papermc.paper.registry.event;
+
+import org.jetbrains.annotations.ApiStatus;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * Holds providers for {@link RegistryEntryAddEvent} and {@link RegistryFreezeEvent}
+ * handlers for each applicable registry.
+ */
+@ApiStatus.Experimental
+@NullMarked
+public final class RegistryEvents {
+
+ private RegistryEvents() {
@ -257,18 +261,18 @@ index 0000000000000000000000000000000000000000..1f89945be2ed68f52a544f41f7a151b8
+}
diff --git a/src/main/java/io/papermc/paper/registry/event/RegistryFreezeEvent.java b/src/main/java/io/papermc/paper/registry/event/RegistryFreezeEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..12ec7e794a5047a30354a485acd40fa0f3438eea
index 0000000000000000000000000000000000000000..59e8ca6c5b7fa0424ad9b2c74545ec53444b5fcb
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/event/RegistryFreezeEvent.java
@@ -0,0 +1,39 @@
@@ -0,0 +1,40 @@
+package io.papermc.paper.registry.event;
+
+import io.papermc.paper.registry.RegistryBuilder;
+import io.papermc.paper.registry.tag.Tag;
+import io.papermc.paper.registry.tag.TagKey;
+import org.bukkit.Keyed;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.jetbrains.annotations.ApiStatus;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * Event object for {@link RegistryEventProvider#freeze()}. This
@ -279,6 +283,7 @@ index 0000000000000000000000000000000000000000..12ec7e794a5047a30354a485acd40fa0
+ * @param <B> registry entry builder type
+ */
+@ApiStatus.Experimental
+@NullMarked
+@ApiStatus.NonExtendable
+public interface RegistryFreezeEvent<T, B extends RegistryBuilder<T>> extends RegistryEvent<T> {
+
@ -287,7 +292,7 @@ index 0000000000000000000000000000000000000000..12ec7e794a5047a30354a485acd40fa0
+ *
+ * @return a writable registry
+ */
+ @NonNull WritableRegistry<T, B> registry();
+ WritableRegistry<T, B> registry();
+
+ /**
+ * Gets or creates a tag for the given tag key. This tag
@ -298,21 +303,21 @@ index 0000000000000000000000000000000000000000..12ec7e794a5047a30354a485acd40fa0
+ * @return the tag
+ * @param <V> the tag value type
+ */
+ @NonNull <V extends Keyed> Tag<V> getOrCreateTag(@NonNull TagKey<V> tagKey);
+ <V extends Keyed> Tag<V> getOrCreateTag(TagKey<V> tagKey);
+}
diff --git a/src/main/java/io/papermc/paper/registry/event/WritableRegistry.java b/src/main/java/io/papermc/paper/registry/event/WritableRegistry.java
new file mode 100644
index 0000000000000000000000000000000000000000..6de377275097f065c38dd59c6db9704018ac81fc
index 0000000000000000000000000000000000000000..744f455b14cdc9131497024088da4ca0e8fc39dc
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/event/WritableRegistry.java
@@ -0,0 +1,27 @@
@@ -0,0 +1,28 @@
+package io.papermc.paper.registry.event;
+
+import io.papermc.paper.registry.RegistryBuilder;
+import io.papermc.paper.registry.TypedKey;
+import java.util.function.Consumer;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.jetbrains.annotations.ApiStatus;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * A registry which supports registering new objects.
@ -320,8 +325,9 @@ index 0000000000000000000000000000000000000000..6de377275097f065c38dd59c6db97040
+ * @param <T> registry entry type
+ * @param <B> registry entry builder type
+ */
+@ApiStatus.NonExtendable
+@ApiStatus.Experimental
+@NullMarked
+@ApiStatus.NonExtendable
+public interface WritableRegistry<T, B extends RegistryBuilder<T>> {
+
+ /**
@ -331,28 +337,29 @@ index 0000000000000000000000000000000000000000..6de377275097f065c38dd59c6db97040
+ * @param key the entry's key (must be unique from others)
+ * @param value a consumer for the entry's builder
+ */
+ void register(@NonNull TypedKey<T> key, @NonNull Consumer<? super B> value);
+ void register(TypedKey<T> key, Consumer<? super B> value);
+}
diff --git a/src/main/java/io/papermc/paper/registry/event/type/RegistryEntryAddConfiguration.java b/src/main/java/io/papermc/paper/registry/event/type/RegistryEntryAddConfiguration.java
new file mode 100644
index 0000000000000000000000000000000000000000..c5b484ee5f4a0f283a8d46f5490afac4b58b06be
index 0000000000000000000000000000000000000000..d9bde790f74982c6cd5870aee35fec8a0c49fc83
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/event/type/RegistryEntryAddConfiguration.java
@@ -0,0 +1,42 @@
@@ -0,0 +1,43 @@
+package io.papermc.paper.registry.event.type;
+
+import io.papermc.paper.plugin.bootstrap.BootstrapContext;
+import io.papermc.paper.plugin.lifecycle.event.handler.configuration.PrioritizedLifecycleEventHandlerConfiguration;
+import io.papermc.paper.registry.TypedKey;
+import java.util.function.Predicate;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.jetbrains.annotations.Contract;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * Specific configuration for {@link io.papermc.paper.registry.event.RegistryEntryAddEvent}s.
+ *
+ * @param <T> registry entry type
+ */
+@NullMarked
+public interface RegistryEntryAddConfiguration<T> extends PrioritizedLifecycleEventHandlerConfiguration<BootstrapContext> {
+
+ /**
@ -362,7 +369,7 @@ index 0000000000000000000000000000000000000000..c5b484ee5f4a0f283a8d46f5490afac4
+ * @return this configuration
+ */
+ @Contract(value = "_ -> this", mutates = "this")
+ default @NonNull RegistryEntryAddConfiguration<T> filter(final @NonNull TypedKey<T> key) {
+ default RegistryEntryAddConfiguration<T> filter(final TypedKey<T> key) {
+ return this.filter(key::equals);
+ }
+
@ -373,20 +380,20 @@ index 0000000000000000000000000000000000000000..c5b484ee5f4a0f283a8d46f5490afac4
+ * @return this configuration
+ */
+ @Contract(value = "_ -> this", mutates = "this")
+ @NonNull RegistryEntryAddConfiguration<T> filter(@NonNull Predicate<TypedKey<T>> filter);
+ RegistryEntryAddConfiguration<T> filter(Predicate<TypedKey<T>> filter);
+
+ @Override
+ @NonNull RegistryEntryAddConfiguration<T> priority(int priority);
+ RegistryEntryAddConfiguration<T> priority(int priority);
+
+ @Override
+ @NonNull RegistryEntryAddConfiguration<T> monitor();
+ RegistryEntryAddConfiguration<T> monitor();
+}
diff --git a/src/main/java/io/papermc/paper/registry/event/type/RegistryEntryAddEventType.java b/src/main/java/io/papermc/paper/registry/event/type/RegistryEntryAddEventType.java
new file mode 100644
index 0000000000000000000000000000000000000000..f4d4ebf6cbed1b4a9955ceb2d0586782181d97e5
index 0000000000000000000000000000000000000000..93447ef58933f37fe12c4507c9a0b233be4c6c0b
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/event/type/RegistryEntryAddEventType.java
@@ -0,0 +1,18 @@
@@ -0,0 +1,20 @@
+package io.papermc.paper.registry.event.type;
+
+import io.papermc.paper.plugin.bootstrap.BootstrapContext;
@ -394,6 +401,7 @@ index 0000000000000000000000000000000000000000..f4d4ebf6cbed1b4a9955ceb2d0586782
+import io.papermc.paper.registry.RegistryBuilder;
+import io.papermc.paper.registry.event.RegistryEntryAddEvent;
+import org.jetbrains.annotations.ApiStatus;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * Lifecycle event type for {@link RegistryEntryAddEvent}s.
@ -402,15 +410,16 @@ index 0000000000000000000000000000000000000000..f4d4ebf6cbed1b4a9955ceb2d0586782
+ * @param <B> registry entry builder type
+ */
+@ApiStatus.Experimental
+@NullMarked
+@ApiStatus.NonExtendable
+public interface RegistryEntryAddEventType<T, B extends RegistryBuilder<T>> extends LifecycleEventType<BootstrapContext, RegistryEntryAddEvent<T, B>, RegistryEntryAddConfiguration<T>> {
+}
diff --git a/src/main/java/io/papermc/paper/registry/set/RegistryKeySet.java b/src/main/java/io/papermc/paper/registry/set/RegistryKeySet.java
new file mode 100644
index 0000000000000000000000000000000000000000..b891101b43148f63c96b7dd611914c85d7b29dbf
index 0000000000000000000000000000000000000000..d5a5e332abc861d8509a5df7b57319f72e6b5449
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/set/RegistryKeySet.java
@@ -0,0 +1,50 @@
@@ -0,0 +1,51 @@
+package io.papermc.paper.registry.set;
+
+import io.papermc.paper.registry.TypedKey;
@ -418,11 +427,12 @@ index 0000000000000000000000000000000000000000..b891101b43148f63c96b7dd611914c85
+import java.util.Iterator;
+import org.bukkit.Keyed;
+import org.bukkit.Registry;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.Unmodifiable;
+import org.jspecify.annotations.NullMarked;
+
+@ApiStatus.Experimental
+@NullMarked
+@ApiStatus.NonExtendable
+public non-sealed interface RegistryKeySet<T extends Keyed> extends Iterable<TypedKey<T>>, RegistrySet<T> { // TODO remove Keyed
+
@ -436,7 +446,7 @@ index 0000000000000000000000000000000000000000..b891101b43148f63c96b7dd611914c85
+ *
+ * @return the keys
+ */
+ @NonNull @Unmodifiable Collection<TypedKey<T>> values();
+ @Unmodifiable Collection<TypedKey<T>> values();
+
+ /**
+ * Resolve this set into a collection of values. Prefer using
@ -446,7 +456,7 @@ index 0000000000000000000000000000000000000000..b891101b43148f63c96b7dd611914c85
+ * @return the resolved values
+ * @see RegistryKeySet#values()
+ */
+ @NonNull @Unmodifiable Collection<T> resolve(final @NonNull Registry<T> registry);
+ @Unmodifiable Collection<T> resolve(final Registry<T> registry);
+
+ /**
+ * Checks if this set contains the value with the given key.
@ -454,19 +464,19 @@ index 0000000000000000000000000000000000000000..b891101b43148f63c96b7dd611914c85
+ * @param valueKey the key to check
+ * @return true if the value is in this set
+ */
+ boolean contains(@NonNull TypedKey<T> valueKey);
+ boolean contains(TypedKey<T> valueKey);
+
+ @Override
+ default @NonNull Iterator<TypedKey<T>> iterator() {
+ default Iterator<TypedKey<T>> iterator() {
+ return this.values().iterator();
+ }
+}
diff --git a/src/main/java/io/papermc/paper/registry/set/RegistryKeySetImpl.java b/src/main/java/io/papermc/paper/registry/set/RegistryKeySetImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..c712181ad4c6a9d00bc04f8a48515a388c692f48
index 0000000000000000000000000000000000000000..fe4a0817d7ac4a9f958a6156d6ae3ac1c96f58cd
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/set/RegistryKeySetImpl.java
@@ -0,0 +1,53 @@
@@ -0,0 +1,52 @@
+package io.papermc.paper.registry.set;
+
+import com.google.common.base.Preconditions;
@ -480,20 +490,19 @@ index 0000000000000000000000000000000000000000..c712181ad4c6a9d00bc04f8a48515a38
+import org.bukkit.Keyed;
+import org.bukkit.NamespacedKey;
+import org.bukkit.Registry;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.framework.qual.DefaultQualifier;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.Nullable;
+import org.jspecify.annotations.NullMarked;
+import org.jspecify.annotations.Nullable;
+
+@ApiStatus.Internal
+@DefaultQualifier(NonNull.class)
+record RegistryKeySetImpl<T extends Keyed>(RegistryKey<T> registryKey, List<TypedKey<T>> values) implements RegistryKeySet<T> { // TODO remove Keyed
+@NullMarked
+record RegistryKeySetImpl<T extends @Nullable Keyed>(RegistryKey<T> registryKey, List<TypedKey<T>> values) implements RegistryKeySet<T> { // TODO remove Keyed
+
+ static <T extends Keyed> RegistryKeySet<T> create(final RegistryKey<T> registryKey, final Iterable<? extends T> values) { // TODO remove Keyed
+ final Registry<T> registry = RegistryAccess.registryAccess().getRegistry(registryKey);
+ final ArrayList<TypedKey<T>> keys = new ArrayList<>();
+ for (final T value : values) {
+ final @Nullable NamespacedKey key = registry.getKey(value);
+ final NamespacedKey key = registry.getKey(value);
+ Preconditions.checkArgument(key != null, value + " does not have a key in " + registryKey);
+ keys.add(TypedKey.create(registryKey, key));
+ }
@ -513,7 +522,7 @@ index 0000000000000000000000000000000000000000..c712181ad4c6a9d00bc04f8a48515a38
+ public Collection<T> resolve(final Registry<T> registry) {
+ final List<T> values = new ArrayList<>(this.values.size());
+ for (final TypedKey<T> key : this.values) {
+ final @Nullable T value = registry.get(key.key());
+ final T value = registry.get(key.key());
+ Preconditions.checkState(value != null, "Trying to access unbound TypedKey: " + key);
+ values.add(value);
+ }
@ -522,10 +531,10 @@ index 0000000000000000000000000000000000000000..c712181ad4c6a9d00bc04f8a48515a38
+}
diff --git a/src/main/java/io/papermc/paper/registry/set/RegistrySet.java b/src/main/java/io/papermc/paper/registry/set/RegistrySet.java
new file mode 100644
index 0000000000000000000000000000000000000000..108df480e16131781a52c7bbba2ca6581e4c1ca1
index 0000000000000000000000000000000000000000..c6fdeb05242418d20472aeed8cb61bfe937911c8
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/set/RegistrySet.java
@@ -0,0 +1,112 @@
@@ -0,0 +1,113 @@
+package io.papermc.paper.registry.set;
+
+import com.google.common.collect.Lists;
@ -533,9 +542,9 @@ index 0000000000000000000000000000000000000000..108df480e16131781a52c7bbba2ca658
+import io.papermc.paper.registry.TypedKey;
+import io.papermc.paper.registry.tag.Tag;
+import org.bukkit.Keyed;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.Contract;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * Represents a collection tied to a registry.
@ -553,6 +562,7 @@ index 0000000000000000000000000000000000000000..108df480e16131781a52c7bbba2ca658
+ * @param <T> registry value type
+ */
+@ApiStatus.Experimental
+@NullMarked
+public sealed interface RegistrySet<T> permits RegistryKeySet, RegistryValueSet {
+
+ // TODO uncomment when direct holder sets need to be exposed to the API
@ -566,7 +576,7 @@ index 0000000000000000000000000000000000000000..108df480e16131781a52c7bbba2ca658
+ // * @param <T> the type of the values
+ // */
+ // @Contract(value = "_, _ -> new", pure = true)
+ // static <T> @NonNull RegistryValueSet<T> valueSet(final @NonNull RegistryKey<T> registryKey, final @NonNull Iterable<? extends T> values) {
+ // static <T> RegistryValueSet<T> valueSet(final RegistryKey<T> registryKey, final Iterable<? extends T> values) {
+ // return RegistryValueSetImpl.create(registryKey, values);
+ // }
+
@ -584,7 +594,7 @@ index 0000000000000000000000000000000000000000..108df480e16131781a52c7bbba2ca658
+ * @throws IllegalArgumentException if the registry isn't available yet or if any value doesn't have a key in that registry
+ */
+ @Contract(value = "_, _ -> new", pure = true)
+ static <T extends Keyed> @NonNull RegistryKeySet<T> keySetFromValues(final @NonNull RegistryKey<T> registryKey, final @NonNull Iterable<? extends T> values) { // TODO remove Keyed
+ static <T extends Keyed> RegistryKeySet<T> keySetFromValues(final RegistryKey<T> registryKey, final Iterable<? extends T> values) { // TODO remove Keyed
+ return RegistryKeySetImpl.create(registryKey, values);
+ }
+
@ -597,7 +607,7 @@ index 0000000000000000000000000000000000000000..108df480e16131781a52c7bbba2ca658
+ * @param <T> the type of the values
+ */
+ @SafeVarargs
+ static <T extends Keyed> RegistryKeySet<T> keySet(final @NonNull RegistryKey<T> registryKey, final @NonNull TypedKey<T> @NonNull... keys) { // TODO remove Keyed
+ static <T extends Keyed> RegistryKeySet<T> keySet(final RegistryKey<T> registryKey, final TypedKey<T>... keys) { // TODO remove Keyed
+ return keySet(registryKey, Lists.newArrayList(keys));
+ }
+
@ -611,7 +621,7 @@ index 0000000000000000000000000000000000000000..108df480e16131781a52c7bbba2ca658
+ */
+ @SuppressWarnings("BoundedWildcard")
+ @Contract(value = "_, _ -> new", pure = true)
+ static <T extends Keyed> @NonNull RegistryKeySet<T> keySet(final @NonNull RegistryKey<T> registryKey, final @NonNull Iterable<TypedKey<T>> keys) { // TODO remove Keyed
+ static <T extends Keyed> RegistryKeySet<T> keySet(final RegistryKey<T> registryKey, final Iterable<TypedKey<T>> keys) { // TODO remove Keyed
+ return new RegistryKeySetImpl<>(registryKey, Lists.newArrayList(keys));
+ }
+
@ -620,7 +630,7 @@ index 0000000000000000000000000000000000000000..108df480e16131781a52c7bbba2ca658
+ *
+ * @return the registry key
+ */
+ @NonNull RegistryKey<T> registryKey();
+ RegistryKey<T> registryKey();
+
+ /**
+ * Get the size of this set.
@ -640,17 +650,17 @@ index 0000000000000000000000000000000000000000..108df480e16131781a52c7bbba2ca658
+}
diff --git a/src/main/java/io/papermc/paper/registry/set/RegistryValueSet.java b/src/main/java/io/papermc/paper/registry/set/RegistryValueSet.java
new file mode 100644
index 0000000000000000000000000000000000000000..58e2e42737d48b243854466eb7f7d3a844a86b6e
index 0000000000000000000000000000000000000000..9f7761535c30c376dad64678fa2ee24bbb041e06
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/set/RegistryValueSet.java
@@ -0,0 +1,34 @@
@@ -0,0 +1,35 @@
+package io.papermc.paper.registry.set;
+
+import java.util.Collection;
+import java.util.Iterator;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.Unmodifiable;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * A collection of anonymous values relating to a registry. These
@ -659,6 +669,7 @@ index 0000000000000000000000000000000000000000..58e2e42737d48b243854466eb7f7d3a8
+ * @param <T> registry value type
+ */
+@ApiStatus.Experimental
+@NullMarked
+public sealed interface RegistryValueSet<T> extends Iterable<T>, RegistrySet<T> permits RegistryValueSetImpl {
+
+ @Override
@ -671,27 +682,29 @@ index 0000000000000000000000000000000000000000..58e2e42737d48b243854466eb7f7d3a8
+ *
+ * @return the values
+ */
+ @NonNull @Unmodifiable Collection<T> values();
+ @Unmodifiable Collection<T> values();
+
+ @Override
+ default @NonNull Iterator<T> iterator() {
+ default Iterator<T> iterator() {
+ return this.values().iterator();
+ }
+}
diff --git a/src/main/java/io/papermc/paper/registry/set/RegistryValueSetImpl.java b/src/main/java/io/papermc/paper/registry/set/RegistryValueSetImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..4ce5b26a1fcaae7b28ac8ed3c25014b66c266318
index 0000000000000000000000000000000000000000..1bcc6b4b9ff0090570d4f39a49e10e1fa03edb7d
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/set/RegistryValueSetImpl.java
@@ -0,0 +1,18 @@
@@ -0,0 +1,20 @@
+package io.papermc.paper.registry.set;
+
+import com.google.common.collect.Lists;
+import io.papermc.paper.registry.RegistryKey;
+import java.util.List;
+import org.jetbrains.annotations.ApiStatus;
+import org.jspecify.annotations.NullMarked;
+
+@ApiStatus.Internal
+@NullMarked
+record RegistryValueSetImpl<T>(RegistryKey<T> registryKey, List<T> values) implements RegistryValueSet<T> {
+
+ RegistryValueSetImpl {
@ -704,16 +717,16 @@ index 0000000000000000000000000000000000000000..4ce5b26a1fcaae7b28ac8ed3c25014b6
+}
diff --git a/src/main/java/io/papermc/paper/registry/tag/Tag.java b/src/main/java/io/papermc/paper/registry/tag/Tag.java
new file mode 100644
index 0000000000000000000000000000000000000000..ae374f68ef9baa16ed90c371f1622de0c0159873
index 0000000000000000000000000000000000000000..245eb1074ce2930e4d9ff42a5df49004d08bbac2
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/tag/Tag.java
@@ -0,0 +1,25 @@
@@ -0,0 +1,26 @@
+package io.papermc.paper.registry.tag;
+
+import io.papermc.paper.registry.set.RegistryKeySet;
+import org.bukkit.Keyed;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.jetbrains.annotations.ApiStatus;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * A named {@link RegistryKeySet} which are created
@ -724,6 +737,7 @@ index 0000000000000000000000000000000000000000..ae374f68ef9baa16ed90c371f1622de0
+ * @see org.bukkit.Registry#getTag(TagKey)
+ */
+@ApiStatus.Experimental
+@NullMarked
+public interface Tag<T extends Keyed> extends RegistryKeySet<T> { // TODO remove Keyed
+
+ /**
@ -731,24 +745,25 @@ index 0000000000000000000000000000000000000000..ae374f68ef9baa16ed90c371f1622de0
+ *
+ * @return the tag key identifier
+ */
+ @NonNull TagKey<T> tagKey();
+ TagKey<T> tagKey();
+}
diff --git a/src/main/java/io/papermc/paper/registry/tag/TagKey.java b/src/main/java/io/papermc/paper/registry/tag/TagKey.java
new file mode 100644
index 0000000000000000000000000000000000000000..a49d328e95f7fda6567ee6c4f5f1878a2c187277
index 0000000000000000000000000000000000000000..f84d95b29fd2c0a52d2769fa9d3e2326ebc8aa3f
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/tag/TagKey.java
@@ -0,0 +1,32 @@
@@ -0,0 +1,33 @@
+package io.papermc.paper.registry.tag;
+
+import io.papermc.paper.registry.RegistryKey;
+import net.kyori.adventure.key.Key;
+import net.kyori.adventure.key.Keyed;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.Contract;
+import org.jspecify.annotations.NullMarked;
+
+@ApiStatus.Experimental
+@NullMarked
+public sealed interface TagKey<T> extends Keyed permits TagKeyImpl {
+
+ /**
@ -760,7 +775,7 @@ index 0000000000000000000000000000000000000000..a49d328e95f7fda6567ee6c4f5f1878a
+ * @param <T> the registry value type
+ */
+ @Contract(value = "_, _ -> new", pure = true)
+ static <T> @NonNull TagKey<T> create(final @NonNull RegistryKey<T> registryKey, final @NonNull Key key) {
+ static <T> TagKey<T> create(final RegistryKey<T> registryKey, final Key key) {
+ return new TagKeyImpl<>(registryKey, key);
+ }
+
@ -769,24 +784,23 @@ index 0000000000000000000000000000000000000000..a49d328e95f7fda6567ee6c4f5f1878a
+ *
+ * @return the registry key
+ */
+ @NonNull RegistryKey<T> registryKey();
+ RegistryKey<T> registryKey();
+}
diff --git a/src/main/java/io/papermc/paper/registry/tag/TagKeyImpl.java b/src/main/java/io/papermc/paper/registry/tag/TagKeyImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..5fde17e97eddcd1d01ee39694c9159d0ff2a09e3
index 0000000000000000000000000000000000000000..bf49125acc8a0508bf59674bba3ed3505ee9481a
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/tag/TagKeyImpl.java
@@ -0,0 +1,17 @@
@@ -0,0 +1,16 @@
+package io.papermc.paper.registry.tag;
+
+import io.papermc.paper.registry.RegistryKey;
+import net.kyori.adventure.key.Key;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.framework.qual.DefaultQualifier;
+import org.jetbrains.annotations.ApiStatus;
+import org.jspecify.annotations.NullMarked;
+
+@ApiStatus.Internal
+@DefaultQualifier(NonNull.class)
+@NullMarked
+record TagKeyImpl<T>(RegistryKey<T> registryKey, Key key) implements TagKey<T> {
+
+ @Override

View file

@ -5,10 +5,10 @@ Subject: [PATCH] Introduce registry entry and builders
diff --git a/src/main/java/io/papermc/paper/registry/RegistryKey.java b/src/main/java/io/papermc/paper/registry/RegistryKey.java
index 1e2765d296decbb3e61b8434e78b5e0214f8fee0..5e580649578d25f567b0f7d40bcdc68f62701f0a 100644
index 8410d7213370f01cbedbf7fac29bac96f150c49a..d8716f855806471728c35b3ec34efb808a5146cf 100644
--- a/src/main/java/io/papermc/paper/registry/RegistryKey.java
+++ b/src/main/java/io/papermc/paper/registry/RegistryKey.java
@@ -77,9 +77,10 @@ public sealed interface RegistryKey<T> extends Keyed permits RegistryKeyImpl {
@@ -79,9 +79,10 @@ public sealed interface RegistryKey<T> extends Keyed permits RegistryKeyImpl {
@ApiStatus.Internal
RegistryKey<BlockType> BLOCK = create("block");
/**
@ -23,7 +23,7 @@ index 1e2765d296decbb3e61b8434e78b5e0214f8fee0..5e580649578d25f567b0f7d40bcdc68f
* Built-in registry for cat variants.
diff --git a/src/main/java/io/papermc/paper/registry/data/EnchantmentRegistryEntry.java b/src/main/java/io/papermc/paper/registry/data/EnchantmentRegistryEntry.java
new file mode 100644
index 0000000000000000000000000000000000000000..6c7d8b98103909428bb4dcf14825fe188db6d12f
index 0000000000000000000000000000000000000000..d0d19a4bb2ba0e92710861b106777a428bdc5ad9
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/data/EnchantmentRegistryEntry.java
@@ -0,0 +1,332 @@
@ -40,18 +40,18 @@ index 0000000000000000000000000000000000000000..6c7d8b98103909428bb4dcf14825fe18
+import org.bukkit.enchantments.Enchantment;
+import org.bukkit.inventory.EquipmentSlotGroup;
+import org.bukkit.inventory.ItemType;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.checker.nullness.qual.Nullable;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Range;
+import org.jetbrains.annotations.Unmodifiable;
+import org.jspecify.annotations.NullMarked;
+import org.jspecify.annotations.Nullable;
+
+/**
+ * A data-centric version-specific registry entry for the {@link Enchantment} type.
+ */
+@ApiStatus.Experimental
+@NullMarked
+@ApiStatus.NonExtendable
+public interface EnchantmentRegistryEntry {
+
@ -61,14 +61,14 @@ index 0000000000000000000000000000000000000000..6c7d8b98103909428bb4dcf14825fe18
+ *
+ * @return the description component.
+ */
+ @NonNull Component description();
+ Component description();
+
+ /**
+ * Provides the registry key set referencing the items this enchantment is supported on.
+ *
+ * @return the registry key set.
+ */
+ @NonNull RegistryKeySet<ItemType> supportedItems();
+ RegistryKeySet<ItemType> supportedItems();
+
+ /**
+ * Provides the registry key set referencing the item types this enchantment can be applied to when
@ -106,7 +106,7 @@ index 0000000000000000000000000000000000000000..6c7d8b98103909428bb4dcf14825fe18
+ * @see <a href="https://minecraft.wiki/w/Enchanting/Levels">https://minecraft.wiki/w/Enchanting/Levels</a> for
+ * examplary costs.
+ */
+ @NonNull EnchantmentCost minimumCost();
+ EnchantmentCost minimumCost();
+
+ /**
+ * Provides the maximum cost allowed to enchant an item with this enchantment.
@ -117,7 +117,7 @@ index 0000000000000000000000000000000000000000..6c7d8b98103909428bb4dcf14825fe18
+ * @see <a href="https://minecraft.wiki/w/Enchanting/Levels">https://minecraft.wiki/w/Enchanting/Levels</a> for
+ * examplary costs.
+ */
+ @NonNull EnchantmentCost maximumCost();
+ EnchantmentCost maximumCost();
+
+ /**
+ * Provides the cost of applying this enchantment using an anvil.
@ -140,7 +140,7 @@ index 0000000000000000000000000000000000000000..6c7d8b98103909428bb4dcf14825fe18
+ * @return a list of equipment slot groups.
+ * @see Enchantment#getActiveSlotGroups()
+ */
+ @NonNull @Unmodifiable List<EquipmentSlotGroup> activeSlots();
+ @Unmodifiable List<EquipmentSlotGroup> activeSlots();
+
+ /**
+ * Provides the registry key set of enchantments that this enchantment is exclusive with.
@ -150,7 +150,7 @@ index 0000000000000000000000000000000000000000..6c7d8b98103909428bb4dcf14825fe18
+ *
+ * @return a registry set of enchantments exclusive to this one.
+ */
+ @NonNull RegistryKeySet<Enchantment> exclusiveWith();
+ RegistryKeySet<Enchantment> exclusiveWith();
+
+ /**
+ * A mutable builder for the {@link EnchantmentRegistryEntry} plugins may change in applicable registry events.
@ -179,7 +179,7 @@ index 0000000000000000000000000000000000000000..6c7d8b98103909428bb4dcf14825fe18
+ * @return this builder.
+ */
+ @Contract(value = "_ -> this", mutates = "this")
+ @NonNull Builder description(@NonNull Component description);
+ Builder description(Component description);
+
+ /**
+ * Configures the set of supported items this enchantment can be applied on. This
@ -195,7 +195,7 @@ index 0000000000000000000000000000000000000000..6c7d8b98103909428bb4dcf14825fe18
+ * @see io.papermc.paper.registry.event.RegistryFreezeEvent#getOrCreateTag(TagKey)
+ */
+ @Contract(value = "_ -> this", mutates = "this")
+ @NonNull Builder supportedItems(@NonNull RegistryKeySet<ItemType> supportedItems);
+ Builder supportedItems(RegistryKeySet<ItemType> supportedItems);
+
+ /**
+ * Configures a set of item types this enchantment can naturally be applied to, when enchanting in an
@ -216,7 +216,7 @@ index 0000000000000000000000000000000000000000..6c7d8b98103909428bb4dcf14825fe18
+ * @see io.papermc.paper.registry.event.RegistryFreezeEvent#getOrCreateTag(TagKey)
+ */
+ @Contract(value = "_ -> this", mutates = "this")
+ @NonNull Builder primaryItems(@Nullable RegistryKeySet<ItemType> primaryItems);
+ Builder primaryItems(@Nullable RegistryKeySet<ItemType> primaryItems);
+
+ /**
+ * Configures the weight of this enchantment used by the weighted random when selecting enchantments.
@ -226,7 +226,7 @@ index 0000000000000000000000000000000000000000..6c7d8b98103909428bb4dcf14825fe18
+ * @see <a href="https://minecraft.wiki/w/Enchanting">https://minecraft.wiki/w/Enchanting</a> for examplary weights.
+ */
+ @Contract(value = "_ -> this", mutates = "this")
+ @NonNull Builder weight(@Range(from = 1, to = 1024) int weight);
+ Builder weight(@Range(from = 1, to = 1024) int weight);
+
+ /**
+ * Configures the maximum level this enchantment can have when applied.
@ -235,7 +235,7 @@ index 0000000000000000000000000000000000000000..6c7d8b98103909428bb4dcf14825fe18
+ * @return this builder.
+ */
+ @Contract(value = "_ -> this", mutates = "this")
+ @NonNull Builder maxLevel(@Range(from = 1, to = 255) int maxLevel);
+ Builder maxLevel(@Range(from = 1, to = 255) int maxLevel);
+
+ /**
+ * Configures the minimum cost needed to enchant an item with this enchantment.
@ -248,7 +248,7 @@ index 0000000000000000000000000000000000000000..6c7d8b98103909428bb4dcf14825fe18
+ * examplary costs.
+ */
+ @Contract(value = "_ -> this", mutates = "this")
+ @NonNull Builder minimumCost(@NotNull EnchantmentCost minimumCost);
+ Builder minimumCost(EnchantmentCost minimumCost);
+
+ /**
+ * Configures the maximum cost to enchant an item with this enchantment.
@ -261,7 +261,7 @@ index 0000000000000000000000000000000000000000..6c7d8b98103909428bb4dcf14825fe18
+ * examplary costs.
+ */
+ @Contract(value = "_ -> this", mutates = "this")
+ @NonNull Builder maximumCost(@NotNull EnchantmentCost maximumCost);
+ Builder maximumCost(EnchantmentCost maximumCost);
+
+ /**
+ * Configures the cost of applying this enchantment using an anvil.
@ -275,7 +275,7 @@ index 0000000000000000000000000000000000000000..6c7d8b98103909428bb4dcf14825fe18
+ * @see Enchantment#getAnvilCost()
+ */
+ @Contract(value = "_ -> this", mutates = "this")
+ @NonNull Builder anvilCost(@Range(from = 0, to = Integer.MAX_VALUE) int anvilCost);
+ Builder anvilCost(@Range(from = 0, to = Integer.MAX_VALUE) int anvilCost);
+
+ /**
+ * Configures the list of slot groups this enchantment may be active in.
@ -288,7 +288,7 @@ index 0000000000000000000000000000000000000000..6c7d8b98103909428bb4dcf14825fe18
+ * @see Enchantment#getActiveSlotGroups()
+ */
+ @Contract(value = "_ -> this", mutates = "this")
+ default @NonNull Builder activeSlots(final @NonNull EquipmentSlotGroup @NonNull... activeSlots) {
+ default Builder activeSlots(final EquipmentSlotGroup... activeSlots) {
+ return this.activeSlots(List.of(activeSlots));
+ }
+
@ -303,7 +303,7 @@ index 0000000000000000000000000000000000000000..6c7d8b98103909428bb4dcf14825fe18
+ * @see Enchantment#getActiveSlotGroups()
+ */
+ @Contract(value = "_ -> this", mutates = "this")
+ @NonNull Builder activeSlots(@NonNull Iterable<@NonNull EquipmentSlotGroup> activeSlots);
+ Builder activeSlots(Iterable<EquipmentSlotGroup> activeSlots);
+
+ /**
+ * Configures the registry key set of enchantments that this enchantment is exclusive with.
@ -319,7 +319,7 @@ index 0000000000000000000000000000000000000000..6c7d8b98103909428bb4dcf14825fe18
+ * @see io.papermc.paper.registry.event.RegistryFreezeEvent#getOrCreateTag(TagKey)
+ */
+ @Contract(value = "_ -> this", mutates = "this")
+ @NonNull Builder exclusiveWith(@NonNull RegistryKeySet<Enchantment> exclusiveWith);
+ Builder exclusiveWith(RegistryKeySet<Enchantment> exclusiveWith);
+ }
+
+ /**
@ -345,7 +345,7 @@ index 0000000000000000000000000000000000000000..6c7d8b98103909428bb4dcf14825fe18
+ /**
+ * Creates a new enchantment cost instance based on the passed values.
+ *
+ * @param baseCost the base cost of the enchantment cost as returned by {@link #baseCost()}
+ * @param baseCost the base cost of the enchantment cost as returned by {@link #baseCost()}
+ * @param additionalPerLevelCost the additional cost per level, as returned by {@link #additionalPerLevelCost()}
+ * @return the created instance.
+ */
@ -361,23 +361,24 @@ index 0000000000000000000000000000000000000000..6c7d8b98103909428bb4dcf14825fe18
+}
diff --git a/src/main/java/io/papermc/paper/registry/data/GameEventRegistryEntry.java b/src/main/java/io/papermc/paper/registry/data/GameEventRegistryEntry.java
new file mode 100644
index 0000000000000000000000000000000000000000..a59620816f8795fc0e585f969741d1940340673a
index 0000000000000000000000000000000000000000..980fe12b75258b51cc2498590cadb9de80805b1f
--- /dev/null
+++ b/src/main/java/io/papermc/paper/registry/data/GameEventRegistryEntry.java
@@ -0,0 +1,48 @@
@@ -0,0 +1,49 @@
+package io.papermc.paper.registry.data;
+
+import io.papermc.paper.registry.RegistryBuilder;
+import org.bukkit.GameEvent;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.Range;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * A data-centric version-specific registry entry for the {@link GameEvent} type.
+ */
+@ApiStatus.Experimental
+@NullMarked
+@ApiStatus.NonExtendable
+public interface GameEventRegistryEntry {
+
@ -410,7 +411,7 @@ index 0000000000000000000000000000000000000000..a59620816f8795fc0e585f969741d194
+ * @see GameEvent#getRange()
+ */
+ @Contract(value = "_ -> this", mutates = "this")
+ @NonNull Builder range(@Range(from = 0, to = Integer.MAX_VALUE) int range);
+ Builder range(@Range(from = 0, to = Integer.MAX_VALUE) int range);
+ }
+}
diff --git a/src/main/java/io/papermc/paper/registry/data/package-info.java b/src/main/java/io/papermc/paper/registry/data/package-info.java
@ -429,10 +430,10 @@ index 0000000000000000000000000000000000000000..4f8f536f437c5f483ac7bce393e664fd
+ */
+package io.papermc.paper.registry.data;
diff --git a/src/main/java/io/papermc/paper/registry/event/RegistryEvents.java b/src/main/java/io/papermc/paper/registry/event/RegistryEvents.java
index 1f89945be2ed68f52a544f41f7a151b8fdfe113e..b32ae215e976bcfcdd86b03037de61b3d896f57c 100644
index 91ae9c0d3ec55ce417d4b447bf3d1b0d0c174b5e..1c8e77c7243cfedef6c4d1491cf98e6ec8f1690f 100644
--- a/src/main/java/io/papermc/paper/registry/event/RegistryEvents.java
+++ b/src/main/java/io/papermc/paper/registry/event/RegistryEvents.java
@@ -1,7 +1,14 @@
@@ -1,8 +1,15 @@
package io.papermc.paper.registry.event;
+import io.papermc.paper.registry.RegistryKey;
@ -441,14 +442,15 @@ index 1f89945be2ed68f52a544f41f7a151b8fdfe113e..b32ae215e976bcfcdd86b03037de61b3
+import org.bukkit.GameEvent;
+import org.bukkit.enchantments.Enchantment;
import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.NullMarked;
+import static io.papermc.paper.registry.event.RegistryEventProviderImpl.create;
+
/**
* Holds providers for {@link RegistryEntryAddEvent} and {@link RegistryFreezeEvent}
* handlers for each applicable registry.
@@ -9,6 +16,9 @@ import org.jetbrains.annotations.ApiStatus;
@ApiStatus.Experimental
@@ -11,6 +18,9 @@ import org.jspecify.annotations.NullMarked;
@NullMarked
public final class RegistryEvents {
+ public static final RegistryEventProvider<GameEvent, GameEventRegistryEntry.Builder> GAME_EVENT = create(RegistryKey.GAME_EVENT);

View file

@ -6,10 +6,10 @@ Subject: [PATCH] Make a PDC view accessible directly from ItemStack
diff --git a/src/main/java/io/papermc/paper/persistence/PersistentDataContainerView.java b/src/main/java/io/papermc/paper/persistence/PersistentDataContainerView.java
new file mode 100644
index 0000000000000000000000000000000000000000..4a4949fde3f03e2ea9d2dde245f7dea119e202bf
index 0000000000000000000000000000000000000000..061ffc1f050a950e041ceef217b5f335e3cd0af1
--- /dev/null
+++ b/src/main/java/io/papermc/paper/persistence/PersistentDataContainerView.java
@@ -0,0 +1,167 @@
@@ -0,0 +1,160 @@
+package io.papermc.paper.persistence;
+
+import java.util.Set;
@ -18,15 +18,17 @@ index 0000000000000000000000000000000000000000..4a4949fde3f03e2ea9d2dde245f7dea1
+import org.bukkit.persistence.PersistentDataContainer;
+import org.bukkit.persistence.PersistentDataHolder;
+import org.bukkit.persistence.PersistentDataType;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.checker.nullness.qual.Nullable;
+import org.jetbrains.annotations.ApiStatus;
+import org.jspecify.annotations.NullMarked;
+import org.jspecify.annotations.Nullable;
+
+/**
+ * This represents a view of a persistent data container. No
+ * methods on this interface mutate the container.
+ *
+ * @see PersistentDataContainer
+ */
+@NullMarked
+@ApiStatus.NonExtendable
+public interface PersistentDataContainerView {
+
@ -52,14 +54,12 @@ index 0000000000000000000000000000000000000000..4a4949fde3f03e2ea9d2dde245f7dea1
+ * @param type the type the primative stored value has to match
+ * @param <P> the generic type of the stored primitive
+ * @param <C> the generic type of the eventually created complex object
+ *
+ * @return if a value with the provided key and type exists
+ *
+ * @throws IllegalArgumentException if the key to look up is null
+ * @throws IllegalArgumentException if the type to cast the found object to is
+ * null
+ */
+ <P, C> boolean has(@NonNull NamespacedKey key, @NonNull PersistentDataType<P, C> type);
+ <P, C> boolean has(NamespacedKey key, PersistentDataType<P, C> type);
+
+ /**
+ * Returns if the persistent metadata provider has metadata registered matching
@ -73,12 +73,10 @@ index 0000000000000000000000000000000000000000..4a4949fde3f03e2ea9d2dde245f7dea1
+ * namespace.
+ *
+ * @param key the key the value is stored under
+ *
+ * @return if a value with the provided key exists
+ *
+ * @throws IllegalArgumentException if the key to look up is null
+ */
+ boolean has(@NonNull NamespacedKey key);
+ boolean has(NamespacedKey key);
+
+ /**
+ * Returns the metadata value that is stored on the
@ -88,10 +86,8 @@ index 0000000000000000000000000000000000000000..4a4949fde3f03e2ea9d2dde245f7dea1
+ * @param type the type the value must have and will be casted to
+ * @param <P> the generic type of the stored primitive
+ * @param <C> the generic type of the eventually created complex object
+ *
+ * @return the value or {@code null} if no value was mapped under the given
+ * value
+ *
+ * @throws IllegalArgumentException if the key to look up is null
+ * @throws IllegalArgumentException if the type to cast the found object to is
+ * null
@ -101,7 +97,7 @@ index 0000000000000000000000000000000000000000..4a4949fde3f03e2ea9d2dde245f7dea1
+ * the {@link
+ * PersistentDataType#getPrimitiveType()}
+ */
+ <P, C> @Nullable C get(@NonNull NamespacedKey key, @NonNull PersistentDataType<P, C> type);
+ <P, C> @Nullable C get(NamespacedKey key, PersistentDataType<P, C> type);
+
+ /**
+ * Returns the metadata value that is stored on the
@ -114,10 +110,8 @@ index 0000000000000000000000000000000000000000..4a4949fde3f03e2ea9d2dde245f7dea1
+ * the provided key
+ * @param <P> the generic type of the stored primitive
+ * @param <C> the generic type of the eventually created complex object
+ *
+ * @return the value or the default value if no value was mapped under the
+ * given key
+ *
+ * @throws IllegalArgumentException if the key to look up is null
+ * @throws IllegalArgumentException if the type to cast the found object to is
+ * null
@ -126,18 +120,18 @@ index 0000000000000000000000000000000000000000..4a4949fde3f03e2ea9d2dde245f7dea1
+ * @throws IllegalArgumentException if no suitable adapter was found for
+ * the {@link PersistentDataType#getPrimitiveType()}
+ */
+ <P, C> @NonNull C getOrDefault(@NonNull NamespacedKey key, @NonNull PersistentDataType<P, C> type, @NonNull C defaultValue);
+ <P, C> C getOrDefault(NamespacedKey key, PersistentDataType<P, C> type, C defaultValue);
+
+ /**
+ * Get the set of keys present on this {@link PersistentDataContainer}
+ * instance.
+ *
+ * <p>
+ * Any changes made to the returned set will not be reflected on the
+ * instance.
+ *
+ * @return the key set
+ */
+ @NonNull Set<NamespacedKey> getKeys();
+ Set<NamespacedKey> getKeys();
+
+ /**
+ * Returns if the container instance is empty, therefore has no entries
@ -154,19 +148,18 @@ index 0000000000000000000000000000000000000000..4a4949fde3f03e2ea9d2dde245f7dea1
+ * This method only copies custom object keys. Existing tags, like the display
+ * name, will not be copied as the values are stored using your namespace.
+ *
+ * @param other the container to copy to
+ * @param other the container to copy to
+ * @param replace whether to replace any matching values in the target container
+ *
+ * @throws IllegalArgumentException if the other container is null
+ */
+ void copyTo(@NonNull PersistentDataContainer other, boolean replace);
+ void copyTo(PersistentDataContainer other, boolean replace);
+
+ /**
+ * Returns the adapter context this tag container uses.
+ *
+ * @return the tag context
+ */
+ @NonNull PersistentDataAdapterContext getAdapterContext();
+ PersistentDataAdapterContext getAdapterContext();
+
+ /**
+ * Serialize this {@link PersistentDataContainer} instance to a
@ -175,23 +168,24 @@ index 0000000000000000000000000000000000000000..4a4949fde3f03e2ea9d2dde245f7dea1
+ * @return a binary representation of this container
+ * @throws java.io.IOException if we fail to write this container to a byte array
+ */
+ byte @NonNull [] serializeToBytes() throws java.io.IOException;
+ byte[] serializeToBytes() throws java.io.IOException;
+}
diff --git a/src/main/java/io/papermc/paper/persistence/PersistentDataViewHolder.java b/src/main/java/io/papermc/paper/persistence/PersistentDataViewHolder.java
new file mode 100644
index 0000000000000000000000000000000000000000..9789916f949374bfb50da535b076180700f7ae73
index 0000000000000000000000000000000000000000..05bc2ac326fb58ea429cee825520e70f7f211f0d
--- /dev/null
+++ b/src/main/java/io/papermc/paper/persistence/PersistentDataViewHolder.java
@@ -0,0 +1,23 @@
@@ -0,0 +1,24 @@
+package io.papermc.paper.persistence;
+
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.jetbrains.annotations.ApiStatus;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * The {@link PersistentDataViewHolder} interface defines an object that can view
+ * custom persistent data on it.
+ */
+@NullMarked
+@ApiStatus.NonExtendable
+public interface PersistentDataViewHolder {
+
@ -204,10 +198,10 @@ index 0000000000000000000000000000000000000000..9789916f949374bfb50da535b0761807
+ *
+ * @return the persistent data container view
+ */
+ @NonNull PersistentDataContainerView getPersistentDataContainer();
+ PersistentDataContainerView getPersistentDataContainer();
+}
diff --git a/src/main/java/org/bukkit/inventory/ItemStack.java b/src/main/java/org/bukkit/inventory/ItemStack.java
index 4fe68a22576933676271f841a5af0ee45b90216f..b3abe3bde05d4a360e31e490bff8a859dc2bd4a6 100644
index 6fc8bddfcabca8c363e0d9f958f0ce7bc39099f1..7f63a3a37eb06049bd5de10466c6dd96cb5dd4ee 100644
--- a/src/main/java/org/bukkit/inventory/ItemStack.java
+++ b/src/main/java/org/bukkit/inventory/ItemStack.java
@@ -27,7 +27,7 @@ import org.jetbrains.annotations.Nullable;

View file

@ -6,19 +6,20 @@ Subject: [PATCH] Leashable API
diff --git a/src/main/java/io/papermc/paper/entity/Leashable.java b/src/main/java/io/papermc/paper/entity/Leashable.java
new file mode 100644
index 0000000000000000000000000000000000000000..7402bab20db0ebcac5b2d492ad002ecf9669caad
index 0000000000000000000000000000000000000000..7e687535d1a622ddf25e3ece387dbfd5b7a49e83
--- /dev/null
+++ b/src/main/java/io/papermc/paper/entity/Leashable.java
@@ -0,0 +1,36 @@
@@ -0,0 +1,37 @@
+package io.papermc.paper.entity;
+
+import org.bukkit.entity.Entity;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.checker.nullness.qual.Nullable;
+import org.jspecify.annotations.NullMarked;
+import org.jspecify.annotations.Nullable;
+
+/**
+ * Represents an entity that can be leashed.
+ */
+@NullMarked
+public interface Leashable extends Entity {
+
+ /**
@ -34,7 +35,7 @@ index 0000000000000000000000000000000000000000..7402bab20db0ebcac5b2d492ad002ecf
+ * @return the entity holding the leash
+ * @throws IllegalStateException if not currently leashed
+ */
+ @NonNull Entity getLeashHolder() throws IllegalStateException;
+ Entity getLeashHolder() throws IllegalStateException;
+
+ /**
+ * Sets the leash on this entity to be held by the supplied entity.

View file

@ -6,17 +6,17 @@ Subject: [PATCH] Add FeatureFlag API
diff --git a/src/main/java/io/papermc/paper/world/flag/FeatureDependant.java b/src/main/java/io/papermc/paper/world/flag/FeatureDependant.java
new file mode 100644
index 0000000000000000000000000000000000000000..0fda065100c17ee00060df16467c0f7f98984ea7
index 0000000000000000000000000000000000000000..f5633319c64bf19cf07e2b9a1cac2dd1d11ab70b
--- /dev/null
+++ b/src/main/java/io/papermc/paper/world/flag/FeatureDependant.java
@@ -0,0 +1,27 @@
@@ -0,0 +1,28 @@
+package io.papermc.paper.world.flag;
+
+import java.util.Set;
+import org.bukkit.FeatureFlag;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.Unmodifiable;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * Implemented by types in built-in registries that are controlled by {@link FeatureFlag FeatureFlags}.
@ -24,6 +24,7 @@ index 0000000000000000000000000000000000000000..0fda065100c17ee00060df16467c0f7f
+ * @apiNote When a type that currently implements this interface transitions to being data-drive, this
+ * interface will be removed from that type in the following major version.
+ */
+@NullMarked
+@ApiStatus.NonExtendable
+public interface FeatureDependant {
+
@ -33,16 +34,16 @@ index 0000000000000000000000000000000000000000..0fda065100c17ee00060df16467c0f7f
+ *
+ * @return the immutable set of feature flags
+ */
+ default @Unmodifiable @NonNull Set<FeatureFlag> requiredFeatures() {
+ default @Unmodifiable Set<FeatureFlag> requiredFeatures() {
+ return FeatureFlagProvider.provider().requiredFeatures(this);
+ }
+}
diff --git a/src/main/java/io/papermc/paper/world/flag/FeatureFlagProvider.java b/src/main/java/io/papermc/paper/world/flag/FeatureFlagProvider.java
new file mode 100644
index 0000000000000000000000000000000000000000..f64cbee35fd3c6fe7b5f7391f6d118166d26dc18
index 0000000000000000000000000000000000000000..388e70628f88d8ee0749a43f82b228453e25d5d0
--- /dev/null
+++ b/src/main/java/io/papermc/paper/world/flag/FeatureFlagProvider.java
@@ -0,0 +1,19 @@
@@ -0,0 +1,21 @@
+package io.papermc.paper.world.flag;
+
+import java.util.Optional;
@ -50,7 +51,9 @@ index 0000000000000000000000000000000000000000..f64cbee35fd3c6fe7b5f7391f6d11816
+import java.util.Set;
+import org.bukkit.FeatureFlag;
+import org.jetbrains.annotations.ApiStatus;
+import org.jspecify.annotations.NullMarked;
+
+@NullMarked
+@ApiStatus.Internal
+interface FeatureFlagProvider {
+
@ -64,22 +67,23 @@ index 0000000000000000000000000000000000000000..f64cbee35fd3c6fe7b5f7391f6d11816
+}
diff --git a/src/main/java/io/papermc/paper/world/flag/FeatureFlagSetHolder.java b/src/main/java/io/papermc/paper/world/flag/FeatureFlagSetHolder.java
new file mode 100644
index 0000000000000000000000000000000000000000..c4432ee20872eeb127852159cf89ed00809fb00e
index 0000000000000000000000000000000000000000..0955699df65ccbb8711cfa48f0b34d5a8805f1af
--- /dev/null
+++ b/src/main/java/io/papermc/paper/world/flag/FeatureFlagSetHolder.java
@@ -0,0 +1,31 @@
@@ -0,0 +1,32 @@
+package io.papermc.paper.world.flag;
+
+import java.util.Set;
+import org.bukkit.FeatureFlag;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.Unmodifiable;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * Implemented by types that hold {@link FeatureFlag FeatureFlags} like
+ * {@link org.bukkit.generator.WorldInfo} and {@link org.bukkit.RegionAccessor}.
+ */
+@NullMarked
+@ApiStatus.NonExtendable
+public interface FeatureFlagSetHolder {
+
@ -88,7 +92,7 @@ index 0000000000000000000000000000000000000000..c4432ee20872eeb127852159cf89ed00
+ *
+ * @return true if enabled
+ */
+ default boolean isEnabled(final @NonNull FeatureDependant featureDependant) {
+ default boolean isEnabled(final FeatureDependant featureDependant) {
+ return this.getFeatureFlags().containsAll(featureDependant.requiredFeatures());
+ }
+
@ -97,7 +101,7 @@ index 0000000000000000000000000000000000000000..c4432ee20872eeb127852159cf89ed00
+ *
+ * @return all enabled {@link FeatureFlag FeatureFlags}
+ */
+ @Unmodifiable @NonNull Set<FeatureFlag> getFeatureFlags();
+ @Unmodifiable Set<FeatureFlag> getFeatureFlags();
+}
diff --git a/src/main/java/org/bukkit/FeatureFlag.java b/src/main/java/org/bukkit/FeatureFlag.java
index 026b1832bcd163ab89668c991bf002e608e36aef..b5e87480b6a1d064547e4e608f3d402825931a00 100644
@ -181,20 +185,19 @@ index 026b1832bcd163ab89668c991bf002e608e36aef..b5e87480b6a1d064547e4e608f3d4028
}
diff --git a/src/main/java/org/bukkit/FeatureFlagImpl.java b/src/main/java/org/bukkit/FeatureFlagImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..a33517dbd69021fde8246836720b6247800ee43c
index 0000000000000000000000000000000000000000..5a43f65f633dc9a1a0ba7dba0c15488375427444
--- /dev/null
+++ b/src/main/java/org/bukkit/FeatureFlagImpl.java
@@ -0,0 +1,29 @@
@@ -0,0 +1,27 @@
+package org.bukkit;
+
+import java.util.HashSet;
+import java.util.Set;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.framework.qual.DefaultQualifier;
+import org.jetbrains.annotations.ApiStatus;
+import org.jspecify.annotations.NullMarked;
+
+@ApiStatus.Internal
+@DefaultQualifier(NonNull.class)
+@NullMarked
+record FeatureFlagImpl(NamespacedKey key) implements FeatureFlag {
+
+ static final Set<FeatureFlag> ALL_FLAGS = new HashSet<>();
@ -205,7 +208,6 @@ index 0000000000000000000000000000000000000000..a33517dbd69021fde8246836720b6247
+ }
+
+ @ApiStatus.Internal
+ @DefaultQualifier(NonNull.class)
+ record Deprecated(NamespacedKey key) implements FeatureFlag {
+
+ @Override

View file

@ -5,10 +5,10 @@ Subject: [PATCH] Tag Lifecycle Events
diff --git a/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/LifecycleEventTypeProvider.java b/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/LifecycleEventTypeProvider.java
index 3b17f056d162dd42cae0d33cbbb22fece82fe525..a52ab9e09420768d89385c881fb7a4c424e5b8d6 100644
index e15e09c2a4d3f43db6a0159fa8af6179362ea8d6..ece6b72c5a93c1faf3de971ee97a1f40d8c4e4e9 100644
--- a/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/LifecycleEventTypeProvider.java
+++ b/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/LifecycleEventTypeProvider.java
@@ -19,4 +19,6 @@ interface LifecycleEventTypeProvider {
@@ -21,4 +21,6 @@ interface LifecycleEventTypeProvider {
<O extends LifecycleEventOwner, E extends LifecycleEvent> LifecycleEventType.Monitorable<O, E> monitor(String name, Class<? extends O> ownerType);
<O extends LifecycleEventOwner, E extends LifecycleEvent> LifecycleEventType.Prioritizable<O, E> prioritized(String name, Class<? extends O> ownerType);
@ -16,10 +16,10 @@ index 3b17f056d162dd42cae0d33cbbb22fece82fe525..a52ab9e09420768d89385c881fb7a4c4
+ TagEventTypeProvider tagProvider();
}
diff --git a/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/LifecycleEvents.java b/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/LifecycleEvents.java
index dae4935d8e9f46c52f700927a43a9202dac13df6..720fe2546015838708ce794c291ca187cf7bca9c 100644
index ab6b262cf0d2d17962ed012b2ea7b8f1db8bc576..b65c5fd5443cef80989d55bf684191f849afa0e7 100644
--- a/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/LifecycleEvents.java
+++ b/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/LifecycleEvents.java
@@ -24,6 +24,12 @@ public final class LifecycleEvents {
@@ -26,6 +26,12 @@ public final class LifecycleEvents {
*/
public static final LifecycleEventType.Prioritizable<LifecycleEventOwner, ReloadableRegistrarEvent<Commands>> COMMANDS = prioritized("commands", LifecycleEventOwner.class);
@ -34,10 +34,10 @@ index dae4935d8e9f46c52f700927a43a9202dac13df6..720fe2546015838708ce794c291ca187
static <E extends LifecycleEvent> LifecycleEventType.Monitorable<Plugin, E> plugin(final String name) {
diff --git a/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/TagEventTypeProvider.java b/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/TagEventTypeProvider.java
new file mode 100644
index 0000000000000000000000000000000000000000..61c5f6cc6986193eab596ee55e1a414afcf4fbd1
index 0000000000000000000000000000000000000000..843ce7848c6f3c94b1d8609922bb256138bdb4d4
--- /dev/null
+++ b/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/TagEventTypeProvider.java
@@ -0,0 +1,40 @@
@@ -0,0 +1,41 @@
+package io.papermc.paper.plugin.lifecycle.event.types;
+
+import io.papermc.paper.plugin.bootstrap.BootstrapContext;
@ -45,8 +45,8 @@ index 0000000000000000000000000000000000000000..61c5f6cc6986193eab596ee55e1a414a
+import io.papermc.paper.registry.RegistryKey;
+import io.papermc.paper.tag.PostFlattenTagRegistrar;
+import io.papermc.paper.tag.PreFlattenTagRegistrar;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.jetbrains.annotations.ApiStatus;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * Provides event types for tag registration.
@ -55,6 +55,7 @@ index 0000000000000000000000000000000000000000..61c5f6cc6986193eab596ee55e1a414a
+ * @see PostFlattenTagRegistrar
+ */
+@ApiStatus.Experimental
+@NullMarked
+@ApiStatus.NonExtendable
+public interface TagEventTypeProvider {
+
@ -66,7 +67,7 @@ index 0000000000000000000000000000000000000000..61c5f6cc6986193eab596ee55e1a414a
+ * @param <T> the type of value in the tag
+ * @see PreFlattenTagRegistrar
+ */
+ <T> LifecycleEventType.@NonNull Prioritizable<BootstrapContext, ReloadableRegistrarEvent<PreFlattenTagRegistrar<T>>> preFlatten(@NonNull RegistryKey<T> registryKey);
+ <T> LifecycleEventType.Prioritizable<BootstrapContext, ReloadableRegistrarEvent<PreFlattenTagRegistrar<T>>> preFlatten(RegistryKey<T> registryKey);
+
+ /**
+ * Get a prioritizable, reloadable registrar event for tags after they are flattened.
@ -76,14 +77,14 @@ index 0000000000000000000000000000000000000000..61c5f6cc6986193eab596ee55e1a414a
+ * @param <T> the type of value in the tag
+ * @see PostFlattenTagRegistrar
+ */
+ <T> LifecycleEventType.@NonNull Prioritizable<BootstrapContext, ReloadableRegistrarEvent<PostFlattenTagRegistrar<T>>> postFlatten(@NonNull RegistryKey<T> registryKey);
+ <T> LifecycleEventType.Prioritizable<BootstrapContext, ReloadableRegistrarEvent<PostFlattenTagRegistrar<T>>> postFlatten(RegistryKey<T> registryKey);
+}
diff --git a/src/main/java/io/papermc/paper/tag/PostFlattenTagRegistrar.java b/src/main/java/io/papermc/paper/tag/PostFlattenTagRegistrar.java
new file mode 100644
index 0000000000000000000000000000000000000000..32d79dac18c402c3d95edb642eef1563eddf5e9e
index 0000000000000000000000000000000000000000..22db9446ca12e7c8e6381e322649ef574d877359
--- /dev/null
+++ b/src/main/java/io/papermc/paper/tag/PostFlattenTagRegistrar.java
@@ -0,0 +1,103 @@
@@ -0,0 +1,104 @@
+package io.papermc.paper.tag;
+
+import io.papermc.paper.plugin.lifecycle.event.registrar.Registrar;
@ -92,10 +93,10 @@ index 0000000000000000000000000000000000000000..32d79dac18c402c3d95edb642eef1563
+import io.papermc.paper.registry.tag.TagKey;
+import java.util.Collection;
+import java.util.Map;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.Unmodifiable;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * Registrar for tags after they have been flattened. Flattened
@ -110,7 +111,7 @@ index 0000000000000000000000000000000000000000..32d79dac18c402c3d95edb642eef1563
+ * class YourBootstrapClass implements PluginBootstrap {
+ *
+ * @Override
+ * public void bootstrap(@NotNull BootstrapContext context) {
+ * public void bootstrap(BootstrapContext context) {
+ * LifecycleEventManager<BootstrapContext> manager = context.getLifecycleManager();
+ * manager.registerEventHandler(LifecycleEvents.TAGS.postFlatten(RegistryKey.ENCHANTMENT), event -> {
+ * final PostFlattenTagRegistrar<Enchantment> registrar = event.registrar();
@ -127,6 +128,7 @@ index 0000000000000000000000000000000000000000..32d79dac18c402c3d95edb642eef1563
+ * @see PreFlattenTagRegistrar
+ */
+@ApiStatus.Experimental
+@NullMarked
+@ApiStatus.NonExtendable
+public interface PostFlattenTagRegistrar<T> extends Registrar {
+
@ -135,7 +137,7 @@ index 0000000000000000000000000000000000000000..32d79dac18c402c3d95edb642eef1563
+ *
+ * @return the registry key
+ */
+ @NonNull RegistryKey<T> registryKey();
+ RegistryKey<T> registryKey();
+
+ /**
+ * Get a copy of all tags currently held in this registrar.
@ -143,7 +145,7 @@ index 0000000000000000000000000000000000000000..32d79dac18c402c3d95edb642eef1563
+ * @return an immutable map of all tags
+ */
+ @Contract(value = "-> new", pure = true)
+ @Unmodifiable @NonNull Map<TagKey<T>, Collection<TypedKey<T>>> getAllTags();
+ @Unmodifiable Map<TagKey<T>, Collection<TypedKey<T>>> getAllTags();
+
+ /**
+ * Checks if this registrar has a tag with the given key.
@ -152,7 +154,7 @@ index 0000000000000000000000000000000000000000..32d79dac18c402c3d95edb642eef1563
+ * @return true if the tag exists, false otherwise
+ */
+ @Contract(pure = true)
+ boolean hasTag(@NonNull TagKey<T> tagKey);
+ boolean hasTag(TagKey<T> tagKey);
+
+ /**
+ * Get the tag with the given key. Use {@link #hasTag(TagKey)} to check
@ -164,7 +166,7 @@ index 0000000000000000000000000000000000000000..32d79dac18c402c3d95edb642eef1563
+ * @see #hasTag(TagKey)
+ */
+ @Contract(value = "_ -> new", pure = true)
+ @Unmodifiable @NonNull Collection<TypedKey<T>> getTag(@NonNull TagKey<T> tagKey);
+ @Unmodifiable Collection<TypedKey<T>> getTag(TagKey<T> tagKey);
+
+ /**
+ * Adds values to the given tag. If the tag does not exist, it will be created.
@ -174,7 +176,7 @@ index 0000000000000000000000000000000000000000..32d79dac18c402c3d95edb642eef1563
+ * @see #setTag(TagKey, Collection)
+ */
+ @Contract(mutates = "this")
+ void addToTag(@NonNull TagKey<T> tagKey, @NonNull Collection<TypedKey<T>> values);
+ void addToTag(TagKey<T> tagKey, Collection<TypedKey<T>> values);
+
+ /**
+ * Sets the values of the given tag. If the tag does not exist, it will be created.
@ -185,14 +187,14 @@ index 0000000000000000000000000000000000000000..32d79dac18c402c3d95edb642eef1563
+ * @see #addToTag(TagKey, Collection)
+ */
+ @Contract(mutates = "this")
+ void setTag(@NonNull TagKey<T> tagKey, @NonNull Collection<TypedKey<T>> values);
+ void setTag(TagKey<T> tagKey, Collection<TypedKey<T>> values);
+}
diff --git a/src/main/java/io/papermc/paper/tag/PreFlattenTagRegistrar.java b/src/main/java/io/papermc/paper/tag/PreFlattenTagRegistrar.java
new file mode 100644
index 0000000000000000000000000000000000000000..44f7a9d3c2734fb00d11890ece9dc4ce2fafa796
index 0000000000000000000000000000000000000000..8d8a00e4d965bf2e76b1924f5dc8973c6469e3a1
--- /dev/null
+++ b/src/main/java/io/papermc/paper/tag/PreFlattenTagRegistrar.java
@@ -0,0 +1,102 @@
@@ -0,0 +1,103 @@
+package io.papermc.paper.tag;
+
+import io.papermc.paper.plugin.lifecycle.event.registrar.Registrar;
@ -201,10 +203,10 @@ index 0000000000000000000000000000000000000000..44f7a9d3c2734fb00d11890ece9dc4ce
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.Unmodifiable;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * Registrar for tags before they are flattened. Flattened
@ -218,7 +220,7 @@ index 0000000000000000000000000000000000000000..44f7a9d3c2734fb00d11890ece9dc4ce
+ * class YourBootstrapClass implements PluginBootstrap {
+ *
+ * @Override
+ * public void bootstrap(@NotNull BootstrapContext context) {
+ * public void bootstrap(BootstrapContext context) {
+ * LifecycleEventManager<BootstrapContext> manager = context.getLifecycleManager();
+ * manager.registerEventHandler(LifecycleEvents.TAGS.preFlatten(RegistryKey.ITEM), event -> {
+ * final PreFlattenTagRegistrar<ItemType> registrar = event.registrar();
@ -235,6 +237,7 @@ index 0000000000000000000000000000000000000000..44f7a9d3c2734fb00d11890ece9dc4ce
+ * @see PostFlattenTagRegistrar
+ */
+@ApiStatus.Experimental
+@NullMarked
+@ApiStatus.NonExtendable
+public interface PreFlattenTagRegistrar<T> extends Registrar {
+
@ -243,7 +246,7 @@ index 0000000000000000000000000000000000000000..44f7a9d3c2734fb00d11890ece9dc4ce
+ *
+ * @return the registry key
+ */
+ @NonNull RegistryKey<T> registryKey();
+ RegistryKey<T> registryKey();
+
+ /**
+ * Get a copy of all tags currently held in this registrar.
@ -251,7 +254,7 @@ index 0000000000000000000000000000000000000000..44f7a9d3c2734fb00d11890ece9dc4ce
+ * @return an immutable map of all tags
+ */
+ @Contract(value = "-> new", pure = true)
+ @Unmodifiable @NonNull Map<TagKey<T>, Collection<TagEntry<T>>> getAllTags();
+ @Unmodifiable Map<TagKey<T>, Collection<TagEntry<T>>> getAllTags();
+
+ /**
+ * Checks if this registrar has a tag with the given key.
@ -260,7 +263,7 @@ index 0000000000000000000000000000000000000000..44f7a9d3c2734fb00d11890ece9dc4ce
+ * @return true if the tag exists, false otherwise
+ */
+ @Contract(pure = true)
+ boolean hasTag(@NonNull TagKey<T> tagKey);
+ boolean hasTag(TagKey<T> tagKey);
+
+ /**
+ * Get the tag with the given key. Use {@link #hasTag(TagKey)} to check
@ -272,7 +275,7 @@ index 0000000000000000000000000000000000000000..44f7a9d3c2734fb00d11890ece9dc4ce
+ * @see #hasTag(TagKey)
+ */
+ @Contract(value = "_ -> new", pure = true)
+ @Unmodifiable @NonNull List<TagEntry<T>> getTag(@NonNull TagKey<T> tagKey);
+ @Unmodifiable List<TagEntry<T>> getTag(TagKey<T> tagKey);
+
+ /**
+ * Adds entries to the given tag. If the tag does not exist, it will be created.
@ -282,7 +285,7 @@ index 0000000000000000000000000000000000000000..44f7a9d3c2734fb00d11890ece9dc4ce
+ * @see #setTag(TagKey, Collection)
+ */
+ @Contract(mutates = "this")
+ void addToTag(@NonNull TagKey<T> tagKey, @NonNull Collection<TagEntry<T>> entries);
+ void addToTag(TagKey<T> tagKey, Collection<TagEntry<T>> entries);
+
+ /**
+ * Sets the entries of the given tag. If the tag does not exist, it will be created.
@ -293,22 +296,22 @@ index 0000000000000000000000000000000000000000..44f7a9d3c2734fb00d11890ece9dc4ce
+ * @see #addToTag(TagKey, Collection)
+ */
+ @Contract(mutates = "this")
+ void setTag(@NonNull TagKey<T> tagKey, @NonNull Collection<TagEntry<T>> entries);
+ void setTag(TagKey<T> tagKey, Collection<TagEntry<T>> entries);
+}
diff --git a/src/main/java/io/papermc/paper/tag/TagEntry.java b/src/main/java/io/papermc/paper/tag/TagEntry.java
new file mode 100644
index 0000000000000000000000000000000000000000..436b701f1231354a78a9e0ab04ffd9097343ecfc
index 0000000000000000000000000000000000000000..efa52b387eb6ab476c3a785b46e7647a1da4a343
--- /dev/null
+++ b/src/main/java/io/papermc/paper/tag/TagEntry.java
@@ -0,0 +1,89 @@
@@ -0,0 +1,90 @@
+package io.papermc.paper.tag;
+
+import io.papermc.paper.registry.TypedKey;
+import io.papermc.paper.registry.tag.TagKey;
+import net.kyori.adventure.key.Keyed;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.Contract;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * An entry is a pre-flattened tag. Represents
@ -318,6 +321,7 @@ index 0000000000000000000000000000000000000000..436b701f1231354a78a9e0ab04ffd909
+ * @see PreFlattenTagRegistrar
+ */
+@ApiStatus.Experimental
+@NullMarked
+@ApiStatus.NonExtendable
+public interface TagEntry<T> extends Keyed {
+
@ -329,7 +333,7 @@ index 0000000000000000000000000000000000000000..436b701f1231354a78a9e0ab04ffd909
+ * @param <T> the type of value
+ */
+ @Contract(value = "_ -> new", pure = true)
+ static <T> @NonNull TagEntry<T> valueEntry(final @NonNull TypedKey<T> entryKey) {
+ static <T> TagEntry<T> valueEntry(final TypedKey<T> entryKey) {
+ return valueEntry(entryKey, true);
+ }
+
@ -342,7 +346,7 @@ index 0000000000000000000000000000000000000000..436b701f1231354a78a9e0ab04ffd909
+ * @param <T> the type of value
+ */
+ @Contract(value = "_, _ -> new", pure = true)
+ static <T> @NonNull TagEntry<T> valueEntry(final @NonNull TypedKey<T> entryKey, final boolean isRequired) {
+ static <T> TagEntry<T> valueEntry(final TypedKey<T> entryKey, final boolean isRequired) {
+ return new TagEntryImpl<>(entryKey.key(), false, isRequired);
+ }
+
@ -354,7 +358,7 @@ index 0000000000000000000000000000000000000000..436b701f1231354a78a9e0ab04ffd909
+ * @param <T> the type of value
+ */
+ @Contract(value = "_ -> new", pure = true)
+ static <T> @NonNull TagEntry<T> tagEntry(final @NonNull TagKey<T> tagKey) {
+ static <T> TagEntry<T> tagEntry(final TagKey<T> tagKey) {
+ return tagEntry(tagKey, true);
+ }
+
@ -367,7 +371,7 @@ index 0000000000000000000000000000000000000000..436b701f1231354a78a9e0ab04ffd909
+ * @param <T> the type of value
+ */
+ @Contract(value = "_, _ -> new", pure = true)
+ static <T> @NonNull TagEntry<T> tagEntry(final @NonNull TagKey<T> tagKey, final boolean isRequired) {
+ static <T> TagEntry<T> tagEntry(final TagKey<T> tagKey, final boolean isRequired) {
+ return new TagEntryImpl<>(tagKey.key(), true, isRequired);
+ }
+
@ -392,15 +396,17 @@ index 0000000000000000000000000000000000000000..436b701f1231354a78a9e0ab04ffd909
+}
diff --git a/src/main/java/io/papermc/paper/tag/TagEntryImpl.java b/src/main/java/io/papermc/paper/tag/TagEntryImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..c0ffd2b9f814d92d0d9b370dfa7e352f1241f614
index 0000000000000000000000000000000000000000..8d46a330ca47d9ca0ed2c35e696841f7a387f7cd
--- /dev/null
+++ b/src/main/java/io/papermc/paper/tag/TagEntryImpl.java
@@ -0,0 +1,8 @@
@@ -0,0 +1,10 @@
+package io.papermc.paper.tag;
+
+import net.kyori.adventure.key.Key;
+import org.jetbrains.annotations.ApiStatus;
+import org.jspecify.annotations.NullMarked;
+
+@NullMarked
+@ApiStatus.Internal
+record TagEntryImpl<T>(Key key, boolean isTag, boolean isRequired) implements TagEntry<T> {
+}

View file

@ -7,20 +7,21 @@ a missing interface to centralize getSnapshotInventory methods
diff --git a/src/main/java/io/papermc/paper/block/TileStateInventoryHolder.java b/src/main/java/io/papermc/paper/block/TileStateInventoryHolder.java
new file mode 100644
index 0000000000000000000000000000000000000000..170a215465678024f8842e1f9f3132f8e45f213d
index 0000000000000000000000000000000000000000..59868ac52854f2e9c628f6c72ecd9fafcce15f9f
--- /dev/null
+++ b/src/main/java/io/papermc/paper/block/TileStateInventoryHolder.java
@@ -0,0 +1,38 @@
@@ -0,0 +1,39 @@
+package io.papermc.paper.block;
+
+import org.bukkit.block.TileState;
+import org.bukkit.inventory.BlockInventoryHolder;
+import org.bukkit.inventory.Inventory;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * Represents a {@link TileState} block that has an inventory.
+ */
+@NullMarked
+public interface TileStateInventoryHolder extends TileState, BlockInventoryHolder {
+
+ /**
@ -35,7 +36,7 @@ index 0000000000000000000000000000000000000000..170a215465678024f8842e1f9f3132f8
+ * @return the inventory
+ */
+ @Override
+ @NonNull Inventory getInventory();
+ Inventory getInventory();
+
+ /**
+ * Gets the captured inventory snapshot of this container.
@ -47,7 +48,7 @@ index 0000000000000000000000000000000000000000..170a215465678024f8842e1f9f3132f8
+ *
+ * @return the captured inventory snapshot
+ */
+ @NonNull Inventory getSnapshotInventory();
+ Inventory getSnapshotInventory();
+}
diff --git a/src/main/java/org/bukkit/block/ChiseledBookshelf.java b/src/main/java/org/bukkit/block/ChiseledBookshelf.java
index 1cc5c2021cf1241b090b28697e09bcd36595ab13..92f41d00dcb40fee18305502783e12d87a1c5c94 100644

View file

@ -0,0 +1,40 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Lulu13022002 <41980282+Lulu13022002@users.noreply.github.com>
Date: Sat, 9 Jul 2022 17:17:04 +0200
Subject: [PATCH] Add Offline PDC API
diff --git a/src/main/java/org/bukkit/OfflinePlayer.java b/src/main/java/org/bukkit/OfflinePlayer.java
index 3993fecec5b4c2bbd77e175a168afcad571ce4d1..a028f2fe541491729856051780b33dba07832fb6 100644
--- a/src/main/java/org/bukkit/OfflinePlayer.java
+++ b/src/main/java/org/bukkit/OfflinePlayer.java
@@ -19,7 +19,7 @@ import org.jetbrains.annotations.Nullable;
* player that is stored on the disk and can, thus, be retrieved without the
* player needing to be online.
*/
-public interface OfflinePlayer extends ServerOperator, AnimalTamer, ConfigurationSerializable {
+public interface OfflinePlayer extends ServerOperator, AnimalTamer, ConfigurationSerializable, io.papermc.paper.persistence.PersistentDataViewHolder { // Paper - Add Offline PDC API
/**
* Checks if this player is currently online
@@ -557,4 +557,20 @@ public interface OfflinePlayer extends ServerOperator, AnimalTamer, Configuratio
*/
@Nullable
public Location getLocation();
+ // Paper start - add pdc to offline player
+ /**
+ * Yields a view of the persistent data container for this offline player.
+ * In case this {@link OfflinePlayer} instance was created for an offline player, the returned view will wrap the persistent
+ * data on disk.
+ * <p>
+ * As such, this method as well as queries to the {@link io.papermc.paper.persistence.PersistentDataContainerView}
+ * may produce blocking IO requests to read the requested data from disk.
+ * Caution in its usage is hence advised.
+ *
+ * @return the persistent data container view
+ * @see io.papermc.paper.persistence.PersistentDataViewHolder#getPersistentDataContainer()
+ */
+ @Override
+ io.papermc.paper.persistence.@NotNull PersistentDataContainerView getPersistentDataContainer();
+ // Paper end - add pdc to offline player
}

View file

@ -0,0 +1,42 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Doc <nachito94@msn.com>
Date: Sun, 6 Oct 2024 16:45:20 -0300
Subject: [PATCH] Add AnvilView#bypassEnchantmentLevelRestriction
Anvils, by default, limit applied enchantment levels to their respective
maximum level. The added API enables plugins to disable this behaviour,
allowing enchantments that are overleveled to be applied via the anvil.
diff --git a/src/main/java/org/bukkit/inventory/view/AnvilView.java b/src/main/java/org/bukkit/inventory/view/AnvilView.java
index 0344b3db789baf1da0b59f2d2cd66154b69b38a9..cc5ffd28a6d6244b99a94e52c6f5a0f73a64aed7 100644
--- a/src/main/java/org/bukkit/inventory/view/AnvilView.java
+++ b/src/main/java/org/bukkit/inventory/view/AnvilView.java
@@ -59,4 +59,28 @@ public interface AnvilView extends InventoryView {
* @param levels the levels to set
*/
void setMaximumRepairCost(int levels);
+
+ // Paper start - bypass anvil level restrictions
+ /**
+ * Returns whether this view will bypass the vanilla enchantment level restriction
+ * when applying enchantments to an item or not.
+ * <p>
+ * By default, vanilla will limit enchantments applied to items to the respective
+ * {@link org.bukkit.enchantments.Enchantment#getMaxLevel()}, even if the applied enchantment itself is above said
+ * limit.
+ * Disabling this limit via {@link AnvilView#bypassEnchantmentLevelRestriction(boolean)} allows for, e.g., enchanted
+ * books to be applied fully, even if their enchantments are beyond the limit.
+ *
+ * @return {@code true} if this view bypasses the vanilla restrictions.
+ */
+ boolean bypassesEnchantmentLevelRestriction();
+
+ /**
+ * Sets if enchantments applied via this anvil view may bypass vanilla's level restrictions.
+ *
+ * @param bypassEnchantmentLevelRestriction if this view bypasses the vanilla level restrictions.
+ * @see AnvilView#bypassesEnchantmentLevelRestriction()
+ */
+ void bypassEnchantmentLevelRestriction(boolean bypassEnchantmentLevelRestriction);
+ // Paper end - bypass anvil level restrictions
}

View file

@ -5603,9 +5603,18 @@ index 4d586e1375ed8782939c9d480479e0dd981f8cbc..7900adb0b158bc17dd792dd082c33854
+
}
diff --git a/src/main/java/org/bukkit/craftbukkit/util/CraftChatMessage.java b/src/main/java/org/bukkit/craftbukkit/util/CraftChatMessage.java
index ff040613083c015d9c52c0995591b64305fd5018..1b552b3f05ac7fc44450de4b1ec78907a0f62424 100644
index ff040613083c015d9c52c0995591b64305fd5018..95444fd9fecc5bda5462ca8dfeca82c5318f0895 100644
--- a/src/main/java/org/bukkit/craftbukkit/util/CraftChatMessage.java
+++ b/src/main/java/org/bukkit/craftbukkit/util/CraftChatMessage.java
@@ -90,7 +90,7 @@ public final class CraftChatMessage {
this.hex.append(c);
if (this.hex.length() == 7) {
- this.modifier = StringMessage.RESET.withColor(TextColor.parseColor(this.hex.toString()).result().get());
+ this.modifier = StringMessage.RESET.withColor(TextColor.parseColor(this.hex.toString()).result().orElse(null)); // Paper
this.hex = null;
}
} else if (format.isFormat() && format != ChatFormatting.RESET) {
@@ -264,6 +264,7 @@ public final class CraftChatMessage {
public static String fromComponent(Component component) {

View file

@ -5,7 +5,7 @@ Subject: [PATCH] ProfileWhitelistVerifyEvent
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
index d5f17d7e3b56ca87ec9070b69265ce098de61f69..768a5c6ebc4466aae4108a79e1564b2bfc93463a 100644
index d5f17d7e3b56ca87ec9070b69265ce098de61f69..c08ffdbc9afb2fe7abbf5567dc1fb1e2bcb01b96 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -624,9 +624,9 @@ public abstract class PlayerList {
@ -21,24 +21,25 @@ index d5f17d7e3b56ca87ec9070b69265ce098de61f69..768a5c6ebc4466aae4108a79e1564b2b
} else if (this.getIpBans().isBanned(socketaddress) && !this.getIpBans().get(socketaddress).hasExpired()) {
IpBanListEntry ipbanentry = this.ipBans.get(socketaddress);
@@ -993,7 +993,24 @@ public abstract class PlayerList {
@@ -993,7 +993,25 @@ public abstract class PlayerList {
}
public boolean isWhiteListed(GameProfile profile) {
- return !this.doWhiteList || this.ops.contains(profile) || this.whitelist.contains(profile);
+ // Paper start - ProfileWhitelistVerifyEvent
+ return isWhiteListed(profile, null);
+ return this.isWhiteListed(profile, null);
+ }
+ public boolean isWhiteListed(GameProfile gameprofile, org.bukkit.event.player.PlayerLoginEvent loginEvent) {
+ public boolean isWhiteListed(GameProfile gameprofile, @Nullable org.bukkit.event.player.PlayerLoginEvent loginEvent) {
+ boolean isOp = this.ops.contains(gameprofile);
+ boolean isWhitelisted = !this.doWhiteList || isOp || this.whitelist.contains(gameprofile);
+ final com.destroystokyo.paper.event.profile.ProfileWhitelistVerifyEvent event;
+
+ event = new com.destroystokyo.paper.event.profile.ProfileWhitelistVerifyEvent(com.destroystokyo.paper.profile.CraftPlayerProfile.asBukkitMirror(gameprofile), this.doWhiteList, isWhitelisted, isOp, org.spigotmc.SpigotConfig.whitelistMessage);
+ final net.kyori.adventure.text.Component configuredMessage = net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(org.spigotmc.SpigotConfig.whitelistMessage);
+ event = new com.destroystokyo.paper.event.profile.ProfileWhitelistVerifyEvent(com.destroystokyo.paper.profile.CraftPlayerProfile.asBukkitMirror(gameprofile), this.doWhiteList, isWhitelisted, isOp, configuredMessage);
+ event.callEvent();
+ if (!event.isWhitelisted()) {
+ if (loginEvent != null) {
+ loginEvent.disallow(PlayerLoginEvent.Result.KICK_WHITELIST, net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(event.getKickMessage() == null ? org.spigotmc.SpigotConfig.whitelistMessage : event.getKickMessage()));
+ loginEvent.disallow(PlayerLoginEvent.Result.KICK_WHITELIST, event.kickMessage() == null ? configuredMessage : event.kickMessage());
+ }
+ return false;
+ }

View file

@ -5,10 +5,10 @@ Subject: [PATCH] Call WhitelistToggleEvent when whitelist is toggled
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
index cdd1d8222ad1796abd0858b9ed0e2ddc9be83c93..2f1d075b8dbcf173c51f3e6396ccbc61b64f75df 100644
index 7f8081446704ea9642275cb2bc139fed174a2f1f..0bac75f111398fd22df978a09dcd4cdc22998894 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -1127,6 +1127,7 @@ public abstract class PlayerList {
@@ -1128,6 +1128,7 @@ public abstract class PlayerList {
}
public void setUsingWhiteList(boolean whitelistEnabled) {

View file

@ -443,7 +443,7 @@ index d2d153e587e624025ef01fbe3dcfa4bf06f1a06b..e0a10f1d8bf2c0df66e62bdf2a174ce6
}
// CraftBukkit end
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
index bbc6f64a3b2c0702a0a752acd48145cdcafd742e..55a2f234436808258ef59f889a9b253fe79b82e8 100644
index f7e442cd5eff3d7c6932d16c93a7a097f11251f9..393a93198b3a1d95a6cc5eb1d19e392f9ab7e2b9 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -675,7 +675,7 @@ public abstract class PlayerList {
@ -455,7 +455,7 @@ index bbc6f64a3b2c0702a0a752acd48145cdcafd742e..55a2f234436808258ef59f889a9b253f
}
// Instead of kicking then returning, we need to store the kick reason
@@ -1277,7 +1277,7 @@ public abstract class PlayerList {
@@ -1278,7 +1278,7 @@ public abstract class PlayerList {
// Paper end
// CraftBukkit start - disconnect safely
for (ServerPlayer player : this.players) {

View file

@ -6,41 +6,42 @@ Subject: [PATCH] More CommandBlock API
diff --git a/src/main/java/io/papermc/paper/commands/PaperCommandBlockHolder.java b/src/main/java/io/papermc/paper/commands/PaperCommandBlockHolder.java
new file mode 100644
index 0000000000000000000000000000000000000000..0b42306f17bf8850a13a51067c2d19e7583187e5
index 0000000000000000000000000000000000000000..0463aef1c8be238b00e73907bde927f1522a46ce
--- /dev/null
+++ b/src/main/java/io/papermc/paper/commands/PaperCommandBlockHolder.java
@@ -0,0 +1,33 @@
@@ -0,0 +1,34 @@
+package io.papermc.paper.commands;
+
+import io.papermc.paper.adventure.PaperAdventure;
+import io.papermc.paper.command.CommandBlockHolder;
+import net.kyori.adventure.text.Component;
+import net.minecraft.world.level.BaseCommandBlock;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jspecify.annotations.NullMarked;
+import org.jspecify.annotations.Nullable;
+
+@NullMarked
+public interface PaperCommandBlockHolder extends CommandBlockHolder {
+
+ BaseCommandBlock getCommandBlockHandle();
+
+ @Override
+ default @NotNull Component lastOutput() {
+ return PaperAdventure.asAdventure(getCommandBlockHandle().getLastOutput());
+ default Component lastOutput() {
+ return PaperAdventure.asAdventure(this.getCommandBlockHandle().getLastOutput());
+ }
+
+ @Override
+ default void lastOutput(@Nullable Component lastOutput) {
+ getCommandBlockHandle().setLastOutput(PaperAdventure.asVanilla(lastOutput));
+ default void lastOutput(final @Nullable Component lastOutput) {
+ this.getCommandBlockHandle().setLastOutput(PaperAdventure.asVanilla(lastOutput));
+ }
+
+ @Override
+ default int getSuccessCount() {
+ return getCommandBlockHandle().getSuccessCount();
+ return this.getCommandBlockHandle().getSuccessCount();
+ }
+
+ @Override
+ default void setSuccessCount(int successCount) {
+ getCommandBlockHandle().setSuccessCount(successCount);
+ default void setSuccessCount(final int successCount) {
+ this.getCommandBlockHandle().setSuccessCount(successCount);
+ }
+}
diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftCommandBlock.java b/src/main/java/org/bukkit/craftbukkit/block/CraftCommandBlock.java

View file

@ -6,10 +6,10 @@ Subject: [PATCH] Use username instead of display name in
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
index e30ad8c7e85c25f3133bbd23dcbe59ae4c2f8db5..03572023ae7f061bf34fed8ac27852e08d6412c0 100644
index 06bcc9974b4a63c174c51df332ff9ddd7eb6a159..0bded2c55d6c826e2f3dcb89995712c92348a3c7 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -1393,7 +1393,7 @@ public abstract class PlayerList {
@@ -1394,7 +1394,7 @@ public abstract class PlayerList {
// CraftBukkit start
public ServerStatsCounter getPlayerStats(ServerPlayer entityhuman) {
ServerStatsCounter serverstatisticmanager = entityhuman.getStats();

View file

@ -5,10 +5,10 @@ Subject: [PATCH] API for updating recipes on clients
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
index e85b91036c2470b2f164a4641d1c07d27553a078..387470d6cfd34dc6f9c895a962ddcdf92583ab8c 100644
index 2912b15ccda373cf52cec020b0e06ac2c5cf2950..a6caf3a0df22f124a4ee1cfb3981bbeb23a8630e 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -1489,6 +1489,13 @@ public abstract class PlayerList {
@@ -1490,6 +1490,13 @@ public abstract class PlayerList {
}
public void reloadResources() {
@ -22,7 +22,7 @@ index e85b91036c2470b2f164a4641d1c07d27553a078..387470d6cfd34dc6f9c895a962ddcdf9
// CraftBukkit start
/*Iterator iterator = this.advancements.values().iterator();
@@ -1504,7 +1511,15 @@ public abstract class PlayerList {
@@ -1505,7 +1512,15 @@ public abstract class PlayerList {
}
// CraftBukkit end

View file

@ -65,6 +65,25 @@ index b98f9246b60daf31460f41ce214dfa7c011f5684..842b0cec0397d7ae5166617627340ffa
}
@Nullable
diff --git a/src/main/java/net/minecraft/world/entity/Leashable.java b/src/main/java/net/minecraft/world/entity/Leashable.java
index e7535f15be3cc1537aafee53779ccfb4f21d1f38..bd6d587cedfe0e345536d7ebb6b7ca204f073efe 100644
--- a/src/main/java/net/minecraft/world/entity/Leashable.java
+++ b/src/main/java/net/minecraft/world/entity/Leashable.java
@@ -54,7 +54,13 @@ public interface Leashable {
@Nullable
default Leashable.LeashData readLeashData(CompoundTag nbt) {
if (nbt.contains("leash", 10)) {
- return new Leashable.LeashData(Either.left(nbt.getCompound("leash").getUUID("UUID")));
+ // Paper start
+ final CompoundTag leashTag = nbt.getCompound("leash");
+ if (!leashTag.hasUUID("UUID")) {
+ return null;
+ }
+ return new Leashable.LeashData(Either.left(leashTag.getUUID("UUID")));
+ // Paper end
} else {
if (nbt.contains("leash", 11)) {
Either<UUID, BlockPos> either = (Either) NbtUtils.readBlockPos(nbt, "leash").map(Either::right).orElse(null); // CraftBukkit - decompile error
diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java
index 13ef1ad250b56dbadba0244186e369d7ba9b5c0e..94169703c5a8111df1ed550d57f59f4a3bb97ae1 100644
--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java

View file

@ -1045,7 +1045,7 @@ index 0000000000000000000000000000000000000000..895addef908e09d527e4bc9210599e88
+}
diff --git a/src/main/java/io/papermc/paper/command/brigadier/argument/SignedMessageResolverImpl.java b/src/main/java/io/papermc/paper/command/brigadier/argument/SignedMessageResolverImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..72966584089d3fee9778f572727c9b7f4a4d4302
index 0000000000000000000000000000000000000000..07a23be2cd7b4592108dee0ae223e71b1d00cec9
--- /dev/null
+++ b/src/main/java/io/papermc/paper/command/brigadier/argument/SignedMessageResolverImpl.java
@@ -0,0 +1,30 @@
@ -1053,13 +1053,13 @@ index 0000000000000000000000000000000000000000..72966584089d3fee9778f572727c9b7f
+
+import com.mojang.brigadier.context.CommandContext;
+import com.mojang.brigadier.exceptions.CommandSyntaxException;
+import java.util.concurrent.CompletableFuture;
+import net.kyori.adventure.chat.SignedMessage;
+import net.minecraft.commands.CommandSourceStack;
+import net.minecraft.commands.arguments.MessageArgument;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.concurrent.CompletableFuture;
+import org.jspecify.annotations.NullMarked;
+
+@NullMarked
+public record SignedMessageResolverImpl(MessageArgument.Message message) implements SignedMessageResolver {
+
+ @Override
@ -1067,13 +1067,13 @@ index 0000000000000000000000000000000000000000..72966584089d3fee9778f572727c9b7f
+ return this.message.text();
+ }
+
+ @SuppressWarnings({"rawtypes", "unchecked"})
+ @Override
+ public @NotNull CompletableFuture<SignedMessage> resolveSignedMessage(final String argumentName, final CommandContext erased) throws CommandSyntaxException {
+ final CommandContext<CommandSourceStack> type = erased;
+ public CompletableFuture<SignedMessage> resolveSignedMessage(final String argumentName, final CommandContext erased) throws CommandSyntaxException {
+ final CompletableFuture<SignedMessage> future = new CompletableFuture<>();
+
+ final MessageArgument.Message response = type.getArgument(argumentName, SignedMessageResolverImpl.class).message;
+ MessageArgument.resolveChatMessage(response, type, argumentName, (message) -> {
+ final MessageArgument.Message response = ((CommandContext<CommandSourceStack>) erased).getArgument(argumentName, SignedMessageResolverImpl.class).message;
+ MessageArgument.resolveChatMessage(response, erased, argumentName, (message) -> {
+ future.complete(message.adventureView());
+ });
+ return future;

View file

@ -9,7 +9,7 @@ public net.minecraft.resources.RegistryOps lookupProvider
public net.minecraft.resources.RegistryOps$HolderLookupAdapter
diff --git a/src/main/java/io/papermc/paper/registry/PaperRegistries.java b/src/main/java/io/papermc/paper/registry/PaperRegistries.java
index c92ce42398a9bfd00eb4e05972289c521ee255cf..fba7c1758439db9044d9f7368bc9b79642d6b1b9 100644
index 633b01431750d4b40159a57bf25fb35c6670ff1b..5cf598905ed6a7ac2b0d9ced3420adaf20ceb6af 100644
--- a/src/main/java/io/papermc/paper/registry/PaperRegistries.java
+++ b/src/main/java/io/papermc/paper/registry/PaperRegistries.java
@@ -2,6 +2,7 @@ package io.papermc.paper.registry;
@ -1357,7 +1357,7 @@ index bd16933a5341908b21e549f66080c33466ad1079..90046c85ce1b9901de7476761da15614
+ // Paper end - RegistrySet API
}
diff --git a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
index 9e0d8fc51196ca07677f45e41614262036155e85..82ebfd09e9baca0a31ee41c0e5228bce3c54e74f 100644
index 9e0d8fc51196ca07677f45e41614262036155e85..0286375531b944ce572708e6c7cc9982e0d2b5b8 100644
--- a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
+++ b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
@@ -668,6 +668,21 @@ public final class CraftMagicNumbers implements UnsafeValues {
@ -1367,7 +1367,7 @@ index 9e0d8fc51196ca07677f45e41614262036155e85..82ebfd09e9baca0a31ee41c0e5228bce
+ // Paper start - hack to get tags for non server-backed registries
+ @Override
+ public <A extends Keyed, M> io.papermc.paper.registry.tag.Tag<A> getTag(final io.papermc.paper.registry.tag.TagKey<A> tagKey) { // TODO remove Keyed
+ if (tagKey.registryKey() != io.papermc.paper.registry.RegistryKey.ENTITY_TYPE || tagKey.registryKey() != io.papermc.paper.registry.RegistryKey.FLUID) {
+ if (tagKey.registryKey() != io.papermc.paper.registry.RegistryKey.ENTITY_TYPE && tagKey.registryKey() != io.papermc.paper.registry.RegistryKey.FLUID) {
+ throw new UnsupportedOperationException(tagKey.registryKey() + " doesn't have tags");
+ }
+ final net.minecraft.resources.ResourceKey<? extends net.minecraft.core.Registry<M>> nmsKey = io.papermc.paper.registry.PaperRegistries.registryToNms(tagKey.registryKey());

View file

@ -6,29 +6,33 @@ Subject: [PATCH] Make a PDC view accessible directly from ItemStack
diff --git a/src/main/java/io/papermc/paper/persistence/PaperPersistentDataContainerView.java b/src/main/java/io/papermc/paper/persistence/PaperPersistentDataContainerView.java
new file mode 100644
index 0000000000000000000000000000000000000000..fac401280d3f3689b00e16c19155ca753faa06e0
index 0000000000000000000000000000000000000000..122c32e82b299cafd7d0c6a9f4818523470c9f05
--- /dev/null
+++ b/src/main/java/io/papermc/paper/persistence/PaperPersistentDataContainerView.java
@@ -0,0 +1,86 @@
@@ -0,0 +1,120 @@
+package io.papermc.paper.persistence;
+
+import com.google.common.base.Preconditions;
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+import net.minecraft.nbt.CompoundTag;
+import net.minecraft.nbt.NbtIo;
+import net.minecraft.nbt.Tag;
+import org.bukkit.NamespacedKey;
+import org.bukkit.craftbukkit.persistence.CraftPersistentDataAdapterContext;
+import org.bukkit.craftbukkit.persistence.CraftPersistentDataContainer;
+import org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry;
+import org.bukkit.persistence.PersistentDataAdapterContext;
+import org.bukkit.persistence.PersistentDataContainer;
+import org.bukkit.persistence.PersistentDataType;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.checker.nullness.qual.Nullable;
+import org.checkerframework.framework.qual.DefaultQualifier;
+import org.jspecify.annotations.NullMarked;
+import org.jspecify.annotations.Nullable;
+
+@DefaultQualifier(NonNull.class)
+@NullMarked
+public abstract class PaperPersistentDataContainerView implements PersistentDataContainerView {
+
+ protected final CraftPersistentDataTypeRegistry registry;
@ -48,7 +52,7 @@ index 0000000000000000000000000000000000000000..fac401280d3f3689b00e16c19155ca75
+ Preconditions.checkArgument(key != null, "The NamespacedKey key cannot be null");
+ Preconditions.checkArgument(type != null, "The provided type cannot be null");
+
+ final @Nullable Tag value = this.getTag(key.toString());
+ final Tag value = this.getTag(key.toString());
+ if (value == null) {
+ return false;
+ }
@ -67,7 +71,7 @@ index 0000000000000000000000000000000000000000..fac401280d3f3689b00e16c19155ca75
+ Preconditions.checkArgument(key != null, "The NamespacedKey key cannot be null");
+ Preconditions.checkArgument(type != null, "The provided type cannot be null");
+
+ final @Nullable Tag value = this.getTag(key.toString());
+ final Tag value = this.getTag(key.toString());
+ if (value == null) {
+ return null;
+ }
@ -82,13 +86,43 @@ index 0000000000000000000000000000000000000000..fac401280d3f3689b00e16c19155ca75
+ }
+
+ @Override
+ public Set<NamespacedKey> getKeys() {
+ final Set<String> names = this.toTagCompound().getAllKeys();
+ final Set<NamespacedKey> keys = new HashSet<>(names.size());
+ names.forEach(key -> {
+ final String[] keyPart = key.split(":", 2);
+ if (keyPart.length == 2) {
+ keys.add(new NamespacedKey(keyPart[0], keyPart[1]));
+ }
+ });
+ return Collections.unmodifiableSet(keys);
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return this.toTagCompound().isEmpty();
+ }
+
+ @Override
+ public void copyTo(final PersistentDataContainer other, final boolean replace) {
+ Preconditions.checkArgument(other != null, "The target container cannot be null");
+ final CraftPersistentDataContainer target = (CraftPersistentDataContainer) other;
+ final CompoundTag tag = this.toTagCompound();
+ for (final String key : tag.getAllKeys()) {
+ if (replace || !target.getRaw().containsKey(key)) {
+ target.getRaw().put(key, tag.get(key).copy());
+ }
+ }
+ }
+
+ @Override
+ public PersistentDataAdapterContext getAdapterContext() {
+ return this.adapterContext;
+ }
+
+ @Override
+ public byte[] serializeToBytes() throws IOException {
+ final net.minecraft.nbt.CompoundTag root = this.toTagCompound();
+ final CompoundTag root = this.toTagCompound();
+ final ByteArrayOutputStream byteArrayOutput = new ByteArrayOutputStream();
+ try (final DataOutputStream dataOutput = new DataOutputStream(byteArrayOutput)) {
+ NbtIo.write(root, dataOutput);
@ -97,10 +131,10 @@ index 0000000000000000000000000000000000000000..fac401280d3f3689b00e16c19155ca75
+ }
+}
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java
index db0fe1b755f59eafca6e57917429fb7889889c3a..454d1ad710159a47f2112f77ea4b6c87241caf4e 100644
index a3c6d2cbdce60b1cf935d798568b8bb5d97e1229..6081c588c61406d0d21a15e8e6140d5d5240f0a8 100644
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java
@@ -484,4 +484,63 @@ public final class CraftItemStack extends ItemStack {
@@ -484,4 +484,34 @@ public final class CraftItemStack extends ItemStack {
return mirrored;
}
// Paper end
@ -128,35 +162,6 @@ index db0fe1b755f59eafca6e57917429fb7889889c3a..454d1ad710159a47f2112f77ea4b6c87
+ public net.minecraft.nbt.Tag getTag(final String key) {
+ return CraftItemStack.this.getPdcTag().get(key);
+ }
+
+ @Override
+ public java.util.Set<org.bukkit.NamespacedKey> getKeys() {
+ java.util.Set<org.bukkit.NamespacedKey> keys = new java.util.HashSet<>();
+ CraftItemStack.this.getPdcTag().getAllKeys().forEach(key -> {
+ final String[] keyData = key.split(":", 2);
+ if (keyData.length == 2) {
+ keys.add(new org.bukkit.NamespacedKey(keyData[0], keyData[1]));
+ }
+ });
+ return java.util.Collections.unmodifiableSet(keys);
+ };
+
+ @Override
+ public boolean isEmpty() {
+ return CraftItemStack.this.getPdcTag().isEmpty();
+ }
+
+ @Override
+ public void copyTo(final org.bukkit.persistence.PersistentDataContainer other, final boolean replace) {
+ Preconditions.checkArgument(other != null, "The target container cannot be null");
+ final org.bukkit.craftbukkit.persistence.CraftPersistentDataContainer target = (org.bukkit.craftbukkit.persistence.CraftPersistentDataContainer) other;
+ final net.minecraft.nbt.CompoundTag pdcTag = org.bukkit.craftbukkit.inventory.CraftItemStack.this.getPdcTag();
+ for (final String key : pdcTag.getAllKeys()) {
+ if (replace || !target.getRaw().containsKey(key)) {
+ target.getRaw().put(key, pdcTag.get(key).copy());
+ }
+ }
+ }
+ };
+ @Override
+ public io.papermc.paper.persistence.PersistentDataContainerView getPersistentDataContainer() {

View file

@ -56,7 +56,7 @@ index b64c3e29e2b8e890073fee0b45c8b8dfc2b642fd..aa38e3b297209cc121e7dc7a6ac9588c
try (co.aikar.timings.Timing ignored = MinecraftTimings.processTasksTimer.startTiming()) {
this.runAllTasks();
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
index 2931e1dd0c8ae5ac1c9ec42f90dd5ab57595bf60..c96f3dcd365bc140b1f4680ef6bd770c80f8eda1 100644
index 202975cf97ae143622a0c19826b0d63ad2afa0ce..f9abf63e12ea930275121b470e4e4906cff0fc12 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
@@ -1315,6 +1315,35 @@ public class ServerLevel extends Level implements WorldGenLevel, ca.spottedleaf.
@ -108,7 +108,7 @@ index 8dc3ba983fd4c61e463867be8d224aa90424215a..6c280abdef5f80b668d6090f9d35283a
private static final int NEUTRAL_MOB_DEATH_NOTIFICATION_RADII_Y = 10;
private static final int FLY_STAT_RECORDING_SPEED = 25;
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
index 0cfaf31273bbec7640eb3a2b16a8d2c816b0ed61..c13922d2fb1c15f895b14d4cb6d5379b59ea1ef8 100644
index 30b6f5d4af1dc799d5ffee6a345bcf92528df7cd..5e2c4969e77c669acbb4a13c07033cb267c3d586 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -569,6 +569,7 @@ public abstract class PlayerList {
@ -119,7 +119,7 @@ index 0cfaf31273bbec7640eb3a2b16a8d2c816b0ed61..c13922d2fb1c15f895b14d4cb6d5379b
this.playerIo.save(player);
ServerStatsCounter serverstatisticmanager = (ServerStatsCounter) player.getStats(); // CraftBukkit
@@ -1192,10 +1193,22 @@ public abstract class PlayerList {
@@ -1193,10 +1194,22 @@ public abstract class PlayerList {
}
public void saveAll() {

View file

@ -0,0 +1,45 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Lulu13022002 <41980282+Lulu13022002@users.noreply.github.com>
Date: Sat, 9 Jul 2022 17:28:42 +0200
Subject: [PATCH] Add Offline PDC API
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java b/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java
index 4f4e3ee18d586f61706504218cddc06a38ca5580..94ca0407303c4493ab4928b12ec6ecc75aaca549 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java
@@ -325,6 +325,34 @@ public class CraftOfflinePlayer implements OfflinePlayer, ConfigurationSerializa
}
// Paper end
+ // Paper start - Add Offline PDC API
+ private static final org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry DATA_TYPE_REGISTRY = new org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry();
+ private io.papermc.paper.persistence.@org.checkerframework.checker.nullness.qual.MonotonicNonNull PersistentDataContainerView persistentDataContainerView;
+
+ @Override
+ public io.papermc.paper.persistence.PersistentDataContainerView getPersistentDataContainer() {
+ if (this.persistentDataContainerView == null) {
+ this.persistentDataContainerView = new io.papermc.paper.persistence.PaperPersistentDataContainerView(DATA_TYPE_REGISTRY) {
+
+ private CompoundTag getPersistentTag() {
+ return net.minecraft.Optionull.map(CraftOfflinePlayer.this.getData(), data -> data.getCompound("BukkitValues"));
+ }
+
+ @Override
+ public CompoundTag toTagCompound() {
+ return java.util.Objects.requireNonNullElseGet(this.getPersistentTag(), CompoundTag::new);
+ }
+
+ @Override
+ public net.minecraft.nbt.Tag getTag(String key) {
+ return net.minecraft.Optionull.map(this.getPersistentTag(), tag -> tag.get(key));
+ }
+ };
+ }
+ return this.persistentDataContainerView;
+ }
+ // Paper end - Add Offline PDC API
+
@Override
public Location getLastDeathLocation() {
if (this.getData().contains("LastDeathLocation", 10)) {

View file

@ -0,0 +1,53 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Doc <nachito94@msn.com>
Date: Sun, 6 Oct 2024 16:46:36 -0300
Subject: [PATCH] Add AnvilView#bypassEnchantmentLevelRestriction
Anvils, by default, limit applied enchantment levels to their respective
maximum level. The added API enables plugins to disable this behaviour,
allowing enchantments that are overleveled to be applied via the anvil.
diff --git a/src/main/java/net/minecraft/world/inventory/AnvilMenu.java b/src/main/java/net/minecraft/world/inventory/AnvilMenu.java
index d685511104ac552dfc9ae2111e1bfb60fa812102..362278407679f245ebcea778f2199b357339e1fe 100644
--- a/src/main/java/net/minecraft/world/inventory/AnvilMenu.java
+++ b/src/main/java/net/minecraft/world/inventory/AnvilMenu.java
@@ -53,6 +53,7 @@ public class AnvilMenu extends ItemCombinerMenu {
public int maximumRepairCost = 40;
private CraftAnvilView bukkitEntity;
// CraftBukkit end
+ public boolean bypassEnchantmentLevelRestriction = false; // Paper - bypass anvil level restrictions
public AnvilMenu(int syncId, Inventory inventory) {
this(syncId, inventory, ContainerLevelAccess.NULL);
@@ -231,7 +232,7 @@ public class AnvilMenu extends ItemCombinerMenu {
flag2 = true;
} else {
flag1 = true;
- if (i2 > enchantment.getMaxLevel()) {
+ if (i2 > enchantment.getMaxLevel() && !this.bypassEnchantmentLevelRestriction) { // Paper - bypass anvil level restrictions
i2 = enchantment.getMaxLevel();
}
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/view/CraftAnvilView.java b/src/main/java/org/bukkit/craftbukkit/inventory/view/CraftAnvilView.java
index e2d8d69c5ae8feb6840462ba8332344972658d83..59f0b1207931a2a10d559f43e2926b17e6991257 100644
--- a/src/main/java/org/bukkit/craftbukkit/inventory/view/CraftAnvilView.java
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/view/CraftAnvilView.java
@@ -50,6 +50,18 @@ public class CraftAnvilView extends CraftInventoryView<AnvilMenu> implements Anv
this.container.maximumRepairCost = cost;
}
+ // Paper start
+ @Override
+ public boolean bypassesEnchantmentLevelRestriction() {
+ return this.container.bypassEnchantmentLevelRestriction;
+ }
+
+ @Override
+ public void bypassEnchantmentLevelRestriction(final boolean bypassEnchantmentLevelRestriction) {
+ this.container.bypassEnchantmentLevelRestriction = bypassEnchantmentLevelRestriction;
+ }
+ // Paper end
+
public void updateFromLegacy(CraftInventoryAnvil legacy) {
if (legacy.isRepairCostSet()) {
this.setRepairCost(legacy.getRepairCost());

View file

@ -98,12 +98,12 @@ public final class Registration {
commands.register(plugin.getPluginMeta(), "example", "test", Collections.emptyList(), new BasicCommand() {
@Override
public void execute(@NotNull final CommandSourceStack commandSourceStack, final @NotNull String[] args) {
public void execute(@NotNull final CommandSourceStack commandSourceStack, final @NotNull String @NotNull [] args) {
System.out.println(Arrays.toString(args));
}
@Override
public @NotNull Collection<String> suggest(final @NotNull CommandSourceStack commandSourceStack, final @NotNull String[] args) {
public @NotNull Collection<String> suggest(final @NotNull CommandSourceStack commandSourceStack, final @NotNull String @NotNull [] args) {
System.out.println(Arrays.toString(args));
return List.of("apple", "banana");
}