diff --git a/patches/api/Adventure.patch b/patches/api/Adventure.patch
index acb17cdd86..11a1296aa8 100644
--- a/patches/api/Adventure.patch
+++ b/patches/api/Adventure.patch
@@ -14,7 +14,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
val annotationsVersion = "24.0.1"
val bungeeCordChatVersion = "1.20-R0.1"
-+val adventureVersion = "4.14.0"
++val adventureVersion = "4.15.0"
+val apiAndDocs: Configuration by configurations.creating {
+ attributes {
+ attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category.DOCUMENTATION))
@@ -2481,11 +2481,21 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
*/
@Deprecated
public void setTexturePack(@NotNull String url);
+@@ -0,0 +0,0 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
+ * @throws IllegalArgumentException Thrown if the URL is null.
+ * @throws IllegalArgumentException Thrown if the URL is too long. The
+ * length restriction is an implementation specific arbitrary value.
++ * @deprecated in favour of {@link #sendResourcePacks(net.kyori.adventure.resource.ResourcePackRequest)}
+ */
++ @Deprecated // Paper - adventure
+ public void setResourcePack(@NotNull String url);
+
+ /**
@@ -0,0 +0,0 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
* pack correctly.
*
*
-+ * @deprecated in favour of {@link #setResourcePack(UUID, String, byte[], net.kyori.adventure.text.Component, boolean)}
++ * @deprecated in favour of {@link #sendResourcePacks(net.kyori.adventure.resource.ResourcePackRequest)}
* @param url The URL from which the client will download the resource
* pack. The string must contain only US-ASCII characters and should
* be encoded as per RFC 1738.
@@ -2498,10 +2508,17 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
/**
@@ -0,0 +0,0 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
+ * {@link PlayerResourcePackStatusEvent} to figure out whether or not
+ * the player loaded the pack!
+ *
To remove a resource pack you can use
+- * {@link #removeResourcePack(UUID)} or {@link #removeResourcePacks()}.
++ * {@link #removeResourcePacks(UUID, UUID...)} or {@link #clearResourcePacks()}.
+ *
The request is sent with empty string as the hash when the hash is
+ * not provided. This might result in newer versions not loading the
* pack correctly.
*
*
-+ * @deprecated in favour of {@link #setResourcePack(UUID, String, byte[], net.kyori.adventure.text.Component, boolean)}
++ * @deprecated in favour of {@link #sendResourcePacks(net.kyori.adventure.resource.ResourcePackRequest)}
* @param url The URL from which the client will download the resource
* pack. The string must contain only US-ASCII characters and should
* be encoded as per RFC 1738.
@@ -2534,6 +2551,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ * case this method will have no affect on them. Use the
+ * {@link PlayerResourcePackStatusEvent} to figure out whether or not
+ * the player loaded the pack!
++ *
To remove a resource pack you can use
++ * {@link #removeResourcePacks(UUID, UUID...)} or {@link #clearResourcePacks()}.
+ *
The request is sent with empty string as the hash when the hash is
+ * not provided. This might result in newer versions not loading the
+ * pack correctly.
@@ -2551,6 +2570,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ * length restriction is an implementation specific arbitrary value.
+ * @throws IllegalArgumentException Thrown if the hash is not 20 bytes
+ * long.
++ * @see #sendResourcePacks(net.kyori.adventure.resource.ResourcePackRequest)
+ */
+ default void setResourcePack(final @NotNull String url, final byte @Nullable [] hash, final net.kyori.adventure.text.@Nullable Component prompt) {
+ this.setResourcePack(url, hash, prompt, false);
@@ -2561,16 +2581,38 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
* Request that the player's client download and switch resource packs.
*
@@ -0,0 +0,0 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
+ * {@link PlayerResourcePackStatusEvent} to figure out whether or not
+ * the player loaded the pack!
+ *
To remove a resource pack you can use
+- * {@link #removeResourcePack(UUID)} or {@link #removeResourcePacks()}.
++ * {@link #removeResourcePacks(UUID, UUID...)} or {@link #clearResourcePacks()}.
+ *
The request is sent with empty string as the hash when the hash is
+ * not provided. This might result in newer versions not loading the
* pack correctly.
- *
- *
-+ * @deprecated in favour of {@link #setResourcePack(String, byte[], net.kyori.adventure.text.Component, boolean)}
- * @param url The URL from which the client will download the resource
- * pack. The string must contain only US-ASCII characters and should
- * be encoded as per RFC 1738.
@@ -0,0 +0,0 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
+ * length restriction is an implementation specific arbitrary value.
* @throws IllegalArgumentException Thrown if the hash is not 20 bytes
* long.
++ * @deprecated in favour of {@link #sendResourcePacks(net.kyori.adventure.resource.ResourcePackRequest)}
+ */
++ @Deprecated // Paper - adventure
+ public void setResourcePack(@NotNull String url, @Nullable byte[] hash, boolean force);
+
+ /**
+@@ -0,0 +0,0 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
+ * {@link PlayerResourcePackStatusEvent} to figure out whether or not
+ * the player loaded the pack!
+ *
To remove a resource pack you can use
+- * {@link #removeResourcePack(UUID)} or {@link #removeResourcePacks()}.
++ * {@link #removeResourcePacks(UUID, UUID...)} or {@link #clearResourcePacks()}.
+ *
The request is sent with empty string as the hash when the hash is
+ * not provided. This might result in newer versions not loading the
+ * pack correctly.
+@@ -0,0 +0,0 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
+ * length restriction is an implementation specific arbitrary value.
+ * @throws IllegalArgumentException Thrown if the hash is not 20 bytes
+ * long.
++ * @deprecated in favour of {@link #sendResourcePacks(net.kyori.adventure.resource.ResourcePackRequest)}
*/
+ @Deprecated // Paper
public void setResourcePack(@NotNull String url, @Nullable byte[] hash, @Nullable String prompt, boolean force);
@@ -2588,8 +2630,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ * perform a file size check against the response content to determine if
+ * the resource pack has changed and needs to be downloaded again. When
+ * this request is sent for the very first time from a given server, the
-+ * client will first display a confirmation GUI to the player before
-+ * proceeding with the download.
++ * client will first display a confirmation GUI with a custom prompt
++ * to the player before proceeding with the download.
+ *
+ * Notes:
+ *
@@ -2597,6 +2639,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ * case this method will have no affect on them. Use the
+ * {@link PlayerResourcePackStatusEvent} to figure out whether or not
+ * the player loaded the pack!
++ *
To remove a resource pack you can use
++ * {@link #removeResourcePacks(UUID, UUID...)} or {@link #clearResourcePacks()}.
+ *
The request is sent with empty string as the hash when the hash is
+ * not provided. This might result in newer versions not loading the
+ * pack correctly.
@@ -2616,6 +2660,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ * length restriction is an implementation specific arbitrary value.
+ * @throws IllegalArgumentException Thrown if the hash is not 20 bytes
+ * long.
++ * @see #sendResourcePacks(net.kyori.adventure.resource.ResourcePackRequest)
+ */
+ default void setResourcePack(final @NotNull String url, final byte @Nullable [] hash, final net.kyori.adventure.text.@Nullable Component prompt, final boolean force) {
+ this.setResourcePack(UUID.nameUUIDFromBytes(url.getBytes(java.nio.charset.StandardCharsets.UTF_8)), url, hash, prompt, force);
@@ -2625,11 +2670,20 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
/**
* Request that the player's client download and switch resource packs.
*
+@@ -0,0 +0,0 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
+ * {@link PlayerResourcePackStatusEvent} to figure out whether or not
+ * the player loaded the pack!
+ *
To remove a resource pack you can use
+- * {@link #removeResourcePack(UUID)} or {@link #removeResourcePacks()}.
++ * {@link #removeResourcePacks(UUID, UUID...)} or {@link #clearResourcePacks()}.
+ *
The request is sent with empty string as the hash when the hash is
+ * not provided. This might result in newer versions not loading the
+ * pack correctly.
@@ -0,0 +0,0 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
* length restriction is an implementation specific arbitrary value.
* @throws IllegalArgumentException Thrown if the hash is not 20 bytes
* long.
-+ * @deprecated use {@link #setResourcePack(UUID, String, byte[], net.kyori.adventure.text.Component, boolean)} )}
++ * @deprecated in favour of {@link #sendResourcePacks(net.kyori.adventure.resource.ResourcePackRequest)}
*/
+ @Deprecated // Paper - adventure
public void setResourcePack(@NotNull UUID id, @NotNull String url, @Nullable byte[] hash, @Nullable String prompt, boolean force);
@@ -2656,6 +2710,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ * case this method will have no affect on them. Use the
+ * {@link PlayerResourcePackStatusEvent} to figure out whether or not
+ * the player loaded the pack!
++ *
To remove a resource pack you can use
++ * {@link #removeResourcePacks(UUID, UUID...)} or {@link #clearResourcePacks()}.
+ *
The request is sent with empty string as the hash when the hash is
+ * not provided. This might result in newer versions not loading the
+ * pack correctly.
@@ -2676,6 +2732,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ * length restriction is an implementation specific arbitrary value.
+ * @throws IllegalArgumentException Thrown if the hash is not 20 bytes
+ * long.
++ * @see #sendResourcePacks(net.kyori.adventure.resource.ResourcePackRequest)
+ */
+ void setResourcePack(@NotNull UUID uuid, @NotNull String url, byte @Nullable [] hash, net.kyori.adventure.text.@Nullable Component prompt, boolean force);
+ // Paper end
@@ -2683,6 +2740,20 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
/**
* Request that the player's client remove a resource pack sent by the
* server.
+ *
+ * @param id the id of the resource pack.
+ * @throws IllegalArgumentException If the ID is null.
++ * @see #removeResourcePacks(UUID, UUID...)
+ */
+ public void removeResourcePack(@NotNull UUID id);
+
+ /**
+ * Request that the player's client remove all loaded resource pack sent by
+ * the server.
++ * @see #clearResourcePacks()
+ */
+ public void removeResourcePacks();
+
@@ -0,0 +0,0 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
*
* @param title Title text
diff --git a/patches/api/Allow-plugins-to-use-SLF4J-for-logging.patch b/patches/api/Allow-plugins-to-use-SLF4J-for-logging.patch
index de2a6f756d..38153e39f4 100644
--- a/patches/api/Allow-plugins-to-use-SLF4J-for-logging.patch
+++ b/patches/api/Allow-plugins-to-use-SLF4J-for-logging.patch
@@ -20,7 +20,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
@@ -0,0 +0,0 @@ java {
val annotationsVersion = "24.0.1"
val bungeeCordChatVersion = "1.20-R0.1"
- val adventureVersion = "4.14.0"
+ val adventureVersion = "4.15.0"
+val slf4jVersion = "2.0.9"
+val log4jVersion = "2.17.1"
val apiAndDocs: Configuration by configurations.creating {
diff --git a/patches/api/Complete-resource-pack-API.patch b/patches/api/Complete-resource-pack-API.patch
index a9c5b4a6de..6b75b46bd6 100644
--- a/patches/api/Complete-resource-pack-API.patch
+++ b/patches/api/Complete-resource-pack-API.patch
@@ -8,16 +8,6 @@ diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/buk
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/entity/Player.java
+++ b/src/main/java/org/bukkit/entity/Player.java
-@@ -0,0 +0,0 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
- * @throws IllegalArgumentException Thrown if the URL is null.
- * @throws IllegalArgumentException Thrown if the URL is too long. The
- * length restriction is an implementation specific arbitrary value.
-+ * @deprecated use {@link #setResourcePack(String, String)}
- */
-+ @Deprecated // Paper
- public void setResourcePack(@NotNull String url);
-
- /**
@@ -0,0 +0,0 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
void setResourcePack(@NotNull UUID uuid, @NotNull String url, byte @Nullable [] hash, net.kyori.adventure.text.@Nullable Component prompt, boolean force);
// Paper end
@@ -39,6 +29,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ *
+ *
Players can disable server resources on their client, in which
+ * case this method will have no affect on them.
++ *
To remove a resource pack you can use
++ * {@link #removeResourcePacks(UUID, UUID...)} or {@link #clearResourcePacks()}.
+ *
+ *
+ * @param url The URL from which the client will download the resource
@@ -70,6 +62,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ *
+ *
Players can disable server resources on their client, in which
+ * case this method will have no affect on them.
++ *
To remove a resource pack you can use
++ * {@link #removeResourcePacks(UUID, UUID...)} or {@link #clearResourcePacks()}.
+ *
+ *
+ * @param url The URL from which the client will download the resource
@@ -102,6 +96,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ *
+ *
Players can disable server resources on their client, in which
+ * case this method will have no affect on them.
++ *
To remove a resource pack you can use
++ * {@link #removeResourcePacks(UUID, UUID...)} or {@link #clearResourcePacks()}.
+ *
+ *
+ * @param url The URL from which the client will download the resource
@@ -135,6 +131,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ *
+ *
Players can disable server resources on their client, in which
+ * case this method will have no affect on them.
++ *
To remove a resource pack you can use
++ * {@link #removeResourcePacks(UUID, UUID...)} or {@link #clearResourcePacks()}.
+ *
+ *
+ * @param uuid Unique resource pack ID.
@@ -149,7 +147,14 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ * @throws IllegalArgumentException Thrown if the URL is too long. The
+ * length restriction is an implementation specific arbitrary value.
+ */
-+ void setResourcePack(@NotNull UUID uuid, @NotNull String url, @NotNull String hash, net.kyori.adventure.text.@Nullable Component resourcePackPrompt, boolean required);
++ default void setResourcePack(final @NotNull UUID uuid, final @NotNull String url, final @NotNull String hash, final net.kyori.adventure.text.@Nullable Component resourcePackPrompt, final boolean required) {
++ this.sendResourcePacks(net.kyori.adventure.resource.ResourcePackRequest.resourcePackRequest()
++ .required(required)
++ .replace(true)
++ .prompt(resourcePackPrompt)
++ .packs(net.kyori.adventure.resource.ResourcePackInfo.resourcePackInfo(uuid, java.net.URI.create(url), hash))
++ );
++ }
+
+ /**
+ * Gets the most recent resource pack status from the player.
diff --git a/patches/server/Add-PlayerKickEvent-causes.patch b/patches/server/Add-PlayerKickEvent-causes.patch
index 150dae0a44..4fdff83987 100644
--- a/patches/server/Add-PlayerKickEvent-causes.patch
+++ b/patches/server/Add-PlayerKickEvent-causes.patch
@@ -146,8 +146,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
- this.disconnect(Component.translatable("multiplayer.requiredTexturePrompt.disconnect"));
+ this.disconnect(Component.translatable("multiplayer.requiredTexturePrompt.disconnect"), org.bukkit.event.player.PlayerKickEvent.Cause.RESOURCE_PACK_REJECTION); // Paper - kick event cause
}
- // Paper start
- PlayerResourcePackStatusEvent.Status packStatus = PlayerResourcePackStatusEvent.Status.values()[packet.action().ordinal()];
+ // Paper start - adventure pack callbacks
+ // call the callbacks before the previously-existing event so the event has final say
@@ -0,0 +0,0 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack
if (this.keepAlivePending) {
if (!this.processedDisconnect && elapsedTime >= KEEPALIVE_LIMIT) { // check keepalive limit, don't fire if already disconnected
diff --git a/patches/server/Adventure.patch b/patches/server/Adventure.patch
index 678fca5d54..1988ae368d 100644
--- a/patches/server/Adventure.patch
+++ b/patches/server/Adventure.patch
@@ -39,6 +39,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+import net.kyori.adventure.nbt.api.BinaryTagHolder;
+import net.kyori.adventure.text.BlockNBTComponent;
+import net.kyori.adventure.text.Component;
++import net.kyori.adventure.text.ComponentLike;
+import net.kyori.adventure.text.EntityNBTComponent;
+import net.kyori.adventure.text.KeybindComponent;
+import net.kyori.adventure.text.NBTComponent;
@@ -48,6 +49,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+import net.kyori.adventure.text.StorageNBTComponent;
+import net.kyori.adventure.text.TextComponent;
+import net.kyori.adventure.text.TranslatableComponent;
++import net.kyori.adventure.text.TranslationArgument;
+import net.kyori.adventure.text.event.ClickEvent;
+import net.kyori.adventure.text.event.HoverEvent;
+import net.kyori.adventure.text.format.NamedTextColor;
@@ -76,6 +78,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+import static com.mojang.serialization.codecs.RecordCodecBuilder.mapCodec;
+import static java.util.function.Function.identity;
+import static net.kyori.adventure.text.Component.text;
++import static net.kyori.adventure.text.TranslationArgument.bool;
++import static net.kyori.adventure.text.TranslationArgument.component;
++import static net.kyori.adventure.text.TranslationArgument.numeric;
+import static net.minecraft.util.ExtraCodecs.recursive;
+import static net.minecraft.util.ExtraCodecs.strictOptionalField;
+
@@ -246,16 +251,36 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
+ return instance.group(Codec.STRING.fieldOf("text").forGetter(TextComponent::content)).apply(instance, Component::text);
+ });
+ static final Codec