diff options
author | Bjarne Koll <[email protected]> | 2024-10-24 23:03:27 +0200 |
---|---|---|
committer | Bjarne Koll <[email protected]> | 2024-10-24 23:03:27 +0200 |
commit | 7616ebccd84d934180e27ae952ad11149b7fc1e9 (patch) | |
tree | 4fbfc6bb02fa919908beed9d1c82de3db714b15b /patches/unapplied | |
parent | 35e01d7a802b25ee85bb096e427496f3daa15dc0 (diff) | |
download | Paper-7616ebccd84d934180e27ae952ad11149b7fc1e9.tar.gz Paper-7616ebccd84d934180e27ae952ad11149b7fc1e9.zip |
1000
Diffstat (limited to 'patches/unapplied')
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 |