aboutsummaryrefslogtreecommitdiffhomepage
path: root/patches/unapplied
diff options
context:
space:
mode:
authorBjarne Koll <[email protected]>2024-10-24 23:03:27 +0200
committerBjarne Koll <[email protected]>2024-10-24 23:03:27 +0200
commit7616ebccd84d934180e27ae952ad11149b7fc1e9 (patch)
tree4fbfc6bb02fa919908beed9d1c82de3db714b15b /patches/unapplied
parent35e01d7a802b25ee85bb096e427496f3daa15dc0 (diff)
downloadPaper-7616ebccd84d934180e27ae952ad11149b7fc1e9.tar.gz
Paper-7616ebccd84d934180e27ae952ad11149b7fc1e9.zip
1000
Diffstat (limited to 'patches/unapplied')
-rw-r--r--patches/unapplied/server/1007-Handle-Oversized-block-entities-in-chunks.patch64
-rw-r--r--patches/unapplied/server/1009-Check-distance-in-entity-interactions.patch68
-rw-r--r--patches/unapplied/server/1010-Configurable-Sand-Duping.patch19
-rw-r--r--patches/unapplied/server/1013-Properly-resend-entities.patch273
-rw-r--r--patches/unapplied/server/1014-Registry-Modification-API.patch1498
-rw-r--r--patches/unapplied/server/1015-Add-registry-entry-and-builders.patch484
-rw-r--r--patches/unapplied/server/1017-Proxy-ItemStack-to-CraftItemStack.patch302
-rw-r--r--patches/unapplied/server/1018-Make-a-PDC-view-accessible-directly-from-ItemStack.patch271
-rw-r--r--patches/unapplied/server/1019-Prioritize-Minecraft-commands-in-function-parsing-an.patch135
-rw-r--r--patches/unapplied/server/1020-optimize-dirt-and-snow-spreading.patch78
-rw-r--r--patches/unapplied/server/1021-Fix-NPE-for-Jukebox-setRecord.patch20
11 files changed, 0 insertions, 3212 deletions
diff --git a/patches/unapplied/server/1007-Handle-Oversized-block-entities-in-chunks.patch b/patches/unapplied/server/1007-Handle-Oversized-block-entities-in-chunks.patch
deleted file mode 100644
index d3283fd63f..0000000000
--- a/patches/unapplied/server/1007-Handle-Oversized-block-entities-in-chunks.patch
+++ /dev/null
@@ -1,64 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Aikar <[email protected]>
-Date: Wed, 6 May 2020 05:00:57 -0400
-Subject: [PATCH] Handle Oversized block entities in chunks
-
-Splits out Extra Packets if too many TE's are encountered to prevent
-creating too large of a packet to sed.
-
-Co-authored-by: Spottedleaf <[email protected]>
-
-diff --git a/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java b/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java
-index 1e75cd33c32f0e2923681da64b9b73b279933c1b..0a8d07bf68b0ceabd13c70196d357fce79dcc2c3 100644
---- a/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java
-+++ b/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java
-@@ -27,6 +27,14 @@ public class ClientboundLevelChunkPacketData {
- private final CompoundTag heightmaps;
- private final byte[] buffer;
- private final List<ClientboundLevelChunkPacketData.BlockEntityInfo> blockEntitiesData;
-+ // Paper start - Handle oversized block entities in chunks
-+ private final java.util.List<net.minecraft.network.protocol.Packet<?>> extraPackets = new java.util.ArrayList<>();
-+ private static final int TE_LIMIT = Integer.getInteger("Paper.excessiveTELimit", 750);
-+
-+ public List<net.minecraft.network.protocol.Packet<?>> getExtraPackets() {
-+ return this.extraPackets;
-+ }
-+ // Paper end - Handle oversized block entities in chunks
-
- // Paper start - Anti-Xray - Add chunk packet info
- @Deprecated @io.papermc.paper.annotation.DoNotUse public ClientboundLevelChunkPacketData(LevelChunk chunk) { this(chunk, null); }
-@@ -50,8 +58,18 @@ public class ClientboundLevelChunkPacketData {
- extractChunkData(new FriendlyByteBuf(this.getWriteBuffer()), chunk, chunkPacketInfo);
- // Paper end
- this.blockEntitiesData = Lists.newArrayList();
-+ int totalTileEntities = 0; // Paper - Handle oversized block entities in chunks
-
- for (Entry<BlockPos, BlockEntity> entry2 : chunk.getBlockEntities().entrySet()) {
-+ // Paper start - Handle oversized block entities in chunks
-+ if (++totalTileEntities > TE_LIMIT) {
-+ var packet = entry2.getValue().getUpdatePacket();
-+ if (packet != null) {
-+ this.extraPackets.add(packet);
-+ continue;
-+ }
-+ }
-+ // Paper end - Handle oversized block entities in chunks
- this.blockEntitiesData.add(ClientboundLevelChunkPacketData.BlockEntityInfo.create(entry2.getValue()));
- }
- }
-diff --git a/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java b/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java
-index 8ead66c134688b11dca15f6509147e726f182e6a..cfcac0fdc130120cb1f8d97c6353d93db7ddf81b 100644
---- a/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java
-+++ b/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java
-@@ -83,4 +83,11 @@ public class ClientboundLevelChunkWithLightPacket implements Packet<ClientGamePa
- public ClientboundLightUpdatePacketData getLightData() {
- return this.lightData;
- }
-+
-+ // Paper start - Handle oversized block entities in chunks
-+ @Override
-+ public java.util.List<Packet<?>> getExtraPackets() {
-+ return this.chunkData.getExtraPackets();
-+ }
-+ // Paper end - Handle oversized block entities in chunks
- }
diff --git a/patches/unapplied/server/1009-Check-distance-in-entity-interactions.patch b/patches/unapplied/server/1009-Check-distance-in-entity-interactions.patch
deleted file mode 100644
index 95e8e99b56..0000000000
--- a/patches/unapplied/server/1009-Check-distance-in-entity-interactions.patch
+++ /dev/null
@@ -1,68 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Spottedleaf <[email protected]>
-Date: Mon, 2 Aug 2021 10:10:40 +0200
-Subject: [PATCH] Check distance in entity interactions
-
-
-diff --git a/src/main/java/net/minecraft/Util.java b/src/main/java/net/minecraft/Util.java
-index 42d7ecfab6f72517904451d9df3f0404b176fdb2..0e38a641d8e537750166b56c57aca4a90d418af1 100644
---- a/src/main/java/net/minecraft/Util.java
-+++ b/src/main/java/net/minecraft/Util.java
-@@ -125,6 +125,7 @@ public class Util {
- .filter(fileSystemProvider -> fileSystemProvider.getScheme().equalsIgnoreCase("jar"))
- .findFirst()
- .orElseThrow(() -> new IllegalStateException("No jar file system provider found"));
-+ public static final double COLLISION_EPSILON = 1.0E-7; // Paper - Check distance in entity interactions
- private static Consumer<String> thePauser = message -> {
- };
-
-diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java
-index 340c4452c09a98bc0220e6fe68dc65afc946986b..05dae689ada8d96009e81aabf95a626bae90ecd3 100644
---- a/src/main/java/net/minecraft/world/entity/LivingEntity.java
-+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java
-@@ -1437,7 +1437,7 @@ public abstract class LivingEntity extends Entity implements Attackable {
- if (!source.is(DamageTypeTags.IS_PROJECTILE)) {
- Entity entity = source.getDirectEntity();
-
-- if (entity instanceof LivingEntity) {
-+ if (entity instanceof LivingEntity && entity.distanceToSqr(this) <= (200.0D * 200.0D)) { // Paper - Check distance in entity interactions
- LivingEntity entityliving = (LivingEntity) entity;
-
- this.blockUsingShield(entityliving);
-@@ -1557,6 +1557,14 @@ public abstract class LivingEntity extends Entity implements Attackable {
- d0 = source.getSourcePosition().x() - this.getX();
- d1 = source.getSourcePosition().z() - this.getZ();
- }
-+ // Paper start - Check distance in entity interactions; see for loop in knockback method
-+ if (Math.abs(d0) > 200) {
-+ d0 = Math.random() - Math.random();
-+ }
-+ if (Math.abs(d1) > 200) {
-+ d1 = Math.random() - Math.random();
-+ }
-+ // Paper end - Check distance in entity interactions
-
- this.knockback(0.4000000059604645D, d0, d1, entity1, entity1 == null ? io.papermc.paper.event.entity.EntityKnockbackEvent.Cause.DAMAGE : io.papermc.paper.event.entity.EntityKnockbackEvent.Cause.ENTITY_ATTACK); // CraftBukkit // Paper - knockback events
- if (!flag) {
-@@ -2351,7 +2359,7 @@ public abstract class LivingEntity extends Entity implements Attackable {
- this.hurtCurrentlyUsedShield((float) -event.getDamage(DamageModifier.BLOCKING));
- Entity entity = damagesource.getDirectEntity();
-
-- if (!damagesource.is(DamageTypeTags.IS_PROJECTILE) && entity instanceof LivingEntity) { // Paper - Fix shield disable inconsistency
-+ if (!damagesource.is(DamageTypeTags.IS_PROJECTILE) && entity instanceof LivingEntity && entity.distanceToSqr(this) <= (200.0D * 200.0D)) { // Paper - Fix shield disable inconsistency & Check distance in entity interactions
- this.blockUsingShield((LivingEntity) entity);
- }
- }
-diff --git a/src/main/java/net/minecraft/world/entity/vehicle/Boat.java b/src/main/java/net/minecraft/world/entity/vehicle/Boat.java
-index 907f751c859855484151fb5d607acee2f2a35076..f1955afc8e367f80ead85bd5ad3b8d66c255565a 100644
---- a/src/main/java/net/minecraft/world/entity/vehicle/Boat.java
-+++ b/src/main/java/net/minecraft/world/entity/vehicle/Boat.java
-@@ -718,7 +718,7 @@ public class Boat extends VehicleEntity implements Leashable, VariantHolder<Boat
- double d2 = (double) (this.getWaterLevelAbove() - this.getBbHeight()) + 0.101D;
-
- if (this.level().noCollision(this, this.getBoundingBox().move(0.0D, d2 - this.getY(), 0.0D))) {
-- this.setPos(this.getX(), d2, this.getZ());
-+ this.move(MoverType.SELF, new Vec3(0.0D, d2 - this.getY(), 0.0D)); // Paper - Check distance in entity interactions // TODO Still needed?
- this.setDeltaMovement(this.getDeltaMovement().multiply(1.0D, 0.0D, 1.0D));
- this.lastYd = 0.0D;
- }
diff --git a/patches/unapplied/server/1010-Configurable-Sand-Duping.patch b/patches/unapplied/server/1010-Configurable-Sand-Duping.patch
deleted file mode 100644
index cbc52697db..0000000000
--- a/patches/unapplied/server/1010-Configurable-Sand-Duping.patch
+++ /dev/null
@@ -1,19 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Owen1212055 <[email protected]>
-Date: Sat, 15 Jun 2024 22:01:39 -0400
-Subject: [PATCH] Configurable Sand Duping
-
-
-diff --git a/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java b/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java
-index caf9273cfafbbaaf8dc95498927383e608773665..b83be9bbb9f348da83c0fd1ecc7f65c8a58b45b9 100644
---- a/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java
-+++ b/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java
-@@ -419,7 +419,7 @@ public class FallingBlockEntity extends Entity {
- boolean flag = (resourcekey1 == Level.END || resourcekey == Level.END) && resourcekey1 != resourcekey;
- Entity entity = super.changeDimension(teleportTarget);
-
-- this.forceTickAfterTeleportToDuplicate = entity != null && flag;
-+ this.forceTickAfterTeleportToDuplicate = entity != null && flag && io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.allowUnsafeEndPortalTeleportation; // Paper
- return entity;
- }
- }
diff --git a/patches/unapplied/server/1013-Properly-resend-entities.patch b/patches/unapplied/server/1013-Properly-resend-entities.patch
deleted file mode 100644
index 148750ed59..0000000000
--- a/patches/unapplied/server/1013-Properly-resend-entities.patch
+++ /dev/null
@@ -1,273 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Owen1212055 <[email protected]>
-Date: Wed, 7 Dec 2022 17:25:19 -0500
-Subject: [PATCH] Properly resend entities
-
-This resolves some issues which caused entities to not be resent correctly.
-Entities that are interacted with need to be resent to the client, so we resend all the entity
-data to the player whilst making sure not to clear dirty entries from the tracker. This makes
-sure that values will be correctly updated to other players.
-
-This also adds utilities to aid in further preventing entity desyncs.
-
-This also also fixes the bug causing cancelling PlayerInteractEvent to cause items to continue
-to be used despite being cancelled on the server.
-
-For example, items being consumed but never finishing, shields being put up, etc.
-The underlying issue of this is that the client modifies their synced data values,
-and so we have to (forcibly) resend them in order for the client to reset their using item state.
-
-See: https://github.com/PaperMC/Paper/pull/1896
-
-== AT ==
-public net.minecraft.server.level.ChunkMap$TrackedEntity serverEntity
-
-diff --git a/src/main/java/net/minecraft/network/syncher/SynchedEntityData.java b/src/main/java/net/minecraft/network/syncher/SynchedEntityData.java
-index 02bf2705ca1c99023a83a22d92e1962181102297..0f99733660f91280e4c6262cf75b3c9cae86f65a 100644
---- a/src/main/java/net/minecraft/network/syncher/SynchedEntityData.java
-+++ b/src/main/java/net/minecraft/network/syncher/SynchedEntityData.java
-@@ -50,7 +50,7 @@ public class SynchedEntityData {
- }
- }
-
-- private <T> SynchedEntityData.DataItem<T> getItem(EntityDataAccessor<T> key) {
-+ public <T> SynchedEntityData.DataItem<T> getItem(EntityDataAccessor<T> key) { // Paper - public
- return (SynchedEntityData.DataItem<T>) this.itemsById[key.id()]; // CraftBukkit - decompile error
- }
-
-@@ -151,6 +151,20 @@ public class SynchedEntityData {
- }
- }
-
-+ // Paper start
-+ // We need to pack all as we cannot rely on "non default values" or "dirty" ones.
-+ // Because these values can possibly be desynced on the client.
-+ @Nullable
-+ public List<SynchedEntityData.DataValue<?>> packAll() {
-+ final List<SynchedEntityData.DataValue<?>> list = new ArrayList<>();
-+ for (final DataItem<?> dataItem : this.itemsById) {
-+ list.add(dataItem.value());
-+ }
-+
-+ return list;
-+ }
-+ // Paper end
-+
- public static class DataItem<T> {
-
- final EntityDataAccessor<T> accessor;
-diff --git a/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java b/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java
-index e9dcdb1e09e84a9b451034ff4bdfa6eae2dd1c04..24b1715397ba8e6f5e9841a030d0e3d964356f89 100644
---- a/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java
-+++ b/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java
-@@ -561,6 +561,7 @@ public class ServerPlayerGameMode {
- }
- // Paper end - extend Player Interact cancellation
- player.getBukkitEntity().updateInventory(); // SPIGOT-2867
-+ this.player.resyncUsingItem(this.player); // Paper - Properly cancel usable items
- return (event.useItemInHand() != Event.Result.ALLOW) ? InteractionResult.SUCCESS : InteractionResult.PASS;
- } else if (this.gameModeForPlayer == GameType.SPECTATOR) {
- MenuProvider itileinventory = iblockdata.getMenuProvider(world, blockposition);
-@@ -612,6 +613,11 @@ public class ServerPlayerGameMode {
-
- return enuminteractionresult;
- } else {
-+ // Paper start - Properly cancel usable items; Cancel only if cancelled + if the interact result is different from default response
-+ if (this.interactResult && this.interactResult != cancelledItem) {
-+ this.player.resyncUsingItem(this.player);
-+ }
-+ // Paper end - Properly cancel usable items
- return InteractionResult.PASS;
- }
- }
-diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
-index 0034483685ba626e5883b857de96ffd36e57e150..4c04eb531b6989f7e618d201ecaa84298eab52c4 100644
---- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
-+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
-@@ -1948,6 +1948,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
- }
-
- if (cancelled) {
-+ this.player.resyncUsingItem(this.player); // Paper - Properly cancel usable items
- this.player.getBukkitEntity().updateInventory(); // SPIGOT-2524
- return;
- }
-@@ -2717,7 +2718,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
-
- // Entity in bucket - SPIGOT-4048 and SPIGOT-6859a
- if ((entity instanceof Bucketable && entity instanceof LivingEntity && origItem != null && origItem.asItem() == Items.WATER_BUCKET) && (event.isCancelled() || ServerGamePacketListenerImpl.this.player.getInventory().getSelected() == null || ServerGamePacketListenerImpl.this.player.getInventory().getSelected().getItem() != origItem)) {
-- entity.getBukkitEntity().update(ServerGamePacketListenerImpl.this.player);
-+ entity.resendPossiblyDesyncedEntityData(ServerGamePacketListenerImpl.this.player); // Paper - The entire mob gets deleted, so resend it
- ServerGamePacketListenerImpl.this.player.containerMenu.sendAllDataToRemote();
- }
-
-diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
-index 3a90dd1289d393426151d4457edaf99731cc34db..ec058eb6a10500831f173dcb47576c32c7516318 100644
---- a/src/main/java/net/minecraft/server/players/PlayerList.java
-+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
-@@ -393,7 +393,7 @@ public abstract class PlayerList {
- ((ServerLevel)player.level()).getChunkSource().chunkMap.addEntity(player); // Paper - Fire PlayerJoinEvent when Player is actually ready; track entity now
- // CraftBukkit end
-
-- player.refreshEntityData(player); // CraftBukkit - BungeeCord#2321, send complete data to self on spawn
-+ //player.refreshEntityData(player); // CraftBukkit - BungeeCord#2321, send complete data to self on spawn // Paper - THIS IS NOT NEEDED ANYMORE
-
- this.sendLevelInfo(player, worldserver1);
-
-@@ -948,12 +948,17 @@ public abstract class PlayerList {
- }
-
- public void sendActiveEffects(LivingEntity entity, ServerGamePacketListenerImpl networkHandler) {
-+ // Paper start - collect packets
-+ this.sendActiveEffects(entity, networkHandler::send);
-+ }
-+ public void sendActiveEffects(LivingEntity entity, java.util.function.Consumer<Packet<? super net.minecraft.network.protocol.game.ClientGamePacketListener>> packetConsumer) {
-+ // Paper end - collect packets
- Iterator iterator = entity.getActiveEffects().iterator();
-
- while (iterator.hasNext()) {
- MobEffectInstance mobeffect = (MobEffectInstance) iterator.next();
-
-- networkHandler.send(new ClientboundUpdateMobEffectPacket(entity.getId(), mobeffect, false));
-+ packetConsumer.accept(new ClientboundUpdateMobEffectPacket(entity.getId(), mobeffect, false)); // Paper - collect packets
- }
-
- }
-diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
-index 52f3d0d4ce28cc6566166ae9a5a1b236ff8c027d..74231cb1c89079473d1727aa3ae2a539d4250317 100644
---- a/src/main/java/net/minecraft/world/entity/Entity.java
-+++ b/src/main/java/net/minecraft/world/entity/Entity.java
-@@ -684,13 +684,45 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
-
- // CraftBukkit start
- public void refreshEntityData(ServerPlayer to) {
-- List<SynchedEntityData.DataValue<?>> list = this.getEntityData().getNonDefaultValues();
-+ List<SynchedEntityData.DataValue<?>> list = this.entityData.packAll(); // Paper - Update EVERYTHING not just not default
-
-- if (list != null) {
-+ if (list != null && to.getBukkitEntity().canSee(this.getBukkitEntity())) { // Paper
- to.connection.send(new ClientboundSetEntityDataPacket(this.getId(), list));
- }
- }
- // CraftBukkit end
-+ // Paper start
-+ // This method should only be used if the data of an entity could have become desynced
-+ // due to interactions on the client.
-+ public void resendPossiblyDesyncedEntityData(net.minecraft.server.level.ServerPlayer player) {
-+ if (player.getBukkitEntity().canSee(this.getBukkitEntity())) {
-+ ServerLevel world = (net.minecraft.server.level.ServerLevel)this.level();
-+ net.minecraft.server.level.ChunkMap.TrackedEntity tracker = world == null ? null : world.getChunkSource().chunkMap.entityMap.get(this.getId());
-+ if (tracker == null) {
-+ return;
-+ }
-+ final net.minecraft.server.level.ServerEntity serverEntity = tracker.serverEntity;
-+ final List<net.minecraft.network.protocol.Packet<? super net.minecraft.network.protocol.game.ClientGamePacketListener>> list = new java.util.ArrayList<>();
-+ serverEntity.sendPairingData(player, list::add);
-+ player.connection.send(new net.minecraft.network.protocol.game.ClientboundBundlePacket(list));
-+ }
-+ }
-+
-+ // This method allows you to specifically resend certain data accessor keys to the client
-+ public void resendPossiblyDesyncedDataValues(List<EntityDataAccessor<?>> keys, ServerPlayer to) {
-+ if (!to.getBukkitEntity().canSee(this.getBukkitEntity())) {
-+ return;
-+ }
-+
-+ final List<SynchedEntityData.DataValue<?>> values = new java.util.ArrayList<>(keys.size());
-+ for (final EntityDataAccessor<?> key : keys) {
-+ final SynchedEntityData.DataItem<?> synchedValue = this.entityData.getItem(key);
-+ values.add(synchedValue.value());
-+ }
-+
-+ to.connection.send(new ClientboundSetEntityDataPacket(this.id, values));
-+ }
-+ // Paper end
-
- public boolean equals(Object object) {
- return object instanceof Entity ? ((Entity) object).id == this.id : false;
-diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java
-index 05dae689ada8d96009e81aabf95a626bae90ecd3..3b15995efc65a351da8dac009b9698494771fefb 100644
---- a/src/main/java/net/minecraft/world/entity/LivingEntity.java
-+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java
-@@ -3879,6 +3879,11 @@ public abstract class LivingEntity extends Entity implements Attackable {
- return ((Byte) this.entityData.get(LivingEntity.DATA_LIVING_ENTITY_FLAGS) & 2) > 0 ? InteractionHand.OFF_HAND : InteractionHand.MAIN_HAND;
- }
-
-+ // Paper start - Properly cancel usable items
-+ public void resyncUsingItem(ServerPlayer serverPlayer) {
-+ this.resendPossiblyDesyncedDataValues(java.util.List.of(DATA_LIVING_ENTITY_FLAGS), serverPlayer);
-+ }
-+ // Paper end - Properly cancel usable items
- private void updatingUsingItem() {
- if (this.isUsingItem()) {
- if (ItemStack.isSameItem(this.getItemInHand(this.getUsedItemHand()), this.useItem)) {
-diff --git a/src/main/java/net/minecraft/world/entity/animal/Bucketable.java b/src/main/java/net/minecraft/world/entity/animal/Bucketable.java
-index b586116d8cca1585f9c9e618ed4d0cb2ef2747be..acf38ef6d8de8b15cf2b09eb7bda390c4e446e9a 100644
---- a/src/main/java/net/minecraft/world/entity/animal/Bucketable.java
-+++ b/src/main/java/net/minecraft/world/entity/animal/Bucketable.java
-@@ -108,8 +108,7 @@ public interface Bucketable {
- itemstack1 = CraftItemStack.asNMSCopy(playerBucketFishEvent.getEntityBucket());
- if (playerBucketFishEvent.isCancelled()) {
- ((ServerPlayer) player).containerMenu.sendAllDataToRemote(); // We need to update inventory to resync client's bucket
-- entity.getBukkitEntity().update((ServerPlayer) player); // We need to play out these packets as the client assumes the fish is gone
-- entity.refreshEntityData((ServerPlayer) player); // Need to send data such as the display name to client
-+ entity.resendPossiblyDesyncedEntityData((ServerPlayer) player); // Paper
- return Optional.of(InteractionResult.FAIL);
- }
- entity.playSound(((Bucketable) entity).getPickupSound(), 1.0F, 1.0F);
-diff --git a/src/main/java/net/minecraft/world/item/SuspiciousStewItem.java b/src/main/java/net/minecraft/world/item/SuspiciousStewItem.java
-index 5b448006debecab983167d15fac59fc2a04805df..9523db353df026f33d7e859788612b97542bd001 100644
---- a/src/main/java/net/minecraft/world/item/SuspiciousStewItem.java
-+++ b/src/main/java/net/minecraft/world/item/SuspiciousStewItem.java
-@@ -58,10 +58,14 @@ public class SuspiciousStewItem extends Item {
- public void cancelUsingItem(net.minecraft.server.level.ServerPlayer entityplayer, ItemStack itemstack) {
- SuspiciousStewEffects suspicioussteweffects = (SuspiciousStewEffects) itemstack.getOrDefault(DataComponents.SUSPICIOUS_STEW_EFFECTS, SuspiciousStewEffects.EMPTY);
-
-+ final List<net.minecraft.network.protocol.Packet<? super net.minecraft.network.protocol.game.ClientGamePacketListener>> packets = new java.util.ArrayList<>(); // Paper - bundlize packets
- for (SuspiciousStewEffects.Entry suspicioussteweffects_a : suspicioussteweffects.effects()) {
-- entityplayer.connection.send(new net.minecraft.network.protocol.game.ClientboundRemoveMobEffectPacket(entityplayer.getId(), suspicioussteweffects_a.effect()));
-+ packets.add(new net.minecraft.network.protocol.game.ClientboundRemoveMobEffectPacket(entityplayer.getId(), suspicioussteweffects_a.effect())); // Paper - bundlize packets
- }
-- entityplayer.server.getPlayerList().sendActivePlayerEffects(entityplayer);
-+ // Paper start - bundlize packets
-+ entityplayer.server.getPlayerList().sendActiveEffects(entityplayer, packets::add);
-+ entityplayer.connection.send(new net.minecraft.network.protocol.game.ClientboundBundlePacket(packets));
-+ // Paper end - bundlize packets
- }
- // CraftBukkit end
- }
-diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
-index 9ca1fee03bfa557f1df7388c6043c9ec6d02a79a..cd789c235acf740ec29c30b180e7fbe1a140caa9 100644
---- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
-+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
-@@ -1012,7 +1012,11 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity {
- return;
- }
-
-- entityTracker.broadcast(this.getHandle().getAddEntityPacket(entityTracker.serverEntity));
-+ // Paper start - resend possibly desynced entity instead of add entity packet
-+ for (final ServerPlayerConnection connection : entityTracker.seenBy) {
-+ this.getHandle().resendPossiblyDesyncedEntityData(connection.getPlayer());
-+ }
-+ // Paper end - resend possibly desynced entity instead of add entity packet
- }
-
- public void update(ServerPlayer player) {
-diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftItemFrame.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftItemFrame.java
-index f3a9b3380246fb2dd4b60a8d1a94c5dfed98d316..350ad61ab3fe66abd528e353b431a4a6dac17506 100644
---- a/src/main/java/org/bukkit/craftbukkit/entity/CraftItemFrame.java
-+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftItemFrame.java
-@@ -39,9 +39,11 @@ public class CraftItemFrame extends CraftHanging implements ItemFrame {
- protected void update() {
- super.update();
-
-+ // Paper start, don't mark as dirty as this is handled in super.update()
- // mark dirty, so that the client gets updated with item and rotation
-- this.getHandle().getEntityData().markDirty(net.minecraft.world.entity.decoration.ItemFrame.DATA_ITEM);
-- this.getHandle().getEntityData().markDirty(net.minecraft.world.entity.decoration.ItemFrame.DATA_ROTATION);
-+ //this.getHandle().getEntityData().markDirty(net.minecraft.world.entity.decoration.ItemFrame.DATA_ITEM);
-+ //this.getHandle().getEntityData().markDirty(net.minecraft.world.entity.decoration.ItemFrame.DATA_ROTATION);
-+ // Paper end
-
- // update redstone
- if (!this.getHandle().generation) {
diff --git a/patches/unapplied/server/1014-Registry-Modification-API.patch b/patches/unapplied/server/1014-Registry-Modification-API.patch
deleted file mode 100644
index dba4fa5830..0000000000
--- a/patches/unapplied/server/1014-Registry-Modification-API.patch
+++ /dev/null
@@ -1,1498 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Jake Potrebic <[email protected]>
-Date: Mon, 27 Feb 2023 18:28:39 -0800
-Subject: [PATCH] Registry Modification API
-
-== AT ==
-public net.minecraft.core.MappedRegistry validateWrite(Lnet/minecraft/resources/ResourceKey;)V
-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 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;
-
- import io.papermc.paper.adventure.PaperAdventure;
- import io.papermc.paper.registry.entry.RegistryEntry;
-+import io.papermc.paper.registry.tag.TagKey;
- import java.util.Collections;
- import java.util.IdentityHashMap;
- import java.util.List;
-@@ -58,6 +59,7 @@ import org.checkerframework.framework.qual.DefaultQualifier;
-
- import static io.papermc.paper.registry.entry.RegistryEntry.apiOnly;
- import static io.papermc.paper.registry.entry.RegistryEntry.entry;
-+import static io.papermc.paper.registry.entry.RegistryEntry.writable;
-
- @DefaultQualifier(NonNull.class)
- public final class PaperRegistries {
-@@ -141,6 +143,15 @@ public final class PaperRegistries {
- return ResourceKey.create((ResourceKey<? extends Registry<M>>) PaperRegistries.registryToNms(typedKey.registryKey()), PaperAdventure.asVanilla(typedKey.key()));
- }
-
-+ public static <M, T> TagKey<T> fromNms(final net.minecraft.tags.TagKey<M> tagKey) {
-+ return TagKey.create(registryFromNms(tagKey.registry()), CraftNamespacedKey.fromMinecraft(tagKey.location()));
-+ }
-+
-+ @SuppressWarnings({"unchecked", "RedundantCast"})
-+ public static <M, T> net.minecraft.tags.TagKey<M> toNms(final TagKey<T> tagKey) {
-+ return net.minecraft.tags.TagKey.create((ResourceKey<? extends Registry<M>>) registryToNms(tagKey.registryKey()), PaperAdventure.asVanilla(tagKey.key()));
-+ }
-+
- private PaperRegistries() {
- }
- }
-diff --git a/src/main/java/io/papermc/paper/registry/PaperRegistryAccess.java b/src/main/java/io/papermc/paper/registry/PaperRegistryAccess.java
-index 35b6a7c5bac9640332a833bd3627f2bcb1bbf2f3..1026b9c04f94ed73049b980822a2eafdbacea7fd 100644
---- a/src/main/java/io/papermc/paper/registry/PaperRegistryAccess.java
-+++ b/src/main/java/io/papermc/paper/registry/PaperRegistryAccess.java
-@@ -83,6 +83,14 @@ public class PaperRegistryAccess implements RegistryAccess {
- return possiblyUnwrap(registryHolder.get());
- }
-
-+ public <M, T extends Keyed, B extends PaperRegistryBuilder<M, T>> WritableCraftRegistry<M, T, B> getWritableRegistry(final RegistryKey<T> key) {
-+ final Registry<T> registry = this.getRegistry(key);
-+ if (registry instanceof WritableCraftRegistry<?, T, ?>) {
-+ return (WritableCraftRegistry<M, T, B>) registry;
-+ }
-+ throw new IllegalArgumentException(key + " does not point to a writable registry");
-+ }
-+
- private static <T extends Keyed> Registry<T> possiblyUnwrap(final Registry<T> registry) {
- if (registry instanceof final DelayedRegistry<T, ?> delayedRegistry) { // if not coming from legacy, unwrap the delayed registry
- return delayedRegistry.delegate();
-diff --git a/src/main/java/io/papermc/paper/registry/PaperRegistryBuilder.java b/src/main/java/io/papermc/paper/registry/PaperRegistryBuilder.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..528c6ee1739d92f766f3904acd7fc5734c93388a
---- /dev/null
-+++ b/src/main/java/io/papermc/paper/registry/PaperRegistryBuilder.java
-@@ -0,0 +1,26 @@
-+package io.papermc.paper.registry;
-+
-+import io.papermc.paper.registry.data.util.Conversions;
-+import net.minecraft.resources.RegistryOps;
-+import org.checkerframework.checker.nullness.qual.Nullable;
-+
-+public interface PaperRegistryBuilder<M, T> extends RegistryBuilder<T> {
-+
-+ M build();
-+
-+ @FunctionalInterface
-+ interface Filler<M, T, B extends PaperRegistryBuilder<M, T>> {
-+
-+ B fill(Conversions conversions, TypedKey<T> key, @Nullable M nms);
-+
-+ default Factory<M, T, B> asFactory() {
-+ return (lookup, key) -> this.fill(lookup, key, null);
-+ }
-+ }
-+
-+ @FunctionalInterface
-+ interface Factory<M, T, B extends PaperRegistryBuilder<M, T>> {
-+
-+ B create(Conversions conversions, TypedKey<T> key);
-+ }
-+}
-diff --git a/src/main/java/io/papermc/paper/registry/PaperRegistryListenerManager.java b/src/main/java/io/papermc/paper/registry/PaperRegistryListenerManager.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..69e946173407eb05b18a2b19b0d45cbb3213570b
---- /dev/null
-+++ b/src/main/java/io/papermc/paper/registry/PaperRegistryListenerManager.java
-@@ -0,0 +1,183 @@
-+package io.papermc.paper.registry;
-+
-+import com.google.common.base.Preconditions;
-+import com.mojang.serialization.Lifecycle;
-+import io.papermc.paper.plugin.bootstrap.BootstrapContext;
-+import io.papermc.paper.plugin.entrypoint.Entrypoint;
-+import io.papermc.paper.plugin.entrypoint.LaunchEntryPointHandler;
-+import io.papermc.paper.plugin.lifecycle.event.LifecycleEventRunner;
-+import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEventType;
-+import io.papermc.paper.registry.data.util.Conversions;
-+import io.papermc.paper.registry.entry.RegistryEntry;
-+import io.papermc.paper.registry.entry.RegistryEntryInfo;
-+import io.papermc.paper.registry.event.RegistryEntryAddEventImpl;
-+import io.papermc.paper.registry.event.RegistryEventMap;
-+import io.papermc.paper.registry.event.RegistryEventProvider;
-+import io.papermc.paper.registry.event.RegistryFreezeEvent;
-+import io.papermc.paper.registry.event.RegistryFreezeEventImpl;
-+import io.papermc.paper.registry.event.type.RegistryEntryAddEventType;
-+import io.papermc.paper.registry.event.type.RegistryEntryAddEventTypeImpl;
-+import io.papermc.paper.registry.event.type.RegistryLifecycleEventType;
-+import java.util.Optional;
-+import net.kyori.adventure.key.Key;
-+import net.minecraft.core.Holder;
-+import net.minecraft.core.MappedRegistry;
-+import net.minecraft.core.RegistrationInfo;
-+import net.minecraft.core.Registry;
-+import net.minecraft.core.WritableRegistry;
-+import net.minecraft.core.registries.BuiltInRegistries;
-+import net.minecraft.resources.ResourceKey;
-+import net.minecraft.resources.ResourceLocation;
-+import org.checkerframework.checker.nullness.qual.Nullable;
-+import org.intellij.lang.annotations.Subst;
-+
-+public class PaperRegistryListenerManager {
-+
-+ public static final PaperRegistryListenerManager INSTANCE = new PaperRegistryListenerManager();
-+
-+ public final RegistryEventMap valueAddEventTypes = new RegistryEventMap("value add");
-+ public final RegistryEventMap freezeEventTypes = new RegistryEventMap("freeze");
-+
-+ private PaperRegistryListenerManager() {
-+ }
-+
-+ /**
-+ * For {@link Registry#register(Registry, String, Object)}
-+ */
-+ public <M> M registerWithListeners(final Registry<M> registry, final String id, final M nms) {
-+ return this.registerWithListeners(registry, ResourceLocation.withDefaultNamespace(id), nms);
-+ }
-+
-+ /**
-+ * For {@link Registry#register(Registry, ResourceLocation, Object)}
-+ */
-+ public <M> M registerWithListeners(final Registry<M> registry, final ResourceLocation loc, final M nms) {
-+ return this.registerWithListeners(registry, ResourceKey.create(registry.key(), loc), nms);
-+ }
-+
-+ /**
-+ * For {@link Registry#register(Registry, ResourceKey, Object)}
-+ */
-+ public <M> M registerWithListeners(final Registry<M> registry, final ResourceKey<M> key, final M nms) {
-+ return this.registerWithListeners(registry, key, nms, RegistrationInfo.BUILT_IN, PaperRegistryListenerManager::registerWithInstance, BuiltInRegistries.BUILT_IN_CONVERSIONS);
-+ }
-+
-+ /**
-+ * For {@link Registry#registerForHolder(Registry, ResourceLocation, Object)}
-+ */
-+ public <M> Holder.Reference<M> registerForHolderWithListeners(final Registry<M> registry, final ResourceLocation loc, final M nms) {
-+ return this.registerForHolderWithListeners(registry, ResourceKey.create(registry.key(), loc), nms);
-+ }
-+
-+ /**
-+ * For {@link Registry#registerForHolder(Registry, ResourceKey, Object)}
-+ */
-+ public <M> Holder.Reference<M> registerForHolderWithListeners(final Registry<M> registry, final ResourceKey<M> key, final M nms) {
-+ return this.registerWithListeners(registry, key, nms, RegistrationInfo.BUILT_IN, WritableRegistry::register, BuiltInRegistries.BUILT_IN_CONVERSIONS);
-+ }
-+
-+ public <M> void registerWithListeners(
-+ final Registry<M> registry,
-+ final ResourceKey<M> key,
-+ final M nms,
-+ final RegistrationInfo registrationInfo,
-+ final Conversions conversions
-+ ) {
-+ this.registerWithListeners(registry, key, nms, registrationInfo, WritableRegistry::register, conversions);
-+ }
-+
-+ // TODO remove Keyed
-+ public <M, T extends org.bukkit.Keyed, B extends PaperRegistryBuilder<M, T>, R> R registerWithListeners(
-+ final Registry<M> registry,
-+ final ResourceKey<M> key,
-+ final M nms,
-+ final RegistrationInfo registrationInfo,
-+ final RegisterMethod<M, R> registerMethod,
-+ final Conversions conversions
-+ ) {
-+ Preconditions.checkState(LaunchEntryPointHandler.INSTANCE.hasEntered(Entrypoint.BOOTSTRAPPER), registry.key() + " tried to run modification listeners before bootstrappers have been called"); // verify that bootstrappers have been called
-+ final @Nullable RegistryEntryInfo<M, T> entry = PaperRegistries.getEntry(registry.key());
-+ if (!RegistryEntry.Modifiable.isModifiable(entry) || !this.valueAddEventTypes.hasHandlers(entry.apiKey())) {
-+ return registerMethod.register((WritableRegistry<M>) registry, key, nms, registrationInfo);
-+ }
-+ final RegistryEntry.Modifiable<M, T, B> modifiableEntry = RegistryEntry.Modifiable.asModifiable(entry);
-+ @SuppressWarnings("PatternValidation") final TypedKey<T> typedKey = TypedKey.create(entry.apiKey(), Key.key(key.location().getNamespace(), key.location().getPath()));
-+ final B builder = modifiableEntry.fillBuilder(conversions, typedKey, nms);
-+ return this.registerWithListeners(registry, modifiableEntry, key, nms, builder, registrationInfo, registerMethod, conversions);
-+ }
-+
-+ <M, T extends org.bukkit.Keyed, B extends PaperRegistryBuilder<M, T>> void registerWithListeners( // TODO remove Keyed
-+ final WritableRegistry<M> registry,
-+ final RegistryEntryInfo<M, T> entry,
-+ final ResourceKey<M> key,
-+ final B builder,
-+ final RegistrationInfo registrationInfo,
-+ final Conversions conversions
-+ ) {
-+ if (!RegistryEntry.Modifiable.isModifiable(entry) || !this.valueAddEventTypes.hasHandlers(entry.apiKey())) {
-+ registry.register(key, builder.build(), registrationInfo);
-+ return;
-+ }
-+ this.registerWithListeners(registry, RegistryEntry.Modifiable.asModifiable(entry), key, null, builder, registrationInfo, WritableRegistry::register, conversions);
-+ }
-+
-+ public <M, T extends org.bukkit.Keyed, B extends PaperRegistryBuilder<M, T>, R> R registerWithListeners( // TODO remove Keyed
-+ final Registry<M> registry,
-+ final RegistryEntry.Modifiable<M, T, B> entry,
-+ final ResourceKey<M> key,
-+ final @Nullable M oldNms,
-+ final B builder,
-+ RegistrationInfo registrationInfo,
-+ final RegisterMethod<M, R> registerMethod,
-+ final Conversions conversions
-+ ) {
-+ @Subst("namespace:key") final ResourceLocation beingAdded = key.location();
-+ @SuppressWarnings("PatternValidation") final TypedKey<T> typedKey = TypedKey.create(entry.apiKey(), Key.key(beingAdded.getNamespace(), beingAdded.getPath()));
-+ final RegistryEntryAddEventImpl<T, B> event = entry.createEntryAddEvent(typedKey, builder, conversions);
-+ LifecycleEventRunner.INSTANCE.callEvent(this.valueAddEventTypes.getEventType(entry.apiKey()), event);
-+ if (oldNms != null) {
-+ ((MappedRegistry<M>) registry).clearIntrusiveHolder(oldNms);
-+ }
-+ final M newNms = event.builder().build();
-+ if (oldNms != null && !newNms.equals(oldNms)) {
-+ registrationInfo = new RegistrationInfo(Optional.empty(), Lifecycle.experimental());
-+ }
-+ return registerMethod.register((WritableRegistry<M>) registry, key, newNms, registrationInfo);
-+ }
-+
-+ private static <M> M registerWithInstance(final WritableRegistry<M> writableRegistry, final ResourceKey<M> key, final M value, final RegistrationInfo registrationInfo) {
-+ writableRegistry.register(key, value, registrationInfo);
-+ return value;
-+ }
-+
-+ @FunctionalInterface
-+ public interface RegisterMethod<M, R> {
-+
-+ R register(WritableRegistry<M> writableRegistry, ResourceKey<M> key, M value, RegistrationInfo registrationInfo);
-+ }
-+
-+ public <M, T extends org.bukkit.Keyed, B extends PaperRegistryBuilder<M, T>> void runFreezeListeners(final ResourceKey<? extends Registry<M>> resourceKey, final Conversions conversions) {
-+ final @Nullable RegistryEntryInfo<M, T> entry = PaperRegistries.getEntry(resourceKey);
-+ if (!RegistryEntry.Addable.isAddable(entry) || !this.freezeEventTypes.hasHandlers(entry.apiKey())) {
-+ return;
-+ }
-+ final RegistryEntry.Addable<M, T, B> writableEntry = RegistryEntry.Addable.asAddable(entry);
-+ final WritableCraftRegistry<M, T, B> writableRegistry = PaperRegistryAccess.instance().getWritableRegistry(entry.apiKey());
-+ final RegistryFreezeEventImpl<T, B> event = writableEntry.createFreezeEvent(writableRegistry, conversions);
-+ LifecycleEventRunner.INSTANCE.callEvent(this.freezeEventTypes.getEventType(entry.apiKey()), event);
-+ }
-+
-+ public <T, B extends RegistryBuilder<T>> RegistryEntryAddEventType<T, B> getRegistryValueAddEventType(final RegistryEventProvider<T, B> type) {
-+ if (!RegistryEntry.Modifiable.isModifiable(PaperRegistries.getEntry(type.registryKey()))) {
-+ throw new IllegalArgumentException(type.registryKey() + " does not support RegistryEntryAddEvent");
-+ }
-+ return this.valueAddEventTypes.getOrCreate(type.registryKey(), RegistryEntryAddEventTypeImpl::new);
-+ }
-+
-+ public <T, B extends RegistryBuilder<T>> LifecycleEventType.Prioritizable<BootstrapContext, RegistryFreezeEvent<T, B>> getRegistryFreezeEventType(final RegistryEventProvider<T, B> type) {
-+ if (!RegistryEntry.Addable.isAddable(PaperRegistries.getEntry(type.registryKey()))) {
-+ throw new IllegalArgumentException(type.registryKey() + " does not support RegistryFreezeEvent");
-+ }
-+ return this.freezeEventTypes.getOrCreate(type.registryKey(), RegistryLifecycleEventType::new);
-+ }
-+}
-diff --git a/src/main/java/io/papermc/paper/registry/WritableCraftRegistry.java b/src/main/java/io/papermc/paper/registry/WritableCraftRegistry.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..78317c7ab42a666f19634593a8f3b696700764c8
---- /dev/null
-+++ b/src/main/java/io/papermc/paper/registry/WritableCraftRegistry.java
-@@ -0,0 +1,92 @@
-+package io.papermc.paper.registry;
-+
-+import com.mojang.serialization.Lifecycle;
-+import io.papermc.paper.adventure.PaperAdventure;
-+import io.papermc.paper.registry.data.util.Conversions;
-+import io.papermc.paper.registry.entry.RegistryEntry;
-+import io.papermc.paper.registry.event.WritableRegistry;
-+import java.util.Optional;
-+import java.util.function.BiFunction;
-+import java.util.function.Consumer;
-+import net.minecraft.core.MappedRegistry;
-+import net.minecraft.core.RegistrationInfo;
-+import net.minecraft.resources.ResourceKey;
-+import org.bukkit.Keyed;
-+import org.bukkit.NamespacedKey;
-+import org.bukkit.craftbukkit.CraftRegistry;
-+import org.bukkit.craftbukkit.util.ApiVersion;
-+import org.checkerframework.checker.nullness.qual.Nullable;
-+
-+public class WritableCraftRegistry<M, T extends Keyed, B extends PaperRegistryBuilder<M, T>> extends CraftRegistry<T, M> {
-+
-+ private static final RegistrationInfo FROM_PLUGIN = new RegistrationInfo(Optional.empty(), Lifecycle.experimental());
-+
-+ private final RegistryEntry.BuilderHolder<M, T, B> entry;
-+ private final MappedRegistry<M> registry;
-+ private final PaperRegistryBuilder.Factory<M, T, ? extends B> builderFactory;
-+ private final BiFunction<? super NamespacedKey, M, T> minecraftToBukkit;
-+
-+ public WritableCraftRegistry(
-+ final RegistryEntry.BuilderHolder<M, T, B> entry,
-+ final Class<?> classToPreload,
-+ final MappedRegistry<M> registry,
-+ final BiFunction<NamespacedKey, ApiVersion, NamespacedKey> serializationUpdater,
-+ final PaperRegistryBuilder.Factory<M, T, ? extends B> builderFactory,
-+ final BiFunction<? super NamespacedKey, M, T> minecraftToBukkit
-+ ) {
-+ super(classToPreload, registry, null, serializationUpdater);
-+ this.entry = entry;
-+ this.registry = registry;
-+ this.builderFactory = builderFactory;
-+ this.minecraftToBukkit = minecraftToBukkit;
-+ }
-+
-+ public void register(final TypedKey<T> key, final Consumer<? super B> value, final Conversions conversions) {
-+ final ResourceKey<M> resourceKey = ResourceKey.create(this.registry.key(), PaperAdventure.asVanilla(key.key()));
-+ this.registry.validateWrite(resourceKey);
-+ final B builder = this.newBuilder(conversions, key);
-+ value.accept(builder);
-+ PaperRegistryListenerManager.INSTANCE.registerWithListeners(
-+ this.registry,
-+ RegistryEntry.Modifiable.asModifiable(this.entry),
-+ resourceKey,
-+ builder,
-+ FROM_PLUGIN,
-+ conversions
-+ );
-+ }
-+
-+ @Override
-+ public final @Nullable T createBukkit(final NamespacedKey namespacedKey, final @Nullable M minecraft) {
-+ if (minecraft == null) {
-+ return null;
-+ }
-+ return this.minecraftToBukkit(namespacedKey, minecraft);
-+ }
-+
-+ public WritableRegistry<T, B> createApiWritableRegistry(final Conversions conversions) {
-+ return new ApiWritableRegistry(conversions);
-+ }
-+
-+ public T minecraftToBukkit(final NamespacedKey namespacedKey, final M minecraft) {
-+ return this.minecraftToBukkit.apply(namespacedKey, minecraft);
-+ }
-+
-+ protected B newBuilder(final Conversions conversions, final TypedKey<T> key) {
-+ return this.builderFactory.create(conversions, key);
-+ }
-+
-+ public class ApiWritableRegistry implements WritableRegistry<T, B> {
-+
-+ private final Conversions conversions;
-+
-+ public ApiWritableRegistry(final Conversions conversions) {
-+ this.conversions = conversions;
-+ }
-+
-+ @Override
-+ public void register(final TypedKey<T> key, final Consumer<? super B> value) {
-+ WritableCraftRegistry.this.register(key, value, this.conversions);
-+ }
-+ }
-+}
-diff --git a/src/main/java/io/papermc/paper/registry/data/util/Conversions.java b/src/main/java/io/papermc/paper/registry/data/util/Conversions.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..eda5cc7d45ef59ccc1c9c7e027c1f044f1dcc86b
---- /dev/null
-+++ b/src/main/java/io/papermc/paper/registry/data/util/Conversions.java
-@@ -0,0 +1,36 @@
-+package io.papermc.paper.registry.data.util;
-+
-+import com.mojang.serialization.JavaOps;
-+import io.papermc.paper.adventure.WrapperAwareSerializer;
-+import net.kyori.adventure.text.Component;
-+import net.minecraft.resources.RegistryOps;
-+import org.checkerframework.checker.nullness.qual.NonNull;
-+import org.checkerframework.checker.nullness.qual.Nullable;
-+import org.checkerframework.framework.qual.DefaultQualifier;
-+import org.jetbrains.annotations.Contract;
-+
-+@DefaultQualifier(NonNull.class)
-+public class Conversions {
-+
-+ private final RegistryOps.RegistryInfoLookup lookup;
-+ private final WrapperAwareSerializer serializer;
-+
-+ public Conversions(final RegistryOps.RegistryInfoLookup lookup) {
-+ this.lookup = lookup;
-+ this.serializer = new WrapperAwareSerializer(() -> RegistryOps.create(JavaOps.INSTANCE, lookup));
-+ }
-+
-+ public RegistryOps.RegistryInfoLookup lookup() {
-+ return this.lookup;
-+ }
-+
-+ @Contract("null -> null; !null -> !null")
-+ public net.minecraft.network.chat.@Nullable Component asVanilla(final @Nullable Component adventure) {
-+ if (adventure == null) return null;
-+ return this.serializer.serialize(adventure);
-+ }
-+
-+ public Component asAdventure(final net.minecraft.network.chat.@Nullable Component vanilla) {
-+ return vanilla == null ? Component.empty() : this.serializer.deserialize(vanilla);
-+ }
-+}
-diff --git a/src/main/java/io/papermc/paper/registry/entry/AddableRegistryEntry.java b/src/main/java/io/papermc/paper/registry/entry/AddableRegistryEntry.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..aeec9b3ae2911f041d000b3db72f37974020ba60
---- /dev/null
-+++ b/src/main/java/io/papermc/paper/registry/entry/AddableRegistryEntry.java
-@@ -0,0 +1,44 @@
-+package io.papermc.paper.registry.entry;
-+
-+import io.papermc.paper.registry.PaperRegistryBuilder;
-+import io.papermc.paper.registry.RegistryHolder;
-+import io.papermc.paper.registry.RegistryKey;
-+import io.papermc.paper.registry.TypedKey;
-+import io.papermc.paper.registry.WritableCraftRegistry;
-+import io.papermc.paper.registry.data.util.Conversions;
-+import java.util.function.BiFunction;
-+import net.minecraft.core.MappedRegistry;
-+import net.minecraft.core.Registry;
-+import net.minecraft.resources.ResourceKey;
-+import org.bukkit.Keyed;
-+import org.bukkit.NamespacedKey;
-+
-+public class AddableRegistryEntry<M, T extends Keyed, B extends PaperRegistryBuilder<M, T>> extends CraftRegistryEntry<M, T> implements RegistryEntry.Addable<M, T, B> {
-+
-+ private final PaperRegistryBuilder.Filler<M, T, B> builderFiller;
-+
-+ protected AddableRegistryEntry(
-+ final ResourceKey<? extends Registry<M>> mcKey,
-+ final RegistryKey<T> apiKey,
-+ final Class<?> classToPreload,
-+ final BiFunction<NamespacedKey, M, T> minecraftToBukkit,
-+ final PaperRegistryBuilder.Filler<M, T, B> builderFiller
-+ ) {
-+ super(mcKey, apiKey, classToPreload, minecraftToBukkit);
-+ this.builderFiller = builderFiller;
-+ }
-+
-+ private WritableCraftRegistry<M, T, B> createRegistry(final Registry<M> registry) {
-+ return new WritableCraftRegistry<>(this, this.classToPreload, (MappedRegistry<M>) registry, this.updater, this.builderFiller.asFactory(), this.minecraftToBukkit);
-+ }
-+
-+ @Override
-+ public RegistryHolder<T> createRegistryHolder(final Registry<M> nmsRegistry) {
-+ return new RegistryHolder.Memoized<>(() -> this.createRegistry(nmsRegistry));
-+ }
-+
-+ @Override
-+ public B fillBuilder(final Conversions conversions, final TypedKey<T> key, final M nms) {
-+ return this.builderFiller.fill(conversions, key, nms);
-+ }
-+}
-diff --git a/src/main/java/io/papermc/paper/registry/entry/ModifiableRegistryEntry.java b/src/main/java/io/papermc/paper/registry/entry/ModifiableRegistryEntry.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..515a995e3862f8e7cb93d149315ea32e04a08716
---- /dev/null
-+++ b/src/main/java/io/papermc/paper/registry/entry/ModifiableRegistryEntry.java
-@@ -0,0 +1,32 @@
-+package io.papermc.paper.registry.entry;
-+
-+import io.papermc.paper.registry.PaperRegistryBuilder;
-+import io.papermc.paper.registry.RegistryKey;
-+import io.papermc.paper.registry.TypedKey;
-+import io.papermc.paper.registry.data.util.Conversions;
-+import java.util.function.BiFunction;
-+import net.minecraft.core.Registry;
-+import net.minecraft.resources.ResourceKey;
-+import org.bukkit.Keyed;
-+import org.bukkit.NamespacedKey;
-+
-+public class ModifiableRegistryEntry<M, T extends Keyed, B extends PaperRegistryBuilder<M, T>> extends CraftRegistryEntry<M, T> implements RegistryEntry.Modifiable<M, T, B> {
-+
-+ protected final PaperRegistryBuilder.Filler<M, T, B> builderFiller;
-+
-+ protected ModifiableRegistryEntry(
-+ final ResourceKey<? extends Registry<M>> mcKey,
-+ final RegistryKey<T> apiKey,
-+ final Class<?> toPreload,
-+ final BiFunction<NamespacedKey, M, T> minecraftToBukkit,
-+ final PaperRegistryBuilder.Filler<M, T, B> builderFiller
-+ ) {
-+ super(mcKey, apiKey, toPreload, minecraftToBukkit);
-+ this.builderFiller = builderFiller;
-+ }
-+
-+ @Override
-+ public B fillBuilder(final Conversions conversions, final TypedKey<T> key, final M nms) {
-+ return this.builderFiller.fill(conversions, key, nms);
-+ }
-+}
-diff --git a/src/main/java/io/papermc/paper/registry/entry/RegistryEntry.java b/src/main/java/io/papermc/paper/registry/entry/RegistryEntry.java
-index 15991bf13894d850f360a520d1815711d25973ec..f2e919705301cb23ed1938ca3c1976378249172c 100644
---- a/src/main/java/io/papermc/paper/registry/entry/RegistryEntry.java
-+++ b/src/main/java/io/papermc/paper/registry/entry/RegistryEntry.java
-@@ -1,7 +1,13 @@
- package io.papermc.paper.registry.entry;
-
-+import io.papermc.paper.registry.PaperRegistryBuilder;
- import io.papermc.paper.registry.RegistryHolder;
- import io.papermc.paper.registry.RegistryKey;
-+import io.papermc.paper.registry.TypedKey;
-+import io.papermc.paper.registry.WritableCraftRegistry;
-+import io.papermc.paper.registry.data.util.Conversions;
-+import io.papermc.paper.registry.event.RegistryEntryAddEventImpl;
-+import io.papermc.paper.registry.event.RegistryFreezeEventImpl;
- import io.papermc.paper.registry.legacy.DelayedRegistryEntry;
- import java.util.function.BiFunction;
- import java.util.function.Supplier;
-@@ -11,6 +17,7 @@ import org.bukkit.Keyed;
- import org.bukkit.NamespacedKey;
- import org.bukkit.craftbukkit.util.ApiVersion;
- import org.checkerframework.checker.nullness.qual.NonNull;
-+import org.checkerframework.checker.nullness.qual.Nullable;
- import org.checkerframework.framework.qual.DefaultQualifier;
-
- @DefaultQualifier(NonNull.class)
-@@ -32,6 +39,65 @@ public interface RegistryEntry<M, B extends Keyed> extends RegistryEntryInfo<M,
- return new DelayedRegistryEntry<>(this);
- }
-
-+ interface BuilderHolder<M, T, B extends PaperRegistryBuilder<M, T>> extends RegistryEntryInfo<M, T> {
-+
-+ B fillBuilder(Conversions conversions, TypedKey<T> key, M nms);
-+ }
-+
-+ /**
-+ * Can mutate values being added to the registry
-+ */
-+ interface Modifiable<M, T, B extends PaperRegistryBuilder<M, T>> extends BuilderHolder<M, T, B> {
-+
-+ static boolean isModifiable(final @Nullable RegistryEntryInfo<?, ?> entry) {
-+ return entry instanceof RegistryEntry.Modifiable<?, ?, ?> || (entry instanceof final DelayedRegistryEntry<?, ?> delayed && delayed.delegate() instanceof RegistryEntry.Modifiable<?, ?, ?>);
-+ }
-+
-+ static <M, T extends Keyed, B extends PaperRegistryBuilder<M, T>> Modifiable<M, T, B> asModifiable(final RegistryEntryInfo<M, T> entry) { // TODO remove Keyed
-+ return (Modifiable<M, T, B>) possiblyUnwrap(entry);
-+ }
-+
-+ default RegistryEntryAddEventImpl<T, B> createEntryAddEvent(final TypedKey<T> key, final B initialBuilder, final Conversions conversions) {
-+ return new RegistryEntryAddEventImpl<>(key, initialBuilder, this.apiKey(), conversions);
-+ }
-+ }
-+
-+ /**
-+ * Can only add new values to the registry, not modify any values.
-+ */
-+ interface Addable<M, T extends Keyed, B extends PaperRegistryBuilder<M, T>> extends BuilderHolder<M, T, B> { // TODO remove Keyed
-+
-+ default RegistryFreezeEventImpl<T, B> createFreezeEvent(final WritableCraftRegistry<M, T, B> writableRegistry, final Conversions conversions) {
-+ return new RegistryFreezeEventImpl<>(this.apiKey(), writableRegistry.createApiWritableRegistry(conversions), conversions);
-+ }
-+
-+ static boolean isAddable(final @Nullable RegistryEntryInfo<?, ?> entry) {
-+ return entry instanceof RegistryEntry.Addable<?, ?, ?> || (entry instanceof final DelayedRegistryEntry<?, ?> delayed && delayed.delegate() instanceof RegistryEntry.Addable<?, ?, ?>);
-+ }
-+
-+ static <M, T extends Keyed, B extends PaperRegistryBuilder<M, T>> Addable<M, T, B> asAddable(final RegistryEntryInfo<M, T> entry) {
-+ return (Addable<M, T, B>) possiblyUnwrap(entry);
-+ }
-+ }
-+
-+ /**
-+ * Can mutate values and add new values.
-+ */
-+ interface Writable<M, T extends Keyed, B extends PaperRegistryBuilder<M, T>> extends Modifiable<M, T, B>, Addable<M, T, B> { // TODO remove Keyed
-+
-+ static boolean isWritable(final @Nullable RegistryEntryInfo<?, ?> entry) {
-+ return entry instanceof RegistryEntry.Writable<?, ?, ?> || (entry instanceof final DelayedRegistryEntry<?, ?> delayed && delayed.delegate() instanceof RegistryEntry.Writable<?, ?, ?>);
-+ }
-+
-+ static <M, T extends Keyed, B extends PaperRegistryBuilder<M, T>> Writable<M, T, B> asWritable(final RegistryEntryInfo<M, T> entry) { // TODO remove Keyed
-+ return (Writable<M, T, B>) possiblyUnwrap(entry);
-+ }
-+ }
-+
-+ private static <M, B extends Keyed> RegistryEntryInfo<M, B> possiblyUnwrap(final RegistryEntryInfo<M, B> entry) {
-+ return entry instanceof final DelayedRegistryEntry<M, B> delayed ? delayed.delegate() : entry;
-+ }
-+
- static <M, B extends Keyed> RegistryEntry<M, B> entry(
- final ResourceKey<? extends Registry<M>> mcKey,
- final RegistryKey<B> apiKey,
-@@ -48,4 +114,24 @@ public interface RegistryEntry<M, B extends Keyed> extends RegistryEntryInfo<M,
- ) {
- return new ApiRegistryEntry<>(mcKey, apiKey, apiRegistrySupplier);
- }
-+
-+ static <M, T extends Keyed, B extends PaperRegistryBuilder<M, T>> RegistryEntry<M, T> modifiable(
-+ final ResourceKey<? extends Registry<M>> mcKey,
-+ final RegistryKey<T> apiKey,
-+ final Class<?> toPreload,
-+ final BiFunction<NamespacedKey, M, T> minecraftToBukkit,
-+ final PaperRegistryBuilder.Filler<M, T, B> filler
-+ ) {
-+ return new ModifiableRegistryEntry<>(mcKey, apiKey, toPreload, minecraftToBukkit, filler);
-+ }
-+
-+ static <M, T extends Keyed, B extends PaperRegistryBuilder<M, T>> RegistryEntry<M, T> writable(
-+ final ResourceKey<? extends Registry<M>> mcKey,
-+ final RegistryKey<T> apiKey,
-+ final Class<?> toPreload,
-+ final BiFunction<NamespacedKey, M, T> minecraftToBukkit,
-+ final PaperRegistryBuilder.Filler<M, T, B> filler
-+ ) {
-+ return new WritableRegistryEntry<>(mcKey, apiKey, toPreload, minecraftToBukkit, filler);
-+ }
- }
-diff --git a/src/main/java/io/papermc/paper/registry/entry/WritableRegistryEntry.java b/src/main/java/io/papermc/paper/registry/entry/WritableRegistryEntry.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..562accce731630327d116afd1c9d559df7e386bd
---- /dev/null
-+++ b/src/main/java/io/papermc/paper/registry/entry/WritableRegistryEntry.java
-@@ -0,0 +1,22 @@
-+package io.papermc.paper.registry.entry;
-+
-+import io.papermc.paper.registry.PaperRegistryBuilder;
-+import io.papermc.paper.registry.RegistryKey;
-+import java.util.function.BiFunction;
-+import net.minecraft.core.Registry;
-+import net.minecraft.resources.ResourceKey;
-+import org.bukkit.Keyed;
-+import org.bukkit.NamespacedKey;
-+
-+public class WritableRegistryEntry<M, T extends Keyed, B extends PaperRegistryBuilder<M, T>> extends AddableRegistryEntry<M, T, B> implements RegistryEntry.Writable<M, T, B> { // TODO remove Keyed
-+
-+ protected WritableRegistryEntry(
-+ final ResourceKey<? extends Registry<M>> mcKey,
-+ final RegistryKey<T> apiKey,
-+ final Class<?> classToPreload,
-+ final BiFunction<NamespacedKey, M, T> minecraftToBukkit,
-+ final PaperRegistryBuilder.Filler<M, T, B> builderFiller
-+ ) {
-+ super(mcKey, apiKey, classToPreload, minecraftToBukkit, builderFiller);
-+ }
-+}
-diff --git a/src/main/java/io/papermc/paper/registry/event/RegistryEntryAddEventImpl.java b/src/main/java/io/papermc/paper/registry/event/RegistryEntryAddEventImpl.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..cc9c8fd313f530777af80ad79e03903f3f8f9829
---- /dev/null
-+++ b/src/main/java/io/papermc/paper/registry/event/RegistryEntryAddEventImpl.java
-@@ -0,0 +1,30 @@
-+package io.papermc.paper.registry.event;
-+
-+import io.papermc.paper.plugin.lifecycle.event.PaperLifecycleEvent;
-+import io.papermc.paper.registry.PaperRegistries;
-+import io.papermc.paper.registry.RegistryBuilder;
-+import io.papermc.paper.registry.RegistryKey;
-+import io.papermc.paper.registry.TypedKey;
-+import io.papermc.paper.registry.data.util.Conversions;
-+import io.papermc.paper.registry.set.NamedRegistryKeySetImpl;
-+import io.papermc.paper.registry.tag.Tag;
-+import io.papermc.paper.registry.tag.TagKey;
-+import net.minecraft.core.HolderSet;
-+import net.minecraft.resources.RegistryOps;
-+import org.bukkit.Keyed;
-+import org.checkerframework.checker.nullness.qual.NonNull;
-+
-+public record RegistryEntryAddEventImpl<T, B extends RegistryBuilder<T>>(
-+ TypedKey<T> key,
-+ B builder,
-+ RegistryKey<T> registryKey,
-+ Conversions conversions
-+) implements RegistryEntryAddEvent<T, B>, PaperLifecycleEvent {
-+
-+ @Override
-+ public @NonNull <V extends Keyed> Tag<V> getOrCreateTag(final TagKey<V> tagKey) {
-+ final RegistryOps.RegistryInfo<Object> registryInfo = this.conversions.lookup().lookup(PaperRegistries.registryToNms(tagKey.registryKey())).orElseThrow();
-+ final HolderSet.Named<?> tagSet = registryInfo.getter().getOrThrow(PaperRegistries.toNms(tagKey));
-+ return new NamedRegistryKeySetImpl<>(tagKey, tagSet);
-+ }
-+}
-diff --git a/src/main/java/io/papermc/paper/registry/event/RegistryEventMap.java b/src/main/java/io/papermc/paper/registry/event/RegistryEventMap.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..bfcd0884d778ca62817fb1d22f0f2ed1f2abe855
---- /dev/null
-+++ b/src/main/java/io/papermc/paper/registry/event/RegistryEventMap.java
-@@ -0,0 +1,45 @@
-+package io.papermc.paper.registry.event;
-+
-+import io.papermc.paper.plugin.bootstrap.BootstrapContext;
-+import io.papermc.paper.plugin.lifecycle.event.LifecycleEvent;
-+import io.papermc.paper.plugin.lifecycle.event.LifecycleEventRunner;
-+import io.papermc.paper.plugin.lifecycle.event.types.AbstractLifecycleEventType;
-+import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEventType;
-+import io.papermc.paper.registry.RegistryKey;
-+import java.util.HashMap;
-+import java.util.Map;
-+import java.util.Objects;
-+import java.util.function.BiFunction;
-+
-+public final class RegistryEventMap {
-+
-+ private final Map<RegistryKey<?>, LifecycleEventType<BootstrapContext, ? extends LifecycleEvent, ?>> eventTypes = new HashMap<>();
-+ private final String name;
-+
-+ public RegistryEventMap(final String name) {
-+ this.name = name;
-+ }
-+
-+ @SuppressWarnings("unchecked")
-+ public <T, E extends LifecycleEvent, ET extends LifecycleEventType<BootstrapContext, E, ?>> ET getOrCreate(final RegistryKey<T> registryKey, final BiFunction<? super RegistryKey<T>, ? super String, ET> eventTypeCreator) {
-+ final ET eventType;
-+ if (this.eventTypes.containsKey(registryKey)) {
-+ eventType = (ET) this.eventTypes.get(registryKey);
-+ } else {
-+ eventType = eventTypeCreator.apply(registryKey, this.name);
-+ this.eventTypes.put(registryKey, eventType);
-+ }
-+ return eventType;
-+ }
-+
-+ @SuppressWarnings("unchecked")
-+ public <T, E extends LifecycleEvent> LifecycleEventType<BootstrapContext, E, ?> getEventType(final RegistryKey<T> registryKey) {
-+ return (LifecycleEventType<BootstrapContext, E, ?>) Objects.requireNonNull(this.eventTypes.get(registryKey), "No hook for " + registryKey);
-+ }
-+
-+ public boolean hasHandlers(final RegistryKey<?> registryKey) {
-+ final AbstractLifecycleEventType<?, ?, ?> type = ((AbstractLifecycleEventType<?, ?, ?>) this.eventTypes.get(registryKey));
-+ return type != null && type.hasHandlers();
-+ }
-+
-+}
-diff --git a/src/main/java/io/papermc/paper/registry/event/RegistryEventTypeProviderImpl.java b/src/main/java/io/papermc/paper/registry/event/RegistryEventTypeProviderImpl.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..34c842ffa355e3c8001dd7b8551bcb49229a6391
---- /dev/null
-+++ b/src/main/java/io/papermc/paper/registry/event/RegistryEventTypeProviderImpl.java
-@@ -0,0 +1,24 @@
-+package io.papermc.paper.registry.event;
-+
-+import io.papermc.paper.plugin.bootstrap.BootstrapContext;
-+import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEventType;
-+import io.papermc.paper.registry.PaperRegistryListenerManager;
-+import io.papermc.paper.registry.RegistryBuilder;
-+import io.papermc.paper.registry.event.type.RegistryEntryAddEventType;
-+
-+public class RegistryEventTypeProviderImpl implements RegistryEventTypeProvider {
-+
-+ public static RegistryEventTypeProviderImpl instance() {
-+ return (RegistryEventTypeProviderImpl) RegistryEventTypeProvider.provider();
-+ }
-+
-+ @Override
-+ public <T, B extends RegistryBuilder<T>> RegistryEntryAddEventType<T, B> registryEntryAdd(final RegistryEventProvider<T, B> type) {
-+ return PaperRegistryListenerManager.INSTANCE.getRegistryValueAddEventType(type);
-+ }
-+
-+ @Override
-+ public <T, B extends RegistryBuilder<T>> LifecycleEventType.Prioritizable<BootstrapContext, RegistryFreezeEvent<T, B>> registryFreeze(final RegistryEventProvider<T, B> type) {
-+ return PaperRegistryListenerManager.INSTANCE.getRegistryFreezeEventType(type);
-+ }
-+}
-diff --git a/src/main/java/io/papermc/paper/registry/event/RegistryFreezeEventImpl.java b/src/main/java/io/papermc/paper/registry/event/RegistryFreezeEventImpl.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..63957d2509e68ccc6eb2fd9ecaa35bfad7b71b81
---- /dev/null
-+++ b/src/main/java/io/papermc/paper/registry/event/RegistryFreezeEventImpl.java
-@@ -0,0 +1,28 @@
-+package io.papermc.paper.registry.event;
-+
-+import io.papermc.paper.plugin.lifecycle.event.PaperLifecycleEvent;
-+import io.papermc.paper.registry.PaperRegistries;
-+import io.papermc.paper.registry.RegistryBuilder;
-+import io.papermc.paper.registry.RegistryKey;
-+import io.papermc.paper.registry.data.util.Conversions;
-+import io.papermc.paper.registry.set.NamedRegistryKeySetImpl;
-+import io.papermc.paper.registry.tag.Tag;
-+import io.papermc.paper.registry.tag.TagKey;
-+import net.minecraft.core.HolderSet;
-+import net.minecraft.resources.RegistryOps;
-+import org.bukkit.Keyed;
-+import org.checkerframework.checker.nullness.qual.NonNull;
-+
-+public record RegistryFreezeEventImpl<T, B extends RegistryBuilder<T>>(
-+ RegistryKey<T> registryKey,
-+ WritableRegistry<T, B> registry,
-+ Conversions conversions
-+) implements RegistryFreezeEvent<T, B>, PaperLifecycleEvent {
-+
-+ @Override
-+ public @NonNull <V extends Keyed> Tag<V> getOrCreateTag(final TagKey<V> tagKey) {
-+ final RegistryOps.RegistryInfo<Object> registryInfo = this.conversions.lookup().lookup(PaperRegistries.registryToNms(tagKey.registryKey())).orElseThrow();
-+ final HolderSet.Named<?> tagSet = registryInfo.getter().getOrThrow(PaperRegistries.toNms(tagKey));
-+ return new NamedRegistryKeySetImpl<>(tagKey, tagSet);
-+ }
-+}
-diff --git a/src/main/java/io/papermc/paper/registry/event/package-info.java b/src/main/java/io/papermc/paper/registry/event/package-info.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..14d2d9766b8dee763f220c397aba3ad432d02aaa
---- /dev/null
-+++ b/src/main/java/io/papermc/paper/registry/event/package-info.java
-@@ -0,0 +1,5 @@
-+@DefaultQualifier(NonNull.class)
-+package io.papermc.paper.registry.event;
-+
-+import org.checkerframework.checker.nullness.qual.NonNull;
-+import org.checkerframework.framework.qual.DefaultQualifier;
-diff --git a/src/main/java/io/papermc/paper/registry/event/type/RegistryEntryAddEventTypeImpl.java b/src/main/java/io/papermc/paper/registry/event/type/RegistryEntryAddEventTypeImpl.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..fbf853bf1cbb3c7bbef531afe185818b9454299b
---- /dev/null
-+++ b/src/main/java/io/papermc/paper/registry/event/type/RegistryEntryAddEventTypeImpl.java
-@@ -0,0 +1,37 @@
-+package io.papermc.paper.registry.event.type;
-+
-+import io.papermc.paper.plugin.bootstrap.BootstrapContext;
-+import io.papermc.paper.plugin.lifecycle.event.handler.LifecycleEventHandler;
-+import io.papermc.paper.plugin.lifecycle.event.types.PrioritizableLifecycleEventType;
-+import io.papermc.paper.registry.RegistryBuilder;
-+import io.papermc.paper.registry.RegistryKey;
-+import io.papermc.paper.registry.event.RegistryEntryAddEvent;
-+import java.util.function.Consumer;
-+import java.util.function.Predicate;
-+
-+public class RegistryEntryAddEventTypeImpl<T, B extends RegistryBuilder<T>> extends PrioritizableLifecycleEventType<BootstrapContext, RegistryEntryAddEvent<T, B>, RegistryEntryAddConfiguration<T>> implements RegistryEntryAddEventType<T, B> {
-+
-+ public RegistryEntryAddEventTypeImpl(final RegistryKey<T> registryKey, final String eventName) {
-+ super(registryKey + " / " + eventName, BootstrapContext.class);
-+ }
-+
-+ @Override
-+ public boolean blocksReloading(final BootstrapContext eventOwner) {
-+ return false; // only runs once
-+ }
-+
-+ @Override
-+ public RegistryEntryAddConfiguration<T> newHandler(final LifecycleEventHandler<? super RegistryEntryAddEvent<T, B>> handler) {
-+ return new RegistryEntryAddHandlerConfiguration<>(handler, this);
-+ }
-+
-+ @Override
-+ public void forEachHandler(final RegistryEntryAddEvent<T, B> event, final Consumer<RegisteredHandler<BootstrapContext, RegistryEntryAddEvent<T, B>>> consumer, final Predicate<RegisteredHandler<BootstrapContext, RegistryEntryAddEvent<T, B>>> predicate) {
-+ super.forEachHandler(event, consumer, predicate.and(handler -> this.matchesTarget(event, handler)));
-+ }
-+
-+ private boolean matchesTarget(final RegistryEntryAddEvent<T, B> event, final RegisteredHandler<BootstrapContext, RegistryEntryAddEvent<T, B>> handler) {
-+ final RegistryEntryAddHandlerConfiguration<T, B> config = (RegistryEntryAddHandlerConfiguration<T, B>) handler.config();
-+ return config.filter() == null || config.filter().test(event.key());
-+ }
-+}
-diff --git a/src/main/java/io/papermc/paper/registry/event/type/RegistryEntryAddHandlerConfiguration.java b/src/main/java/io/papermc/paper/registry/event/type/RegistryEntryAddHandlerConfiguration.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..548f5bf979e88708e98d04dfe22ccaa300c91ddd
---- /dev/null
-+++ b/src/main/java/io/papermc/paper/registry/event/type/RegistryEntryAddHandlerConfiguration.java
-@@ -0,0 +1,42 @@
-+package io.papermc.paper.registry.event.type;
-+
-+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.PrioritizedLifecycleEventHandlerConfigurationImpl;
-+import io.papermc.paper.plugin.lifecycle.event.types.AbstractLifecycleEventType;
-+import io.papermc.paper.registry.RegistryBuilder;
-+import io.papermc.paper.registry.TypedKey;
-+import io.papermc.paper.registry.event.RegistryEntryAddEvent;
-+import java.util.function.Predicate;
-+import org.checkerframework.checker.nullness.qual.Nullable;
-+import org.jetbrains.annotations.Contract;
-+
-+public class RegistryEntryAddHandlerConfiguration<T, B extends RegistryBuilder<T>> extends PrioritizedLifecycleEventHandlerConfigurationImpl<BootstrapContext, RegistryEntryAddEvent<T, B>> implements RegistryEntryAddConfiguration<T> {
-+
-+ private @Nullable Predicate<TypedKey<T>> filter;
-+
-+ public RegistryEntryAddHandlerConfiguration(final LifecycleEventHandler<? super RegistryEntryAddEvent<T, B>> handler, final AbstractLifecycleEventType<BootstrapContext, RegistryEntryAddEvent<T, B>, ?> eventType) {
-+ super(handler, eventType);
-+ }
-+
-+ @Contract(pure = true)
-+ public @Nullable Predicate<TypedKey<T>> filter() {
-+ return this.filter;
-+ }
-+
-+ @Override
-+ public RegistryEntryAddConfiguration<T> filter(final Predicate<TypedKey<T>> filter) {
-+ this.filter = filter;
-+ return this;
-+ }
-+
-+ @Override
-+ public RegistryEntryAddConfiguration<T> priority(final int priority) {
-+ return (RegistryEntryAddConfiguration<T>) super.priority(priority);
-+ }
-+
-+ @Override
-+ public RegistryEntryAddConfiguration<T> monitor() {
-+ return (RegistryEntryAddConfiguration<T>) super.monitor();
-+ }
-+}
-diff --git a/src/main/java/io/papermc/paper/registry/event/type/RegistryLifecycleEventType.java b/src/main/java/io/papermc/paper/registry/event/type/RegistryLifecycleEventType.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..7ee77022198bf5f9f88c6a1917a1da30b1863883
---- /dev/null
-+++ b/src/main/java/io/papermc/paper/registry/event/type/RegistryLifecycleEventType.java
-@@ -0,0 +1,18 @@
-+package io.papermc.paper.registry.event.type;
-+
-+import io.papermc.paper.plugin.bootstrap.BootstrapContext;
-+import io.papermc.paper.plugin.lifecycle.event.types.PrioritizableLifecycleEventType;
-+import io.papermc.paper.registry.RegistryKey;
-+import io.papermc.paper.registry.event.RegistryEvent;
-+
-+public final class RegistryLifecycleEventType<T, E extends RegistryEvent<T>> extends PrioritizableLifecycleEventType.Simple<BootstrapContext, E> {
-+
-+ public RegistryLifecycleEventType(final RegistryKey<T> registryKey, final String eventName) {
-+ super(registryKey + " / " + eventName, BootstrapContext.class);
-+ }
-+
-+ @Override
-+ public boolean blocksReloading(final BootstrapContext eventOwner) {
-+ return false; // only runs once
-+ }
-+}
-diff --git a/src/main/java/io/papermc/paper/registry/legacy/DelayedRegistry.java b/src/main/java/io/papermc/paper/registry/legacy/DelayedRegistry.java
-index 9400fed345344a0a8e4fb301cca6a1867adf625b..0cdc92acd3ebb6efb10e1b66419cc05618301581 100644
---- a/src/main/java/io/papermc/paper/registry/legacy/DelayedRegistry.java
-+++ b/src/main/java/io/papermc/paper/registry/legacy/DelayedRegistry.java
-@@ -1,5 +1,7 @@
- package io.papermc.paper.registry.legacy;
-
-+import io.papermc.paper.registry.tag.Tag;
-+import io.papermc.paper.registry.tag.TagKey;
- import java.util.Iterator;
- import java.util.function.Supplier;
- import java.util.stream.Stream;
-@@ -7,7 +9,6 @@ import net.kyori.adventure.key.Key;
- import org.bukkit.Keyed;
- import org.bukkit.NamespacedKey;
- import org.bukkit.Registry;
--import org.bukkit.craftbukkit.CraftRegistry;
- import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
- import org.checkerframework.checker.nullness.qual.Nullable;
- import org.jetbrains.annotations.NotNull;
-@@ -58,4 +59,14 @@ public final class DelayedRegistry<T extends Keyed, R extends Registry<T>> imple
- public NamespacedKey getKey(final T value) {
- return this.delegate().getKey(value);
- }
-+
-+ @Override
-+ public boolean hasTag(final TagKey<T> key) {
-+ return this.delegate().hasTag(key);
-+ }
-+
-+ @Override
-+ public @NotNull Tag<T> getTag(final TagKey<T> key) {
-+ return this.delegate().getTag(key);
-+ }
- }
-diff --git a/src/main/java/io/papermc/paper/registry/package-info.java b/src/main/java/io/papermc/paper/registry/package-info.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..0b80179ff90e085568d7ceafd9b17511789dc99b
---- /dev/null
-+++ b/src/main/java/io/papermc/paper/registry/package-info.java
-@@ -0,0 +1,5 @@
-+@DefaultQualifier(NonNull.class)
-+package io.papermc.paper.registry;
-+
-+import org.checkerframework.checker.nullness.qual.NonNull;
-+import org.checkerframework.framework.qual.DefaultQualifier;
-diff --git a/src/main/java/io/papermc/paper/registry/set/NamedRegistryKeySetImpl.java b/src/main/java/io/papermc/paper/registry/set/NamedRegistryKeySetImpl.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..e8c2c18a1ed5cd587266bd415170610781531a12
---- /dev/null
-+++ b/src/main/java/io/papermc/paper/registry/set/NamedRegistryKeySetImpl.java
-@@ -0,0 +1,76 @@
-+package io.papermc.paper.registry.set;
-+
-+import com.google.common.collect.ImmutableList;
-+import com.google.common.collect.Iterables;
-+import io.papermc.paper.registry.PaperRegistries;
-+import io.papermc.paper.registry.RegistryAccess;
-+import io.papermc.paper.registry.RegistryKey;
-+import io.papermc.paper.registry.TypedKey;
-+import io.papermc.paper.registry.tag.Tag;
-+import io.papermc.paper.registry.tag.TagKey;
-+import java.util.Collection;
-+import java.util.Set;
-+import net.kyori.adventure.key.Key;
-+import net.minecraft.core.Holder;
-+import net.minecraft.core.HolderSet;
-+import org.bukkit.Keyed;
-+import org.bukkit.NamespacedKey;
-+import org.bukkit.Registry;
-+import org.bukkit.craftbukkit.util.CraftNamespacedKey;
-+import org.checkerframework.checker.nullness.qual.NonNull;
-+import org.checkerframework.framework.qual.DefaultQualifier;
-+import org.jetbrains.annotations.NotNull;
-+import org.jetbrains.annotations.Unmodifiable;
-+
-+@DefaultQualifier(NonNull.class)
-+public record NamedRegistryKeySetImpl<T extends Keyed, M>( // TODO remove Keyed
-+ TagKey<T> tagKey,
-+ HolderSet.Named<M> namedSet
-+) implements Tag<T>, org.bukkit.Tag<T> {
-+
-+ @Override
-+ public @Unmodifiable Collection<TypedKey<T>> values() {
-+ final ImmutableList.Builder<TypedKey<T>> builder = ImmutableList.builder();
-+ for (final Holder<M> holder : this.namedSet) {
-+ builder.add(TypedKey.create(this.tagKey.registryKey(), CraftNamespacedKey.fromMinecraft(((Holder.Reference<?>) holder).key().location())));
-+ }
-+ return builder.build();
-+ }
-+
-+ @Override
-+ public RegistryKey<T> registryKey() {
-+ return this.tagKey.registryKey();
-+ }
-+
-+ @Override
-+ public boolean contains(final TypedKey<T> valueKey) {
-+ return Iterables.any(this.namedSet, h -> {
-+ return PaperRegistries.fromNms(((Holder.Reference<?>) h).key()).equals(valueKey);
-+ });
-+ }
-+
-+ @Override
-+ public @Unmodifiable Collection<T> resolve(final Registry<T> registry) {
-+ final ImmutableList.Builder<T> builder = ImmutableList.builder();
-+ for (final Holder<M> holder : this.namedSet) {
-+ builder.add(registry.getOrThrow(CraftNamespacedKey.fromMinecraft(((Holder.Reference<?>) holder).key().location())));
-+ }
-+ return builder.build();
-+ }
-+
-+ @Override
-+ public boolean isTagged(final T item) {
-+ return this.getValues().contains(item);
-+ }
-+
-+ @Override
-+ public Set<T> getValues() {
-+ return Set.copyOf(this.resolve(RegistryAccess.registryAccess().getRegistry(this.registryKey())));
-+ }
-+
-+ @Override
-+ public @NotNull NamespacedKey getKey() {
-+ final Key key = this.tagKey().key();
-+ return new NamespacedKey(key.namespace(), key.value());
-+ }
-+}
-diff --git a/src/main/java/io/papermc/paper/registry/set/PaperRegistrySets.java b/src/main/java/io/papermc/paper/registry/set/PaperRegistrySets.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..f09ce9c8547ef05153847245746473dd9a8acbe6
---- /dev/null
-+++ b/src/main/java/io/papermc/paper/registry/set/PaperRegistrySets.java
-@@ -0,0 +1,48 @@
-+package io.papermc.paper.registry.set;
-+
-+import io.papermc.paper.registry.PaperRegistries;
-+import io.papermc.paper.registry.RegistryKey;
-+import io.papermc.paper.registry.TypedKey;
-+import java.util.ArrayList;
-+import java.util.List;
-+import net.minecraft.core.Holder;
-+import net.minecraft.core.HolderSet;
-+import net.minecraft.core.Registry;
-+import net.minecraft.resources.RegistryOps;
-+import net.minecraft.resources.ResourceKey;
-+import org.bukkit.Keyed;
-+import org.checkerframework.checker.nullness.qual.NonNull;
-+import org.checkerframework.framework.qual.DefaultQualifier;
-+
-+@DefaultQualifier(NonNull.class)
-+public final class PaperRegistrySets {
-+
-+ public static <A extends Keyed, M> HolderSet<M> convertToNms(final ResourceKey<? extends Registry<M>> resourceKey, final RegistryOps.RegistryInfoLookup lookup, final RegistryKeySet<A> registryKeySet) { // TODO remove Keyed
-+ if (registryKeySet instanceof NamedRegistryKeySetImpl<A, ?>) {
-+ return ((NamedRegistryKeySetImpl<A, M>) registryKeySet).namedSet();
-+ } else {
-+ final RegistryOps.RegistryInfo<M> registryInfo = lookup.lookup(resourceKey).orElseThrow();
-+ return HolderSet.direct(key -> {
-+ return registryInfo.getter().getOrThrow(PaperRegistries.toNms(key));
-+ }, registryKeySet.values());
-+ }
-+ }
-+
-+ public static <A extends Keyed, M> RegistryKeySet<A> convertToApi(final RegistryKey<A> registryKey, final HolderSet<M> holders) { // TODO remove Keyed
-+ if (holders instanceof final HolderSet.Named<M> named) {
-+ return new NamedRegistryKeySetImpl<>(PaperRegistries.fromNms(named.key()), named);
-+ } else {
-+ final List<TypedKey<A>> keys = new ArrayList<>();
-+ for (final Holder<M> holder : holders) {
-+ if (!(holder instanceof final Holder.Reference<M> reference)) {
-+ throw new UnsupportedOperationException("Cannot convert a holder set containing direct holders");
-+ }
-+ keys.add(PaperRegistries.fromNms(reference.key()));
-+ }
-+ return RegistrySet.keySet(registryKey, keys);
-+ }
-+ }
-+
-+ private PaperRegistrySets() {
-+ }
-+}
-diff --git a/src/main/java/net/minecraft/core/MappedRegistry.java b/src/main/java/net/minecraft/core/MappedRegistry.java
-index edbbafd1705345282e5e6251eb71bfde5793b7d4..f22d22ebcedcc9c20225677844c86a1ad27c4211 100644
---- a/src/main/java/net/minecraft/core/MappedRegistry.java
-+++ b/src/main/java/net/minecraft/core/MappedRegistry.java
-@@ -441,4 +441,12 @@ public class MappedRegistry<T> implements WritableRegistry<T> {
- public HolderLookup.RegistryLookup<T> asLookup() {
- return this.lookup;
- }
-+ // Paper start
-+ // used to clear intrusive holders from GameEvent, Item, Block, EntityType, and Fluid from unused instances of those types
-+ public void clearIntrusiveHolder(final T instance) {
-+ if (this.unregisteredIntrusiveHolders != null) {
-+ this.unregisteredIntrusiveHolders.remove(instance);
-+ }
-+ }
-+ // Paper end
- }
-diff --git a/src/main/java/net/minecraft/core/registries/BuiltInRegistries.java b/src/main/java/net/minecraft/core/registries/BuiltInRegistries.java
-index 44b7927081b476813505cab6b3a2da2ec2942c54..0497318e8f647453f38f3a16a8be6bd9aa19253f 100644
---- a/src/main/java/net/minecraft/core/registries/BuiltInRegistries.java
-+++ b/src/main/java/net/minecraft/core/registries/BuiltInRegistries.java
-@@ -288,6 +288,17 @@ public class BuiltInRegistries {
- Registries.ENCHANTMENT_PROVIDER_TYPE, EnchantmentProviderTypes::bootstrap
- );
- public static final Registry<? extends Registry<?>> REGISTRY = WRITABLE_REGISTRY;
-+ // Paper start - add built-in registry conversions
-+ public static final io.papermc.paper.registry.data.util.Conversions BUILT_IN_CONVERSIONS = new io.papermc.paper.registry.data.util.Conversions(new net.minecraft.resources.RegistryOps.RegistryInfoLookup() {
-+ @Override
-+ public <T> java.util.Optional<net.minecraft.resources.RegistryOps.RegistryInfo<T>> lookup(final ResourceKey<? extends Registry<? extends T>> registryRef) {
-+ final Registry<T> registry = net.minecraft.server.RegistryLayer.STATIC_ACCESS.registryOrThrow(registryRef);
-+ return java.util.Optional.of(
-+ new net.minecraft.resources.RegistryOps.RegistryInfo<>(registry.asLookup(), registry.asTagAddingLookup(), Lifecycle.experimental())
-+ );
-+ }
-+ });
-+ // Paper end - add built-in registry conversions
-
- private static <T> Registry<T> registerSimple(ResourceKey<? extends Registry<T>> key, BuiltInRegistries.RegistryBootstrap<T> initializer) {
- return internalRegister(key, new MappedRegistry<>(key, Lifecycle.stable(), false), initializer);
-@@ -328,6 +339,7 @@ public class BuiltInRegistries {
- }
- public static void bootStrap(Runnable runnable) {
- // Paper end
-+ REGISTRY.freeze(); // Paper - freeze main registry early
- createContents();
- runnable.run(); // Paper
- freeze();
-@@ -346,6 +358,7 @@ public class BuiltInRegistries {
- REGISTRY.freeze();
-
- for (Registry<?> registry : REGISTRY) {
-+ io.papermc.paper.registry.PaperRegistryListenerManager.INSTANCE.runFreezeListeners(registry.key(), BUILT_IN_CONVERSIONS); // Paper
- registry.freeze();
- }
- }
-diff --git a/src/main/java/net/minecraft/resources/RegistryDataLoader.java b/src/main/java/net/minecraft/resources/RegistryDataLoader.java
-index abadf4abe08dc3bb6612b42cbb3f7df3ffa28ce9..3053243866c655829fe2e980446b4abf1da6d37c 100644
---- a/src/main/java/net/minecraft/resources/RegistryDataLoader.java
-+++ b/src/main/java/net/minecraft/resources/RegistryDataLoader.java
-@@ -115,7 +115,7 @@ public class RegistryDataLoader {
- );
-
- public static RegistryAccess.Frozen load(ResourceManager resourceManager, RegistryAccess registryManager, List<RegistryDataLoader.RegistryData<?>> entries) {
-- return load((loader, infoGetter) -> loader.loadFromResources(resourceManager, infoGetter), registryManager, entries);
-+ return load((loader, infoGetter, conversions) -> loader.loadFromResources(resourceManager, infoGetter, conversions), registryManager, entries); // Paper - pass conversions
- }
-
- public static RegistryAccess.Frozen load(
-@@ -124,7 +124,7 @@ public class RegistryDataLoader {
- RegistryAccess registryManager,
- List<RegistryDataLoader.RegistryData<?>> entries
- ) {
-- return load((loader, infoGetter) -> loader.loadFromNetwork(data, factory, infoGetter), registryManager, entries);
-+ return load((loader, infoGetter, conversions) -> loader.loadFromNetwork(data, factory, infoGetter, conversions), registryManager, entries); // Paper - pass conversions
- }
-
- private static RegistryAccess.Frozen load(
-@@ -133,9 +133,11 @@ public class RegistryDataLoader {
- Map<ResourceKey<?>, Exception> map = new HashMap<>();
- List<RegistryDataLoader.Loader<?>> list = entries.stream().map(entry -> entry.create(Lifecycle.stable(), map)).collect(Collectors.toUnmodifiableList());
- RegistryOps.RegistryInfoLookup registryInfoLookup = createContext(baseRegistryManager, list);
-- list.forEach(loader -> loadable.apply((RegistryDataLoader.Loader<?>)loader, registryInfoLookup));
-+ final io.papermc.paper.registry.data.util.Conversions conversions = new io.papermc.paper.registry.data.util.Conversions(registryInfoLookup); // Paper - create conversions
-+ list.forEach(loader -> loadable.apply((RegistryDataLoader.Loader<?>)loader, registryInfoLookup, conversions));
- list.forEach(loader -> {
- Registry<?> registry = loader.registry();
-+ io.papermc.paper.registry.PaperRegistryListenerManager.INSTANCE.runFreezeListeners(loader.registry.key(), conversions); // Paper - run pre-freeze listeners
-
- try {
- registry.freeze();
-@@ -193,13 +195,13 @@ public class RegistryDataLoader {
- }
-
- private static <E> void loadElementFromResource(
-- WritableRegistry<E> registry, Decoder<E> decoder, RegistryOps<JsonElement> ops, ResourceKey<E> key, Resource resource, RegistrationInfo entryInfo
-+ WritableRegistry<E> registry, Decoder<E> decoder, RegistryOps<JsonElement> ops, ResourceKey<E> key, Resource resource, RegistrationInfo entryInfo, io.papermc.paper.registry.data.util.Conversions conversions
- ) throws IOException {
- try (Reader reader = resource.openAsReader()) {
- JsonElement jsonElement = JsonParser.parseReader(reader);
- DataResult<E> dataResult = decoder.parse(ops, jsonElement);
- E object = dataResult.getOrThrow();
-- registry.register(key, object, entryInfo);
-+ io.papermc.paper.registry.PaperRegistryListenerManager.INSTANCE.registerWithListeners(registry, key, object, entryInfo, conversions); // Paper - register with listeners
- }
- }
-
-@@ -208,7 +210,8 @@ public class RegistryDataLoader {
- RegistryOps.RegistryInfoLookup infoGetter,
- WritableRegistry<E> registry,
- Decoder<E> elementDecoder,
-- Map<ResourceKey<?>, Exception> errors
-+ Map<ResourceKey<?>, Exception> errors,
-+ io.papermc.paper.registry.data.util.Conversions conversions // Paper - pass conversions
- ) {
- String string = Registries.elementsDirPath(registry.key());
- FileToIdConverter fileToIdConverter = FileToIdConverter.json(string);
-@@ -221,7 +224,7 @@ public class RegistryDataLoader {
- RegistrationInfo registrationInfo = REGISTRATION_INFO_CACHE.apply(resource.knownPackInfo());
-
- try {
-- loadElementFromResource(registry, elementDecoder, registryOps, resourceKey, resource, registrationInfo);
-+ loadElementFromResource(registry, elementDecoder, registryOps, resourceKey, resource, registrationInfo, conversions); // Paper - pass conversions
- } catch (Exception var15) {
- errors.put(
- resourceKey,
-@@ -237,7 +240,8 @@ public class RegistryDataLoader {
- RegistryOps.RegistryInfoLookup infoGetter,
- WritableRegistry<E> registry,
- Decoder<E> decoder,
-- Map<ResourceKey<?>, Exception> loadingErrors
-+ Map<ResourceKey<?>, Exception> loadingErrors,
-+ io.papermc.paper.registry.data.util.Conversions conversions // Paper - pass conversions
- ) {
- List<RegistrySynchronization.PackedRegistryEntry> list = data.get(registry.key());
- if (list != null) {
-@@ -264,7 +268,7 @@ public class RegistryDataLoader {
-
- try {
- Resource resource = factory.getResourceOrThrow(resourceLocation);
-- loadElementFromResource(registry, decoder, registryOps2, resourceKey, resource, NETWORK_REGISTRATION_INFO);
-+ loadElementFromResource(registry, decoder, registryOps2, resourceKey, resource, NETWORK_REGISTRATION_INFO, conversions); // Paper - pass conversions
- } catch (Exception var18) {
- loadingErrors.put(resourceKey, new IllegalStateException("Failed to parse local data", var18));
- }
-@@ -274,22 +278,23 @@ public class RegistryDataLoader {
- }
-
- static record Loader<T>(RegistryDataLoader.RegistryData<T> data, WritableRegistry<T> registry, Map<ResourceKey<?>, Exception> loadingErrors) {
-- public void loadFromResources(ResourceManager resourceManager, RegistryOps.RegistryInfoLookup infoGetter) {
-- RegistryDataLoader.loadContentsFromManager(resourceManager, infoGetter, this.registry, this.data.elementCodec, this.loadingErrors);
-+ public void loadFromResources(ResourceManager resourceManager, RegistryOps.RegistryInfoLookup infoGetter, io.papermc.paper.registry.data.util.Conversions conversions) { // Paper - pass conversions
-+ RegistryDataLoader.loadContentsFromManager(resourceManager, infoGetter, this.registry, this.data.elementCodec, this.loadingErrors, conversions); // Paper - pass conversions
- }
-
- public void loadFromNetwork(
- Map<ResourceKey<? extends Registry<?>>, List<RegistrySynchronization.PackedRegistryEntry>> data,
- ResourceProvider factory,
-- RegistryOps.RegistryInfoLookup infoGetter
-+ RegistryOps.RegistryInfoLookup infoGetter,
-+ io.papermc.paper.registry.data.util.Conversions conversions // Paper
- ) {
-- RegistryDataLoader.loadContentsFromNetwork(data, factory, infoGetter, this.registry, this.data.elementCodec, this.loadingErrors);
-+ RegistryDataLoader.loadContentsFromNetwork(data, factory, infoGetter, this.registry, this.data.elementCodec, this.loadingErrors, conversions); // Paper - pass conversions
- }
- }
-
- @FunctionalInterface
- interface LoadingFunction {
-- void apply(RegistryDataLoader.Loader<?> loader, RegistryOps.RegistryInfoLookup infoGetter);
-+ void apply(RegistryDataLoader.Loader<?> loader, RegistryOps.RegistryInfoLookup infoGetter, io.papermc.paper.registry.data.util.Conversions conversions); // Paper - pass conversions
- }
-
- public static record RegistryData<T>(ResourceKey<? extends Registry<T>> key, Codec<T> elementCodec, boolean requiredNonEmpty) {
-diff --git a/src/main/java/net/minecraft/server/ReloadableServerRegistries.java b/src/main/java/net/minecraft/server/ReloadableServerRegistries.java
-index 397bdacab9517354875ebc0bc68d35059b3c318b..908431652a0fea79b5a0cee1efd0c7a7d524b614 100644
---- a/src/main/java/net/minecraft/server/ReloadableServerRegistries.java
-+++ b/src/main/java/net/minecraft/server/ReloadableServerRegistries.java
-@@ -47,15 +47,16 @@ public class ReloadableServerRegistries {
- ) {
- RegistryAccess.Frozen frozen = dynamicRegistries.getAccessForLoading(RegistryLayer.RELOADABLE);
- RegistryOps<JsonElement> registryOps = new ReloadableServerRegistries.EmptyTagLookupWrapper(frozen).createSerializationContext(JsonOps.INSTANCE);
-+ final io.papermc.paper.registry.data.util.Conversions conversions = new io.papermc.paper.registry.data.util.Conversions(registryOps.lookupProvider); // Paper
- List<CompletableFuture<WritableRegistry<?>>> list = LootDataType.values()
-- .map(type -> scheduleElementParse((LootDataType<?>)type, registryOps, resourceManager, prepareExecutor))
-+ .map(type -> scheduleElementParse((LootDataType<?>)type, registryOps, resourceManager, prepareExecutor, conversions)) // Paper
- .toList();
- CompletableFuture<List<WritableRegistry<?>>> completableFuture = Util.sequence(list);
- return completableFuture.thenApplyAsync(registries -> apply(dynamicRegistries, (List<WritableRegistry<?>>)registries), prepareExecutor);
- }
-
- private static <T> CompletableFuture<WritableRegistry<?>> scheduleElementParse(
-- LootDataType<T> type, RegistryOps<JsonElement> ops, ResourceManager resourceManager, Executor prepareExecutor
-+ LootDataType<T> type, RegistryOps<JsonElement> ops, ResourceManager resourceManager, Executor prepareExecutor, io.papermc.paper.registry.data.util.Conversions conversions // Paper
- ) {
- return CompletableFuture.supplyAsync(
- () -> {
-@@ -66,7 +67,7 @@ public class ReloadableServerRegistries {
- SimpleJsonResourceReloadListener.scanDirectory(resourceManager, string, GSON, map);
- map.forEach(
- (id, json) -> type.deserialize(id, ops, json)
-- .ifPresent(value -> writableRegistry.register(ResourceKey.create(type.registryKey(), id), (T)value, DEFAULT_REGISTRATION_INFO))
-+ .ifPresent(value -> io.papermc.paper.registry.PaperRegistryListenerManager.INSTANCE.registerWithListeners(writableRegistry, ResourceKey.create(type.registryKey(), id), value, DEFAULT_REGISTRATION_INFO, conversions)) // Paper - register with listeners
- );
- return writableRegistry;
- },
-diff --git a/src/main/java/org/bukkit/craftbukkit/CraftRegistry.java b/src/main/java/org/bukkit/craftbukkit/CraftRegistry.java
-index 4dff173bbed34a49c22532bbee2b35ebf5920d22..53c70846666b746af6706ed2e363fe388e463e56 100644
---- a/src/main/java/org/bukkit/craftbukkit/CraftRegistry.java
-+++ b/src/main/java/org/bukkit/craftbukkit/CraftRegistry.java
-@@ -167,11 +167,11 @@ public class CraftRegistry<B extends Keyed, M> implements Registry<B> {
- private final Map<NamespacedKey, B> cache = new HashMap<>();
- private final Map<B, NamespacedKey> byValue = new java.util.IdentityHashMap<>(); // Paper - improve Registry
- private final net.minecraft.core.Registry<M> minecraftRegistry;
-- private final BiFunction<NamespacedKey, M, B> minecraftToBukkit;
-+ private final BiFunction<? super NamespacedKey, M, B> minecraftToBukkit; // Paper
- private final BiFunction<NamespacedKey, ApiVersion, NamespacedKey> serializationUpdater; // Paper - rename to make it *clear* what it is *only* for
- private boolean init;
-
-- public CraftRegistry(Class<?> bukkitClass, net.minecraft.core.Registry<M> minecraftRegistry, BiFunction<NamespacedKey, M, B> minecraftToBukkit, BiFunction<NamespacedKey, ApiVersion, NamespacedKey> serializationUpdater) { // Paper - relax preload class
-+ public CraftRegistry(Class<?> bukkitClass, net.minecraft.core.Registry<M> minecraftRegistry, BiFunction<? super NamespacedKey, M, B> minecraftToBukkit, BiFunction<NamespacedKey, ApiVersion, NamespacedKey> serializationUpdater) { // Paper - relax preload class
- this.bukkitClass = bukkitClass;
- this.minecraftRegistry = minecraftRegistry;
- this.minecraftToBukkit = minecraftToBukkit;
-@@ -254,4 +254,17 @@ public class CraftRegistry<B extends Keyed, M> implements Registry<B> {
- return this.byValue.get(value);
- }
- // Paper end - improve Registry
-+
-+ // Paper start - RegistrySet API
-+ @Override
-+ public boolean hasTag(final io.papermc.paper.registry.tag.TagKey<B> key) {
-+ return this.minecraftRegistry.getTag(net.minecraft.tags.TagKey.create(this.minecraftRegistry.key(), io.papermc.paper.adventure.PaperAdventure.asVanilla(key.key()))).isPresent();
-+ }
-+
-+ @Override
-+ public io.papermc.paper.registry.tag.Tag<B> getTag(final io.papermc.paper.registry.tag.TagKey<B> key) {
-+ final net.minecraft.core.HolderSet.Named<M> namedHolderSet = this.minecraftRegistry.getTag(io.papermc.paper.registry.PaperRegistries.toNms(key)).orElseThrow();
-+ return new io.papermc.paper.registry.set.NamedRegistryKeySetImpl<>(key, namedHolderSet);
-+ }
-+ // 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 9f2ddd47dc0658db2f95ef80543fb9a4d2f94f9f..68a6cd43042e87501f5bd48565222638dd58a1cf 100644
---- a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
-+++ b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
-@@ -674,6 +674,21 @@ public final class CraftMagicNumbers implements UnsafeValues {
- }
- // Paper end - lifecycle event API
-
-+ // 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) {
-+ 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());
-+ final net.minecraft.core.Registry<M> nmsRegistry = org.bukkit.craftbukkit.CraftRegistry.getMinecraftRegistry().registryOrThrow(nmsKey);
-+ return nmsRegistry
-+ .getTag(io.papermc.paper.registry.PaperRegistries.toNms(tagKey))
-+ .map(named -> new io.papermc.paper.registry.set.NamedRegistryKeySetImpl<>(tagKey, named))
-+ .orElse(null);
-+ }
-+ // Paper end - hack to get tags for non server-backed registries
-+
- /**
- * This helper class represents the different NBT Tags.
- * <p>
-diff --git a/src/main/resources/META-INF/services/io.papermc.paper.registry.event.RegistryEventTypeProvider b/src/main/resources/META-INF/services/io.papermc.paper.registry.event.RegistryEventTypeProvider
-new file mode 100644
-index 0000000000000000000000000000000000000000..8bee1a5ed877a04e4d027593df1f42cefdd824e7
---- /dev/null
-+++ b/src/main/resources/META-INF/services/io.papermc.paper.registry.event.RegistryEventTypeProvider
-@@ -0,0 +1 @@
-+io.papermc.paper.registry.event.RegistryEventTypeProviderImpl
-diff --git a/src/test/java/io/papermc/paper/registry/RegistryBuilderTest.java b/src/test/java/io/papermc/paper/registry/RegistryBuilderTest.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..47b8ebac8496179008b8932c5ca2aadc274e24e0
---- /dev/null
-+++ b/src/test/java/io/papermc/paper/registry/RegistryBuilderTest.java
-@@ -0,0 +1,36 @@
-+package io.papermc.paper.registry;
-+
-+import io.papermc.paper.registry.data.util.Conversions;
-+import java.util.List;
-+import java.util.Map;
-+import net.minecraft.core.Registry;
-+import net.minecraft.resources.RegistryOps;
-+import net.minecraft.resources.ResourceKey;
-+import org.bukkit.support.RegistryHelper;
-+import org.bukkit.support.environment.AllFeatures;
-+import org.junit.jupiter.api.Disabled;
-+import org.junit.jupiter.params.ParameterizedTest;
-+import org.junit.jupiter.params.provider.Arguments;
-+import org.junit.jupiter.params.provider.MethodSource;
-+
-+import static org.junit.jupiter.api.Assertions.assertEquals;
-+
-+@AllFeatures
-+class RegistryBuilderTest {
-+
-+ static List<Arguments> registries() {
-+ return List.of(
-+ );
-+ }
-+
-+ @Disabled
-+ @ParameterizedTest
-+ @MethodSource("registries")
-+ <M, T> void testEquality(final ResourceKey<? extends Registry<M>> resourceKey, final PaperRegistryBuilder.Filler<M, T, ?> filler) {
-+ final Registry<M> registry = RegistryHelper.getRegistry().registryOrThrow(resourceKey);
-+ for (final Map.Entry<ResourceKey<M>, M> entry : registry.entrySet()) {
-+ final M built = filler.fill(new Conversions(new RegistryOps.HolderLookupAdapter(RegistryHelper.getRegistry())), PaperRegistries.fromNms(entry.getKey()), entry.getValue()).build();
-+ assertEquals(entry.getValue(), built);
-+ }
-+ }
-+}
-diff --git a/src/test/java/org/bukkit/registry/RegistryClassTest.java b/src/test/java/org/bukkit/registry/RegistryClassTest.java
-index 575a06125e0b60b5bb8b6f85131f7d6cf86f5083..85f93d8c9b3a48b267e0575ba7fbb3b9f273e70c 100644
---- a/src/test/java/org/bukkit/registry/RegistryClassTest.java
-+++ b/src/test/java/org/bukkit/registry/RegistryClassTest.java
-@@ -111,7 +111,7 @@ public class RegistryClassTest {
- outsideRequest.clear();
- MockUtil.resetMock(spyRegistry);
- doAnswer(invocation -> {
-- Keyed item = realRegistry.get(invocation.getArgument(0));
-+ Keyed item = realRegistry.get((NamespacedKey) invocation.getArgument(0)); // Paper - registry modification api - specifically call namespaced key overload
-
- if (item == null) {
- nullValue.add(invocation.getArgument(0));
-@@ -120,10 +120,10 @@ public class RegistryClassTest {
- nullAble.add(invocation.getArgument(0));
-
- return item;
-- }).when(spyRegistry).get(any());
-+ }).when(spyRegistry).get((NamespacedKey) any()); // Paper - registry modification api - specifically call namespaced key overload
-
- doAnswer(invocation -> {
-- Keyed item = realRegistry.get(invocation.getArgument(0));
-+ Keyed item = realRegistry.get((NamespacedKey) invocation.getArgument(0)); // Paper - registry modification api - specifically call namespaced key overload
-
- if (item == null) {
- nullValue.add(invocation.getArgument(0));
-@@ -138,7 +138,7 @@ public class RegistryClassTest {
- notNullAble.add(invocation.getArgument(0));
-
- return item;
-- }).when(spyRegistry).getOrThrow(any());
-+ }).when(spyRegistry).getOrThrow((NamespacedKey) any()); // Paper - registry modification api - specifically call namespaced key overload
-
- // Load class
- try {
-@@ -171,13 +171,13 @@ public class RegistryClassTest {
- outsideRequest
- .computeIfAbsent(type, ty -> new ArrayList<>()).add(invocation.getArgument(0));
- return mock(type);
-- }).when(spyRegistry).get(any());
-+ }).when(spyRegistry).get((NamespacedKey) any()); // Paper - registry modification api - specifically call namespaced key overload
-
- doAnswer(invocation -> {
- outsideRequest
- .computeIfAbsent(type, ty -> new ArrayList<>()).add(invocation.getArgument(0));
- return mock(type);
-- }).when(spyRegistry).getOrThrow(any());
-+ }).when(spyRegistry).getOrThrow((NamespacedKey) any()); // Paper - registry modification api - specifically call namespaced key overload
- }
-
- private static void initFieldDataCache() {
-diff --git a/src/test/java/org/bukkit/support/extension/NormalExtension.java b/src/test/java/org/bukkit/support/extension/NormalExtension.java
-index 8b5dcc4d0ecf7f9c51f73080c123ca08e31b1898..a809ea2f0d2b477c61857aa02a7e55024b2a7e0d 100644
---- a/src/test/java/org/bukkit/support/extension/NormalExtension.java
-+++ b/src/test/java/org/bukkit/support/extension/NormalExtension.java
-@@ -62,7 +62,7 @@ public class NormalExtension extends BaseExtension {
-
- doAnswer(invocation ->
- mocks.computeIfAbsent(invocation.getArgument(0), k -> mock(RegistryHelper.updateClass(keyed, invocation.getArgument(0)), withSettings().stubOnly().defaultAnswer(DEFAULT_ANSWER)))
-- ).when(registry).get(any()); // Allow static classes to fill there fields, so that it does not error out, just by loading them
-+ ).when(registry).get((NamespacedKey) any()); // Allow static classes to fill there fields, so that it does not error out, just by loading them // Paper - registry modification api - specifically call namespaced key overload
-
- return registry;
- }
diff --git a/patches/unapplied/server/1015-Add-registry-entry-and-builders.patch b/patches/unapplied/server/1015-Add-registry-entry-and-builders.patch
deleted file mode 100644
index 8f2663ac94..0000000000
--- a/patches/unapplied/server/1015-Add-registry-entry-and-builders.patch
+++ /dev/null
@@ -1,484 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Bjarne Koll <[email protected]>
-Date: Thu, 13 Jun 2024 23:45:32 +0200
-Subject: [PATCH] Add registry entry and builders
-
-
-diff --git a/src/main/java/io/papermc/paper/registry/PaperRegistries.java b/src/main/java/io/papermc/paper/registry/PaperRegistries.java
-index 5cf598905ed6a7ac2b0d9ced3420adaf20ceb6af..12220f78ffaf06433ada72fd0c7f22b97d55287d 100644
---- a/src/main/java/io/papermc/paper/registry/PaperRegistries.java
-+++ b/src/main/java/io/papermc/paper/registry/PaperRegistries.java
-@@ -1,6 +1,8 @@
- package io.papermc.paper.registry;
-
- import io.papermc.paper.adventure.PaperAdventure;
-+import io.papermc.paper.registry.data.PaperEnchantmentRegistryEntry;
-+import io.papermc.paper.registry.data.PaperGameEventRegistryEntry;
- import io.papermc.paper.registry.entry.RegistryEntry;
- import io.papermc.paper.registry.tag.TagKey;
- import java.util.Collections;
-@@ -70,7 +72,7 @@ public final class PaperRegistries {
- static {
- REGISTRY_ENTRIES = List.of(
- // built-ins
-- entry(Registries.GAME_EVENT, RegistryKey.GAME_EVENT, GameEvent.class, CraftGameEvent::new),
-+ writable(Registries.GAME_EVENT, RegistryKey.GAME_EVENT, GameEvent.class, CraftGameEvent::new, PaperGameEventRegistryEntry.PaperBuilder::new),
- entry(Registries.STRUCTURE_TYPE, RegistryKey.STRUCTURE_TYPE, StructureType.class, CraftStructureType::new),
- entry(Registries.INSTRUMENT, RegistryKey.INSTRUMENT, MusicInstrument.class, CraftMusicInstrument::new),
- entry(Registries.MOB_EFFECT, RegistryKey.MOB_EFFECT, PotionEffectType.class, CraftPotionEffectType::new),
-@@ -89,7 +91,7 @@ public final class PaperRegistries {
- entry(Registries.TRIM_PATTERN, RegistryKey.TRIM_PATTERN, TrimPattern.class, CraftTrimPattern::new).delayed(),
- entry(Registries.DAMAGE_TYPE, RegistryKey.DAMAGE_TYPE, DamageType.class, CraftDamageType::new).delayed(),
- entry(Registries.WOLF_VARIANT, RegistryKey.WOLF_VARIANT, Wolf.Variant.class, CraftWolf.CraftVariant::new).delayed(),
-- entry(Registries.ENCHANTMENT, RegistryKey.ENCHANTMENT, Enchantment.class, CraftEnchantment::new).withSerializationUpdater(FieldRename.ENCHANTMENT_RENAME).delayed(),
-+ writable(Registries.ENCHANTMENT, RegistryKey.ENCHANTMENT, Enchantment.class, CraftEnchantment::new, PaperEnchantmentRegistryEntry.PaperBuilder::new).withSerializationUpdater(FieldRename.ENCHANTMENT_RENAME).delayed(),
- entry(Registries.JUKEBOX_SONG, RegistryKey.JUKEBOX_SONG, JukeboxSong.class, CraftJukeboxSong::new).delayed(),
- entry(Registries.BANNER_PATTERN, RegistryKey.BANNER_PATTERN, PatternType.class, CraftPatternType::new).delayed(),
-
-diff --git a/src/main/java/io/papermc/paper/registry/data/PaperEnchantmentRegistryEntry.java b/src/main/java/io/papermc/paper/registry/data/PaperEnchantmentRegistryEntry.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..481f5f0cfae1fada3bc3f873fb7e04c3086ea9bf
---- /dev/null
-+++ b/src/main/java/io/papermc/paper/registry/data/PaperEnchantmentRegistryEntry.java
-@@ -0,0 +1,234 @@
-+package io.papermc.paper.registry.data;
-+
-+import com.google.common.base.Preconditions;
-+import com.google.common.collect.Iterables;
-+import com.google.common.collect.Lists;
-+import io.papermc.paper.registry.PaperRegistryBuilder;
-+import io.papermc.paper.registry.RegistryKey;
-+import io.papermc.paper.registry.TypedKey;
-+import io.papermc.paper.registry.data.util.Checks;
-+import io.papermc.paper.registry.data.util.Conversions;
-+import io.papermc.paper.registry.set.PaperRegistrySets;
-+import io.papermc.paper.registry.set.RegistryKeySet;
-+import java.util.Collections;
-+import java.util.List;
-+import java.util.Optional;
-+import java.util.OptionalInt;
-+import net.minecraft.core.HolderSet;
-+import net.minecraft.core.component.DataComponentMap;
-+import net.minecraft.core.registries.Registries;
-+import net.minecraft.network.chat.Component;
-+import net.minecraft.world.entity.EquipmentSlotGroup;
-+import net.minecraft.world.item.Item;
-+import net.minecraft.world.item.enchantment.Enchantment;
-+import org.bukkit.craftbukkit.CraftEquipmentSlot;
-+import org.bukkit.inventory.ItemType;
-+import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
-+import org.checkerframework.checker.nullness.qual.NonNull;
-+import org.checkerframework.checker.nullness.qual.Nullable;
-+import org.checkerframework.framework.qual.DefaultQualifier;
-+import org.jetbrains.annotations.Range;
-+
-+import static io.papermc.paper.registry.data.util.Checks.asArgument;
-+import static io.papermc.paper.registry.data.util.Checks.asArgumentMin;
-+import static io.papermc.paper.registry.data.util.Checks.asConfigured;
-+
-+@DefaultQualifier(NonNull.class)
-+public class PaperEnchantmentRegistryEntry implements EnchantmentRegistryEntry {
-+
-+ // Top level
-+ protected @MonotonicNonNull Component description;
-+
-+ // Definition
-+ protected @MonotonicNonNull HolderSet<Item> supportedItems;
-+ protected @Nullable HolderSet<Item> primaryItems;
-+ protected OptionalInt weight = OptionalInt.empty();
-+ protected OptionalInt maxLevel = OptionalInt.empty();
-+ protected Enchantment.@MonotonicNonNull Cost minimumCost;
-+ protected Enchantment.@MonotonicNonNull Cost maximumCost;
-+ protected OptionalInt anvilCost = OptionalInt.empty();
-+ protected @MonotonicNonNull List<EquipmentSlotGroup> activeSlots;
-+
-+ // Exclusive
-+ protected HolderSet<Enchantment> exclusiveWith = HolderSet.empty(); // Paper added default to empty.
-+
-+ // Effects
-+ protected DataComponentMap effects;
-+
-+ protected final Conversions conversions;
-+
-+ public PaperEnchantmentRegistryEntry(
-+ final Conversions conversions,
-+ final TypedKey<org.bukkit.enchantments.Enchantment> ignoredKey,
-+ final @Nullable Enchantment internal
-+ ) {
-+ this.conversions = conversions;
-+ if (internal == null) {
-+ this.effects = DataComponentMap.EMPTY;
-+ return;
-+ }
-+
-+ // top level
-+ this.description = internal.description();
-+
-+ // definition
-+ final Enchantment.EnchantmentDefinition definition = internal.definition();
-+ this.supportedItems = definition.supportedItems();
-+ this.primaryItems = definition.primaryItems().orElse(null);
-+ this.weight = OptionalInt.of(definition.weight());
-+ this.maxLevel = OptionalInt.of(definition.maxLevel());
-+ this.minimumCost = definition.minCost();
-+ this.maximumCost = definition.maxCost();
-+ this.anvilCost = OptionalInt.of(definition.anvilCost());
-+ this.activeSlots = definition.slots();
-+
-+ // exclusive
-+ this.exclusiveWith = internal.exclusiveSet();
-+
-+ // effects
-+ this.effects = internal.effects();
-+ }
-+
-+ @Override
-+ public net.kyori.adventure.text.Component description() {
-+ return this.conversions.asAdventure(asConfigured(this.description, "description"));
-+ }
-+
-+ @Override
-+ public RegistryKeySet<ItemType> supportedItems() {
-+ return PaperRegistrySets.convertToApi(RegistryKey.ITEM, asConfigured(this.supportedItems, "supportedItems"));
-+ }
-+
-+ @Override
-+ public @Nullable RegistryKeySet<ItemType> primaryItems() {
-+ return this.primaryItems == null ? null : PaperRegistrySets.convertToApi(RegistryKey.ITEM, this.primaryItems);
-+ }
-+
-+ @Override
-+ public @Range(from = 1, to = 1024) int weight() {
-+ return asConfigured(this.weight, "weight");
-+ }
-+
-+ @Override
-+ public @Range(from = 1, to = 255) int maxLevel() {
-+ return asConfigured(this.maxLevel, "maxLevel");
-+ }
-+
-+ @Override
-+ public EnchantmentCost minimumCost() {
-+ final Enchantment.@MonotonicNonNull Cost cost = asConfigured(this.minimumCost, "minimumCost");
-+ return EnchantmentRegistryEntry.EnchantmentCost.of(cost.base(), cost.perLevelAboveFirst());
-+ }
-+
-+ @Override
-+ public EnchantmentCost maximumCost() {
-+ final Enchantment.@MonotonicNonNull Cost cost = asConfigured(this.maximumCost, "maximumCost");
-+ return EnchantmentRegistryEntry.EnchantmentCost.of(cost.base(), cost.perLevelAboveFirst());
-+ }
-+
-+ @Override
-+ public @Range(from = 0, to = Integer.MAX_VALUE) int anvilCost() {
-+ return asConfigured(this.anvilCost, "anvilCost");
-+ }
-+
-+ @Override
-+ public List<org.bukkit.inventory.EquipmentSlotGroup> activeSlots() {
-+ return Collections.unmodifiableList(Lists.transform(asConfigured(this.activeSlots, "activeSlots"), CraftEquipmentSlot::getSlot));
-+ }
-+
-+ @Override
-+ public RegistryKeySet<org.bukkit.enchantments.Enchantment> exclusiveWith() {
-+ return PaperRegistrySets.convertToApi(RegistryKey.ENCHANTMENT, this.exclusiveWith);
-+ }
-+
-+ public static final class PaperBuilder extends PaperEnchantmentRegistryEntry implements EnchantmentRegistryEntry.Builder,
-+ PaperRegistryBuilder<Enchantment, org.bukkit.enchantments.Enchantment> {
-+
-+ public PaperBuilder(final Conversions conversions, final TypedKey<org.bukkit.enchantments.Enchantment> key, final @Nullable Enchantment internal) {
-+ super(conversions, key, internal);
-+ }
-+
-+ @Override
-+ public Builder description(final net.kyori.adventure.text.Component description) {
-+ this.description = this.conversions.asVanilla(asArgument(description, "description"));
-+ return this;
-+ }
-+
-+ @Override
-+ public Builder supportedItems(final RegistryKeySet<ItemType> supportedItems) {
-+ this.supportedItems = PaperRegistrySets.convertToNms(Registries.ITEM, this.conversions.lookup(), asArgument(supportedItems, "supportedItems"));
-+ return this;
-+ }
-+
-+ @Override
-+ public Builder primaryItems(final @Nullable RegistryKeySet<ItemType> primaryItems) {
-+ this.primaryItems = primaryItems == null ? null : PaperRegistrySets.convertToNms(Registries.ITEM, this.conversions.lookup(), primaryItems);
-+ return this;
-+ }
-+
-+ @Override
-+ public Builder weight(final @Range(from = 1, to = 1024) int weight) {
-+ this.weight = OptionalInt.of(Checks.asArgumentRange(weight, "weight", 1, 1024));
-+ return this;
-+ }
-+
-+ @Override
-+ public Builder maxLevel(final @Range(from = 1, to = 255) int maxLevel) {
-+ this.maxLevel = OptionalInt.of(Checks.asArgumentRange(maxLevel, "maxLevel", 1, 255));
-+ return this;
-+ }
-+
-+ @Override
-+ public Builder minimumCost(final EnchantmentCost minimumCost) {
-+ final EnchantmentCost validCost = asArgument(minimumCost, "minimumCost");
-+ this.minimumCost = Enchantment.dynamicCost(validCost.baseCost(), validCost.additionalPerLevelCost());
-+ return this;
-+ }
-+
-+ @Override
-+ public Builder maximumCost(final EnchantmentCost maximumCost) {
-+ final EnchantmentCost validCost = asArgument(maximumCost, "maximumCost");
-+ this.maximumCost = Enchantment.dynamicCost(validCost.baseCost(), validCost.additionalPerLevelCost());
-+ return this;
-+ }
-+
-+ @Override
-+ public Builder anvilCost(final @Range(from = 0, to = Integer.MAX_VALUE) int anvilCost) {
-+ Preconditions.checkArgument(anvilCost >= 0, "anvilCost must be non-negative");
-+ this.anvilCost = OptionalInt.of(asArgumentMin(anvilCost, "anvilCost", 0));
-+ return this;
-+ }
-+
-+ @Override
-+ public Builder activeSlots(final Iterable<org.bukkit.inventory.EquipmentSlotGroup> activeSlots) {
-+ this.activeSlots = Lists.newArrayList(Iterables.transform(asArgument(activeSlots, "activeSlots"), CraftEquipmentSlot::getNMSGroup));
-+ return this;
-+ }
-+
-+ @Override
-+ public Builder exclusiveWith(final RegistryKeySet<org.bukkit.enchantments.Enchantment> exclusiveWith) {
-+ this.exclusiveWith = PaperRegistrySets.convertToNms(Registries.ENCHANTMENT, this.conversions.lookup(), asArgument(exclusiveWith, "exclusiveWith"));
-+ return this;
-+ }
-+
-+ @Override
-+ public Enchantment build() {
-+ final Enchantment.EnchantmentDefinition def = new Enchantment.EnchantmentDefinition(
-+ asConfigured(this.supportedItems, "supportedItems"),
-+ Optional.ofNullable(this.primaryItems),
-+ this.weight(),
-+ this.maxLevel(),
-+ asConfigured(this.minimumCost, "minimumCost"),
-+ asConfigured(this.maximumCost, "maximumCost"),
-+ this.anvilCost(),
-+ Collections.unmodifiableList(asConfigured(this.activeSlots, "activeSlots"))
-+ );
-+ return new Enchantment(
-+ asConfigured(this.description, "description"),
-+ def,
-+ this.exclusiveWith,
-+ this.effects
-+ );
-+ }
-+ }
-+}
-diff --git a/src/main/java/io/papermc/paper/registry/data/PaperGameEventRegistryEntry.java b/src/main/java/io/papermc/paper/registry/data/PaperGameEventRegistryEntry.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..18f9463ae23ba2d9c65ffb7531a87c925a5a8d6f
---- /dev/null
-+++ b/src/main/java/io/papermc/paper/registry/data/PaperGameEventRegistryEntry.java
-@@ -0,0 +1,57 @@
-+package io.papermc.paper.registry.data;
-+
-+import io.papermc.paper.registry.PaperRegistryBuilder;
-+import io.papermc.paper.registry.data.util.Conversions;
-+import java.util.OptionalInt;
-+import net.minecraft.world.level.gameevent.GameEvent;
-+import org.checkerframework.checker.nullness.qual.NonNull;
-+import org.checkerframework.checker.nullness.qual.Nullable;
-+import org.checkerframework.framework.qual.DefaultQualifier;
-+import org.jetbrains.annotations.Range;
-+
-+import static io.papermc.paper.registry.data.util.Checks.asArgumentMin;
-+import static io.papermc.paper.registry.data.util.Checks.asConfigured;
-+
-+@DefaultQualifier(NonNull.class)
-+public class PaperGameEventRegistryEntry implements GameEventRegistryEntry {
-+
-+ protected OptionalInt range = OptionalInt.empty();
-+
-+ public PaperGameEventRegistryEntry(
-+ final Conversions ignoredConversions,
-+ final io.papermc.paper.registry.TypedKey<org.bukkit.GameEvent> ignoredKey,
-+ final @Nullable GameEvent nms
-+ ) {
-+ if (nms == null) return;
-+
-+ this.range = OptionalInt.of(nms.notificationRadius());
-+ }
-+
-+ @Override
-+ public @Range(from = 0, to = Integer.MAX_VALUE) int range() {
-+ return asConfigured(this.range, "range");
-+ }
-+
-+ public static final class PaperBuilder extends PaperGameEventRegistryEntry implements GameEventRegistryEntry.Builder,
-+ PaperRegistryBuilder<GameEvent, org.bukkit.GameEvent> {
-+
-+ public PaperBuilder(
-+ final Conversions conversions,
-+ final io.papermc.paper.registry.TypedKey<org.bukkit.GameEvent> key,
-+ final @Nullable GameEvent nms
-+ ) {
-+ super(conversions, key, nms);
-+ }
-+
-+ @Override
-+ public GameEventRegistryEntry.Builder range(final @Range(from = 0, to = Integer.MAX_VALUE) int range) {
-+ this.range = OptionalInt.of(asArgumentMin(range, "range", 0));
-+ return this;
-+ }
-+
-+ @Override
-+ public GameEvent build() {
-+ return new GameEvent(this.range());
-+ }
-+ }
-+}
-diff --git a/src/main/java/io/papermc/paper/registry/data/util/Checks.java b/src/main/java/io/papermc/paper/registry/data/util/Checks.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..3241a94731fe8163876614efdcf30f8b551535af
---- /dev/null
-+++ b/src/main/java/io/papermc/paper/registry/data/util/Checks.java
-@@ -0,0 +1,48 @@
-+package io.papermc.paper.registry.data.util;
-+
-+import java.util.OptionalInt;
-+import org.checkerframework.checker.nullness.qual.NonNull;
-+import org.checkerframework.checker.nullness.qual.Nullable;
-+import org.checkerframework.framework.qual.DefaultQualifier;
-+
-+@DefaultQualifier(NonNull.class)
-+public final class Checks {
-+
-+ public static <T> T asConfigured(final @Nullable T value, final String field) {
-+ if (value == null) {
-+ throw new IllegalStateException(field + " has not been configured");
-+ }
-+ return value;
-+ }
-+
-+ public static int asConfigured(final OptionalInt value, final String field) {
-+ if (value.isEmpty()) {
-+ throw new IllegalStateException(field + " has not been configured");
-+ }
-+ return value.getAsInt();
-+ }
-+
-+ public static <T> T asArgument(final @Nullable T value, final String field) {
-+ if (value == null) {
-+ throw new IllegalArgumentException("argument " + field + " cannot be null");
-+ }
-+ return value;
-+ }
-+
-+ public static int asArgumentRange(final int value, final String field, final int min, final int max) {
-+ if (value < min || value > max) {
-+ throw new IllegalArgumentException("argument " + field + " must be [" + min + ", " + max + "]");
-+ }
-+ return value;
-+ }
-+
-+ public static int asArgumentMin(final int value, final String field, final int min) {
-+ if (value < min) {
-+ throw new IllegalArgumentException("argument " + field + " must be [" + min + ",+inf)");
-+ }
-+ return value;
-+ }
-+
-+ private Checks() {
-+ }
-+}
-diff --git a/src/main/java/net/minecraft/world/level/gameevent/GameEvent.java b/src/main/java/net/minecraft/world/level/gameevent/GameEvent.java
-index cc3c56224e64816b885c0131ce2a800a2efe3113..7acd9c71c5c4b487e792b8c36a8e52e10b691e98 100644
---- a/src/main/java/net/minecraft/world/level/gameevent/GameEvent.java
-+++ b/src/main/java/net/minecraft/world/level/gameevent/GameEvent.java
-@@ -85,7 +85,7 @@ public record GameEvent(int notificationRadius) {
- }
-
- private static Holder.Reference<GameEvent> register(String id, int range) {
-- return Registry.registerForHolder(BuiltInRegistries.GAME_EVENT, ResourceLocation.withDefaultNamespace(id), new GameEvent(range));
-+ return io.papermc.paper.registry.PaperRegistryListenerManager.INSTANCE.registerForHolderWithListeners(BuiltInRegistries.GAME_EVENT, ResourceLocation.withDefaultNamespace(id), new GameEvent(range)); // Paper - run with listeners
- }
-
- public static record Context(@Nullable Entity sourceEntity, @Nullable BlockState affectedState) {
-diff --git a/src/main/java/org/bukkit/craftbukkit/CraftGameEvent.java b/src/main/java/org/bukkit/craftbukkit/CraftGameEvent.java
-index ac9b4328cd55a68664a3f71186bc9a7be7cd9658..ea9fe1f8b1a1685ea975eba0ca418a831006065a 100644
---- a/src/main/java/org/bukkit/craftbukkit/CraftGameEvent.java
-+++ b/src/main/java/org/bukkit/craftbukkit/CraftGameEvent.java
-@@ -18,10 +18,12 @@ public class CraftGameEvent extends GameEvent implements Handleable<net.minecraf
- }
-
- private final NamespacedKey key;
-+ private final net.minecraft.resources.ResourceKey<net.minecraft.world.level.gameevent.GameEvent> handleKey; // Paper
- private final net.minecraft.world.level.gameevent.GameEvent handle;
-
- public CraftGameEvent(NamespacedKey key, net.minecraft.world.level.gameevent.GameEvent handle) {
- this.key = key;
-+ this.handleKey = net.minecraft.resources.ResourceKey.create(net.minecraft.core.registries.Registries.GAME_EVENT, org.bukkit.craftbukkit.util.CraftNamespacedKey.toMinecraft(key)); // Paper
- this.handle = handle;
- }
-
-@@ -30,6 +32,18 @@ public class CraftGameEvent extends GameEvent implements Handleable<net.minecraf
- return this.handle;
- }
-
-+ // Paper start
-+ @Override
-+ public int getRange() {
-+ return this.handle.notificationRadius();
-+ }
-+
-+ @Override
-+ public int getVibrationLevel() {
-+ return net.minecraft.world.level.gameevent.vibrations.VibrationSystem.getGameEventFrequency(this.handleKey);
-+ }
-+ // Paper end
-+
- @NotNull
- @Override
- public NamespacedKey getKey() {
-diff --git a/src/test/java/io/papermc/paper/registry/RegistryBuilderTest.java b/src/test/java/io/papermc/paper/registry/RegistryBuilderTest.java
-index 47b8ebac8496179008b8932c5ca2aadc274e24e0..814675bf67fd02e8cd2311dce60eeef651ef16f1 100644
---- a/src/test/java/io/papermc/paper/registry/RegistryBuilderTest.java
-+++ b/src/test/java/io/papermc/paper/registry/RegistryBuilderTest.java
-@@ -1,11 +1,16 @@
- package io.papermc.paper.registry;
-
-+import io.papermc.paper.registry.data.PaperEnchantmentRegistryEntry;
-+import io.papermc.paper.registry.data.PaperGameEventRegistryEntry;
- import io.papermc.paper.registry.data.util.Conversions;
- import java.util.List;
- import java.util.Map;
- import net.minecraft.core.Registry;
-+import net.minecraft.core.registries.Registries;
- import net.minecraft.resources.RegistryOps;
- import net.minecraft.resources.ResourceKey;
-+import net.minecraft.world.item.enchantment.Enchantment;
-+import net.minecraft.world.level.gameevent.GameEvent;
- import org.bukkit.support.RegistryHelper;
- import org.bukkit.support.environment.AllFeatures;
- import org.junit.jupiter.api.Disabled;
-@@ -14,16 +19,18 @@ import org.junit.jupiter.params.provider.Arguments;
- import org.junit.jupiter.params.provider.MethodSource;
-
- import static org.junit.jupiter.api.Assertions.assertEquals;
-+import static org.junit.jupiter.params.provider.Arguments.arguments;
-
- @AllFeatures
- class RegistryBuilderTest {
-
- static List<Arguments> registries() {
- return List.of(
-+ arguments(Registries.ENCHANTMENT, (PaperRegistryBuilder.Filler<Enchantment, org.bukkit.enchantments.Enchantment, PaperEnchantmentRegistryEntry.PaperBuilder>) PaperEnchantmentRegistryEntry.PaperBuilder::new),
-+ arguments(Registries.GAME_EVENT, (PaperRegistryBuilder.Filler<GameEvent, org.bukkit.GameEvent, PaperGameEventRegistryEntry.PaperBuilder>) PaperGameEventRegistryEntry.PaperBuilder::new)
- );
- }
-
-- @Disabled
- @ParameterizedTest
- @MethodSource("registries")
- <M, T> void testEquality(final ResourceKey<? extends Registry<M>> resourceKey, final PaperRegistryBuilder.Filler<M, T, ?> filler) {
diff --git a/patches/unapplied/server/1017-Proxy-ItemStack-to-CraftItemStack.patch b/patches/unapplied/server/1017-Proxy-ItemStack-to-CraftItemStack.patch
deleted file mode 100644
index 781b18ae7a..0000000000
--- a/patches/unapplied/server/1017-Proxy-ItemStack-to-CraftItemStack.patch
+++ /dev/null
@@ -1,302 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Jake Potrebic <[email protected]>
-Date: Tue, 14 May 2024 11:57:43 -0700
-Subject: [PATCH] Proxy ItemStack to CraftItemStack
-
-
-diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java
-index efb7fb8dbaa7446e394f55b021692c11a25fd29f..a3c6d2cbdce60b1cf935d798568b8bb5d97e1229 100644
---- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java
-+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java
-@@ -26,15 +26,57 @@ import org.jetbrains.annotations.ApiStatus;
- @DelegateDeserialization(ItemStack.class)
- public final class CraftItemStack extends ItemStack {
-
-- // Paper start - MC Utils
-- public static net.minecraft.world.item.ItemStack unwrap(ItemStack bukkit) {
-- if (bukkit instanceof CraftItemStack craftItemStack) {
-- return craftItemStack.handle != null ? craftItemStack.handle : net.minecraft.world.item.ItemStack.EMPTY;
-+ // Paper start - delegate api-ItemStack to CraftItemStack
-+ private static final java.lang.invoke.VarHandle API_ITEM_STACK_CRAFT_DELEGATE_FIELD;
-+ static {
-+ try {
-+ API_ITEM_STACK_CRAFT_DELEGATE_FIELD = java.lang.invoke.MethodHandles.privateLookupIn(
-+ ItemStack.class,
-+ java.lang.invoke.MethodHandles.lookup()
-+ ).findVarHandle(ItemStack.class, "craftDelegate", ItemStack.class);
-+ } catch (final IllegalAccessException | NoSuchFieldException exception) {
-+ throw new RuntimeException(exception);
-+ }
-+ }
-+
-+ private static CraftItemStack getCraftStack(final ItemStack bukkit) {
-+ if (bukkit instanceof final CraftItemStack craftItemStack) {
-+ return craftItemStack;
- } else {
-- return asNMSCopy(bukkit);
-+ return (CraftItemStack) API_ITEM_STACK_CRAFT_DELEGATE_FIELD.get(bukkit);
- }
- }
-
-+ @Override
-+ public int hashCode() {
-+ if (this.handle == null || this.handle.isEmpty()) {
-+ return net.minecraft.world.item.ItemStack.EMPTY.hashCode();
-+ } else {
-+ int hash = net.minecraft.world.item.ItemStack.hashItemAndComponents(this.handle);
-+ hash = hash * 31 + this.handle.getCount();
-+ return hash;
-+ }
-+ }
-+
-+ @Override
-+ public boolean equals(final Object obj) {
-+ if (!(obj instanceof final org.bukkit.inventory.ItemStack bukkit)) return false;
-+ final CraftItemStack craftStack = getCraftStack(bukkit);
-+ if (this.handle == craftStack.handle) return true;
-+ else if (this.handle == null || craftStack.handle == null) return false;
-+ else if (this.handle.isEmpty() && craftStack.handle.isEmpty()) return true;
-+ else return net.minecraft.world.item.ItemStack.matches(this.handle, craftStack.handle);
-+ }
-+ // Paper end
-+
-+ // Paper start - MC Utils
-+ public static net.minecraft.world.item.ItemStack unwrap(ItemStack bukkit) {
-+ // Paper start - re-implement after delegating all api ItemStack calls to CraftItemStack
-+ final CraftItemStack craftItemStack = getCraftStack(bukkit);
-+ return craftItemStack.handle == null ? net.minecraft.world.item.ItemStack.EMPTY : craftItemStack.handle;
-+ // Paper end - re-implement after delegating all api ItemStack calls to CraftItemStack
-+ }
-+
- public static net.minecraft.world.item.ItemStack getOrCloneOnMutation(ItemStack old, ItemStack newInstance) {
- return old == newInstance ? unwrap(old) : asNMSCopy(newInstance);
- }
-@@ -48,25 +90,13 @@ public final class CraftItemStack extends ItemStack {
- // Paper end - override isEmpty to use vanilla's impl
-
- public static net.minecraft.world.item.ItemStack asNMSCopy(ItemStack original) {
-- if (original instanceof CraftItemStack) {
-- CraftItemStack stack = (CraftItemStack) original;
-- return stack.handle == null ? net.minecraft.world.item.ItemStack.EMPTY : stack.handle.copy();
-- }
-- if (original == null || original.isEmpty()) { // Paper - override isEmpty to use vanilla's impl; use isEmpty
-+ // Paper start - re-implement after delegating all api ItemStack calls to CraftItemStack
-+ if (original == null || original.isEmpty()) {
- return net.minecraft.world.item.ItemStack.EMPTY;
- }
--
-- Item item = CraftItemType.bukkitToMinecraft(original.getType());
--
-- if (item == null) {
-- return net.minecraft.world.item.ItemStack.EMPTY;
-- }
--
-- net.minecraft.world.item.ItemStack stack = new net.minecraft.world.item.ItemStack(item, original.getAmount());
-- if (original.hasItemMeta()) {
-- CraftItemStack.setItemMeta(stack, original.getItemMeta());
-- }
-- return stack;
-+ final CraftItemStack stack = getCraftStack(original);
-+ return stack.handle == null ? net.minecraft.world.item.ItemStack.EMPTY : stack.handle.copy();
-+ // Paper end - re-implement after delegating all api ItemStack calls to CraftItemStack
- }
-
- // Paper start
-@@ -89,14 +119,10 @@ public final class CraftItemStack extends ItemStack {
- * Copies the NMS stack to return as a strictly-Bukkit stack
- */
- public static ItemStack asBukkitCopy(net.minecraft.world.item.ItemStack original) {
-- if (original.isEmpty()) {
-- return new ItemStack(Material.AIR);
-- }
-- ItemStack stack = new ItemStack(CraftItemType.minecraftToBukkit(original.getItem()), original.getCount());
-- if (CraftItemStack.hasItemMeta(original)) {
-- stack.setItemMeta(CraftItemStack.getItemMeta(original));
-- }
-- return stack;
-+ // Paper start - no such thing as a "strictly-Bukkit stack" anymore
-+ // we copy the stack since it should be a complete copy not a mirror
-+ return asCraftMirror(original.copy());
-+ // Paper end
- }
-
- public static CraftItemStack asCraftMirror(net.minecraft.world.item.ItemStack original) {
-@@ -317,11 +343,7 @@ public final class CraftItemStack extends ItemStack {
-
- @Override
- public CraftItemStack clone() {
-- CraftItemStack itemStack = (CraftItemStack) super.clone();
-- if (this.handle != null) {
-- itemStack.handle = this.handle.copy();
-- }
-- return itemStack;
-+ return new org.bukkit.craftbukkit.inventory.CraftItemStack(this.handle != null ? this.handle.copy() : null); // Paper
- }
-
- @Override
-@@ -424,22 +446,14 @@ public final class CraftItemStack extends ItemStack {
- if (stack == this) {
- return true;
- }
-- if (!(stack instanceof CraftItemStack)) {
-- return stack.getClass() == ItemStack.class && stack.isSimilar(this);
-- }
--
-- CraftItemStack that = (CraftItemStack) stack;
-+ final CraftItemStack that = getCraftStack(stack); // Paper - re-implement after delegating all api ItemStack calls to CraftItemStack
- if (this.handle == that.handle) {
- return true;
- }
- if (this.handle == null || that.handle == null) {
- return false;
- }
-- Material comparisonType = CraftLegacy.fromLegacy(that.getType()); // This may be called from legacy item stacks, try to get the right material
-- if (!(comparisonType == this.getType() && this.getDurability() == that.getDurability())) {
-- return false;
-- }
-- return this.hasItemMeta() ? that.hasItemMeta() && this.handle.getComponents().equals(that.handle.getComponents()) : !that.hasItemMeta();
-+ return net.minecraft.world.item.ItemStack.isSameItemSameComponents(this.handle, that.handle); // Paper - re-implement after delegating all api ItemStack calls to CraftItemStack
- }
-
- @Override
-diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemType.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemType.java
-index d03f4a767f6c7fe7d6bcef20e6676c39d9657584..bae3dd5fc67e6b3d98a5e63ffbf639c5042f8843 100644
---- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemType.java
-+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemType.java
-@@ -100,13 +100,14 @@ public class CraftItemType<M extends ItemMeta> implements ItemType.Typed<M>, Han
- @NotNull
- @Override
- public ItemStack createItemStack(final int amount, @Nullable final Consumer<? super M> metaConfigurator) {
-- final ItemStack itemStack = new ItemStack(this.asMaterial(), amount);
-+ // Paper start - re-implement to return CraftItemStack
-+ final net.minecraft.world.item.ItemStack stack = new net.minecraft.world.item.ItemStack(this.item, amount);
-+ final CraftItemStack mirror = CraftItemStack.asCraftMirror(stack);
- if (metaConfigurator != null) {
-- final ItemMeta itemMeta = itemStack.getItemMeta();
-- metaConfigurator.accept((M) itemMeta);
-- itemStack.setItemMeta(itemMeta);
-+ mirror.editMeta(this.getItemMetaClass(), metaConfigurator);
- }
-- return itemStack;
-+ return mirror;
-+ // Paper start - reimplement to return CraftItemStack
- }
-
- @Override
-diff --git a/src/main/java/org/bukkit/craftbukkit/legacy/MaterialRerouting.java b/src/main/java/org/bukkit/craftbukkit/legacy/MaterialRerouting.java
-index 6cc9d7a9e6d4bfdc27e52fc581b2bb832616f121..6930d0afb230a88aa813b02e4d55c95d3a049688 100644
---- a/src/main/java/org/bukkit/craftbukkit/legacy/MaterialRerouting.java
-+++ b/src/main/java/org/bukkit/craftbukkit/legacy/MaterialRerouting.java
-@@ -678,4 +678,16 @@ public class MaterialRerouting {
- return itemStack.withType(material);
- }
- // Paper end - register paper API specific material consumers in rerouting
-+
-+ // Paper start - methods added post 1.13, no-op
-+ @RerouteStatic("org/bukkit/inventory/ItemStack")
-+ public static ItemStack of(final Material material) {
-+ return ItemStack.of(material);
-+ }
-+
-+ @RerouteStatic("org/bukkit/inventory/ItemStack")
-+ public static ItemStack of(final Material material, final int amount) {
-+ return ItemStack.of(material, amount);
-+ }
-+ // Paper end
- }
-diff --git a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
-index 68a6cd43042e87501f5bd48565222638dd58a1cf..6adc18c40d5d62e2ebc8deec197cec630a366937 100644
---- a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
-+++ b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
-@@ -689,6 +689,13 @@ public final class CraftMagicNumbers implements UnsafeValues {
- }
- // Paper end - hack to get tags for non server-backed registries
-
-+ // Paper start - proxy ItemStack
-+ @Override
-+ public org.bukkit.inventory.ItemStack createEmptyStack() {
-+ return CraftItemStack.asCraftMirror(null);
-+ }
-+ // Paper end - proxy ItemStack
-+
- /**
- * This helper class represents the different NBT Tags.
- * <p>
-diff --git a/src/test/java/io/papermc/paper/configuration/ConfigurationSectionTest.java b/src/test/java/io/papermc/paper/configuration/ConfigurationSectionTest.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..ed45bfa577579afcbd54d655c3b5d05d6c6f3e86
---- /dev/null
-+++ b/src/test/java/io/papermc/paper/configuration/ConfigurationSectionTest.java
-@@ -0,0 +1,53 @@
-+package io.papermc.paper.configuration;
-+
-+import org.bukkit.Material;
-+import org.bukkit.configuration.ConfigurationSection;
-+import org.bukkit.inventory.ItemStack;
-+import org.bukkit.support.environment.VanillaFeature;
-+import org.junit.jupiter.api.Test;
-+
-+import static org.junit.jupiter.api.Assertions.assertEquals;
-+import static org.junit.jupiter.api.Assertions.assertFalse;
-+import static org.junit.jupiter.api.Assertions.assertNull;
-+import static org.junit.jupiter.api.Assertions.assertTrue;
-+
-+public abstract class ConfigurationSectionTest {
-+ public abstract ConfigurationSection getConfigurationSection();
-+
-+ @Test
-+ public void testGetItemStack_String() {
-+ ConfigurationSection section = getConfigurationSection();
-+ String key = "exists";
-+ ItemStack value = new ItemStack(Material.ACACIA_WOOD, 50);
-+
-+ section.set(key, value);
-+
-+ assertEquals(value, section.getItemStack(key));
-+ assertNull(section.getString("doesntExist"));
-+ }
-+
-+ @Test
-+ public void testGetItemStack_String_ItemStack() {
-+ ConfigurationSection section = getConfigurationSection();
-+ String key = "exists";
-+ ItemStack value = new ItemStack(Material.ACACIA_WOOD, 50);
-+ ItemStack def = new ItemStack(Material.STONE, 1);
-+
-+ section.set(key, value);
-+
-+ assertEquals(value, section.getItemStack(key, def));
-+ assertEquals(def, section.getItemStack("doesntExist", def));
-+ }
-+
-+ @Test
-+ public void testIsItemStack() {
-+ ConfigurationSection section = getConfigurationSection();
-+ String key = "exists";
-+ ItemStack value = new ItemStack(Material.ACACIA_WOOD, 50);
-+
-+ section.set(key, value);
-+
-+ assertTrue(section.isItemStack(key));
-+ assertFalse(section.isItemStack("doesntExist"));
-+ }
-+}
-diff --git a/src/test/java/io/papermc/paper/configuration/MemorySectionTest.java b/src/test/java/io/papermc/paper/configuration/MemorySectionTest.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..c00085328ce8a00fc274632a556ab27660fa57ed
---- /dev/null
-+++ b/src/test/java/io/papermc/paper/configuration/MemorySectionTest.java
-@@ -0,0 +1,13 @@
-+package io.papermc.paper.configuration;
-+
-+import org.bukkit.configuration.ConfigurationSection;
-+import org.bukkit.configuration.MemoryConfiguration;
-+import org.bukkit.support.environment.Normal;
-+
-+@Normal
-+public class MemorySectionTest extends ConfigurationSectionTest {
-+ @Override
-+ public ConfigurationSection getConfigurationSection() {
-+ return new MemoryConfiguration().createSection("section");
-+ }
-+}
diff --git a/patches/unapplied/server/1018-Make-a-PDC-view-accessible-directly-from-ItemStack.patch b/patches/unapplied/server/1018-Make-a-PDC-view-accessible-directly-from-ItemStack.patch
deleted file mode 100644
index f86fe8c7dd..0000000000
--- a/patches/unapplied/server/1018-Make-a-PDC-view-accessible-directly-from-ItemStack.patch
+++ /dev/null
@@ -1,271 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Jake Potrebic <[email protected]>
-Date: Wed, 12 Jun 2024 10:29:40 -0700
-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..122c32e82b299cafd7d0c6a9f4818523470c9f05
---- /dev/null
-+++ b/src/main/java/io/papermc/paper/persistence/PaperPersistentDataContainerView.java
-@@ -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.jspecify.annotations.NullMarked;
-+import org.jspecify.annotations.Nullable;
-+
-+@NullMarked
-+public abstract class PaperPersistentDataContainerView implements PersistentDataContainerView {
-+
-+ protected final CraftPersistentDataTypeRegistry registry;
-+ protected final CraftPersistentDataAdapterContext adapterContext;
-+
-+ public PaperPersistentDataContainerView(final CraftPersistentDataTypeRegistry registry) {
-+ this.registry = registry;
-+ this.adapterContext = new CraftPersistentDataAdapterContext(this.registry);
-+ }
-+
-+ public abstract @Nullable Tag getTag(final String key);
-+
-+ public abstract CompoundTag toTagCompound();
-+
-+ @Override
-+ public <P, C> boolean has(final NamespacedKey key, final PersistentDataType<P, C> type) {
-+ Preconditions.checkArgument(key != null, "The NamespacedKey key cannot be null");
-+ Preconditions.checkArgument(type != null, "The provided type cannot be null");
-+
-+ final Tag value = this.getTag(key.toString());
-+ if (value == null) {
-+ return false;
-+ }
-+
-+ return this.registry.isInstanceOf(type, value);
-+ }
-+
-+ @Override
-+ public boolean has(final NamespacedKey key) {
-+ Preconditions.checkArgument(key != null, "The provided key for the custom value was null"); // Paper
-+ return this.getTag(key.toString()) != null;
-+ }
-+
-+ @Override
-+ public <P, C> @Nullable C get(final NamespacedKey key, final PersistentDataType<P, C> type) {
-+ Preconditions.checkArgument(key != null, "The NamespacedKey key cannot be null");
-+ Preconditions.checkArgument(type != null, "The provided type cannot be null");
-+
-+ final Tag value = this.getTag(key.toString());
-+ if (value == null) {
-+ return null;
-+ }
-+
-+ return type.fromPrimitive(this.registry.extract(type, value), this.adapterContext);
-+ }
-+
-+ @Override
-+ public <P, C> C getOrDefault(final NamespacedKey key, final PersistentDataType<P, C> type, final C defaultValue) {
-+ final C c = this.get(key, type);
-+ return c != null ? c : defaultValue;
-+ }
-+
-+ @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 CompoundTag root = this.toTagCompound();
-+ final ByteArrayOutputStream byteArrayOutput = new ByteArrayOutputStream();
-+ try (final DataOutputStream dataOutput = new DataOutputStream(byteArrayOutput)) {
-+ NbtIo.write(root, dataOutput);
-+ return byteArrayOutput.toByteArray();
-+ }
-+ }
-+}
-diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java
-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,34 @@ public final class CraftItemStack extends ItemStack {
- return mirrored;
- }
- // Paper end
-+
-+ // Paper start - pdc
-+ private net.minecraft.nbt.CompoundTag getPdcTag() {
-+ if (this.handle == null) {
-+ return new net.minecraft.nbt.CompoundTag();
-+ }
-+ final net.minecraft.world.item.component.CustomData customData = this.handle.getOrDefault(DataComponents.CUSTOM_DATA, net.minecraft.world.item.component.CustomData.EMPTY);
-+ // getUnsafe is OK here because we are only ever *reading* the data so immutability is preserved
-+ //noinspection deprecation
-+ return customData.getUnsafe().getCompound("PublicBukkitValues");
-+ }
-+
-+ private static final org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry REGISTRY = new org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry();
-+ private final io.papermc.paper.persistence.PaperPersistentDataContainerView pdcView = new io.papermc.paper.persistence.PaperPersistentDataContainerView(REGISTRY) {
-+
-+ @Override
-+ public net.minecraft.nbt.CompoundTag toTagCompound() {
-+ return CraftItemStack.this.getPdcTag();
-+ }
-+
-+ @Override
-+ public net.minecraft.nbt.Tag getTag(final String key) {
-+ return CraftItemStack.this.getPdcTag().get(key);
-+ }
-+ };
-+ @Override
-+ public io.papermc.paper.persistence.PersistentDataContainerView getPersistentDataContainer() {
-+ return this.pdcView;
-+ }
-+ // Paper end - pdc
- }
-diff --git a/src/main/java/org/bukkit/craftbukkit/persistence/CraftPersistentDataContainer.java b/src/main/java/org/bukkit/craftbukkit/persistence/CraftPersistentDataContainer.java
-index f55fdd57ced259ad5a95878840e98ffaa3db2e05..9d867f1659433ea15f281c8b441db7e339013100 100644
---- a/src/main/java/org/bukkit/craftbukkit/persistence/CraftPersistentDataContainer.java
-+++ b/src/main/java/org/bukkit/craftbukkit/persistence/CraftPersistentDataContainer.java
-@@ -16,11 +16,10 @@ import org.bukkit.persistence.PersistentDataContainer;
- import org.bukkit.persistence.PersistentDataType;
- import org.jetbrains.annotations.NotNull;
-
--public class CraftPersistentDataContainer implements PersistentDataContainer {
-+public class CraftPersistentDataContainer extends io.papermc.paper.persistence.PaperPersistentDataContainerView implements PersistentDataContainer { // Paper - split up view and mutable
-
- private final Map<String, Tag> customDataTags = new HashMap<>();
-- private final CraftPersistentDataTypeRegistry registry;
-- private final CraftPersistentDataAdapterContext adapterContext;
-+ // Paper - move to PersistentDataContainerView
-
- public CraftPersistentDataContainer(Map<String, Tag> customTags, CraftPersistentDataTypeRegistry registry) {
- this(registry);
-@@ -28,10 +27,15 @@ public class CraftPersistentDataContainer implements PersistentDataContainer {
- }
-
- public CraftPersistentDataContainer(CraftPersistentDataTypeRegistry registry) {
-- this.registry = registry;
-- this.adapterContext = new CraftPersistentDataAdapterContext(this.registry);
-+ super(registry); // Paper - move to PersistentDataContainerView
- }
-
-+ // Paper start
-+ @Override
-+ public Tag getTag(final String key) {
-+ return this.customDataTags.get(key);
-+ }
-+ // Paper end
-
- @Override
- public <T, Z> void set(@NotNull NamespacedKey key, @NotNull PersistentDataType<T, Z> type, @NotNull Z value) {
-@@ -42,44 +46,7 @@ public class CraftPersistentDataContainer implements PersistentDataContainer {
- this.customDataTags.put(key.toString(), this.registry.wrap(type, type.toPrimitive(value, this.adapterContext)));
- }
-
-- @Override
-- public <T, Z> boolean has(@NotNull NamespacedKey key, @NotNull PersistentDataType<T, Z> type) {
-- Preconditions.checkArgument(key != null, "The NamespacedKey key cannot be null");
-- Preconditions.checkArgument(type != null, "The provided type cannot be null");
--
-- Tag value = this.customDataTags.get(key.toString());
-- if (value == null) {
-- return false;
-- }
--
-- return this.registry.isInstanceOf(type, value);
-- }
--
-- @Override
-- public boolean has(NamespacedKey key) {
-- Preconditions.checkArgument(key != null, "The provided key for the custom value was null"); // Paper
-- return this.customDataTags.get(key.toString()) != null;
-- }
--
-- @Override
-- public <T, Z> Z get(@NotNull NamespacedKey key, @NotNull PersistentDataType<T, Z> type) {
-- Preconditions.checkArgument(key != null, "The NamespacedKey key cannot be null");
-- Preconditions.checkArgument(type != null, "The provided type cannot be null");
--
-- Tag value = this.customDataTags.get(key.toString());
-- if (value == null) {
-- return null;
-- }
--
-- return type.fromPrimitive(this.registry.extract(type, value), this.adapterContext);
-- }
--
-- @NotNull
-- @Override
-- public <T, Z> Z getOrDefault(@NotNull NamespacedKey key, @NotNull PersistentDataType<T, Z> type, @NotNull Z defaultValue) {
-- Z z = this.get(key, type);
-- return z != null ? z : defaultValue;
-- }
-+ // Paper - move to PersistentDataContainerView
-
- @NotNull
- @Override
-@@ -186,16 +153,7 @@ public class CraftPersistentDataContainer implements PersistentDataContainer {
- // Paper end
-
- // Paper start - byte array serialization
-- @Override
-- public byte[] serializeToBytes() throws java.io.IOException {
-- final net.minecraft.nbt.CompoundTag root = this.toTagCompound();
-- final java.io.ByteArrayOutputStream byteArrayOutput = new java.io.ByteArrayOutputStream();
-- try (final java.io.DataOutputStream dataOutput = new java.io.DataOutputStream(byteArrayOutput)) {
-- net.minecraft.nbt.NbtIo.write(root, dataOutput);
-- return byteArrayOutput.toByteArray();
-- }
-- }
--
-+ // Paper - move to PersistentDataContainerView
- @Override
- public void readFromBytes(final byte[] bytes, final boolean clear) throws java.io.IOException {
- if (clear) {
diff --git a/patches/unapplied/server/1019-Prioritize-Minecraft-commands-in-function-parsing-an.patch b/patches/unapplied/server/1019-Prioritize-Minecraft-commands-in-function-parsing-an.patch
deleted file mode 100644
index 6e36d9a7b3..0000000000
--- a/patches/unapplied/server/1019-Prioritize-Minecraft-commands-in-function-parsing-an.patch
+++ /dev/null
@@ -1,135 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Jason Penilla <[email protected]>
-Date: Mon, 1 Jul 2024 11:58:49 -0700
-Subject: [PATCH] Prioritize Minecraft commands in function parsing and command
- blocks
-
-
-diff --git a/src/main/java/com/mojang/brigadier/CommandDispatcher.java b/src/main/java/com/mojang/brigadier/CommandDispatcher.java
-index a4d5d7017e0be79844b996de85a63cad5f8488bc..770aec1976bd391eb5712b57b6c6a0290b4723a8 100644
---- a/src/main/java/com/mojang/brigadier/CommandDispatcher.java
-+++ b/src/main/java/com/mojang/brigadier/CommandDispatcher.java
-@@ -298,7 +298,7 @@ public class CommandDispatcher<S> {
- List<ParseResults<S>> potentials = null;
- final int cursor = originalReader.getCursor();
-
-- for (final CommandNode<S> child : node.getRelevantNodes(originalReader)) {
-+ for (final CommandNode<S> child : node.getRelevantNodes(originalReader, source)) { // Paper - prioritize mc commands in function parsing
- if (!child.canUse(source)) {
- continue;
- }
-diff --git a/src/main/java/com/mojang/brigadier/tree/CommandNode.java b/src/main/java/com/mojang/brigadier/tree/CommandNode.java
-index 03ce8a2abb6dceaa922dcce7f3adbc228bbde4bc..dc76fcf4c6cc6cd65ce117b1855c15ede60f30ab 100644
---- a/src/main/java/com/mojang/brigadier/tree/CommandNode.java
-+++ b/src/main/java/com/mojang/brigadier/tree/CommandNode.java
-@@ -173,6 +173,12 @@ public abstract class CommandNode<S> implements Comparable<CommandNode<S>> {
- protected abstract String getSortedKey();
-
- public Collection<? extends CommandNode<S>> getRelevantNodes(final StringReader input) {
-+ // Paper start - prioritize mc commands in function parsing
-+ return this.getRelevantNodes(input, null);
-+ }
-+ @org.jetbrains.annotations.ApiStatus.Internal
-+ public Collection<? extends CommandNode<S>> getRelevantNodes(final StringReader input, final Object source) {
-+ // Paper end - prioritize mc commands in function parsing
- if (this.literals.size() > 0) {
- final int cursor = input.getCursor();
- while (input.canRead() && input.peek() != ' ') {
-@@ -180,7 +186,21 @@ public abstract class CommandNode<S> implements Comparable<CommandNode<S>> {
- }
- final String text = input.getString().substring(cursor, input.getCursor());
- input.setCursor(cursor);
-- final LiteralCommandNode<S> literal = this.literals.get(text);
-+ // Paper start - prioritize mc commands in function parsing
-+ LiteralCommandNode<S> literal = null;
-+ if (source instanceof CommandSourceStack css && css.source == net.minecraft.commands.CommandSource.NULL) {
-+ if (!text.contains(":")) {
-+ literal = this.literals.get("minecraft:" + text);
-+ }
-+ } else if (source instanceof CommandSourceStack css && css.source instanceof net.minecraft.world.level.BaseCommandBlock) {
-+ if (css.getServer().server.getCommandBlockOverride(text) && !text.contains(":")) {
-+ literal = this.literals.get("minecraft:" + text);
-+ }
-+ }
-+ if (literal == null) {
-+ literal = this.literals.get(text);
-+ }
-+ // Paper end - prioritize mc commands in function parsing
- if (literal != null) {
- return Collections.singleton(literal);
- } else {
-diff --git a/src/main/java/com/mojang/brigadier/tree/LiteralCommandNode.java b/src/main/java/com/mojang/brigadier/tree/LiteralCommandNode.java
-index 85a890403645f0f9d381e85b48efcae126673945..bcc27fec043a57eb5064934c967982deff9cdee4 100644
---- a/src/main/java/com/mojang/brigadier/tree/LiteralCommandNode.java
-+++ b/src/main/java/com/mojang/brigadier/tree/LiteralCommandNode.java
-@@ -23,11 +23,19 @@ import java.util.function.Predicate;
- public class LiteralCommandNode<S> extends CommandNode<S> {
- private final String literal;
- private final String literalLowerCase;
-+ private final String nonPrefixed; // Paper - prioritize mc commands in function parsing
-
- public LiteralCommandNode(final String literal, final Command<S> command, final Predicate<S> requirement, final CommandNode<S> redirect, final RedirectModifier<S> modifier, final boolean forks) {
- super(command, requirement, redirect, modifier, forks);
- this.literal = literal;
- this.literalLowerCase = literal.toLowerCase(Locale.ROOT);
-+ // Paper start - prioritize mc commands in function parsing
-+ if (literal.startsWith("minecraft:")) {
-+ this.nonPrefixed = literal.substring("minecraft:".length());
-+ } else {
-+ this.nonPrefixed = null;
-+ }
-+ // Paper end - prioritize mc commands in function parsing
- }
-
- public String getLiteral() {
-@@ -42,7 +50,12 @@ public class LiteralCommandNode<S> extends CommandNode<S> {
- @Override
- public void parse(final StringReader reader, final CommandContextBuilder<S> contextBuilder) throws CommandSyntaxException {
- final int start = reader.getCursor();
-- final int end = parse(reader);
-+ // Paper start - prioritize mc commands in function parsing
-+ int end = parse(reader, false);
-+ if (end == -1 && this.nonPrefixed != null) {
-+ end = parse(reader, true);
-+ }
-+ // Paper end - prioritize mc commands in function parsing
- if (end > -1) {
- contextBuilder.withNode(this, StringRange.between(start, end));
- return;
-@@ -51,7 +64,10 @@ public class LiteralCommandNode<S> extends CommandNode<S> {
- throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.literalIncorrect().createWithContext(reader, literal);
- }
-
-- private int parse(final StringReader reader) {
-+ // Paper start - prioritize mc commands in function parsing
-+ private int parse(final StringReader reader, final boolean secondPass) {
-+ String literal = secondPass ? this.nonPrefixed : this.literal;
-+ // Paper end - prioritize mc commands in function parsing
- final int start = reader.getCursor();
- if (reader.canRead(literal.length())) {
- final int end = start + literal.length();
-@@ -78,7 +94,7 @@ public class LiteralCommandNode<S> extends CommandNode<S> {
-
- @Override
- public boolean isValidInput(final String input) {
-- return parse(new StringReader(input)) > -1;
-+ return parse(new StringReader(input), false) > -1; // Paper - prioritize mc commands in function parsing
- }
-
- @Override
-diff --git a/src/main/java/net/minecraft/commands/Commands.java b/src/main/java/net/minecraft/commands/Commands.java
-index 3e454515360c22a26c9329e4032d525579110d7e..1d1e76de60e40224f5cb81893f9ee50fe987badb 100644
---- a/src/main/java/net/minecraft/commands/Commands.java
-+++ b/src/main/java/net/minecraft/commands/Commands.java
-@@ -310,10 +310,7 @@ public class Commands {
-
- // Paper - Fix permission levels for command blocks
-
-- // Handle vanilla commands;
-- if (sender.getLevel().getCraftServer().getCommandBlockOverride(args[0])) {
-- args[0] = "minecraft:" + args[0];
-- }
-+ // Handle vanilla commands; // Paper - handled in CommandNode/CommandDispatcher
-
- String newCommand = joiner.join(args);
- this.performPrefixedCommand(sender, newCommand, newCommand);
diff --git a/patches/unapplied/server/1020-optimize-dirt-and-snow-spreading.patch b/patches/unapplied/server/1020-optimize-dirt-and-snow-spreading.patch
deleted file mode 100644
index 49de7fcab9..0000000000
--- a/patches/unapplied/server/1020-optimize-dirt-and-snow-spreading.patch
+++ /dev/null
@@ -1,78 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: lukas81298 <[email protected]>
-Date: Fri, 22 Jan 2021 21:50:18 +0100
-Subject: [PATCH] optimize dirt and snow spreading
-
-
-diff --git a/src/main/java/net/minecraft/world/level/block/SpreadingSnowyDirtBlock.java b/src/main/java/net/minecraft/world/level/block/SpreadingSnowyDirtBlock.java
-index 5a39e8d359dc13383711e49ffb2d1294dad26192..b7165ec19bef1a07f6618fc0429d86cda1b08da4 100644
---- a/src/main/java/net/minecraft/world/level/block/SpreadingSnowyDirtBlock.java
-+++ b/src/main/java/net/minecraft/world/level/block/SpreadingSnowyDirtBlock.java
-@@ -18,8 +18,13 @@ public abstract class SpreadingSnowyDirtBlock extends SnowyDirtBlock {
- }
-
- private static boolean canBeGrass(BlockState state, LevelReader world, BlockPos pos) {
-+ // Paper start - Perf: optimize dirt and snow spreading
-+ return canBeGrass(world.getChunk(pos), state, world, pos);
-+ }
-+ private static boolean canBeGrass(net.minecraft.world.level.chunk.ChunkAccess chunk, BlockState state, LevelReader world, BlockPos pos) {
-+ // Paper end - Perf: optimize dirt and snow spreading
- BlockPos blockposition1 = pos.above();
-- BlockState iblockdata1 = world.getBlockState(blockposition1);
-+ BlockState iblockdata1 = chunk.getBlockState(blockposition1); // Paper - Perf: optimize dirt and snow spreading
-
- if (iblockdata1.is(Blocks.SNOW) && (Integer) iblockdata1.getValue(SnowLayerBlock.LAYERS) == 1) {
- return true;
-@@ -36,15 +41,27 @@ public abstract class SpreadingSnowyDirtBlock extends SnowyDirtBlock {
- protected abstract MapCodec<? extends SpreadingSnowyDirtBlock> codec();
-
- private static boolean canPropagate(BlockState state, LevelReader world, BlockPos pos) {
-+ // Paper start - Perf: optimize dirt and snow spreading
-+ return canPropagate(world.getChunk(pos), state, world, pos);
-+ }
-+
-+ private static boolean canPropagate(net.minecraft.world.level.chunk.ChunkAccess chunk, BlockState state, LevelReader world, BlockPos pos) {
-+ // Paper end - Perf: optimize dirt and snow spreading
- BlockPos blockposition1 = pos.above();
-
-- return SpreadingSnowyDirtBlock.canBeGrass(state, world, pos) && !world.getFluidState(blockposition1).is(FluidTags.WATER);
-+ return SpreadingSnowyDirtBlock.canBeGrass(chunk, state, world, pos) && !chunk.getFluidState(blockposition1).is(FluidTags.WATER); // Paper - Perf: optimize dirt and snow spreading
- }
-
- @Override
- protected void randomTick(BlockState state, ServerLevel world, BlockPos pos, RandomSource random) {
- if (this instanceof GrassBlock && world.paperConfig().tickRates.grassSpread != 1 && (world.paperConfig().tickRates.grassSpread < 1 || (net.minecraft.server.MinecraftServer.currentTick + pos.hashCode()) % world.paperConfig().tickRates.grassSpread != 0)) { return; } // Paper - Configurable random tick rates for blocks
-- if (!SpreadingSnowyDirtBlock.canBeGrass(state, world, pos)) {
-+ // Paper start - Perf: optimize dirt and snow spreading
-+ final net.minecraft.world.level.chunk.ChunkAccess cachedBlockChunk = world.getChunkIfLoaded(pos);
-+ if (cachedBlockChunk == null) { // Is this needed?
-+ return;
-+ }
-+ if (!SpreadingSnowyDirtBlock.canBeGrass(cachedBlockChunk, state, world, pos)) {
-+ // Paper end - Perf: optimize dirt and snow spreading
- // CraftBukkit start
- if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockFadeEvent(world, pos, Blocks.DIRT.defaultBlockState()).isCancelled()) {
- return;
-@@ -57,9 +74,19 @@ public abstract class SpreadingSnowyDirtBlock extends SnowyDirtBlock {
-
- for (int i = 0; i < 4; ++i) {
- BlockPos blockposition1 = pos.offset(random.nextInt(3) - 1, random.nextInt(5) - 3, random.nextInt(3) - 1);
--
-- if (world.getBlockState(blockposition1).is(Blocks.DIRT) && SpreadingSnowyDirtBlock.canPropagate(iblockdata1, world, blockposition1)) {
-- org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(world, pos, blockposition1, (BlockState) iblockdata1.setValue(SpreadingSnowyDirtBlock.SNOWY, world.getBlockState(blockposition1.above()).is(Blocks.SNOW))); // CraftBukkit
-+ // Paper start - Perf: optimize dirt and snow spreading
-+ if (pos.getX() == blockposition1.getX() && pos.getY() == blockposition1.getY() && pos.getZ() == blockposition1.getZ()) {
-+ continue;
-+ }
-+ net.minecraft.world.level.chunk.ChunkAccess access;
-+ if (cachedBlockChunk.locX == blockposition1.getX() >> 4 && cachedBlockChunk.locZ == blockposition1.getZ() >> 4) {
-+ access = cachedBlockChunk;
-+ } else {
-+ access = world.getChunkAt(blockposition1);
-+ }
-+ if (access.getBlockState(blockposition1).is(Blocks.DIRT) && SpreadingSnowyDirtBlock.canPropagate(access, iblockdata1, world, blockposition1)) {
-+ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockSpreadEvent(world, pos, blockposition1, (BlockState) iblockdata1.setValue(SpreadingSnowyDirtBlock.SNOWY, access.getBlockState(blockposition1.above()).is(Blocks.SNOW))); // CraftBukkit
-+ // Paper end - Perf: optimize dirt and snow spreading
- }
- }
- }
diff --git a/patches/unapplied/server/1021-Fix-NPE-for-Jukebox-setRecord.patch b/patches/unapplied/server/1021-Fix-NPE-for-Jukebox-setRecord.patch
deleted file mode 100644
index e15a77ee75..0000000000
--- a/patches/unapplied/server/1021-Fix-NPE-for-Jukebox-setRecord.patch
+++ /dev/null
@@ -1,20 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Jake Potrebic <[email protected]>
-Date: Mon, 17 Jun 2024 17:41:09 -0700
-Subject: [PATCH] Fix NPE for Jukebox#setRecord
-
-Fallback to the global registry if no level exists
-
-diff --git a/src/main/java/net/minecraft/world/level/block/entity/JukeboxBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/JukeboxBlockEntity.java
-index 1497e76b548ad76b5aaa297bdd35723e6a8f1f8d..c5069730b25e6f0dfb4e5db3271c91116b485f58 100644
---- a/src/main/java/net/minecraft/world/level/block/entity/JukeboxBlockEntity.java
-+++ b/src/main/java/net/minecraft/world/level/block/entity/JukeboxBlockEntity.java
-@@ -200,7 +200,7 @@ public class JukeboxBlockEntity extends BlockEntity implements Clearable, Contai
- public void setSongItemWithoutPlaying(ItemStack itemstack, long ticksSinceSongStarted) { // CraftBukkit - add argument
- this.item = itemstack;
- this.jukeboxSongPlayer.song = null; // CraftBukkit - reset
-- JukeboxSong.fromStack(this.level.registryAccess(), itemstack).ifPresent((holder) -> {
-+ JukeboxSong.fromStack(this.level != null ? this.level.registryAccess() : org.bukkit.craftbukkit.CraftRegistry.getMinecraftRegistry(), itemstack).ifPresent((holder) -> { // Paper - fallback to other RegistyrAccess if no level
- this.jukeboxSongPlayer.setSongWithoutPlaying(holder, ticksSinceSongStarted); // CraftBukkit - add argument
- });
- // CraftBukkit start - add null check for level