aboutsummaryrefslogtreecommitdiffhomepage
path: root/patches/unapplied/server
diff options
context:
space:
mode:
Diffstat (limited to 'patches/unapplied/server')
-rw-r--r--patches/unapplied/server/0994-Protect-Bedrock-and-End-Portal-Frames-from-being-des.patch169
-rw-r--r--patches/unapplied/server/1010-Fix-tripwire-disarming-not-working-as-intended.patch22
-rw-r--r--patches/unapplied/server/1025-Add-config-for-mobs-immune-to-default-effects.patch44
-rw-r--r--patches/unapplied/server/1026-Deep-clone-nbt-tags-in-PDC.patch45
-rw-r--r--patches/unapplied/server/1027-Support-old-UUID-format-for-NBT.patch63
-rw-r--r--patches/unapplied/server/1028-Fix-shield-disable-inconsistency.patch22
-rw-r--r--patches/unapplied/server/1030-Don-t-lose-removed-data-components-in-ItemMeta.patch47
-rw-r--r--patches/unapplied/server/1031-Add-experimental-improved-give-command.patch255
-rw-r--r--patches/unapplied/server/1032-Handle-Large-Packets-disconnecting-client.patch135
-rw-r--r--patches/unapplied/server/1033-Fix-ItemFlags.patch200
-rw-r--r--patches/unapplied/server/1034-Fix-helmet-damage-reduction-inconsistencies.patch21
-rw-r--r--patches/unapplied/server/1035-Revert-to-vanilla-handling-of-LivingEntity-actuallyH.patch34
-rw-r--r--patches/unapplied/server/1036-improve-checking-handled-tags-in-itemmeta.patch804
-rw-r--r--patches/unapplied/server/1037-General-ItemMeta-fixes.patch1494
-rw-r--r--patches/unapplied/server/1038-Expose-hasColor-to-leather-armor.patch38
-rw-r--r--patches/unapplied/server/1039-Added-API-to-get-player-ha-proxy-address.patch56
-rw-r--r--patches/unapplied/server/1040-More-Chest-Block-API.patch73
-rw-r--r--patches/unapplied/server/1041-Print-data-component-type-on-encoding-error.patch24
-rw-r--r--patches/unapplied/server/1043-Brigadier-based-command-API.patch2831
-rw-r--r--patches/unapplied/server/1044-Fix-issues-with-Recipe-API.patch116
-rw-r--r--patches/unapplied/server/1045-Fix-equipment-slot-and-group-API.patch114
-rw-r--r--patches/unapplied/server/1046-Allow-Bukkit-plugin-to-use-Paper-PluginLoader-API.patch130
-rw-r--r--patches/unapplied/server/1047-Prevent-sending-oversized-item-data-in-equipment-and.patch228
-rw-r--r--patches/unapplied/server/1048-Prevent-NPE-if-hooked-entity-was-cleared.patch24
-rw-r--r--patches/unapplied/server/1049-Fix-cancelling-BlockPlaceEvent-calling-onRemove.patch47
-rw-r--r--patches/unapplied/server/1050-Add-missing-fishing-event-state.patch26
-rw-r--r--patches/unapplied/server/1051-Deprecate-InvAction-HOTBAR_MOVE_AND_READD.patch26
-rw-r--r--patches/unapplied/server/1052-Fix-sending-disconnect-packet-in-phases-where-it-doe.patch21
-rw-r--r--patches/unapplied/server/1053-Adopt-MaterialRerouting.patch134
29 files changed, 0 insertions, 7243 deletions
diff --git a/patches/unapplied/server/0994-Protect-Bedrock-and-End-Portal-Frames-from-being-des.patch b/patches/unapplied/server/0994-Protect-Bedrock-and-End-Portal-Frames-from-being-des.patch
deleted file mode 100644
index 66aba39377..0000000000
--- a/patches/unapplied/server/0994-Protect-Bedrock-and-End-Portal-Frames-from-being-des.patch
+++ /dev/null
@@ -1,169 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Aikar <[email protected]>
-Date: Wed, 13 May 2020 23:01:26 -0400
-Subject: [PATCH] Protect Bedrock and End Portal/Frames from being destroyed
-
-This fixes exploits that let players destroy bedrock by Pistons, explosions
-and Mushrooom/Tree generation.
-
-These blocks are designed to not be broken except by creative players/commands.
-So protect them from a multitude of methods of destroying them.
-
-A config is provided if you rather let players use these exploits, and let
-them destroy the worlds End Portals and get on top of the nether easy.
-
-diff --git a/src/main/java/net/minecraft/world/level/Explosion.java b/src/main/java/net/minecraft/world/level/Explosion.java
-index 4107756fb31e4ad99f643184118d877aef09e9f4..ef40b996864c81d7e8fbb0727ea0a96f866c725f 100644
---- a/src/main/java/net/minecraft/world/level/Explosion.java
-+++ b/src/main/java/net/minecraft/world/level/Explosion.java
-@@ -192,6 +192,7 @@ public class Explosion {
- for (float f1 = 0.3F; f > 0.0F; f -= 0.22500001F) {
- BlockPos blockposition = BlockPos.containing(d4, d5, d6);
- BlockState iblockdata = this.level.getBlockState(blockposition);
-+ if (!iblockdata.isDestroyable()) continue; // Paper - Protect Bedrock and End Portal/Frames from being destroyed
- FluidState fluid = iblockdata.getFluidState(); // Paper - Perf: Optimize call to getFluid for explosions
-
- if (!this.level.isInWorldBounds(blockposition)) {
-diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
-index ee49f59ba8ce5708fc5e244eba7b1f910cf69263..d3d7abb2d31e8ce9f9c53eca66a83a1c28fec792 100644
---- a/src/main/java/net/minecraft/world/level/Level.java
-+++ b/src/main/java/net/minecraft/world/level/Level.java
-@@ -533,6 +533,10 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
- public boolean setBlock(BlockPos pos, BlockState state, int flags, int maxUpdateDepth) {
- // CraftBukkit start - tree generation
- if (this.captureTreeGeneration) {
-+ // Paper start - Protect Bedrock and End Portal/Frames from being destroyed
-+ BlockState type = getBlockState(pos);
-+ if (!type.isDestroyable()) return false;
-+ // Paper end - Protect Bedrock and End Portal/Frames from being destroyed
- CraftBlockState blockstate = this.capturedBlockStates.get(pos);
- if (blockstate == null) {
- blockstate = CapturedBlockState.getTreeBlockState(this, pos, flags);
-diff --git a/src/main/java/net/minecraft/world/level/block/Block.java b/src/main/java/net/minecraft/world/level/block/Block.java
-index 7f0c0ca49e7575c18935b71e3180d112440289f7..054593fc0b8d13f6bf449cc20a1f7ddfd5f1d1f0 100644
---- a/src/main/java/net/minecraft/world/level/block/Block.java
-+++ b/src/main/java/net/minecraft/world/level/block/Block.java
-@@ -90,6 +90,19 @@ public class Block extends BlockBehaviour implements ItemLike {
- protected final StateDefinition<Block, BlockState> stateDefinition;
- private BlockState defaultBlockState;
- // Paper start
-+ public final boolean isDestroyable() {
-+ return io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.allowPermanentBlockBreakExploits ||
-+ this != Blocks.BEDROCK &&
-+ this != Blocks.END_PORTAL_FRAME &&
-+ this != Blocks.END_PORTAL &&
-+ this != Blocks.END_GATEWAY &&
-+ this != Blocks.COMMAND_BLOCK &&
-+ this != Blocks.REPEATING_COMMAND_BLOCK &&
-+ this != Blocks.CHAIN_COMMAND_BLOCK &&
-+ this != Blocks.BARRIER &&
-+ this != Blocks.STRUCTURE_BLOCK &&
-+ this != Blocks.JIGSAW;
-+ }
- public co.aikar.timings.Timing timing;
- public co.aikar.timings.Timing getTiming() {
- if (timing == null) {
-diff --git a/src/main/java/net/minecraft/world/level/block/piston/PistonBaseBlock.java b/src/main/java/net/minecraft/world/level/block/piston/PistonBaseBlock.java
-index e6bfbe2588e0c2a1be14e38d654e889d392ad4db..e0c62227b279a5fe0f3868fbf9ce8c78c515a09c 100644
---- a/src/main/java/net/minecraft/world/level/block/piston/PistonBaseBlock.java
-+++ b/src/main/java/net/minecraft/world/level/block/piston/PistonBaseBlock.java
-@@ -213,6 +213,12 @@ public class PistonBaseBlock extends DirectionalBlock {
- @Override
- protected boolean triggerEvent(BlockState state, Level world, BlockPos pos, int type, int data) {
- Direction enumdirection = (Direction) state.getValue(PistonBaseBlock.FACING);
-+ // Paper start - Protect Bedrock and End Portal/Frames from being destroyed; prevent retracting when we're facing the wrong way (we were replaced before retraction could occur)
-+ Direction directionQueuedAs = Direction.from3DDataValue(data & 7); // Paper - copied from below
-+ if (!io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.allowPermanentBlockBreakExploits && enumdirection != directionQueuedAs) {
-+ return false;
-+ }
-+ // Paper end - Protect Bedrock and End Portal/Frames from being destroyed
- BlockState iblockdata1 = (BlockState) state.setValue(PistonBaseBlock.EXTENDED, true);
-
- if (!world.isClientSide) {
-@@ -253,7 +259,7 @@ public class PistonBaseBlock extends DirectionalBlock {
- }
- // Paper end - Fix sticky pistons and BlockPistonRetractEvent
- world.setBlock(pos, iblockdata2, 20);
-- world.setBlockEntity(MovingPistonBlock.newMovingBlockEntity(pos, iblockdata2, (BlockState) this.defaultBlockState().setValue(PistonBaseBlock.FACING, Direction.from3DDataValue(data & 7)), enumdirection, false, true));
-+ world.setBlockEntity(MovingPistonBlock.newMovingBlockEntity(pos, iblockdata2, (BlockState) this.defaultBlockState().setValue(PistonBaseBlock.FACING, Direction.from3DDataValue(data & 7)), enumdirection, false, true)); // Paper - Protect Bedrock and End Portal/Frames from being destroyed; diff on change
- world.blockUpdated(pos, iblockdata2.getBlock());
- iblockdata2.updateNeighbourShapes(world, pos, 2);
- if (this.isSticky) {
-@@ -289,7 +295,14 @@ public class PistonBaseBlock extends DirectionalBlock {
- }
- }
- } else {
-- world.removeBlock(pos.relative(enumdirection), false);
-+ // Paper start - Protect Bedrock and End Portal/Frames from being destroyed; fix headless pistons breaking blocks
-+ BlockPos headPos = pos.relative(enumdirection);
-+ if (io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.allowPermanentBlockBreakExploits || world.getBlockState(headPos) == Blocks.PISTON_HEAD.defaultBlockState().setValue(FACING, enumdirection)) { // double check to make sure we're not a headless piston.
-+ world.removeBlock(headPos, false);
-+ } else {
-+ ((ServerLevel) world).getChunkSource().blockChanged(headPos); // ... fix client desync
-+ }
-+ // Paper end - Protect Bedrock and End Portal/Frames from being destroyed
- }
-
- world.playSound((Player) null, pos, SoundEvents.PISTON_CONTRACT, SoundSource.BLOCKS, 0.5F, world.random.nextFloat() * 0.15F + 0.6F);
-diff --git a/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java b/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java
-index c7da359c525522b55763e594a1db0c26a026b73f..55efd0d379bac79935f62446cd3479d1e59361a4 100644
---- a/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java
-+++ b/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java
-@@ -174,7 +174,7 @@ public abstract class BlockBehaviour implements FeatureElement {
- }
-
- protected void onExplosionHit(BlockState state, Level world, BlockPos pos, Explosion explosion, BiConsumer<ItemStack, BlockPos> stackMerger) {
-- if (!state.isAir() && explosion.getBlockInteraction() != Explosion.BlockInteraction.TRIGGER_BLOCK) {
-+ if (!state.isAir() && explosion.getBlockInteraction() != Explosion.BlockInteraction.TRIGGER_BLOCK && state.isDestroyable()) { // Paper - Protect Bedrock and End Portal/Frames from being destroyed
- Block block = state.getBlock();
- boolean flag = explosion.getIndirectSourceEntity() instanceof Player;
-
-@@ -254,7 +254,7 @@ public abstract class BlockBehaviour implements FeatureElement {
- }
-
- protected boolean canBeReplaced(BlockState state, BlockPlaceContext context) {
-- return state.canBeReplaced() && (context.getItemInHand().isEmpty() || !context.getItemInHand().is(this.asItem()));
-+ return state.canBeReplaced() && (context.getItemInHand().isEmpty() || !context.getItemInHand().is(this.asItem())) && (state.isDestroyable() || (context.getPlayer() != null && context.getPlayer().getAbilities().instabuild)); // Paper - Protect Bedrock and End Portal/Frames from being destroyed
- }
-
- protected boolean canBeReplaced(BlockState state, Fluid fluid) {
-@@ -896,6 +896,12 @@ public abstract class BlockBehaviour implements FeatureElement {
- return this.legacySolid;
- }
-
-+ // Paper start - Protect Bedrock and End Portal/Frames from being destroyed
-+ public final boolean isDestroyable() {
-+ return getBlock().isDestroyable();
-+ }
-+ // Paper end - Protect Bedrock and End Portal/Frames from being destroyed
-+
- public boolean isValidSpawn(BlockGetter world, BlockPos pos, EntityType<?> type) {
- return this.getBlock().properties.isValidSpawn.test(this.asState(), world, pos, type);
- }
-@@ -999,7 +1005,7 @@ public abstract class BlockBehaviour implements FeatureElement {
- }
-
- public PushReaction getPistonPushReaction() {
-- return this.pushReaction;
-+ return !this.isDestroyable() ? PushReaction.BLOCK : this.pushReaction; // Paper - Protect Bedrock and End Portal/Frames from being destroyed
- }
-
- public boolean isSolidRender(BlockGetter world, BlockPos pos) {
-diff --git a/src/main/java/net/minecraft/world/level/portal/PortalForcer.java b/src/main/java/net/minecraft/world/level/portal/PortalForcer.java
-index 03dd833d61d5152af3032f23dd1fc4c75da9bc4f..a61959700d5e00739a79eaa617ac383160335f26 100644
---- a/src/main/java/net/minecraft/world/level/portal/PortalForcer.java
-+++ b/src/main/java/net/minecraft/world/level/portal/PortalForcer.java
-@@ -221,6 +221,13 @@ public class PortalForcer {
- for (int j = -1; j < 3; ++j) {
- for (int k = -1; k < 4; ++k) {
- temp.setWithOffset(pos, portalDirection.getStepX() * j + enumdirection1.getStepX() * distanceOrthogonalToPortal, k, portalDirection.getStepZ() * j + enumdirection1.getStepZ() * distanceOrthogonalToPortal);
-+ // Paper start - Protect Bedrock and End Portal/Frames from being destroyed
-+ if (!io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.allowPermanentBlockBreakExploits) {
-+ if (!this.level.getBlockState(temp).isDestroyable()) {
-+ return false;
-+ }
-+ }
-+ // Paper end - Protect Bedrock and End Portal/Frames from being destroyed
- if (k < 0 && !this.level.getBlockState(temp).isSolid()) {
- return false;
- }
diff --git a/patches/unapplied/server/1010-Fix-tripwire-disarming-not-working-as-intended.patch b/patches/unapplied/server/1010-Fix-tripwire-disarming-not-working-as-intended.patch
deleted file mode 100644
index fa7f6bde06..0000000000
--- a/patches/unapplied/server/1010-Fix-tripwire-disarming-not-working-as-intended.patch
+++ /dev/null
@@ -1,22 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: DungeonDev <[email protected]>
-Date: Sun, 2 Jul 2023 02:34:54 +0100
-Subject: [PATCH] Fix tripwire disarming not working as intended
-
-Fixes MC-129055
-
-diff --git a/src/main/java/net/minecraft/world/level/block/TripWireHookBlock.java b/src/main/java/net/minecraft/world/level/block/TripWireHookBlock.java
-index 8614fad5b3df7a6030384b108b1689bf6b9f1209..76aca266d3f3222502ff4c196228f08fcd88c5f8 100644
---- a/src/main/java/net/minecraft/world/level/block/TripWireHookBlock.java
-+++ b/src/main/java/net/minecraft/world/level/block/TripWireHookBlock.java
-@@ -202,9 +202,8 @@ public class TripWireHookBlock extends Block {
- BlockState iblockdata4 = aiblockdata[l];
-
- if (iblockdata4 != null) {
-+ if (world.getBlockState(blockposition2).is(Blocks.TRIPWIRE) || io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.allowTripwireDisarmingExploits) { // Paper - Fix tripwire disarming not working as intended
- world.setBlock(blockposition2, (BlockState) iblockdata4.trySetValue(TripWireHookBlock.ATTACHED, flag4), 3);
-- if (!world.getBlockState(blockposition2).isAir()) {
-- ;
- }
- }
- }
diff --git a/patches/unapplied/server/1025-Add-config-for-mobs-immune-to-default-effects.patch b/patches/unapplied/server/1025-Add-config-for-mobs-immune-to-default-effects.patch
deleted file mode 100644
index 7bf145751f..0000000000
--- a/patches/unapplied/server/1025-Add-config-for-mobs-immune-to-default-effects.patch
+++ /dev/null
@@ -1,44 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Jake Potrebic <[email protected]>
-Date: Wed, 2 Dec 2020 21:03:02 -0800
-Subject: [PATCH] Add config for mobs immune to default effects
-
-
-diff --git a/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java b/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java
-index c23d4ee0a16d1ae7168b2496d97189a14256bdcc..7ddca52f7fe3f289b4b867e134326b1ead1a2aee 100644
---- a/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java
-+++ b/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java
-@@ -604,7 +604,7 @@ public class WitherBoss extends Monster implements PowerableMob, RangedAttackMob
-
- @Override
- public boolean canBeAffected(MobEffectInstance effect) {
-- return effect.is(MobEffects.WITHER) ? false : super.canBeAffected(effect);
-+ return effect.is(MobEffects.WITHER) && this.level().paperConfig().entities.mobEffects.immuneToWitherEffect.wither ? false : super.canBeAffected(effect); // Paper - Add config for mobs immune to default effects
- }
-
- private class WitherDoNothingGoal extends Goal {
-diff --git a/src/main/java/net/minecraft/world/entity/monster/Spider.java b/src/main/java/net/minecraft/world/entity/monster/Spider.java
-index ef8911f7bcf6a97496675abb4689bb09cf322e85..fa0316e9d2a4cf213982994dc8bf310299cca984 100644
---- a/src/main/java/net/minecraft/world/entity/monster/Spider.java
-+++ b/src/main/java/net/minecraft/world/entity/monster/Spider.java
-@@ -126,7 +126,7 @@ public class Spider extends Monster {
-
- @Override
- public boolean canBeAffected(MobEffectInstance effect) {
-- return effect.is(MobEffects.POISON) ? false : super.canBeAffected(effect);
-+ return effect.is(MobEffects.POISON) && this.level().paperConfig().entities.mobEffects.spidersImmuneToPoisonEffect ? false : super.canBeAffected(effect); // Paper - Add config for mobs immune to default effects
- }
-
- public boolean isClimbing() {
-diff --git a/src/main/java/net/minecraft/world/entity/monster/WitherSkeleton.java b/src/main/java/net/minecraft/world/entity/monster/WitherSkeleton.java
-index 05e9b083f7e49bd7a24f04fd2c46acef6d011e48..3f1191795e58f31b7e2fe34ef2774df13b9a789f 100644
---- a/src/main/java/net/minecraft/world/entity/monster/WitherSkeleton.java
-+++ b/src/main/java/net/minecraft/world/entity/monster/WitherSkeleton.java
-@@ -113,6 +113,6 @@ public class WitherSkeleton extends AbstractSkeleton {
-
- @Override
- public boolean canBeAffected(MobEffectInstance effect) {
-- return effect.is(MobEffects.WITHER) ? false : super.canBeAffected(effect);
-+ return effect.is(MobEffects.WITHER) && this.level().paperConfig().entities.mobEffects.immuneToWitherEffect.witherSkeleton ? false : super.canBeAffected(effect); // Paper - Add config for mobs immune to default effects
- }
- }
diff --git a/patches/unapplied/server/1026-Deep-clone-nbt-tags-in-PDC.patch b/patches/unapplied/server/1026-Deep-clone-nbt-tags-in-PDC.patch
deleted file mode 100644
index 01f03163e4..0000000000
--- a/patches/unapplied/server/1026-Deep-clone-nbt-tags-in-PDC.patch
+++ /dev/null
@@ -1,45 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: SoSeDiK <[email protected]>
-Date: Thu, 26 May 2022 03:30:05 +0300
-Subject: [PATCH] Deep clone nbt tags in PDC
-
-
-diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java
-index e3ac829ae4f2b39c103e5626180ec9220c2b1f33..2b131cc6f511416d4c8964848caff373a9c6325d 100644
---- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java
-+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java
-@@ -307,7 +307,7 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
- this.damage = meta.damage;
- this.maxDamage = meta.maxDamage;
- this.unhandledTags = meta.unhandledTags;
-- this.persistentDataContainer.putAll(meta.persistentDataContainer.getRaw());
-+ this.persistentDataContainer.putAll(meta.persistentDataContainer.getTagsCloned()); // Paper - deep clone NBT tags
-
- this.customTag = meta.customTag;
-
-@@ -1601,7 +1601,7 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
- if (this.customTag != null) {
- clone.customTag = this.customTag.copy();
- }
-- clone.persistentDataContainer = new CraftPersistentDataContainer(this.persistentDataContainer.getRaw(), CraftMetaItem.DATA_TYPE_REGISTRY);
-+ clone.persistentDataContainer = new CraftPersistentDataContainer(this.persistentDataContainer.getTagsCloned(), CraftMetaItem.DATA_TYPE_REGISTRY); // Paper - deep clone NBT tags
- clone.hideFlag = this.hideFlag;
- clone.hideTooltip = this.hideTooltip;
- clone.unbreakable = this.unbreakable;
-diff --git a/src/main/java/org/bukkit/craftbukkit/persistence/CraftPersistentDataContainer.java b/src/main/java/org/bukkit/craftbukkit/persistence/CraftPersistentDataContainer.java
-index 5a4e7e7150b7c137b077e0b393f17ed35b5aec34..f55fdd57ced259ad5a95878840e98ffaa3db2e05 100644
---- a/src/main/java/org/bukkit/craftbukkit/persistence/CraftPersistentDataContainer.java
-+++ b/src/main/java/org/bukkit/craftbukkit/persistence/CraftPersistentDataContainer.java
-@@ -207,4 +207,12 @@ public class CraftPersistentDataContainer implements PersistentDataContainer {
- }
- }
- // Paper end - byte array serialization
-+
-+ // Paper start - deep clone tags
-+ public Map<String, Tag> getTagsCloned() {
-+ final Map<String, Tag> tags = new HashMap<>();
-+ this.customDataTags.forEach((key, tag) -> tags.put(key, tag.copy()));
-+ return tags;
-+ }
-+ // Paper end - deep clone tags
- }
diff --git a/patches/unapplied/server/1027-Support-old-UUID-format-for-NBT.patch b/patches/unapplied/server/1027-Support-old-UUID-format-for-NBT.patch
deleted file mode 100644
index a38c2a7f7b..0000000000
--- a/patches/unapplied/server/1027-Support-old-UUID-format-for-NBT.patch
+++ /dev/null
@@ -1,63 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Aikar <[email protected]>
-Date: Mon, 29 Jun 2020 03:26:17 -0400
-Subject: [PATCH] Support old UUID format for NBT
-
-We have stored UUID in plenty of places that did not get DFU'd
-
-So just look for old format and load it if it exists.
-
-diff --git a/src/main/java/net/minecraft/nbt/CompoundTag.java b/src/main/java/net/minecraft/nbt/CompoundTag.java
-index df246d69591e1a5822a0109c99b0f67996da71fa..4e005b7b062e3231f564d284887ea1c2783a4e7d 100644
---- a/src/main/java/net/minecraft/nbt/CompoundTag.java
-+++ b/src/main/java/net/minecraft/nbt/CompoundTag.java
-@@ -232,6 +232,12 @@ public class CompoundTag implements Tag {
- }
-
- public void putUUID(String key, UUID value) {
-+ // Paper start - Support old UUID format
-+ if (this.contains(key + "Most", net.minecraft.nbt.Tag.TAG_ANY_NUMERIC) && this.contains(key + "Least", net.minecraft.nbt.Tag.TAG_ANY_NUMERIC)) {
-+ this.tags.remove(key + "Most");
-+ this.tags.remove(key + "Least");
-+ }
-+ // Paper end - Support old UUID format
- this.tags.put(key, NbtUtils.createUUID(value));
- }
-
-@@ -240,10 +246,20 @@ public class CompoundTag implements Tag {
- * You must use {@link #hasUUID(String)} before or else it <b>will</b> throw an NPE.
- */
- public UUID getUUID(String key) {
-+ // Paper start - Support old UUID format
-+ if (!contains(key, 11) && this.contains(key + "Most", net.minecraft.nbt.Tag.TAG_ANY_NUMERIC) && this.contains(key + "Least", net.minecraft.nbt.Tag.TAG_ANY_NUMERIC)) {
-+ return new UUID(this.getLong(key + "Most"), this.getLong(key + "Least"));
-+ }
-+ // Paper end - Support old UUID format
- return NbtUtils.loadUUID(this.get(key));
- }
-
- public boolean hasUUID(String key) {
-+ // Paper start - Support old UUID format
-+ if (this.contains(key + "Most", net.minecraft.nbt.Tag.TAG_ANY_NUMERIC) && this.contains(key + "Least", net.minecraft.nbt.Tag.TAG_ANY_NUMERIC)) {
-+ return true;
-+ }
-+ // Paper end - Support old UUID format
- Tag tag = this.get(key);
- return tag != null && tag.getType() == IntArrayTag.TYPE && ((IntArrayTag)tag).getAsIntArray().length == 4;
- }
-diff --git a/src/main/java/net/minecraft/world/item/component/ResolvableProfile.java b/src/main/java/net/minecraft/world/item/component/ResolvableProfile.java
-index 78863e72239a0f3535bc85758479da84d58c11c1..38bbe39a5cd96710b208d70ed78619057bb6e6fa 100644
---- a/src/main/java/net/minecraft/world/item/component/ResolvableProfile.java
-+++ b/src/main/java/net/minecraft/world/item/component/ResolvableProfile.java
-@@ -20,9 +20,10 @@ public record ResolvableProfile(Optional<String> name, Optional<UUID> id, Proper
- instance -> instance.group(
- ExtraCodecs.PLAYER_NAME.optionalFieldOf("name").forGetter(ResolvableProfile::name),
- UUIDUtil.CODEC.optionalFieldOf("id").forGetter(ResolvableProfile::id),
-+ UUIDUtil.STRING_CODEC.lenientOptionalFieldOf("Id").forGetter($ -> Optional.empty()), // Paper
- ExtraCodecs.PROPERTY_MAP.optionalFieldOf("properties", new PropertyMap()).forGetter(ResolvableProfile::properties)
- )
-- .apply(instance, ResolvableProfile::new)
-+ .apply(instance, (s, uuid, uuid2, propertyMap) -> new ResolvableProfile(s, uuid2.or(() -> uuid), propertyMap)) // Paper
- );
- public static final Codec<ResolvableProfile> CODEC = Codec.withAlternative(
- FULL_CODEC, ExtraCodecs.PLAYER_NAME, name -> new ResolvableProfile(Optional.of(name), Optional.empty(), new PropertyMap())
diff --git a/patches/unapplied/server/1028-Fix-shield-disable-inconsistency.patch b/patches/unapplied/server/1028-Fix-shield-disable-inconsistency.patch
deleted file mode 100644
index 7d1573c1b0..0000000000
--- a/patches/unapplied/server/1028-Fix-shield-disable-inconsistency.patch
+++ /dev/null
@@ -1,22 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Jake Potrebic <[email protected]>
-Date: Fri, 26 Apr 2024 19:08:37 -0700
-Subject: [PATCH] Fix shield disable inconsistency
-
-In vanilla, if the damage source is tagged as a projectile,
-it will not disable the shield if the attacker is holding
-an axe item.
-
-diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java
-index 4928dc3c879ddad0fe8c377b1b26e543a1c40cca..6118de380a95b0c927a239ac3e288780f114289e 100644
---- a/src/main/java/net/minecraft/world/entity/LivingEntity.java
-+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java
-@@ -2325,7 +2325,7 @@ public abstract class LivingEntity extends Entity implements Attackable {
- this.hurtCurrentlyUsedShield((float) -event.getDamage(DamageModifier.BLOCKING));
- Entity entity = damagesource.getDirectEntity();
-
-- if (entity instanceof LivingEntity && entity.distanceToSqr(this) <= (200.0D * 200.0D)) { // Paper - Improve boat collision performance
-+ if (!damagesource.is(DamageTypeTags.IS_PROJECTILE) && entity instanceof LivingEntity && entity.distanceToSqr(this) <= (200.0D * 200.0D)) { // Paper - Improve boat collision performance
- this.blockUsingShield((LivingEntity) entity);
- }
- }
diff --git a/patches/unapplied/server/1030-Don-t-lose-removed-data-components-in-ItemMeta.patch b/patches/unapplied/server/1030-Don-t-lose-removed-data-components-in-ItemMeta.patch
deleted file mode 100644
index 6c562f91cf..0000000000
--- a/patches/unapplied/server/1030-Don-t-lose-removed-data-components-in-ItemMeta.patch
+++ /dev/null
@@ -1,47 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Jake Potrebic <[email protected]>
-Date: Fri, 26 Apr 2024 21:33:20 -0700
-Subject: [PATCH] Don't lose removed data components in ItemMeta
-
-
-diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java
-index 2b131cc6f511416d4c8964848caff373a9c6325d..b525bfbab2c4a5ea408981287f477a8b35d699ca 100644
---- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java
-+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java
-@@ -191,6 +191,13 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
- return this;
- }
-
-+ // Paper start - support removing component types
-+ <T> Applicator remove(DataComponentType<T> type) {
-+ this.builder.remove(type);
-+ return this;
-+ }
-+ // Paper end - support removing component types
-+
- DataComponentPatch build() {
- return this.builder.build();
- }
-@@ -410,7 +417,9 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
-
- Set<Map.Entry<DataComponentType<?>, Optional<?>>> keys = tag.entrySet();
- for (Map.Entry<DataComponentType<?>, Optional<?>> key : keys) {
-- if (!CraftMetaItem.getHandledTags().contains(key.getKey())) {
-+ if (key.getValue().isEmpty()) {
-+ this.unhandledTags.remove(key.getKey());
-+ } else if (!CraftMetaItem.getHandledTags().contains(key.getKey())) {
- key.getValue().ifPresentOrElse((value) -> {
- this.unhandledTags.set((DataComponentType) key.getKey(), value);
- }, () -> {
-@@ -809,9 +818,9 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
- }
-
- for (Map.Entry<DataComponentType<?>, Optional<?>> e : this.unhandledTags.build().entrySet()) {
-- e.getValue().ifPresent((value) -> {
-+ e.getValue().ifPresentOrElse((value) -> {
- itemTag.builder.set((DataComponentType) e.getKey(), value);
-- });
-+ }, () -> itemTag.remove(e.getKey()));
- }
-
- CompoundTag customTag = (this.customTag != null) ? this.customTag.copy() : null;
diff --git a/patches/unapplied/server/1031-Add-experimental-improved-give-command.patch b/patches/unapplied/server/1031-Add-experimental-improved-give-command.patch
deleted file mode 100644
index fd226dcb27..0000000000
--- a/patches/unapplied/server/1031-Add-experimental-improved-give-command.patch
+++ /dev/null
@@ -1,255 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Jake Potrebic <[email protected]>
-Date: Fri, 26 Apr 2024 23:15:27 -0700
-Subject: [PATCH] Add experimental improved give command
-
-Supports removing data components from itemstacks
-
-diff --git a/src/main/java/net/minecraft/commands/arguments/item/ItemArgument.java b/src/main/java/net/minecraft/commands/arguments/item/ItemArgument.java
-index d76296c6d53065aecb010d8ea682c9acd7365f17..9314a94764786982eff0974411f8341bb0353ecf 100644
---- a/src/main/java/net/minecraft/commands/arguments/item/ItemArgument.java
-+++ b/src/main/java/net/minecraft/commands/arguments/item/ItemArgument.java
-@@ -16,7 +16,12 @@ public class ItemArgument implements ArgumentType<ItemInput> {
- private final ItemParser parser;
-
- public ItemArgument(CommandBuildContext commandRegistryAccess) {
-- this.parser = new ItemParser(commandRegistryAccess);
-+ // Paper start - support component removals
-+ this(commandRegistryAccess, false);
-+ }
-+ public ItemArgument(CommandBuildContext commandRegistryAccess, boolean allowRemovals) {
-+ this.parser = new ItemParser(commandRegistryAccess, allowRemovals);
-+ // Paper end - support component removals
- }
-
- public static ItemArgument item(CommandBuildContext commandRegistryAccess) {
-@@ -25,7 +30,7 @@ public class ItemArgument implements ArgumentType<ItemInput> {
-
- public ItemInput parse(StringReader stringReader) throws CommandSyntaxException {
- ItemParser.ItemResult itemResult = this.parser.parse(stringReader);
-- return new ItemInput(itemResult.item(), itemResult.components());
-+ return new ItemInput(itemResult.item(), itemResult.components(), itemResult.patch()); // Paper - support component removals
- }
-
- public static <S> ItemInput getItem(CommandContext<S> context, String name) {
-diff --git a/src/main/java/net/minecraft/commands/arguments/item/ItemInput.java b/src/main/java/net/minecraft/commands/arguments/item/ItemInput.java
-index 3d24fbca90bc7d8bdbac1be2176555c15ae75039..94ea5f0b1913ffa03794d231a6768dd786dc9697 100644
---- a/src/main/java/net/minecraft/commands/arguments/item/ItemInput.java
-+++ b/src/main/java/net/minecraft/commands/arguments/item/ItemInput.java
-@@ -25,8 +25,15 @@ public class ItemInput {
- );
- private final Holder<Item> item;
- private final DataComponentMap components;
-+ @javax.annotation.Nullable private final net.minecraft.core.component.DataComponentPatch patch; // Paper
-
- public ItemInput(Holder<Item> item, DataComponentMap components) {
-+ // Paper start
-+ this(item, components, null);
-+ }
-+ public ItemInput(Holder<Item> item, DataComponentMap components, @javax.annotation.Nullable final net.minecraft.core.component.DataComponentPatch patch) {
-+ this.patch = patch;
-+ // Paper end
- this.item = item;
- this.components = components;
- }
-@@ -37,7 +44,13 @@ public class ItemInput {
-
- public ItemStack createItemStack(int amount, boolean checkOverstack) throws CommandSyntaxException {
- ItemStack itemStack = new ItemStack(this.item, amount);
-- itemStack.applyComponents(this.components);
-+ // Paper start - support component removals
-+ if (this.patch != null) {
-+ itemStack.applyComponents(this.patch);
-+ } else {
-+ itemStack.applyComponents(this.components);
-+ }
-+ // Paper end - support component removals
- if (checkOverstack && amount > itemStack.getMaxStackSize()) {
- throw ERROR_STACK_TOO_BIG.create(this.getItemName(), itemStack.getMaxStackSize());
- } else {
-diff --git a/src/main/java/net/minecraft/commands/arguments/item/ItemParser.java b/src/main/java/net/minecraft/commands/arguments/item/ItemParser.java
-index 5347a96be3bfbbd2963747ba4b5f222215d80371..fa431de18de902c580855e9c4419125519b6176b 100644
---- a/src/main/java/net/minecraft/commands/arguments/item/ItemParser.java
-+++ b/src/main/java/net/minecraft/commands/arguments/item/ItemParser.java
-@@ -59,8 +59,15 @@ public class ItemParser {
- static final Function<SuggestionsBuilder, CompletableFuture<Suggestions>> SUGGEST_NOTHING = SuggestionsBuilder::buildFuture;
- final HolderLookup.RegistryLookup<Item> items;
- final DynamicOps<Tag> registryOps;
-+ final boolean allowRemoves; // Paper - support component removals
-
- public ItemParser(HolderLookup.Provider registriesLookup) {
-+ // Paper start - support component removals
-+ this(registriesLookup, false);
-+ }
-+ public ItemParser(HolderLookup.Provider registriesLookup, boolean allowRemoves) {
-+ this.allowRemoves = allowRemoves;
-+ // Paper end - support component removals
- this.items = registriesLookup.lookupOrThrow(Registries.ITEM);
- this.registryOps = registriesLookup.createSerializationContext(NbtOps.INSTANCE);
- }
-@@ -68,6 +75,7 @@ public class ItemParser {
- public ItemParser.ItemResult parse(StringReader reader) throws CommandSyntaxException {
- final MutableObject<Holder<Item>> mutableObject = new MutableObject<>();
- final DataComponentMap.Builder builder = DataComponentMap.builder();
-+ final net.minecraft.core.component.DataComponentPatch.Builder patchBuilder = net.minecraft.core.component.DataComponentPatch.builder(); // Paper - support component removals
- this.parse(reader, new ItemParser.Visitor() {
- @Override
- public void visitItem(Holder<Item> item) {
-@@ -77,12 +85,19 @@ public class ItemParser {
- @Override
- public <T> void visitComponent(DataComponentType<T> type, T value) {
- builder.set(type, value);
-+ // Paper start - support component removals
-+ patchBuilder.set(type, value);
-+ }
-+ @Override
-+ public <T> void visitComponentRemove(final DataComponentType<T> type) {
-+ patchBuilder.remove(type);
-+ // Paper end - support component removals
- }
- });
- Holder<Item> holder = Objects.requireNonNull(mutableObject.getValue(), "Parser gave no item");
- DataComponentMap dataComponentMap = builder.build();
- validateComponents(reader, holder, dataComponentMap);
-- return new ItemParser.ItemResult(holder, dataComponentMap);
-+ return new ItemParser.ItemResult(holder, dataComponentMap, this.allowRemoves ? patchBuilder.build() : null); // Paper - support component removals
- }
-
- private static void validateComponents(StringReader reader, Holder<Item> item, DataComponentMap components) throws CommandSyntaxException {
-@@ -116,7 +131,7 @@ public class ItemParser {
- return suggestionsVisitor.resolveSuggestions(builder, stringReader);
- }
-
-- public static record ItemResult(Holder<Item> item, DataComponentMap components) {
-+ public static record ItemResult(Holder<Item> item, DataComponentMap components, @javax.annotation.Nullable net.minecraft.core.component.DataComponentPatch patch) { // Paper
- }
-
- class State {
-@@ -154,17 +169,28 @@ public class ItemParser {
-
- while (this.reader.canRead() && this.reader.peek() != ']') {
- this.reader.skipWhitespace();
-+ boolean removing = ItemParser.this.allowRemoves && this.reader.canRead() && this.reader.peek() == '!';
-+ if (removing) {
-+ this.reader.skip();
-+ this.visitor.visitSuggestions(builder -> this.suggestComponentAssignment(builder, false));
-+ }
- DataComponentType<?> dataComponentType = readComponentType(this.reader);
- if (!set.add(dataComponentType)) {
- throw ItemParser.ERROR_REPEATED_COMPONENT.create(dataComponentType);
- }
-
-+ // Paper start - support component removals
-+ if (removing) {
-+ this.visitor.visitComponentRemove(dataComponentType);
-+ } else {
-+ // Paper end - support component removals
- this.visitor.visitSuggestions(this::suggestAssignment);
- this.reader.skipWhitespace();
- this.reader.expect('=');
- this.visitor.visitSuggestions(ItemParser.SUGGEST_NOTHING);
- this.reader.skipWhitespace();
- this.readComponent(dataComponentType);
-+ } // Paper - support component removals
- this.reader.skipWhitespace();
- this.visitor.visitSuggestions(this::suggestNextOrEndComponents);
- if (!this.reader.canRead() || this.reader.peek() != ',') {
-@@ -239,12 +265,18 @@ public class ItemParser {
- }
-
- private CompletableFuture<Suggestions> suggestComponentAssignment(SuggestionsBuilder builder) {
-+ // Paper start - support component removals
-+ return this.suggestComponentAssignment(builder, true);
-+ }
-+ private CompletableFuture<Suggestions> suggestComponentAssignment(SuggestionsBuilder builder, boolean suggestRemove) {
- String string = builder.getRemaining().toLowerCase(Locale.ROOT);
-+ if (suggestRemove && string.isBlank()) builder.suggest("!", Component.literal("Remove a data component"));
-+ // Paper end - support component removals
- SharedSuggestionProvider.filterResources(BuiltInRegistries.DATA_COMPONENT_TYPE.entrySet(), string, entry -> entry.getKey().location(), entry -> {
- DataComponentType<?> dataComponentType = entry.getValue();
- if (dataComponentType.codec() != null) {
- ResourceLocation resourceLocation = entry.getKey().location();
-- builder.suggest(resourceLocation.toString() + "=");
-+ builder.suggest(resourceLocation.toString() + (suggestRemove ? "=" : "")); // Paper - support component removals
- }
- });
- return builder.buildFuture();
-@@ -270,6 +302,7 @@ public class ItemParser {
-
- default <T> void visitComponent(DataComponentType<T> type, T value) {
- }
-+ default <T> void visitComponentRemove(DataComponentType<T> type) {} // Paper
-
- default void visitSuggestions(Function<SuggestionsBuilder, CompletableFuture<Suggestions>> suggestor) {
- }
-diff --git a/src/main/java/net/minecraft/server/commands/GiveCommand.java b/src/main/java/net/minecraft/server/commands/GiveCommand.java
-index 0d9de4c61c7b26a6ff37c12fde629161fd0c3d5a..47355158e5e762540a10dc67b23092a0fc53bce3 100644
---- a/src/main/java/net/minecraft/server/commands/GiveCommand.java
-+++ b/src/main/java/net/minecraft/server/commands/GiveCommand.java
-@@ -34,6 +34,38 @@ public class GiveCommand {
- })).then(net.minecraft.commands.Commands.argument("count", IntegerArgumentType.integer(1)).executes((commandcontext) -> {
- return GiveCommand.giveItem((CommandSourceStack) commandcontext.getSource(), ItemArgument.getItem(commandcontext, "item"), EntityArgument.getPlayers(commandcontext, "targets"), IntegerArgumentType.getInteger(commandcontext, "count"));
- })))));
-+ // Paper start - support component removals with a custom pgive command
-+ final com.mojang.brigadier.tree.CommandNode<net.minecraft.commands.CommandSourceStack> node = net.minecraft.commands.Commands
-+ .literal("pgive").requires((css) -> css.hasPermission(2))
-+ .then(net.minecraft.commands.Commands.argument("targets", EntityArgument.players())
-+ .then(net.minecraft.commands.Commands.argument("item", new ItemArgument(commandRegistryAccess, true)).executes((ctx) -> {
-+ return GiveCommand.giveItem(ctx.getSource(), ItemArgument.getItem(ctx, "item"), EntityArgument.getPlayers(ctx, "targets"), 1);
-+ })
-+ .then(net.minecraft.commands.Commands.argument("count", IntegerArgumentType.integer(1)).executes((ctx) -> {
-+ return GiveCommand.giveItem(ctx.getSource(), ItemArgument.getItem(ctx, "item"), EntityArgument.getPlayers(ctx, "targets"), IntegerArgumentType.getInteger(ctx, "count"));
-+ }))
-+ )
-+ ).build();
-+ setClientNodes(node);
-+ dispatcher.getRoot().addChild(node);
-+ }
-+ static void setClientNodes(com.mojang.brigadier.tree.CommandNode<net.minecraft.commands.CommandSourceStack> node) {
-+ if (node instanceof com.mojang.brigadier.tree.ArgumentCommandNode<net.minecraft.commands.CommandSourceStack,?> argumentNode) {
-+ if (argumentNode.getType() instanceof ItemArgument) {
-+ node.clientNode = new com.mojang.brigadier.tree.ArgumentCommandNode<>(
-+ argumentNode.getName(),
-+ com.mojang.brigadier.arguments.StringArgumentType.greedyString(),
-+ argumentNode.getCommand(),
-+ argumentNode.getRequirement(),
-+ argumentNode.getRedirect(),
-+ argumentNode.getRedirectModifier(),
-+ argumentNode.isFork(),
-+ (ctx, builder) -> builder.buildFuture()
-+ );
-+ }
-+ }
-+ node.getChildren().forEach(GiveCommand::setClientNodes);
-+ // Paper end - support component removals with a custom pgive command
- }
-
- private static int giveItem(CommandSourceStack source, ItemInput item, Collection<ServerPlayer> targets, int count) throws CommandSyntaxException {
-diff --git a/src/main/java/org/bukkit/craftbukkit/command/VanillaCommandWrapper.java b/src/main/java/org/bukkit/craftbukkit/command/VanillaCommandWrapper.java
-index 2ee33c55890fa659f6d251e486264c85d9e89802..dd1507f65a7f1d84bc7f236f81a60ac1302a13b8 100644
---- a/src/main/java/org/bukkit/craftbukkit/command/VanillaCommandWrapper.java
-+++ b/src/main/java/org/bukkit/craftbukkit/command/VanillaCommandWrapper.java
-@@ -96,6 +96,9 @@ public final class VanillaCommandWrapper extends BukkitCommand {
- vanillaCommand = vanillaCommand.getRedirect();
- }
- final String commandName = vanillaCommand.getName();
-+ if ("pgive".equals(stripDefaultNamespace(commandName))) {
-+ return "bukkit.command.paper.pgive";
-+ }
- return "minecraft.command." + stripDefaultNamespace(commandName);
- }
-
-diff --git a/src/test/java/io/papermc/paper/permissions/MinecraftCommandPermissionsTest.java b/src/test/java/io/papermc/paper/permissions/MinecraftCommandPermissionsTest.java
-index ca71c688b37ce2c8b712a4f9216cf872c8edf78e..2f3ff50bf3f70b6b404d02d5ffcc079162a63bc1 100644
---- a/src/test/java/io/papermc/paper/permissions/MinecraftCommandPermissionsTest.java
-+++ b/src/test/java/io/papermc/paper/permissions/MinecraftCommandPermissionsTest.java
-@@ -45,6 +45,9 @@ public class MinecraftCommandPermissionsTest extends AbstractTestingBase {
- Set<String> foundPerms = new HashSet<>();
- for (CommandNode<CommandSourceStack> child : root.getChildren()) {
- final String vanillaPerm = VanillaCommandWrapper.getPermission(child);
-+ if ("bukkit.command.paper.pgive".equals(vanillaPerm)) { // skip our custom give command
-+ continue;
-+ }
- if (!perms.contains(vanillaPerm)) {
- missing.add("Missing permission for " + child.getName() + " (" + vanillaPerm + ") command");
- } else {
diff --git a/patches/unapplied/server/1032-Handle-Large-Packets-disconnecting-client.patch b/patches/unapplied/server/1032-Handle-Large-Packets-disconnecting-client.patch
deleted file mode 100644
index 49f2553189..0000000000
--- a/patches/unapplied/server/1032-Handle-Large-Packets-disconnecting-client.patch
+++ /dev/null
@@ -1,135 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Aikar <[email protected]>
-Date: Tue, 27 Nov 2018 21:18:06 -0500
-Subject: [PATCH] Handle Large Packets disconnecting client
-
-If a players inventory is too big to send in a single packet,
-split the inventory set into multiple packets instead.
-
-diff --git a/src/main/java/net/minecraft/network/Connection.java b/src/main/java/net/minecraft/network/Connection.java
-index 7fb162fa031fd76aa9a94f5fdaa3e32ceb9b9abc..58d28b6c1cc7da7d786f78308db971f7502ad844 100644
---- a/src/main/java/net/minecraft/network/Connection.java
-+++ b/src/main/java/net/minecraft/network/Connection.java
-@@ -180,6 +180,21 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
- }
-
- public void exceptionCaught(ChannelHandlerContext channelhandlercontext, Throwable throwable) {
-+ // Paper start - Handle large packets disconnecting client
-+ if (throwable instanceof io.netty.handler.codec.EncoderException && throwable.getCause() instanceof PacketEncoder.PacketTooLargeException packetTooLargeException) {
-+ final Packet<?> packet = packetTooLargeException.getPacket();
-+ if (packet.packetTooLarge(this)) {
-+ ProtocolSwapHandler.handleOutboundTerminalPacket(channelhandlercontext, packet);
-+ return;
-+ } else if (packet.isSkippable()) {
-+ Connection.LOGGER.debug("Skipping packet due to errors", throwable.getCause());
-+ ProtocolSwapHandler.handleOutboundTerminalPacket(channelhandlercontext, packet);
-+ return;
-+ } else {
-+ throwable = throwable.getCause();
-+ }
-+ }
-+ // Paper end - Handle large packets disconnecting client
- if (throwable instanceof SkipPacketException) {
- Connection.LOGGER.debug("Skipping packet due to errors", throwable.getCause());
- } else {
-diff --git a/src/main/java/net/minecraft/network/PacketEncoder.java b/src/main/java/net/minecraft/network/PacketEncoder.java
-index 046bfc212b640de174b300e7a05cc30bb3cac93e..af3ec112e142a2c91c46882dad6180b18f39eec2 100644
---- a/src/main/java/net/minecraft/network/PacketEncoder.java
-+++ b/src/main/java/net/minecraft/network/PacketEncoder.java
-@@ -40,7 +40,33 @@ public class PacketEncoder<T extends PacketListener> extends MessageToByteEncode
-
- throw var9;
- } finally {
-+ // Paper start - Handle large packets disconnecting client
-+ int packetLength = byteBuf.readableBytes();
-+ if (packetLength > MAX_PACKET_SIZE || (packetLength > MAX_FINAL_PACKET_SIZE && packet.hasLargePacketFallback())) {
-+ throw new PacketTooLargeException(packet, packetLength);
-+ }
-+ // Paper end - Handle large packets disconnecting client
- ProtocolSwapHandler.handleOutboundTerminalPacket(channelHandlerContext, packet);
- }
- }
-+
-+ // Paper start
-+ // packet size is encoded into 3-byte varint
-+ private static final int MAX_FINAL_PACKET_SIZE = (1 << 21) - 1;
-+ // Vanilla Max size for the encoder (before compression)
-+ private static final int MAX_PACKET_SIZE = 8388608;
-+
-+ public static class PacketTooLargeException extends RuntimeException {
-+ private final Packet<?> packet;
-+
-+ PacketTooLargeException(Packet<?> packet, int packetLength) {
-+ super("PacketTooLarge - " + packet.getClass().getSimpleName() + " is " + packetLength + ". Max is " + MAX_PACKET_SIZE);
-+ this.packet = packet;
-+ }
-+
-+ public Packet<?> getPacket() {
-+ return this.packet;
-+ }
-+ }
-+ // Paper end
- }
-diff --git a/src/main/java/net/minecraft/network/protocol/Packet.java b/src/main/java/net/minecraft/network/protocol/Packet.java
-index 5ee2ba1225fb7e4f02152b45adeb66f79ed1650d..c9d283b7fc9ede79dc6cbc39dfc9e7ae986a6a47 100644
---- a/src/main/java/net/minecraft/network/protocol/Packet.java
-+++ b/src/main/java/net/minecraft/network/protocol/Packet.java
-@@ -11,6 +11,19 @@ public interface Packet<T extends PacketListener> {
-
- void handle(T listener);
-
-+ // Paper start
-+ default boolean hasLargePacketFallback() {
-+ return false;
-+ }
-+
-+ /**
-+ * override {@link #hasLargePacketFallback()} to return true when overriding in subclasses
-+ */
-+ default boolean packetTooLarge(net.minecraft.network.Connection manager) {
-+ return false;
-+ }
-+ // Paper end
-+
- default boolean isSkippable() {
- return false;
- }
-diff --git a/src/main/java/net/minecraft/network/protocol/game/ClientboundContainerSetContentPacket.java b/src/main/java/net/minecraft/network/protocol/game/ClientboundContainerSetContentPacket.java
-index 7e555ece0555b3d2a983ab2c39c5e7ec23fc7e88..8cca2ac616a2c80268c96b9f95e33f834a0fc8fd 100644
---- a/src/main/java/net/minecraft/network/protocol/game/ClientboundContainerSetContentPacket.java
-+++ b/src/main/java/net/minecraft/network/protocol/game/ClientboundContainerSetContentPacket.java
-@@ -36,6 +36,21 @@ public class ClientboundContainerSetContentPacket implements Packet<ClientGamePa
- this.carriedItem = ItemStack.OPTIONAL_STREAM_CODEC.decode(buf);
- }
-
-+ // Paper start - Handle large packets disconnecting client
-+ @Override
-+ public boolean hasLargePacketFallback() {
-+ return true;
-+ }
-+
-+ @Override
-+ public boolean packetTooLarge(net.minecraft.network.Connection manager) {
-+ for (int i = 0 ; i < this.items.size() ; i++) {
-+ manager.send(new ClientboundContainerSetSlotPacket(this.containerId, this.stateId, i, this.items.get(i)));
-+ }
-+ return true;
-+ }
-+ // Paper end - Handle large packets disconnecting client
-+
- private void write(RegistryFriendlyByteBuf buf) {
- buf.writeByte(this.containerId);
- buf.writeVarInt(this.stateId);
-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 2501fbaf497d226051800c53d60a39bbc80db91c..0a8d07bf68b0ceabd13c70196d357fce79dcc2c3 100644
---- a/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java
-+++ b/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java
-@@ -80,7 +80,7 @@ public class ClientboundLevelChunkPacketData {
- throw new RuntimeException("Can't read heightmap in packet for [" + x + ", " + z + "]");
- } else {
- int i = buf.readVarInt();
-- if (i > 2097152) {
-+ if (i > 2097152) { // Paper - diff on change - if this changes, update PacketEncoder
- throw new RuntimeException("Chunk Packet trying to allocate too much memory on read.");
- } else {
- this.buffer = new byte[i];
diff --git a/patches/unapplied/server/1033-Fix-ItemFlags.patch b/patches/unapplied/server/1033-Fix-ItemFlags.patch
deleted file mode 100644
index 24f5e95581..0000000000
--- a/patches/unapplied/server/1033-Fix-ItemFlags.patch
+++ /dev/null
@@ -1,200 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Jake Potrebic <[email protected]>
-Date: Sat, 27 Apr 2024 12:16:38 -0700
-Subject: [PATCH] Fix ItemFlags
-
-Re-adds missing functionality for HIDE_DESTROYS and
-HIDE_PLACED_ON. Also adds new flag in HIDE_STORED_ENCHANTS
-which was split from HIDE_ADDITIONAL_TOOLTIP.
-
-== AT ==
-public net.minecraft.world.item.AdventureModePredicate predicates
-
-diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaEnchantedBook.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaEnchantedBook.java
-index fca0cfba14dd2cc6f24b56eaf269594b2d87fd04..8734f0b777432cd8639094b75a3da1b9595823ed 100644
---- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaEnchantedBook.java
-+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaEnchantedBook.java
-@@ -39,7 +39,7 @@ class CraftMetaEnchantedBook extends CraftMetaItem implements EnchantmentStorage
- getOrEmpty(tag, CraftMetaEnchantedBook.STORED_ENCHANTMENTS).ifPresent((itemEnchantments) -> {
- this.enchantments = buildEnchantments(itemEnchantments);
- if (!itemEnchantments.showInTooltip) {
-- this.addItemFlags(ItemFlag.HIDE_ADDITIONAL_TOOLTIP);
-+ this.addItemFlags(ItemFlag.HIDE_STORED_ENCHANTS); // Paper - new ItemFlag
- }
- });
- }
-@@ -54,7 +54,7 @@ class CraftMetaEnchantedBook extends CraftMetaItem implements EnchantmentStorage
- void applyToItem(CraftMetaItem.Applicator itemTag) {
- super.applyToItem(itemTag);
-
-- this.applyEnchantments(this.enchantments, itemTag, CraftMetaEnchantedBook.STORED_ENCHANTMENTS, ItemFlag.HIDE_ADDITIONAL_TOOLTIP);
-+ this.applyEnchantments(this.enchantments, itemTag, CraftMetaEnchantedBook.STORED_ENCHANTMENTS, ItemFlag.HIDE_STORED_ENCHANTS);
- }
-
- @Override
-diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java
-index b525bfbab2c4a5ea408981287f477a8b35d699ca..43a4a76d3829fb2ed7b5635d804fd826484c16db 100644
---- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java
-+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java
-@@ -243,6 +243,12 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
- static final ItemMetaKeyType<Unit> HIDE_ADDITIONAL_TOOLTIP = new ItemMetaKeyType(DataComponents.HIDE_ADDITIONAL_TOOLTIP);
- @Specific(Specific.To.NBT)
- static final ItemMetaKeyType<CustomData> CUSTOM_DATA = new ItemMetaKeyType<>(DataComponents.CUSTOM_DATA);
-+ // Paper start - fix ItemFlags
-+ static final ItemMetaKeyType<net.minecraft.world.item.AdventureModePredicate> CAN_PLACE_ON = new ItemMetaKeyType<>(DataComponents.CAN_PLACE_ON);
-+ static final ItemMetaKeyType<net.minecraft.world.item.AdventureModePredicate> CAN_BREAK = new ItemMetaKeyType<>(DataComponents.CAN_BREAK);
-+ private List<net.minecraft.advancements.critereon.BlockPredicate> canPlaceOnPredicates;
-+ private List<net.minecraft.advancements.critereon.BlockPredicate> canBreakPredicates;
-+ // Paper end - fix ItemFlags
-
- // We store the raw original JSON representation of all text data. See SPIGOT-5063, SPIGOT-5656, SPIGOT-5304
- private Component displayName;
-@@ -319,6 +325,10 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
- this.customTag = meta.customTag;
-
- this.version = meta.version;
-+ // Paper start
-+ this.canPlaceOnPredicates = meta.canPlaceOnPredicates;
-+ this.canBreakPredicates = meta.canBreakPredicates;
-+ // Paper end
- }
-
- CraftMetaItem(DataComponentPatch tag) {
-@@ -414,6 +424,20 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
- this.customTag = null;
- }
- });
-+ // Paper start - fix ItemFlags
-+ CraftMetaItem.getOrEmpty(tag, CraftMetaItem.CAN_PLACE_ON).ifPresent(data -> {
-+ this.canPlaceOnPredicates = List.copyOf(data.predicates);
-+ if (!data.showInTooltip()) {
-+ this.addItemFlags(ItemFlag.HIDE_PLACED_ON);
-+ }
-+ });
-+ CraftMetaItem.getOrEmpty(tag, CraftMetaItem.CAN_BREAK).ifPresent(data -> {
-+ this.canBreakPredicates = List.copyOf(data.predicates);
-+ if (!data.showInTooltip()) {
-+ this.addItemFlags(ItemFlag.HIDE_DESTROYS);
-+ }
-+ });
-+ // Paper end - fix ItemFlags
-
- Set<Map.Entry<DataComponentType<?>, Optional<?>>> keys = tag.entrySet();
- for (Map.Entry<DataComponentType<?>, Optional<?>> key : keys) {
-@@ -603,7 +627,16 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
- ByteArrayInputStream buf = new ByteArrayInputStream(Base64.getDecoder().decode(unhandled));
- try {
- CompoundTag unhandledTag = NbtIo.readCompressed(buf, NbtAccounter.unlimitedHeap());
-- this.unhandledTags.copy(DataComponentPatch.CODEC.parse(MinecraftServer.getDefaultRegistryAccess().createSerializationContext(NbtOps.INSTANCE), unhandledTag).result().get());
-+ // Paper start
-+ final net.minecraft.core.component.DataComponentPatch patch = net.minecraft.core.component.DataComponentPatch.CODEC.parse(net.minecraft.server.MinecraftServer.getDefaultRegistryAccess().createSerializationContext(net.minecraft.nbt.NbtOps.INSTANCE), unhandledTag).result().get();
-+ CraftMetaItem.getOrEmpty(patch, CraftMetaItem.CAN_PLACE_ON).ifPresent(data -> {
-+ this.canPlaceOnPredicates = List.copyOf(data.predicates);
-+ });
-+ CraftMetaItem.getOrEmpty(patch, CraftMetaItem.CAN_BREAK).ifPresent(data -> {
-+ this.canBreakPredicates = List.copyOf(data.predicates);
-+ });
-+ this.unhandledTags.copy(patch.forget(type -> type == CraftMetaItem.CAN_PLACE_ON.TYPE || type == CraftMetaItem.CAN_BREAK.TYPE));
-+ // Paper end
- } catch (IOException ex) {
- Logger.getLogger(CraftMetaItem.class.getName()).log(Level.SEVERE, null, ex);
- }
-@@ -817,6 +850,15 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
- itemTag.put(CraftMetaItem.MAX_DAMAGE, this.maxDamage);
- }
-
-+ // Paper start
-+ if (this.canPlaceOnPredicates != null && !this.canPlaceOnPredicates.isEmpty()) {
-+ itemTag.put(CraftMetaItem.CAN_PLACE_ON, new net.minecraft.world.item.AdventureModePredicate(this.canPlaceOnPredicates, !this.hasItemFlag(ItemFlag.HIDE_PLACED_ON)));
-+ }
-+ if (this.canBreakPredicates != null && !this.canBreakPredicates.isEmpty()) {
-+ itemTag.put(CraftMetaItem.CAN_BREAK, new net.minecraft.world.item.AdventureModePredicate(this.canBreakPredicates, !this.hasItemFlag(ItemFlag.HIDE_DESTROYS)));
-+ }
-+ // Paper end
-+
- for (Map.Entry<DataComponentType<?>, Optional<?>> e : this.unhandledTags.build().entrySet()) {
- e.getValue().ifPresentOrElse((value) -> {
- itemTag.builder.set((DataComponentType) e.getKey(), value);
-@@ -891,7 +933,7 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
-
- @Overridden
- boolean isEmpty() {
-- return !(this.hasDisplayName() || this.hasItemName() || this.hasLocalizedName() || this.hasEnchants() || (this.lore != null) || this.hasCustomModelData() || this.hasBlockData() || this.hasRepairCost() || !this.unhandledTags.build().isEmpty() || !this.persistentDataContainer.isEmpty() || this.hideFlag != 0 || this.isHideTooltip() || this.isUnbreakable() || this.hasEnchantmentGlintOverride() || this.isFireResistant() || this.hasMaxStackSize() || this.hasRarity() || this.hasFood() || this.hasTool() || this.hasDamage() || this.hasMaxDamage() || this.hasAttributeModifiers() || this.customTag != null);
-+ return !(this.hasDisplayName() || this.hasItemName() || this.hasLocalizedName() || this.hasEnchants() || (this.lore != null) || this.hasCustomModelData() || this.hasBlockData() || this.hasRepairCost() || !this.unhandledTags.build().isEmpty() || !this.persistentDataContainer.isEmpty() || this.hideFlag != 0 || this.isHideTooltip() || this.isUnbreakable() || this.hasEnchantmentGlintOverride() || this.isFireResistant() || this.hasMaxStackSize() || this.hasRarity() || this.hasFood() || this.hasTool() || this.hasDamage() || this.hasMaxDamage() || this.hasAttributeModifiers() || this.customTag != null || this.canPlaceOnPredicates != null || this.canBreakPredicates != null); // Paper
- }
-
- // Paper start
-@@ -1544,6 +1586,8 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
- && (this.hasTool() ? that.hasTool() && this.tool.equals(that.tool) : !that.hasTool())
- && (this.hasDamage() ? that.hasDamage() && this.damage == that.damage : !that.hasDamage())
- && (this.hasMaxDamage() ? that.hasMaxDamage() && this.maxDamage.equals(that.maxDamage) : !that.hasMaxDamage())
-+ && (this.canPlaceOnPredicates != null ? that.canPlaceOnPredicates != null && this.canPlaceOnPredicates.equals(that.canPlaceOnPredicates) : that.canPlaceOnPredicates == null) // Paper
-+ && (this.canBreakPredicates != null ? that.canBreakPredicates != null && this.canBreakPredicates.equals(that.canBreakPredicates) : that.canBreakPredicates == null) // Paper
- && (this.version == that.version);
- }
-
-@@ -1587,6 +1631,8 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
- hash = 61 * hash + (this.hasDamage() ? this.damage : 0);
- hash = 61 * hash + (this.hasMaxDamage() ? 1231 : 1237);
- hash = 61 * hash + (this.hasAttributeModifiers() ? this.attributeModifiers.hashCode() : 0);
-+ hash = 61 * hash + (this.canPlaceOnPredicates != null ? this.canPlaceOnPredicates.hashCode() : 0); // Paper
-+ hash = 61 * hash + (this.canBreakPredicates != null ? this.canBreakPredicates.hashCode() : 0); // Paper
- hash = 61 * hash + this.version;
- return hash;
- }
-@@ -1627,6 +1673,14 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
- clone.damage = this.damage;
- clone.maxDamage = this.maxDamage;
- clone.version = this.version;
-+ // Paper start
-+ if (this.canPlaceOnPredicates != null) {
-+ clone.canPlaceOnPredicates = List.copyOf(this.canPlaceOnPredicates);
-+ }
-+ if (this.canBreakPredicates != null) {
-+ clone.canBreakPredicates = List.copyOf(this.canBreakPredicates);
-+ }
-+ // Paper end
- return clone;
- } catch (CloneNotSupportedException e) {
- throw new Error(e);
-@@ -1740,6 +1794,16 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
- }
- }
-
-+ // Paper start
-+ final boolean canBreakAddToUnhandled = this.canBreakPredicates != null && !this.canBreakPredicates.isEmpty();
-+ if (canBreakAddToUnhandled) {
-+ this.unhandledTags.set(DataComponents.CAN_BREAK, new net.minecraft.world.item.AdventureModePredicate(this.canBreakPredicates, !this.hasItemFlag(ItemFlag.HIDE_DESTROYS)));
-+ }
-+ final boolean canPlaceOnAddToUnhandled = this.canPlaceOnPredicates != null && !this.canPlaceOnPredicates.isEmpty();
-+ if (canPlaceOnAddToUnhandled) {
-+ this.unhandledTags.set(DataComponents.CAN_PLACE_ON, new net.minecraft.world.item.AdventureModePredicate(this.canPlaceOnPredicates, !this.hasItemFlag(ItemFlag.HIDE_PLACED_ON)));
-+ }
-+ // Paper end
- if (!this.unhandledTags.isEmpty()) {
- Tag unhandled = DataComponentPatch.CODEC.encodeStart(MinecraftServer.getDefaultRegistryAccess().createSerializationContext(NbtOps.INSTANCE), this.unhandledTags.build()).getOrThrow(IllegalStateException::new);
- try {
-@@ -1750,6 +1814,14 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
- Logger.getLogger(CraftMetaItem.class.getName()).log(Level.SEVERE, null, ex);
- }
- }
-+ // Paper start
-+ if (canBreakAddToUnhandled) {
-+ this.unhandledTags.clear(DataComponents.CAN_BREAK);
-+ }
-+ if (canPlaceOnAddToUnhandled) {
-+ this.unhandledTags.clear(DataComponents.CAN_PLACE_ON);
-+ }
-+ // Paper end
-
- if (!this.persistentDataContainer.isEmpty()) { // Store custom tags, wrapped in their compound
- builder.put(CraftMetaItem.BUKKIT_CUSTOM_TAG.BUKKIT, this.persistentDataContainer.serialize());
-@@ -1888,6 +1960,8 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
- CraftMetaItem.MAX_DAMAGE.TYPE,
- CraftMetaItem.CUSTOM_DATA.TYPE,
- CraftMetaItem.ATTRIBUTES.TYPE,
-+ CraftMetaItem.CAN_PLACE_ON.TYPE, // Paper
-+ CraftMetaItem.CAN_BREAK.TYPE, // Paper
- CraftMetaArmor.TRIM.TYPE,
- CraftMetaArmorStand.ENTITY_TAG.TYPE,
- CraftMetaBanner.PATTERNS.TYPE,
diff --git a/patches/unapplied/server/1034-Fix-helmet-damage-reduction-inconsistencies.patch b/patches/unapplied/server/1034-Fix-helmet-damage-reduction-inconsistencies.patch
deleted file mode 100644
index 76f8306225..0000000000
--- a/patches/unapplied/server/1034-Fix-helmet-damage-reduction-inconsistencies.patch
+++ /dev/null
@@ -1,21 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Lulu13022002 <[email protected]>
-Date: Sat, 27 Apr 2024 21:51:58 +0200
-Subject: [PATCH] Fix helmet damage reduction inconsistencies
-
-Affect the falling stalactite damage type where the
-reduction is not applied like in Vanilla
-
-diff --git a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
-index 96070da795755f71e99979288e32b7d7e2d869fb..dfbe0914ab2771ac632fd064719878ac47559e9f 100644
---- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
-+++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
-@@ -1212,7 +1212,7 @@ public class CraftEventFactory {
- Map<DamageModifier, Function<? super Double, Double>> modifierFunctions = new EnumMap<>(DamageModifier.class);
- modifiers.put(DamageModifier.BASE, rawDamage);
- modifierFunctions.put(DamageModifier.BASE, CraftEventFactory.ZERO);
-- if (source.is(DamageTypes.FALLING_BLOCK) || source.is(DamageTypes.FALLING_ANVIL)) {
-+ if (source.is(DamageTypeTags.DAMAGES_HELMET)) { // Paper
- modifiers.put(DamageModifier.HARD_HAT, hardHatModifier);
- modifierFunctions.put(DamageModifier.HARD_HAT, hardHat);
- }
diff --git a/patches/unapplied/server/1035-Revert-to-vanilla-handling-of-LivingEntity-actuallyH.patch b/patches/unapplied/server/1035-Revert-to-vanilla-handling-of-LivingEntity-actuallyH.patch
deleted file mode 100644
index dbc57ee0c9..0000000000
--- a/patches/unapplied/server/1035-Revert-to-vanilla-handling-of-LivingEntity-actuallyH.patch
+++ /dev/null
@@ -1,34 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Jake Potrebic <[email protected]>
-Date: Sat, 27 Apr 2024 09:44:53 -0700
-Subject: [PATCH] Revert to vanilla handling of LivingEntity#actuallyHurt
-
-
-diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java
-index 6118de380a95b0c927a239ac3e288780f114289e..d0b6ade676d94e768c92432dc6cee9f200acf5f2 100644
---- a/src/main/java/net/minecraft/world/entity/LivingEntity.java
-+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java
-@@ -2210,7 +2210,7 @@ public abstract class LivingEntity extends Entity implements Attackable {
- }
-
- // CraftBukkit start
-- protected boolean actuallyHurt(final DamageSource damagesource, float f) { // void -> boolean, add final
-+ protected boolean actuallyHurt(final DamageSource damagesource, float f) { // void -> boolean, add final // Paper - return false ONLY if event cancelled
- if (!this.isInvulnerableTo(damagesource)) {
- final boolean human = this instanceof net.minecraft.world.entity.player.Player;
- float originalDamage = f;
-@@ -2382,12 +2382,12 @@ public abstract class LivingEntity extends Entity implements Attackable {
-
- return true;
- } else {
-- return originalDamage > 0;
-+ return true; // Paper - return false ONLY if event was cancelled
- }
- // CraftBukkit end
- }
- }
-- return false; // CraftBukkit
-+ return true; // CraftBukkit // Paper - return false ONLY if event was cancelled
- }
-
- public CombatTracker getCombatTracker() {
diff --git a/patches/unapplied/server/1036-improve-checking-handled-tags-in-itemmeta.patch b/patches/unapplied/server/1036-improve-checking-handled-tags-in-itemmeta.patch
deleted file mode 100644
index a5308ead10..0000000000
--- a/patches/unapplied/server/1036-improve-checking-handled-tags-in-itemmeta.patch
+++ /dev/null
@@ -1,804 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Jake Potrebic <[email protected]>
-Date: Mon, 10 Jul 2023 16:10:15 -0700
-Subject: [PATCH] improve checking handled tags in itemmeta
-
-
-diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java
-index 7249ff939dfa786395595f687338315b779e0931..c0bf7efac56e558052992d2ce2455fccff4d9897 100644
---- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java
-+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java
-@@ -156,10 +156,11 @@ public final class CraftItemStack extends ItemStack {
- } else if (this.handle == null) {
- this.handle = new net.minecraft.world.item.ItemStack(CraftItemType.bukkitToMinecraft(type), 1);
- } else {
-+ final Material oldType = CraftMagicNumbers.getMaterial(this.handle.getItem()); // Paper
- this.handle.setItem(CraftItemType.bukkitToMinecraft(type));
- if (this.hasItemMeta()) {
- // This will create the appropriate item meta, which will contain all the data we intend to keep
-- CraftItemStack.setItemMeta(this.handle, CraftItemStack.getItemMeta(this.handle));
-+ this.adjustTagForItemMeta(oldType); // Paper
- }
- }
- this.setData(null);
-@@ -312,6 +313,19 @@ public final class CraftItemStack extends ItemStack {
- public ItemMeta getItemMeta() {
- return CraftItemStack.getItemMeta(this.handle);
- }
-+ // Paper start - improve handled tags on type change
-+ public void adjustTagForItemMeta(final Material oldType) {
-+ final CraftMetaItem oldMeta = (CraftMetaItem) CraftItemFactory.instance().getItemMeta(oldType);
-+ final ItemMeta newMeta;
-+ if (oldMeta == null) {
-+ newMeta = getItemMeta(this.handle);
-+ } else {
-+ final java.util.Set<net.minecraft.core.component.DataComponentType<?>> extraHandledDcts = new java.util.HashSet<>(CraftMetaItem.getTopLevelHandledDcts(oldMeta.getClass()));
-+ newMeta = getItemMeta(this.handle, CraftItemStack.getType(this.handle), extraHandledDcts);
-+ }
-+ this.setItemMeta(newMeta);
-+ }
-+ // Paper end - improve handled tags on type change
- // Paper start
- public static void applyMetaToItem(net.minecraft.world.item.ItemStack itemStack, ItemMeta itemMeta) {
- final CraftMetaItem.Applicator tag = new CraftMetaItem.Applicator();
-@@ -324,14 +338,19 @@ public final class CraftItemStack extends ItemStack {
- }
- public static ItemMeta getItemMeta(net.minecraft.world.item.ItemStack item, Material material) {
- // Paper end
-+ // Paper start - handled tags on type change
-+ return getItemMeta(item, material, null);
-+ }
-+ public static ItemMeta getItemMeta(net.minecraft.world.item.ItemStack item, Material material, final java.util.Set<net.minecraft.core.component.DataComponentType<?>> extraHandledDcts) {
-+ // Paper end - handled tags on type change
- if (!CraftItemStack.hasItemMeta(item)) {
- return CraftItemFactory.instance().getItemMeta(material); // Paper
- }
- switch (material) { // Paper
- case WRITTEN_BOOK:
-- return new CraftMetaBookSigned(item.getComponentsPatch());
-+ return new CraftMetaBookSigned(item.getComponentsPatch(), extraHandledDcts); // Paper
- case WRITABLE_BOOK:
-- return new CraftMetaBook(item.getComponentsPatch());
-+ return new CraftMetaBook(item.getComponentsPatch(), extraHandledDcts); // Paper
- case CREEPER_HEAD:
- case CREEPER_WALL_HEAD:
- case DRAGON_HEAD:
-@@ -346,7 +365,7 @@ public final class CraftItemStack extends ItemStack {
- case WITHER_SKELETON_WALL_SKULL:
- case ZOMBIE_HEAD:
- case ZOMBIE_WALL_HEAD:
-- return new CraftMetaSkull(item.getComponentsPatch());
-+ return new CraftMetaSkull(item.getComponentsPatch(), extraHandledDcts); // Paper
- case CHAINMAIL_HELMET:
- case CHAINMAIL_CHESTPLATE:
- case CHAINMAIL_LEGGINGS:
-@@ -368,28 +387,28 @@ public final class CraftItemStack extends ItemStack {
- case NETHERITE_LEGGINGS:
- case NETHERITE_BOOTS:
- case TURTLE_HELMET:
-- return new CraftMetaArmor(item.getComponentsPatch());
-+ return new CraftMetaArmor(item.getComponentsPatch(), extraHandledDcts); // Paper
- case LEATHER_HELMET:
- case LEATHER_CHESTPLATE:
- case LEATHER_LEGGINGS:
- case LEATHER_BOOTS:
- case WOLF_ARMOR:
-- return new CraftMetaColorableArmor(item.getComponentsPatch());
-+ return new CraftMetaColorableArmor(item.getComponentsPatch(), extraHandledDcts); // Paper
- case LEATHER_HORSE_ARMOR:
-- return new CraftMetaLeatherArmor(item.getComponentsPatch());
-+ return new CraftMetaLeatherArmor(item.getComponentsPatch(), extraHandledDcts); // Paper
- case POTION:
- case SPLASH_POTION:
- case LINGERING_POTION:
- case TIPPED_ARROW:
-- return new CraftMetaPotion(item.getComponentsPatch());
-+ return new CraftMetaPotion(item.getComponentsPatch(), extraHandledDcts); // Paper
- case FILLED_MAP:
-- return new CraftMetaMap(item.getComponentsPatch());
-+ return new CraftMetaMap(item.getComponentsPatch(), extraHandledDcts); // Paper
- case FIREWORK_ROCKET:
-- return new CraftMetaFirework(item.getComponentsPatch());
-+ return new CraftMetaFirework(item.getComponentsPatch(), extraHandledDcts); // Paper
- case FIREWORK_STAR:
-- return new CraftMetaCharge(item.getComponentsPatch());
-+ return new CraftMetaCharge(item.getComponentsPatch(), extraHandledDcts); // Paper;
- case ENCHANTED_BOOK:
-- return new CraftMetaEnchantedBook(item.getComponentsPatch());
-+ return new CraftMetaEnchantedBook(item.getComponentsPatch(), extraHandledDcts); // Paper
- case BLACK_BANNER:
- case BLACK_WALL_BANNER:
- case BLUE_BANNER:
-@@ -422,7 +441,7 @@ public final class CraftItemStack extends ItemStack {
- case WHITE_WALL_BANNER:
- case YELLOW_BANNER:
- case YELLOW_WALL_BANNER:
-- return new CraftMetaBanner(item.getComponentsPatch());
-+ return new CraftMetaBanner(item.getComponentsPatch(), extraHandledDcts); // Paper
- case ARMADILLO_SPAWN_EGG:
- case ALLAY_SPAWN_EGG:
- case AXOLOTL_SPAWN_EGG:
-@@ -503,11 +522,11 @@ public final class CraftItemStack extends ItemStack {
- case ZOMBIE_SPAWN_EGG:
- case ZOMBIE_VILLAGER_SPAWN_EGG:
- case ZOMBIFIED_PIGLIN_SPAWN_EGG:
-- return new CraftMetaSpawnEgg(item.getComponentsPatch());
-+ return new CraftMetaSpawnEgg(item.getComponentsPatch(), extraHandledDcts); // Paper
- case ARMOR_STAND:
-- return new CraftMetaArmorStand(item.getComponentsPatch());
-+ return new CraftMetaArmorStand(item.getComponentsPatch(), extraHandledDcts); // Paper
- case KNOWLEDGE_BOOK:
-- return new CraftMetaKnowledgeBook(item.getComponentsPatch());
-+ return new CraftMetaKnowledgeBook(item.getComponentsPatch(), extraHandledDcts); // Paper
- case FURNACE:
- case CHEST:
- case TRAPPED_CHEST:
-@@ -609,15 +628,15 @@ public final class CraftItemStack extends ItemStack {
- case CRAFTER:
- case TRIAL_SPAWNER:
- case VAULT:
-- return new CraftMetaBlockState(item.getComponentsPatch(), CraftItemType.minecraftToBukkit(item.getItem()));
-+ return new CraftMetaBlockState(item.getComponentsPatch(), CraftItemType.minecraftToBukkit(item.getItem()), extraHandledDcts); // Paper
- case TROPICAL_FISH_BUCKET:
-- return new CraftMetaTropicalFishBucket(item.getComponentsPatch());
-+ return new CraftMetaTropicalFishBucket(item.getComponentsPatch(), extraHandledDcts); // Paper
- case AXOLOTL_BUCKET:
-- return new CraftMetaAxolotlBucket(item.getComponentsPatch());
-+ return new CraftMetaAxolotlBucket(item.getComponentsPatch(), extraHandledDcts); // Paper
- case CROSSBOW:
-- return new CraftMetaCrossbow(item.getComponentsPatch());
-+ return new CraftMetaCrossbow(item.getComponentsPatch(), extraHandledDcts); // Paper
- case SUSPICIOUS_STEW:
-- return new CraftMetaSuspiciousStew(item.getComponentsPatch());
-+ return new CraftMetaSuspiciousStew(item.getComponentsPatch(), extraHandledDcts); // Paper
- case COD_BUCKET:
- case PUFFERFISH_BUCKET:
- case SALMON_BUCKET:
-@@ -625,17 +644,17 @@ public final class CraftItemStack extends ItemStack {
- case ITEM_FRAME:
- case GLOW_ITEM_FRAME:
- case PAINTING:
-- return new CraftMetaEntityTag(item.getComponentsPatch());
-+ return new CraftMetaEntityTag(item.getComponentsPatch(), extraHandledDcts); // Paper
- case COMPASS:
-- return new CraftMetaCompass(item.getComponentsPatch());
-+ return new CraftMetaCompass(item.getComponentsPatch(), extraHandledDcts); // Paper
- case BUNDLE:
-- return new CraftMetaBundle(item.getComponentsPatch());
-+ return new CraftMetaBundle(item.getComponentsPatch(), extraHandledDcts); // Paper
- case GOAT_HORN:
-- return new CraftMetaMusicInstrument(item.getComponentsPatch());
-+ return new CraftMetaMusicInstrument(item.getComponentsPatch(), extraHandledDcts); // Paper
- case OMINOUS_BOTTLE:
-- return new CraftMetaOminousBottle(item.getComponentsPatch());
-+ return new CraftMetaOminousBottle(item.getComponentsPatch(), extraHandledDcts); // Paper
- default:
-- return new CraftMetaItem(item.getComponentsPatch());
-+ return new CraftMetaItem(item.getComponentsPatch(), extraHandledDcts); // Paper
- }
- }
-
-diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaArmor.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaArmor.java
-index 13b91cddffbe8ae6f07ce5c0ae45beba151e1aca..569f7157a625b981bff43650e9dd0a8c1831a29d 100644
---- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaArmor.java
-+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaArmor.java
-@@ -65,8 +65,8 @@ public class CraftMetaArmor extends CraftMetaItem implements ArmorMeta {
- }
- }
-
-- CraftMetaArmor(DataComponentPatch tag) {
-- super(tag);
-+ CraftMetaArmor(DataComponentPatch tag, java.util.Set<net.minecraft.core.component.DataComponentType<?>> extraHandledDcts) { // Paper
-+ super(tag, extraHandledDcts); // Paper
-
- getOrEmpty(tag, CraftMetaArmor.TRIM).ifPresent((trimCompound) -> {
- TrimMaterial trimMaterial = org.bukkit.craftbukkit.CraftRegistry.unwrapAndConvertHolder(io.papermc.paper.registry.RegistryKey.TRIM_MATERIAL, trimCompound.material()).orElse(null); // Paper - fix upstream not being correct
-diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaArmorStand.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaArmorStand.java
-index 59bdac414e8205ed608f79ef0d1502acd826d216..53df7e876c9f3e67aa2326fa1a5ce5e90ab7efd6 100644
---- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaArmorStand.java
-+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaArmorStand.java
-@@ -47,8 +47,8 @@ public class CraftMetaArmorStand extends CraftMetaItem implements com.destroysto
- this.entityTag = armorStand.entityTag;
- }
-
-- CraftMetaArmorStand(DataComponentPatch tag) {
-- super(tag);
-+ CraftMetaArmorStand(DataComponentPatch tag, final java.util.Set<net.minecraft.core.component.DataComponentType<?>> extraHandledDcts) { // Paper
-+ super(tag, extraHandledDcts); // Paper
-
- getOrEmpty(tag, CraftMetaArmorStand.ENTITY_TAG).ifPresent((nbt) -> {
- this.entityTag = nbt.copyTag();
-diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaAxolotlBucket.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaAxolotlBucket.java
-index 3377fdd445db33b2ee1735942b391c6bfa92ab91..44d8aa7123ac22cf9a22720ecadc8c5f63bafc0a 100644
---- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaAxolotlBucket.java
-+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaAxolotlBucket.java
-@@ -37,8 +37,8 @@ public class CraftMetaAxolotlBucket extends CraftMetaItem implements AxolotlBuck
- this.bucketEntityTag = bucket.bucketEntityTag;
- }
-
-- CraftMetaAxolotlBucket(DataComponentPatch tag) {
-- super(tag);
-+ CraftMetaAxolotlBucket(DataComponentPatch tag, final java.util.Set<net.minecraft.core.component.DataComponentType<?>> extraHandledDcts) { // Paper
-+ super(tag, extraHandledDcts); // Paper
-
- getOrEmpty(tag, CraftMetaAxolotlBucket.ENTITY_TAG).ifPresent((nbt) -> {
- this.entityTag = nbt.copyTag();
-diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBanner.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBanner.java
-index d53df6f114c285b880167385807775e400c80fc9..1ac3bec02fce28d5ce698305a7482a9eccbb1867 100644
---- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBanner.java
-+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBanner.java
-@@ -72,8 +72,8 @@ public class CraftMetaBanner extends CraftMetaItem implements BannerMeta {
- this.patterns = new ArrayList<Pattern>(banner.patterns);
- }
-
-- CraftMetaBanner(DataComponentPatch tag) {
-- super(tag);
-+ CraftMetaBanner(DataComponentPatch tag, final java.util.Set<net.minecraft.core.component.DataComponentType<?>> extraHandledDcts) { // Paper
-+ super(tag, extraHandledDcts); // Paper
-
- getOrEmpty(tag, CraftMetaBanner.PATTERNS).ifPresent((entityTag) -> {
- List<BannerPatternLayers.Layer> patterns = entityTag.layers();
-diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBlockState.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBlockState.java
-index 9034905aabf057f387b65957a254d056b12e0519..12911233c01d0ac1af9adbd157d56d28361fc76f 100644
---- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBlockState.java
-+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBlockState.java
-@@ -161,8 +161,8 @@ public class CraftMetaBlockState extends CraftMetaItem implements BlockStateMeta
- this.blockEntityTag = te.blockEntityTag;
- }
-
-- CraftMetaBlockState(DataComponentPatch tag, Material material) {
-- super(tag);
-+ CraftMetaBlockState(DataComponentPatch tag, Material material, final Set<DataComponentType<?>> extraHandledDcts) { // Paper
-+ super(tag, extraHandledDcts); // Paper
- this.material = material;
-
- getOrEmpty(tag, CraftMetaBlockState.BLOCK_ENTITY_TAG).ifPresent((nbt) -> {
-diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBook.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBook.java
-index 4da38ebb7fdbdb0f8fa422ebcd2e3eec2b2be846..a395c7ce952f4a60a5edf80e8731afa6388d18ea 100644
---- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBook.java
-+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBook.java
-@@ -64,8 +64,8 @@ public class CraftMetaBook extends CraftMetaItem implements BookMeta, WritableBo
- }
- }
-
-- CraftMetaBook(DataComponentPatch tag) {
-- super(tag);
-+ CraftMetaBook(DataComponentPatch tag, java.util.Set<net.minecraft.core.component.DataComponentType<?>> extraHandledDcts) { // Paper
-+ super(tag, extraHandledDcts); // Paper
-
- getOrEmpty(tag, CraftMetaBook.BOOK_CONTENT).ifPresent((writable) -> {
- List<Filterable<String>> pages = writable.pages();
-diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBookSigned.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBookSigned.java
-index c7360e2b2d6e50abc371c21b09cdadd63892f439..3f78a0935d738854182254b345064e3c225dcd5f 100644
---- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBookSigned.java
-+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBookSigned.java
-@@ -78,8 +78,8 @@ public class CraftMetaBookSigned extends CraftMetaItem implements BookMeta {
- }
- }
-
-- CraftMetaBookSigned(DataComponentPatch tag) {
-- super(tag);
-+ CraftMetaBookSigned(DataComponentPatch tag, java.util.Set<net.minecraft.core.component.DataComponentType<?>> extraHandledDcts) { // Paper
-+ super(tag, extraHandledDcts); // Paper
-
- getOrEmpty(tag, CraftMetaBookSigned.BOOK_CONTENT).ifPresent((written) -> {
- this.title = written.title().raw();
-diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBundle.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBundle.java
-index dfaec374cf35107714b6f49d58c508dba94e7d3d..f8c02fe01fd95aa5de8523c9ad452d91f5d3c16f 100644
---- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBundle.java
-+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBundle.java
-@@ -35,8 +35,8 @@ public class CraftMetaBundle extends CraftMetaItem implements BundleMeta {
- }
- }
-
-- CraftMetaBundle(DataComponentPatch tag) {
-- super(tag);
-+ CraftMetaBundle(DataComponentPatch tag, java.util.Set<net.minecraft.core.component.DataComponentType<?>> extraHandledDcts) { // Paper
-+ super(tag, extraHandledDcts); // Paper
-
- getOrEmpty(tag, CraftMetaBundle.ITEMS).ifPresent((bundle) -> {
- bundle.items().forEach((item) -> {
-diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaCharge.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaCharge.java
-index 40d55374a78bcbaa958cf0010c46071c6dc833f9..12d128e0246e66aa4e53fef870ee9125fa1687bf 100644
---- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaCharge.java
-+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaCharge.java
-@@ -30,8 +30,8 @@ class CraftMetaCharge extends CraftMetaItem implements FireworkEffectMeta {
- this.setEffect(SerializableMeta.getObject(FireworkEffect.class, map, CraftMetaCharge.EXPLOSION.BUKKIT, true));
- }
-
-- CraftMetaCharge(DataComponentPatch tag) {
-- super(tag);
-+ CraftMetaCharge(DataComponentPatch tag, java.util.Set<net.minecraft.core.component.DataComponentType<?>> extraHandledDcts) { // Paper
-+ super(tag, extraHandledDcts); // Paper
-
- getOrEmpty(tag, CraftMetaCharge.EXPLOSION).ifPresent((f) -> {
- try {
-diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaColorableArmor.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaColorableArmor.java
-index c74d597633d023bd12c10bd4801bc103eb2beef1..2c9ca54267579a210d4ea192517fc0fbce8e467a 100644
---- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaColorableArmor.java
-+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaColorableArmor.java
-@@ -29,8 +29,8 @@ public class CraftMetaColorableArmor extends CraftMetaArmor implements Colorable
- CraftMetaLeatherArmor.readColor(this, meta);
- }
-
-- CraftMetaColorableArmor(DataComponentPatch tag) {
-- super(tag);
-+ CraftMetaColorableArmor(DataComponentPatch tag, java.util.Set<net.minecraft.core.component.DataComponentType<?>> extraHandledDcts) { // Paper
-+ super(tag, extraHandledDcts); // Paper
- CraftMetaLeatherArmor.readColor(this, tag);
- }
-
-diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaCompass.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaCompass.java
-index 820b4e611342fb62c61a8b3b19e19967da35cbe0..bbca26f5debb263b04516e68f6e49f68a38fa5b1 100644
---- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaCompass.java
-+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaCompass.java
-@@ -51,8 +51,8 @@ public class CraftMetaCompass extends CraftMetaItem implements CompassMeta {
- this.tracked = compassMeta.tracked;
- }
-
-- CraftMetaCompass(DataComponentPatch tag) {
-- super(tag);
-+ CraftMetaCompass(DataComponentPatch tag, java.util.Set<net.minecraft.core.component.DataComponentType<?>> extraHandledDcts) { // Paper
-+ super(tag, extraHandledDcts); // Paper
- getOrEmpty(tag, CraftMetaCompass.LODESTONE_TARGET).ifPresent((lodestoneTarget) -> {
- lodestoneTarget.target().ifPresent((target) -> {
- this.lodestoneWorld = target.dimension();
-diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaCrossbow.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaCrossbow.java
-index de7b06f9da17418cf0065249438a4182043160e6..a3fa95377e083e51ad7596d21eeb08172bdb18b2 100644
---- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaCrossbow.java
-+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaCrossbow.java
-@@ -36,8 +36,8 @@ public class CraftMetaCrossbow extends CraftMetaItem implements CrossbowMeta {
- }
- }
-
-- CraftMetaCrossbow(DataComponentPatch tag) {
-- super(tag);
-+ CraftMetaCrossbow(DataComponentPatch tag, java.util.Set<net.minecraft.core.component.DataComponentType<?>> extraHandledDcts) { // Paper
-+ super(tag, extraHandledDcts); // Paper
-
- getOrEmpty(tag, CraftMetaCrossbow.CHARGED_PROJECTILES).ifPresent((p) -> {
- List<net.minecraft.world.item.ItemStack> list = p.getItems();
-diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaEnchantedBook.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaEnchantedBook.java
-index 8734f0b777432cd8639094b75a3da1b9595823ed..eb80239949e54c0a698ad4e2d9262242ecb28e41 100644
---- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaEnchantedBook.java
-+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaEnchantedBook.java
-@@ -33,8 +33,8 @@ class CraftMetaEnchantedBook extends CraftMetaItem implements EnchantmentStorage
- }
- }
-
-- CraftMetaEnchantedBook(DataComponentPatch tag) {
-- super(tag);
-+ CraftMetaEnchantedBook(DataComponentPatch tag, java.util.Set<net.minecraft.core.component.DataComponentType<?>> extraHandledDcts) { // Paper
-+ super(tag, extraHandledDcts); // Paper
-
- getOrEmpty(tag, CraftMetaEnchantedBook.STORED_ENCHANTMENTS).ifPresent((itemEnchantments) -> {
- this.enchantments = buildEnchantments(itemEnchantments);
-diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaEntityTag.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaEntityTag.java
-index 3ff0340c40e9dc9a6e690de15ccade7a0c4e8f02..3f6c5cbbf63631e4b72dc43558651ea94f31ca78 100644
---- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaEntityTag.java
-+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaEntityTag.java
-@@ -39,8 +39,8 @@ public class CraftMetaEntityTag extends CraftMetaItem {
- this.entityTag = entity.entityTag;
- }
-
-- CraftMetaEntityTag(DataComponentPatch tag) {
-- super(tag);
-+ CraftMetaEntityTag(DataComponentPatch tag, final java.util.Set<net.minecraft.core.component.DataComponentType<?>> extraHandledDcts) { // Paper
-+ super(tag, extraHandledDcts); // Paper
-
- getOrEmpty(tag, CraftMetaEntityTag.ENTITY_TAG).ifPresent((nbt) -> {
- this.entityTag = nbt.copyTag();
-diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaFirework.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaFirework.java
-index b444bd26d6c3def3494d3cc0520e462408272be3..8e0dd4b7a7a25a8beb27b507047bc48d8227627c 100644
---- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaFirework.java
-+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaFirework.java
-@@ -60,8 +60,8 @@ class CraftMetaFirework extends CraftMetaItem implements FireworkMeta {
- }
- }
-
-- CraftMetaFirework(DataComponentPatch tag) {
-- super(tag);
-+ CraftMetaFirework(DataComponentPatch tag, java.util.Set<net.minecraft.core.component.DataComponentType<?>> extraHandledDcts) { // Paper
-+ super(tag, extraHandledDcts); // Paper
-
- getOrEmpty(tag, CraftMetaFirework.FIREWORKS).ifPresent((fireworks) -> {
- this.power = fireworks.flightDuration();
-diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java
-index 43a4a76d3829fb2ed7b5635d804fd826484c16db..5d86861a0df7308ae9b8440e5d9136fa7c8f1835 100644
---- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java
-+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java
-@@ -331,7 +331,7 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
- // Paper end
- }
-
-- CraftMetaItem(DataComponentPatch tag) {
-+ CraftMetaItem(DataComponentPatch tag, Set<DataComponentType<?>> extraHandledTags) { // Paper - improve handled tags on type changes
- CraftMetaItem.getOrEmpty(tag, CraftMetaItem.NAME).ifPresent((component) -> {
- this.displayName = component;
- });
-@@ -439,11 +439,18 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
- });
- // Paper end - fix ItemFlags
-
-+ // Paper start - improve checking handled data component types
-+ Set<DataComponentType<?>> handledTags = getTopLevelHandledDcts(this.getClass());
-+ if (extraHandledTags != null) {
-+ extraHandledTags.addAll(handledTags);
-+ handledTags = extraHandledTags;
-+ }
-+ // Paper end - improve checking handled data component types
- Set<Map.Entry<DataComponentType<?>, Optional<?>>> keys = tag.entrySet();
- for (Map.Entry<DataComponentType<?>, Optional<?>> key : keys) {
- if (key.getValue().isEmpty()) {
- this.unhandledTags.remove(key.getKey());
-- } else if (!CraftMetaItem.getHandledTags().contains(key.getKey())) {
-+ } else if (!handledTags.contains(key.getKey())) { // Paper - improve checking handled data component types
- key.getValue().ifPresentOrElse((value) -> {
- this.unhandledTags.set((DataComponentType) key.getKey(), value);
- }, () -> {
-@@ -1936,67 +1943,74 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
- this.version = version;
- }
-
-- public static Set<DataComponentType> getHandledTags() {
-- synchronized (CraftMetaItem.HANDLED_TAGS) {
-- if (CraftMetaItem.HANDLED_TAGS.isEmpty()) {
-- CraftMetaItem.HANDLED_TAGS.addAll(Arrays.asList(
-- CraftMetaItem.NAME.TYPE,
-- CraftMetaItem.ITEM_NAME.TYPE,
-- CraftMetaItem.LORE.TYPE,
-- CraftMetaItem.CUSTOM_MODEL_DATA.TYPE,
-- CraftMetaItem.BLOCK_DATA.TYPE,
-- CraftMetaItem.REPAIR.TYPE,
-- CraftMetaItem.ENCHANTMENTS.TYPE,
-- CraftMetaItem.HIDE_ADDITIONAL_TOOLTIP.TYPE,
-- CraftMetaItem.HIDE_TOOLTIP.TYPE,
-- CraftMetaItem.UNBREAKABLE.TYPE,
-- CraftMetaItem.ENCHANTMENT_GLINT_OVERRIDE.TYPE,
-- CraftMetaItem.FIRE_RESISTANT.TYPE,
-- CraftMetaItem.MAX_STACK_SIZE.TYPE,
-- CraftMetaItem.RARITY.TYPE,
-- CraftMetaItem.FOOD.TYPE,
-- CraftMetaItem.TOOL.TYPE,
-- CraftMetaItem.DAMAGE.TYPE,
-- CraftMetaItem.MAX_DAMAGE.TYPE,
-- CraftMetaItem.CUSTOM_DATA.TYPE,
-- CraftMetaItem.ATTRIBUTES.TYPE,
-- CraftMetaItem.CAN_PLACE_ON.TYPE, // Paper
-- CraftMetaItem.CAN_BREAK.TYPE, // Paper
-- CraftMetaArmor.TRIM.TYPE,
-- CraftMetaArmorStand.ENTITY_TAG.TYPE,
-- CraftMetaBanner.PATTERNS.TYPE,
-- CraftMetaEntityTag.ENTITY_TAG.TYPE,
-- CraftMetaLeatherArmor.COLOR.TYPE,
-- CraftMetaMap.MAP_POST_PROCESSING.TYPE,
-- CraftMetaMap.MAP_COLOR.TYPE,
-- CraftMetaMap.MAP_ID.TYPE,
-- CraftMetaPotion.POTION_CONTENTS.TYPE,
-- CraftMetaSkull.SKULL_PROFILE.TYPE,
-- CraftMetaSkull.NOTE_BLOCK_SOUND.TYPE,
-- CraftMetaSpawnEgg.ENTITY_TAG.TYPE,
-- CraftMetaBlockState.BLOCK_ENTITY_TAG.TYPE,
-- CraftMetaBook.BOOK_CONTENT.TYPE,
-- CraftMetaBookSigned.BOOK_CONTENT.TYPE,
-- CraftMetaFirework.FIREWORKS.TYPE,
-- CraftMetaEnchantedBook.STORED_ENCHANTMENTS.TYPE,
-- CraftMetaCharge.EXPLOSION.TYPE,
-- CraftMetaBlockState.BLOCK_ENTITY_TAG.TYPE,
-- CraftMetaKnowledgeBook.BOOK_RECIPES.TYPE,
-- CraftMetaTropicalFishBucket.ENTITY_TAG.TYPE,
-- CraftMetaTropicalFishBucket.BUCKET_ENTITY_TAG.TYPE,
-- CraftMetaAxolotlBucket.ENTITY_TAG.TYPE,
-- CraftMetaAxolotlBucket.BUCKET_ENTITY_TAG.TYPE,
-- CraftMetaCrossbow.CHARGED_PROJECTILES.TYPE,
-- CraftMetaSuspiciousStew.EFFECTS.TYPE,
-- CraftMetaCompass.LODESTONE_TARGET.TYPE,
-- CraftMetaBundle.ITEMS.TYPE,
-- CraftMetaMusicInstrument.GOAT_HORN_INSTRUMENT.TYPE,
-- CraftMetaOminousBottle.OMINOUS_BOTTLE_AMPLIFIER.TYPE
-- ));
-- }
-- return CraftMetaItem.HANDLED_TAGS;
-+ // Paper start - improve checking handled tags
-+ @org.jetbrains.annotations.VisibleForTesting
-+ public static final Map<Class<? extends CraftMetaItem>, Set<DataComponentType<?>>> HANDLED_DCTS_PER_TYPE = new HashMap<>();
-+ private static final Set<DataComponentType<?>> DEFAULT_HANDLED_DCTS = Set.of(
-+ CraftMetaItem.NAME.TYPE,
-+ CraftMetaItem.ITEM_NAME.TYPE,
-+ CraftMetaItem.LORE.TYPE,
-+ CraftMetaItem.CUSTOM_MODEL_DATA.TYPE,
-+ CraftMetaItem.BLOCK_DATA.TYPE,
-+ CraftMetaItem.REPAIR.TYPE,
-+ CraftMetaItem.ENCHANTMENTS.TYPE,
-+ CraftMetaItem.HIDE_ADDITIONAL_TOOLTIP.TYPE,
-+ CraftMetaItem.HIDE_TOOLTIP.TYPE,
-+ CraftMetaItem.UNBREAKABLE.TYPE,
-+ CraftMetaItem.ENCHANTMENT_GLINT_OVERRIDE.TYPE,
-+ CraftMetaItem.FIRE_RESISTANT.TYPE,
-+ CraftMetaItem.MAX_STACK_SIZE.TYPE,
-+ CraftMetaItem.RARITY.TYPE,
-+ CraftMetaItem.FOOD.TYPE,
-+ CraftMetaItem.TOOL.TYPE,
-+ CraftMetaItem.DAMAGE.TYPE,
-+ CraftMetaItem.MAX_DAMAGE.TYPE,
-+ CraftMetaItem.CUSTOM_DATA.TYPE,
-+ CraftMetaItem.ATTRIBUTES.TYPE,
-+ CraftMetaItem.CAN_PLACE_ON.TYPE, // Paper
-+ CraftMetaItem.CAN_BREAK.TYPE // Paper
-+ );
-+ public static Set<DataComponentType<?>> getTopLevelHandledDcts(final Class<? extends CraftMetaItem> clazz) {
-+ synchronized (HANDLED_DCTS_PER_TYPE) {
-+ if (HANDLED_DCTS_PER_TYPE.isEmpty()) {
-+ final Map<Class<? extends CraftMetaItem>, Set<DataComponentType<?>>> map = new HashMap<>();
-+ map.put(CraftMetaArmor.class, Set.of(CraftMetaArmor.TRIM.TYPE));
-+ map.put(CraftMetaArmorStand.class, Set.of(CraftMetaArmorStand.ENTITY_TAG.TYPE));
-+ map.put(CraftMetaAxolotlBucket.class, Set.of(CraftMetaAxolotlBucket.ENTITY_TAG.TYPE, CraftMetaAxolotlBucket.BUCKET_ENTITY_TAG.TYPE));
-+ map.put(CraftMetaBanner.class, Set.of(/*CraftMetaBlockState.BLOCK_ENTITY_TAG.NBT, */CraftMetaBanner.PATTERNS.TYPE)); // banner uses same tag as block state
-+ map.put(CraftMetaBlockState.class, Set.of(CraftMetaBlockState.BLOCK_ENTITY_TAG.TYPE));
-+ map.put(CraftMetaBook.class, Set.of(CraftMetaBook.BOOK_CONTENT.TYPE));
-+ map.put(CraftMetaBookSigned.class, Set.of(CraftMetaBookSigned.BOOK_CONTENT.TYPE));
-+ map.put(CraftMetaBundle.class, Set.of(CraftMetaBundle.ITEMS.TYPE));
-+ map.put(CraftMetaCharge.class, Set.of(CraftMetaCharge.EXPLOSION.TYPE));
-+ map.put(CraftMetaColorableArmor.class, Set.of(CraftMetaArmor.TRIM.TYPE, CraftMetaLeatherArmor.COLOR.TYPE));
-+ map.put(CraftMetaCompass.class, Set.of(CraftMetaCompass.LODESTONE_TARGET.TYPE));
-+ map.put(CraftMetaCrossbow.class, Set.of(CraftMetaCrossbow.CHARGED_PROJECTILES.TYPE));
-+ map.put(CraftMetaEnchantedBook.class, Set.of(CraftMetaEnchantedBook.STORED_ENCHANTMENTS.TYPE));
-+ map.put(CraftMetaEntityTag.class, Set.of(CraftMetaEntityTag.ENTITY_TAG.TYPE));
-+ map.put(CraftMetaFirework.class, Set.of(CraftMetaFirework.FIREWORKS.TYPE));
-+ map.put(CraftMetaKnowledgeBook.class, Set.of(CraftMetaKnowledgeBook.BOOK_RECIPES.TYPE));
-+ map.put(CraftMetaLeatherArmor.class, Set.of(CraftMetaLeatherArmor.COLOR.TYPE));
-+ map.put(CraftMetaMap.class, Set.of(CraftMetaMap.MAP_COLOR.TYPE, CraftMetaMap.MAP_POST_PROCESSING.TYPE, CraftMetaMap.MAP_ID.TYPE));
-+ map.put(CraftMetaMusicInstrument.class, Set.of(CraftMetaMusicInstrument.GOAT_HORN_INSTRUMENT.TYPE));
-+ map.put(CraftMetaOminousBottle.class, Set.of(CraftMetaOminousBottle.OMINOUS_BOTTLE_AMPLIFIER.TYPE));
-+ map.put(CraftMetaPotion.class, Set.of(CraftMetaPotion.POTION_CONTENTS.TYPE));
-+ map.put(CraftMetaSkull.class, Set.of(CraftMetaSkull.SKULL_PROFILE.TYPE, CraftMetaSkull.NOTE_BLOCK_SOUND.TYPE));
-+ map.put(CraftMetaSpawnEgg.class, Set.of(CraftMetaSpawnEgg.ENTITY_TAG.TYPE));
-+ map.put(CraftMetaSuspiciousStew.class, Set.of(CraftMetaSuspiciousStew.EFFECTS.TYPE));
-+ map.put(CraftMetaTropicalFishBucket.class, Set.of(CraftMetaTropicalFishBucket.ENTITY_TAG.TYPE, CraftMetaTropicalFishBucket.BUCKET_ENTITY_TAG.TYPE));
-+
-+ for (final Map.Entry<Class<? extends CraftMetaItem>, Set<DataComponentType<?>>> entry : map.entrySet()) {
-+ final ArrayList<DataComponentType<?>> topLevelTags = new ArrayList<>(entry.getValue());
-+ // add tags common to CraftMetaItem to all
-+ topLevelTags.addAll(DEFAULT_HANDLED_DCTS);
-+ HANDLED_DCTS_PER_TYPE.put(entry.getKey(), Set.copyOf(topLevelTags));
-+ }
-+ }
-+ return HANDLED_DCTS_PER_TYPE.getOrDefault(clazz, DEFAULT_HANDLED_DCTS);
- }
- }
-+ // Paper end - improve checking handled data component types
-
- protected static <T> Optional<? extends T> getOrEmpty(DataComponentPatch tag, ItemMetaKeyType<T> type) {
- Optional<? extends T> result = tag.get(type.TYPE);
-diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaKnowledgeBook.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaKnowledgeBook.java
-index bd44481a7d794943cb8695bea2a773a4562f0fae..20638aa593e0a6c78e4bfdb936e69f3d36e18f4e 100644
---- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaKnowledgeBook.java
-+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaKnowledgeBook.java
-@@ -31,8 +31,8 @@ public class CraftMetaKnowledgeBook extends CraftMetaItem implements KnowledgeBo
- }
- }
-
-- CraftMetaKnowledgeBook(DataComponentPatch tag) {
-- super(tag);
-+ CraftMetaKnowledgeBook(DataComponentPatch tag, java.util.Set<net.minecraft.core.component.DataComponentType<?>> extraHandledDcts) { // Paper
-+ super(tag, extraHandledDcts); // Paper
-
- getOrEmpty(tag, CraftMetaKnowledgeBook.BOOK_RECIPES).ifPresent((pages) -> {
- for (int i = 0; i < pages.size(); i++) {
-diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaLeatherArmor.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaLeatherArmor.java
-index b97e9a8f163adbb30d2e7db16aeb99544fcb2916..157a7b7351f48e68d2923c72ed3bbe3dcae21383 100644
---- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaLeatherArmor.java
-+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaLeatherArmor.java
-@@ -35,8 +35,8 @@ class CraftMetaLeatherArmor extends CraftMetaItem implements LeatherArmorMeta {
- CraftMetaLeatherArmor.readColor(this, meta);
- }
-
-- CraftMetaLeatherArmor(DataComponentPatch tag) {
-- super(tag);
-+ CraftMetaLeatherArmor(DataComponentPatch tag, java.util.Set<net.minecraft.core.component.DataComponentType<?>> extraHandledDcts) { // Paper
-+ super(tag, extraHandledDcts); // Paper
- CraftMetaLeatherArmor.readColor(this, tag);
- }
-
-diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaMap.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaMap.java
-index 6b34a8d33faa49ffa9082995e67af10d3cb38c03..d829f4da371b44e7480896118547734be400a314 100644
---- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaMap.java
-+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaMap.java
-@@ -45,8 +45,8 @@ class CraftMetaMap extends CraftMetaItem implements MapMeta {
- this.color = map.color;
- }
-
-- CraftMetaMap(DataComponentPatch tag) {
-- super(tag);
-+ CraftMetaMap(DataComponentPatch tag, java.util.Set<net.minecraft.core.component.DataComponentType<?>> extraHandledDcts) { // Paper
-+ super(tag, extraHandledDcts); // Paper
-
- getOrEmpty(tag, CraftMetaMap.MAP_ID).ifPresent((mapId) -> {
- this.mapId = mapId.id();
-diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaMusicInstrument.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaMusicInstrument.java
-index 4eb2993903f5fa9fb9fd65282a42f26b3aa1e7bd..f33f1d250a3a4068df79cd1eb37baffda981aab3 100644
---- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaMusicInstrument.java
-+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaMusicInstrument.java
-@@ -27,8 +27,8 @@ public class CraftMetaMusicInstrument extends CraftMetaItem implements MusicInst
- }
- }
-
-- CraftMetaMusicInstrument(DataComponentPatch tag) {
-- super(tag);
-+ CraftMetaMusicInstrument(DataComponentPatch tag, java.util.Set<net.minecraft.core.component.DataComponentType<?>> extraHandledDcts) { // Paper
-+ super(tag, extraHandledDcts); // Paper
-
- getOrEmpty(tag, CraftMetaMusicInstrument.GOAT_HORN_INSTRUMENT).ifPresent((instrument) -> {
- this.instrument = org.bukkit.craftbukkit.CraftRegistry.unwrapAndConvertHolder(org.bukkit.Registry.INSTRUMENT, instrument).orElse(null); // Paper - fix upstream not handling custom instruments
-diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaOminousBottle.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaOminousBottle.java
-index 19f1425ae86e1b8b8fd46a5c6a193d1b77aeefe9..7197c4f5698fd041c4db6d0f6a80c55f77661789 100644
---- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaOminousBottle.java
-+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaOminousBottle.java
-@@ -24,8 +24,8 @@ public class CraftMetaOminousBottle extends CraftMetaItem implements OminousBott
- this.ominousBottleAmplifier = bottleMeta.ominousBottleAmplifier;
- }
-
-- CraftMetaOminousBottle(DataComponentPatch tag) {
-- super(tag);
-+ CraftMetaOminousBottle(DataComponentPatch tag, java.util.Set<net.minecraft.core.component.DataComponentType<?>> extraHandledDcts) { // Paper
-+ super(tag, extraHandledDcts); // Paper
- getOrEmpty(tag, CraftMetaOminousBottle.OMINOUS_BOTTLE_AMPLIFIER).ifPresent((amplifier) -> {
- this.ominousBottleAmplifier = amplifier;
- });
-diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaPotion.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaPotion.java
-index e2aa305dcaf94d76fa3b74fc33b4d8bbc6d92b2b..db7f71af22d904de08d4badaa7f66d1286d5bf16 100644
---- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaPotion.java
-+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaPotion.java
-@@ -61,8 +61,8 @@ class CraftMetaPotion extends CraftMetaItem implements PotionMeta {
- }
- }
-
-- CraftMetaPotion(DataComponentPatch tag) {
-- super(tag);
-+ CraftMetaPotion(DataComponentPatch tag, java.util.Set<net.minecraft.core.component.DataComponentType<?>> extraHandledDcts) { // Paper
-+ super(tag, extraHandledDcts); // Paper
- getOrEmpty(tag, CraftMetaPotion.POTION_CONTENTS).ifPresent((potionContents) -> {
- potionContents.potion().ifPresent((potion) -> {
- this.type = CraftPotionType.minecraftHolderToBukkit(potion);
-diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSkull.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSkull.java
-index a08c57770c658bb289c96b69b966d98af72eef67..7bdc94c3ba7d8a0d74c2d88edbb32112a90c5980 100644
---- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSkull.java
-+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSkull.java
-@@ -69,8 +69,8 @@ class CraftMetaSkull extends CraftMetaItem implements SkullMeta {
- this.noteBlockSound = skullMeta.noteBlockSound;
- }
-
-- CraftMetaSkull(DataComponentPatch tag) {
-- super(tag);
-+ CraftMetaSkull(DataComponentPatch tag, java.util.Set<net.minecraft.core.component.DataComponentType<?>> extraHandledDcts) { // Paper
-+ super(tag, extraHandledDcts); // Paper
-
- getOrEmpty(tag, CraftMetaSkull.SKULL_PROFILE).ifPresent((resolvableProfile) -> {
- this.setProfile(resolvableProfile.gameProfile());
-diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSpawnEgg.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSpawnEgg.java
-index 1c2b0407b51906a255e6d240fab969578743938e..b98e656c0bb382667bd186a500c5505f1ed3f7cd 100644
---- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSpawnEgg.java
-+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSpawnEgg.java
-@@ -119,8 +119,8 @@ public class CraftMetaSpawnEgg extends CraftMetaItem implements SpawnEggMeta {
- this.entityTag = egg.entityTag;
- }
-
-- CraftMetaSpawnEgg(DataComponentPatch tag) {
-- super(tag);
-+ CraftMetaSpawnEgg(DataComponentPatch tag, java.util.Set<net.minecraft.core.component.DataComponentType<?>> extraHandledDcts) { // Paper
-+ super(tag, extraHandledDcts); // Paper
-
- getOrEmpty(tag, CraftMetaSpawnEgg.ENTITY_TAG).ifPresent((nbt) -> {
- this.entityTag = nbt.copyTag();
-diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSuspiciousStew.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSuspiciousStew.java
-index 14e944b4e83b80e0fc6d81e346cc305ab00561c5..39cab624de062514358a2a2942aea0e58cbd6e3e 100644
---- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSuspiciousStew.java
-+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSuspiciousStew.java
-@@ -34,8 +34,8 @@ public class CraftMetaSuspiciousStew extends CraftMetaItem implements Suspicious
- }
- }
-
-- CraftMetaSuspiciousStew(DataComponentPatch tag) {
-- super(tag);
-+ CraftMetaSuspiciousStew(DataComponentPatch tag, java.util.Set<net.minecraft.core.component.DataComponentType<?>> extraHandledDcts) { // Paper
-+ super(tag, extraHandledDcts); // Paper
- getOrEmpty(tag, CraftMetaSuspiciousStew.EFFECTS).ifPresent((suspiciousStewEffects) -> {
- List<SuspiciousStewEffects.Entry> list = suspiciousStewEffects.effects();
- int length = list.size();
-diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaTropicalFishBucket.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaTropicalFishBucket.java
-index 911bdce0795a6b11cd1d5ad5211202456e5225d4..b5392a3a6f6f3d0a54549e6bb93f28590ee048f0 100644
---- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaTropicalFishBucket.java
-+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaTropicalFishBucket.java
-@@ -39,8 +39,8 @@ class CraftMetaTropicalFishBucket extends CraftMetaItem implements TropicalFishB
- this.bucketEntityTag = bucket.bucketEntityTag;
- }
-
-- CraftMetaTropicalFishBucket(DataComponentPatch tag) {
-- super(tag);
-+ CraftMetaTropicalFishBucket(DataComponentPatch tag, java.util.Set<net.minecraft.core.component.DataComponentType<?>> extraHandledDcts) { // Paper
-+ super(tag, extraHandledDcts); // Paper
-
- getOrEmpty(tag, CraftMetaTropicalFishBucket.ENTITY_TAG).ifPresent((nbt) -> {
- this.entityTag = nbt.copyTag();
-diff --git a/src/test/java/org/bukkit/craftbukkit/inventory/DeprecatedItemMetaCustomValueTest.java b/src/test/java/org/bukkit/craftbukkit/inventory/DeprecatedItemMetaCustomValueTest.java
-index 51e2acf125bdff2ba6d8fd8af9f22e233d7c74a7..6bed0a5c8d9f1ca72678cdf4699128e441a24541 100644
---- a/src/test/java/org/bukkit/craftbukkit/inventory/DeprecatedItemMetaCustomValueTest.java
-+++ b/src/test/java/org/bukkit/craftbukkit/inventory/DeprecatedItemMetaCustomValueTest.java
-@@ -96,7 +96,7 @@ public class DeprecatedItemMetaCustomValueTest extends AbstractTestingBase {
- CraftMetaItem.Applicator compound = new CraftMetaItem.Applicator();
- itemMeta.applyToItem(compound);
-
-- assertEquals(itemMeta, new CraftMetaItem(compound.build()));
-+ assertEquals(itemMeta, new CraftMetaItem(compound.build(), null)); // Paper
- }
-
- @Test
-diff --git a/src/test/java/org/bukkit/craftbukkit/inventory/MetaHandledTagsTest.java b/src/test/java/org/bukkit/craftbukkit/inventory/MetaHandledTagsTest.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..d9692972e3ad089885d43711b6a7fb3e96da59b1
---- /dev/null
-+++ b/src/test/java/org/bukkit/craftbukkit/inventory/MetaHandledTagsTest.java
-@@ -0,0 +1,32 @@
-+package org.bukkit.craftbukkit.inventory;
-+
-+import io.github.classgraph.ClassGraph;
-+import io.github.classgraph.ClassInfo;
-+import io.github.classgraph.ClassInfoList;
-+import io.github.classgraph.ScanResult;
-+import org.bukkit.support.AbstractTestingBase;
-+import org.junit.jupiter.api.Test;
-+
-+import static org.junit.jupiter.api.Assertions.assertFalse;
-+import static org.junit.jupiter.api.Assertions.assertTrue;
-+
-+// in cb package because of package-private stuff
-+class MetaHandledTagsTest extends AbstractTestingBase {
-+
-+ @Test
-+ public void checkAllMetasHaveHandledTags() {
-+ try (final ScanResult result = new ClassGraph()
-+ .whitelistPackages("org.bukkit.craftbukkit.inventory")
-+ .enableAllInfo().scan()) {
-+ final ClassInfoList subclasses = result.getSubclasses(CraftMetaItem.class.getName());
-+ assertFalse(subclasses.isEmpty(), "found 0 sub types");
-+ for (final ClassInfo subclass : subclasses) {
-+ final Class<CraftMetaItem> clazz = subclass.loadClass(CraftMetaItem.class);
-+ CraftMetaItem.getTopLevelHandledDcts(clazz); // load into map
-+ assertTrue(CraftMetaItem.HANDLED_DCTS_PER_TYPE.containsKey(clazz), subclass.getName() + " not found in handled tags map");
-+ }
-+ } catch (Exception e) {
-+ throw new RuntimeException(e);
-+ }
-+ }
-+}
-diff --git a/src/test/java/org/bukkit/craftbukkit/inventory/PersistentDataContainerTest.java b/src/test/java/org/bukkit/craftbukkit/inventory/PersistentDataContainerTest.java
-index 30da18cbc878fb1ac2a134f3bcbfcb8d7bec3938..6f94c7a19f2f598a836ec7db30332dd95f8675a6 100644
---- a/src/test/java/org/bukkit/craftbukkit/inventory/PersistentDataContainerTest.java
-+++ b/src/test/java/org/bukkit/craftbukkit/inventory/PersistentDataContainerTest.java
-@@ -130,7 +130,7 @@ public class PersistentDataContainerTest extends AbstractTestingBase {
- CraftMetaItem.Applicator compound = new CraftMetaItem.Applicator();
- itemMeta.applyToItem(compound);
-
-- assertEquals(itemMeta, new CraftMetaItem(compound.build()));
-+ assertEquals(itemMeta, new CraftMetaItem(compound.build(), null)); // Paper
- }
-
- @Test
-@@ -463,7 +463,7 @@ public class PersistentDataContainerTest extends AbstractTestingBase {
-
- @Test
- public void testEmptyListApplicationToAnyType() throws IOException {
-- final CraftMetaItem craftItem = new CraftMetaItem(DataComponentPatch.EMPTY);
-+ final CraftMetaItem craftItem = new CraftMetaItem(DataComponentPatch.EMPTY, null); // Paper
- final PersistentDataContainer container = craftItem.getPersistentDataContainer();
-
- container.set(PersistentDataContainerTest.requestKey("list"), PersistentDataType.LIST.strings(), List.of());
-@@ -476,7 +476,7 @@ public class PersistentDataContainerTest extends AbstractTestingBase {
- final CraftMetaItem.Applicator storage = new CraftMetaItem.Applicator();
- craftItem.applyToItem(storage);
-
-- final CraftMetaItem readItem = new CraftMetaItem(storage.build());
-+ final CraftMetaItem readItem = new CraftMetaItem(storage.build(), null); // Paper
- final PersistentDataContainer readContainer = readItem.getPersistentDataContainer();
-
- assertTrue(readContainer.has(PersistentDataContainerTest.requestKey("list"), PersistentDataType.LIST.strings()));
diff --git a/patches/unapplied/server/1037-General-ItemMeta-fixes.patch b/patches/unapplied/server/1037-General-ItemMeta-fixes.patch
deleted file mode 100644
index 0c4bb01b9e..0000000000
--- a/patches/unapplied/server/1037-General-ItemMeta-fixes.patch
+++ /dev/null
@@ -1,1494 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Jake Potrebic <[email protected]>
-Date: Sat, 27 Apr 2024 20:56:17 -0700
-Subject: [PATCH] General ItemMeta fixes
-
-== AT ==
-private-f net/minecraft/world/item/ItemStack components
-public net/minecraft/world/food/FoodProperties DEFAULT_EAT_SECONDS
-public org/bukkit/craftbukkit/block/CraftBlockStates getBlockState(Lorg/bukkit/World;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/level/block/entity/BlockEntity;)Lorg/bukkit/craftbukkit/block/CraftBlockState;
-public net/minecraft/world/level/block/entity/BlockEntity saveId(Lnet/minecraft/nbt/CompoundTag;)V
-
-diff --git a/src/main/java/net/minecraft/world/item/ItemStack.java b/src/main/java/net/minecraft/world/item/ItemStack.java
-index 066feef97f92b3f788dd6d25d188f2cc36fc4c80..7c7b9b1e0b604b0164b431873e6753b60421f970 100644
---- a/src/main/java/net/minecraft/world/item/ItemStack.java
-+++ b/src/main/java/net/minecraft/world/item/ItemStack.java
-@@ -1258,6 +1258,11 @@ public final class ItemStack implements DataComponentHolder {
- public void setItem(Item item) {
- this.bukkitStack = null; // Paper
- this.item = item;
-+ // Paper start - change base component prototype
-+ final DataComponentPatch patch = this.getComponentsPatch();
-+ this.components = new PatchedDataComponentMap(this.item.components());
-+ this.applyComponents(patch);
-+ // Paper end - change base component prototype
- }
- // CraftBukkit end
-
-diff --git a/src/main/java/net/minecraft/world/level/block/entity/BlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/BlockEntity.java
-index 139cc0123921bf981d10334d9bd7378d19ec5f3b..c0563260277f9f4bd9ff08993b2efb4bca9a0c60 100644
---- a/src/main/java/net/minecraft/world/level/block/entity/BlockEntity.java
-+++ b/src/main/java/net/minecraft/world/level/block/entity/BlockEntity.java
-@@ -140,6 +140,11 @@ public abstract class BlockEntity {
- CompoundTag nbttagcompound = new CompoundTag();
-
- this.saveAdditional(nbttagcompound, registryLookup);
-+ // Paper start - store PDC here as well
-+ if (this.persistentDataContainer != null && !this.persistentDataContainer.isEmpty()) {
-+ nbttagcompound.put("PublicBukkitValues", this.persistentDataContainer.toTagCompound());
-+ }
-+ // Paper end
- return nbttagcompound;
- }
-
-diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java
-index 397eb1a101bd60f49dbb2fa8eddf28f6f233167f..2c61e8d5bbab59c691f4cb003041e7e50e406b85 100644
---- a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java
-+++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java
-@@ -135,6 +135,19 @@ public abstract class CraftBlockEntityState<T extends BlockEntity> extends Craft
- return nbt;
- }
-
-+ // Paper start - properly save blockentity itemstacks
-+ public CompoundTag getSnapshotCustomNbtOnly() {
-+ this.applyTo(this.snapshot);
-+ final CompoundTag nbt = this.snapshot.saveCustomOnly(this.getRegistryAccess());
-+ this.snapshot.removeComponentsFromTag(nbt);
-+ if (!nbt.isEmpty()) {
-+ // have to include the "id" if it's going to have block entity data
-+ this.snapshot.saveId(nbt);
-+ }
-+ return nbt;
-+ }
-+ // Paper end
-+
- // copies the data of the given tile entity to this block state
- protected void load(T tileEntity) {
- if (tileEntity != null && tileEntity != this.snapshot) {
-diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java
-index c0bf7efac56e558052992d2ce2455fccff4d9897..3eb650bcb60f23ce0ac0f856526b4bd131cb7a20 100644
---- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java
-+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java
-@@ -328,7 +328,14 @@ public final class CraftItemStack extends ItemStack {
- // Paper end - improve handled tags on type change
- // Paper start
- public static void applyMetaToItem(net.minecraft.world.item.ItemStack itemStack, ItemMeta itemMeta) {
-- final CraftMetaItem.Applicator tag = new CraftMetaItem.Applicator();
-+ // Paper start - support updating profile after resolving it
-+ final CraftMetaItem.Applicator tag = new CraftMetaItem.Applicator() {
-+ @Override
-+ void skullCallback(final com.mojang.authlib.GameProfile gameProfile) {
-+ itemStack.set(DataComponents.PROFILE, new net.minecraft.world.item.component.ResolvableProfile(gameProfile));
-+ }
-+ };
-+ // Paper end - support updating profile after resolving it
- ((CraftMetaItem) itemMeta).applyToItem(tag);
- itemStack.applyComponents(tag.build());
- }
-@@ -683,15 +690,20 @@ public final class CraftItemStack extends ItemStack {
- if (itemMeta == null) return true;
-
- if (!((CraftMetaItem) itemMeta).isEmpty()) {
-- CraftMetaItem.Applicator tag = new CraftMetaItem.Applicator();
-+ // Paper start - support updating profile after resolving it
-+ CraftMetaItem.Applicator tag = new CraftMetaItem.Applicator() {
-+ @Override
-+ void skullCallback(final com.mojang.authlib.GameProfile gameProfile) {
-+ item.set(DataComponents.PROFILE, new net.minecraft.world.item.component.ResolvableProfile(gameProfile));
-+ }
-+ };
-+ // Paper end - support updating profile after resolving it
-
- ((CraftMetaItem) itemMeta).applyToItem(tag);
-- item.restorePatch(tag.build());
-- }
-- // SpigotCraft#463 this is required now by the Vanilla client, so mimic ItemStack constructor in ensuring it
-- if (item.getItem() != null && item.getMaxDamage() > 0) {
-- item.setDamageValue(item.getDamageValue());
-+ item.restorePatch(DataComponentPatch.EMPTY); // Paper - properly apply the new patch from itemmeta
-+ item.applyComponents(tag.build()); // Paper - properly apply the new patch from itemmeta
- }
-+ // Paper - this is no longer needed
-
- return true;
- }
-diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaAxolotlBucket.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaAxolotlBucket.java
-index 44d8aa7123ac22cf9a22720ecadc8c5f63bafc0a..9665dc043b257bb0d6f7b40fd938ff549ab685c6 100644
---- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaAxolotlBucket.java
-+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaAxolotlBucket.java
-@@ -124,14 +124,13 @@ public class CraftMetaAxolotlBucket extends CraftMetaItem implements AxolotlBuck
-
- @Override
- public Axolotl.Variant getVariant() {
-+ com.google.common.base.Preconditions.checkState(this.hasVariant(), "Variant is absent, check hasVariant first!"); // Paper - fix NPE
- return Axolotl.Variant.values()[this.variant];
- }
-
- @Override
- public void setVariant(Axolotl.Variant variant) {
-- if (variant == null) {
-- variant = Axolotl.Variant.LUCY;
-- }
-+ com.google.common.base.Preconditions.checkArgument(variant != null, "Variant cannot be null!"); // Paper
- this.variant = variant.ordinal();
- }
-
-diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBanner.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBanner.java
-index 1ac3bec02fce28d5ce698305a7482a9eccbb1867..b494568f833dc21d4e2447ac3e5c5002288b5533 100644
---- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBanner.java
-+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBanner.java
-@@ -107,6 +107,7 @@ public class CraftMetaBanner extends CraftMetaItem implements BannerMeta {
- void applyToItem(CraftMetaItem.Applicator tag) {
- super.applyToItem(tag);
-
-+ if (this.patterns.isEmpty()) return; // Paper - don't write empty patterns
- List<BannerPatternLayers.Layer> newPatterns = new ArrayList<>();
-
- for (Pattern p : this.patterns) {
-diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBlockState.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBlockState.java
-index 12911233c01d0ac1af9adbd157d56d28361fc76f..3faef745d9b5d3baaea8c20c6f8e06f7bb529665 100644
---- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBlockState.java
-+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBlockState.java
-@@ -142,9 +142,24 @@ public class CraftMetaBlockState extends CraftMetaItem implements BlockStateMeta
-
- @ItemMetaKey.Specific(ItemMetaKey.Specific.To.NBT)
- static final ItemMetaKeyType<CustomData> BLOCK_ENTITY_TAG = new ItemMetaKeyType<>(DataComponents.BLOCK_ENTITY_DATA, "BlockEntityTag");
-+ static final ItemMetaKey BLOCK_ENTITY_TAG_CUSTOM_DATA = new ItemMetaKey("block-entity-tag"); // Paper
-+ static final ItemMetaKey BLOCK_ENTITY_COMPONENTS = new ItemMetaKey("block-entity-components"); // Paper
-
- final Material material;
-- private CraftBlockEntityState<?> blockEntityTag;
-+ // Paper start - store data separately
-+ DataComponentMap components;
-+ CustomData blockEntityTag;
-+ {
-+ // this is because the fields are possibly assigned in the super constructor (via deserializeInternal)
-+ // and a direct field initialization happens **after** the super constructor. So we only want to
-+ // set them to empty if they weren't assigned by the super constructor (via deserializeInternal)
-+ this.components = this.components != null ? this.components : DataComponentMap.EMPTY;
-+ this.blockEntityTag = this.blockEntityTag != null ? this.blockEntityTag : CustomData.EMPTY;
-+ }
-+ private Material materialForBlockEntityType() {
-+ return (this.material != Material.SHIELD) ? this.material : CraftMetaBlockState.shieldToBannerHack();
-+ }
-+ // Paper end
- private CompoundTag internalTag;
-
- CraftMetaBlockState(CraftMetaItem meta, Material material) {
-@@ -153,41 +168,61 @@ public class CraftMetaBlockState extends CraftMetaItem implements BlockStateMeta
-
- if (!(meta instanceof CraftMetaBlockState)
- || ((CraftMetaBlockState) meta).material != material) {
-- this.blockEntityTag = null;
-+ // Paper start
-+ this.components = DataComponentMap.EMPTY;
-+ this.blockEntityTag = CustomData.EMPTY;
-+ // Paper end
- return;
- }
-
- CraftMetaBlockState te = (CraftMetaBlockState) meta;
-+ // Paper start
-+ this.components = te.components;
- this.blockEntityTag = te.blockEntityTag;
-+ // Paper end
- }
-
- CraftMetaBlockState(DataComponentPatch tag, Material material, final Set<DataComponentType<?>> extraHandledDcts) { // Paper
- super(tag, extraHandledDcts); // Paper
- this.material = material;
-
-+ // Paper start - move to separate method to be re-called
-+ this.updateBlockState(tag);
-+ }
-+
-+ private void updateBlockState(final DataComponentPatch tag) {
-+ // Paper end
- getOrEmpty(tag, CraftMetaBlockState.BLOCK_ENTITY_TAG).ifPresent((nbt) -> {
-- this.blockEntityTag = CraftMetaBlockState.getBlockState(material, nbt.copyTag());
-+ this.blockEntityTag = nbt; // Paper
- });
-
- if (!tag.isEmpty()) {
-- CraftBlockEntityState<?> blockEntityTag = this.blockEntityTag;
-- if (blockEntityTag == null) {
-- blockEntityTag = CraftMetaBlockState.getBlockState(material, null);
-- }
--
-- // Convert to map
-- PatchedDataComponentMap map = new PatchedDataComponentMap(DataComponentMap.EMPTY);
-- map.applyPatch(tag);
-- // Apply
-- Set<DataComponentType<?>> applied = blockEntityTag.applyComponents(map, tag);
-+ // Paper start - store data in a DataComponentMap to be used to construct CraftBlockEntityStates
-+ final DataComponentMap.Builder map = DataComponentMap.builder();
-+ final net.minecraft.world.level.block.entity.BlockEntity dummyBlockEntity = java.util.Objects.requireNonNull(
-+ org.bukkit.craftbukkit.block.CraftBlockStates.createNewTileEntity(this.materialForBlockEntityType())
-+ );
-+
-+ // we don't care about what's in here, all
-+ // we want is to know which data component types are referenced
-+ Set<DataComponentType<?>> applied = dummyBlockEntity.applyComponentsSet(DataComponentMap.EMPTY, DataComponentPatch.EMPTY);
-+ // Paper end - store data in a DataComponentMap to be used to construct CraftBlockEntityStates
- // Mark applied components as handled
- for (DataComponentType<?> seen : applied) {
- this.unhandledTags.clear(seen);
- }
- // Only set blockEntityTag if something was applied
- if (!applied.isEmpty()) {
-- this.blockEntityTag = blockEntityTag;
-+ // Paper start
-+ for (final DataComponentType type : applied) {
-+ if (CraftMetaItem.DEFAULT_HANDLED_DCTS.contains(type)) continue;
-+ getOrEmpty(tag, type).ifPresent(value -> {
-+ map.set(type, value);
-+ });
-+ }
-+ // Paper end
- }
-+ this.components = map.build(); // Paper
- }
- }
-
-@@ -200,7 +235,10 @@ public class CraftMetaBlockState extends CraftMetaItem implements BlockStateMeta
- } else {
- this.material = Material.AIR;
- }
-- this.blockEntityTag = CraftMetaBlockState.getBlockState(this.material, this.internalTag);
-+ // Paper start
-+ if (this.internalTag != null) { // legacy
-+ this.setBlockState(CraftMetaBlockState.getBlockState(this.material, this.internalTag));
-+ }
- this.internalTag = null;
- }
-
-@@ -208,13 +246,21 @@ public class CraftMetaBlockState extends CraftMetaItem implements BlockStateMeta
- void applyToItem(CraftMetaItem.Applicator tag) {
- super.applyToItem(tag);
-
-- if (this.blockEntityTag != null) {
-- tag.put(CraftMetaBlockState.BLOCK_ENTITY_TAG, CustomData.of(this.blockEntityTag.getSnapshotNBTWithoutComponents()));
-+ // Paper start - accurately replicate logic for creating ItemStack from BlockEntity
-+ // taken from BlockEntity#saveToItem and BlockItem#setBlockEntityData
-+ final CompoundTag nbt = this.blockEntityTag.copyTag();
-+ if (nbt.contains("id", CraftMagicNumbers.NBT.TAG_STRING)) {
-+ tag.put(CraftMetaBlockState.BLOCK_ENTITY_TAG, CustomData.of(nbt));
-+ } else if (!nbt.isEmpty()) {
-+ BlockEntity.addEntityType(nbt, java.util.Objects.requireNonNull(CraftBlockStates.getBlockEntityType(this.materialForBlockEntityType())));
-+ tag.put(CraftMetaBlockState.BLOCK_ENTITY_TAG, CustomData.of(nbt));
-+ }
-
-- for (TypedDataComponent<?> component : this.blockEntityTag.collectComponents()) {
-- tag.putIfAbsent(component);
-- }
-+ for (final TypedDataComponent<?> component : this.components) {
-+ if (CraftMetaItem.DEFAULT_HANDLED_DCTS.contains(component.type())) continue; // if the component type was already handled by CraftMetaItem, don't add it again
-+ tag.builder.set(component);
- }
-+ // Paper end
- }
-
- @Override
-@@ -223,14 +269,29 @@ public class CraftMetaBlockState extends CraftMetaItem implements BlockStateMeta
-
- if (tag.contains(CraftMetaBlockState.BLOCK_ENTITY_TAG.NBT, CraftMagicNumbers.NBT.TAG_COMPOUND)) {
- this.internalTag = tag.getCompound(CraftMetaBlockState.BLOCK_ENTITY_TAG.NBT);
-+ return; // Paper - if legacy, don't check anything else
-+ }
-+ // Paper start - new serialization format
-+ if (tag.contains(CraftMetaBlockState.BLOCK_ENTITY_TAG_CUSTOM_DATA.NBT, CraftMagicNumbers.NBT.TAG_COMPOUND)) {
-+ this.blockEntityTag = CustomData.of(tag.getCompound(CraftMetaBlockState.BLOCK_ENTITY_TAG_CUSTOM_DATA.NBT));
- }
-+ if (tag.contains(CraftMetaBlockState.BLOCK_ENTITY_COMPONENTS.NBT, CraftMagicNumbers.NBT.TAG_COMPOUND)) {
-+ this.components = DataComponentMap.CODEC.parse(org.bukkit.craftbukkit.CraftRegistry.getMinecraftRegistry().createSerializationContext(net.minecraft.nbt.NbtOps.INSTANCE), tag.getCompound(CraftMetaBlockState.BLOCK_ENTITY_COMPONENTS.NBT)).getOrThrow();
-+ }
-+ // Paper end - new serialization format
- }
-
- @Override
- void serializeInternal(final Map<String, Tag> internalTags) {
-- if (this.blockEntityTag != null) {
-- internalTags.put(CraftMetaBlockState.BLOCK_ENTITY_TAG.NBT, this.blockEntityTag.getSnapshotNBT());
-+ // Paper start - new serialization format
-+ if (!this.blockEntityTag.isEmpty()) {
-+ internalTags.put(CraftMetaBlockState.BLOCK_ENTITY_TAG_CUSTOM_DATA.NBT, this.blockEntityTag.getUnsafe()); // unsafe because it's serialized right away
-+ }
-+ if (!this.components.isEmpty()) {
-+ final Tag componentsTag = DataComponentMap.CODEC.encodeStart(org.bukkit.craftbukkit.CraftRegistry.getMinecraftRegistry().createSerializationContext(net.minecraft.nbt.NbtOps.INSTANCE), this.components).getOrThrow();
-+ internalTags.put(CraftMetaBlockState.BLOCK_ENTITY_COMPONENTS.NBT, componentsTag);
- }
-+ // Paper end - new serialization format
- }
-
- @Override
-@@ -244,9 +305,10 @@ public class CraftMetaBlockState extends CraftMetaItem implements BlockStateMeta
- int applyHash() {
- final int original;
- int hash = original = super.applyHash();
-- if (this.blockEntityTag != null) {
-- hash = 61 * hash + this.blockEntityTag.hashCode();
-- }
-+ // Paper start
-+ hash = 61 * hash + this.blockEntityTag.hashCode();
-+ hash = 61 * hash + this.components.hashCode();
-+ // Paper end
- return original != hash ? CraftMetaBlockState.class.hashCode() ^ hash : hash;
- }
-
-@@ -258,19 +320,19 @@ public class CraftMetaBlockState extends CraftMetaItem implements BlockStateMeta
- if (meta instanceof CraftMetaBlockState) {
- CraftMetaBlockState that = (CraftMetaBlockState) meta;
-
-- return Objects.equal(this.blockEntityTag, that.blockEntityTag);
-+ return Objects.equal(this.blockEntityTag, that.blockEntityTag) && Objects.equal(this.components, that.components); // Paper
- }
- return true;
- }
-
- @Override
- boolean notUncommon(CraftMetaItem meta) {
-- return super.notUncommon(meta) && (meta instanceof CraftMetaBlockState || this.blockEntityTag == null);
-+ return super.notUncommon(meta) && (meta instanceof CraftMetaBlockState || (this.blockEntityTag.isEmpty() && this.components.isEmpty())); // Paper
- }
-
- @Override
- boolean isEmpty() {
-- return super.isEmpty() && this.blockEntityTag == null;
-+ return super.isEmpty() && this.blockEntityTag.isEmpty() && this.components.isEmpty(); // Paper
- }
-
- @Override
-@@ -281,27 +343,53 @@ public class CraftMetaBlockState extends CraftMetaItem implements BlockStateMeta
- @Override
- public CraftMetaBlockState clone() {
- CraftMetaBlockState meta = (CraftMetaBlockState) super.clone();
-- if (this.blockEntityTag != null) {
-- meta.blockEntityTag = this.blockEntityTag.copy();
-- }
-+ // Paper start - no need for "clone" because they are essentially immutables
-+ meta.blockEntityTag = this.blockEntityTag;
-+ meta.components = this.components;
-+ // Paper end
- return meta;
- }
-
- @Override
- public boolean hasBlockState() {
-- return this.blockEntityTag != null;
-+ return !this.blockEntityTag.isEmpty() || !this.components.isEmpty(); // Paper
- }
-
- // Paper start - add method to clear block state
- @Override
- public void clearBlockState() {
-- this.blockEntityTag = null;
-+ // Paper start
-+ this.blockEntityTag = CustomData.EMPTY;
-+ this.components = DataComponentMap.EMPTY;
-+ // Paper end
- }
- // Paper end - add method to clear block state
-
- @Override
-- public BlockState getBlockState() {
-- return (this.blockEntityTag != null) ? this.blockEntityTag.copy() : CraftMetaBlockState.getBlockState(this.material, null);
-+ // Paper start - create blockstate on-demand
-+ public CraftBlockEntityState<?> getBlockState() {
-+ BlockPos pos = BlockPos.ZERO;
-+ final Material stateMaterial = this.materialForBlockEntityType();
-+ if (!this.blockEntityTag.isEmpty()) {
-+ // Paper "id" field is always present now
-+ pos = BlockEntity.getPosFromTag(this.blockEntityTag.getUnsafe()); // unsafe is fine here, just querying
-+ }
-+ final net.minecraft.world.level.block.entity.BlockEntityType<?> type = java.util.Objects.requireNonNull(CraftBlockStates.getBlockEntityType(stateMaterial));
-+ final net.minecraft.world.level.block.state.BlockState nmsBlockState = ((org.bukkit.craftbukkit.block.data.CraftBlockData) this.getBlockData(stateMaterial)).getState();
-+ final net.minecraft.world.level.block.entity.BlockEntity blockEntity = java.util.Objects.requireNonNull(type.create(pos, nmsBlockState));
-+ if (!this.blockEntityTag.isEmpty()) {
-+ this.blockEntityTag.loadInto(blockEntity, org.bukkit.craftbukkit.CraftRegistry.getMinecraftRegistry());
-+ }
-+ final PatchedDataComponentMap patchedMap = new PatchedDataComponentMap(nmsBlockState.getBlock().asItem().components());
-+ patchedMap.setAll(this.components);
-+ final Applicator applicator = new Applicator() {};
-+ super.applyToItem(applicator);
-+ patchedMap.applyPatch(applicator.build());
-+ blockEntity.applyComponents(nmsBlockState.getBlock().asItem().components(), patchedMap.asPatch());
-+
-+ // This is expected to always return a CraftBlockEntityState for the passed material:
-+ return (CraftBlockEntityState<?>) CraftBlockStates.getBlockState(null, pos, nmsBlockState, blockEntity);
-+ // Paper end
- }
-
- private static CraftBlockEntityState<?> getBlockState(Material material, CompoundTag blockEntityTag) {
-@@ -331,7 +419,23 @@ public class CraftMetaBlockState extends CraftMetaItem implements BlockStateMeta
- Class<?> blockStateType = CraftBlockStates.getBlockStateType(stateMaterial);
- Preconditions.checkArgument(blockStateType == blockState.getClass() && blockState instanceof CraftBlockEntityState, "Invalid blockState for " + this.material);
-
-- this.blockEntityTag = (CraftBlockEntityState<?>) blockState;
-+ // Paper start - when a new BlockState is set, the components from that block entity
-+ // have to be used to update the fields on CraftMetaItem
-+ final CraftBlockEntityState<?> craftBlockState = (CraftBlockEntityState<?>) blockState;
-+ final CompoundTag data = craftBlockState.getSnapshotCustomNbtOnly();
-+ final PatchedDataComponentMap patchedMap = new net.minecraft.core.component.PatchedDataComponentMap(craftBlockState.getHandle().getBlock().asItem().components());
-+ final net.minecraft.core.component.DataComponentMap map = craftBlockState.collectComponents();
-+ patchedMap.setAll(map);
-+ if (!data.isEmpty()) {
-+ patchedMap.set(BLOCK_ENTITY_TAG.TYPE, CustomData.of(data));
-+ }
-+ final DataComponentPatch patch = patchedMap.asPatch();
-+ this.updateFromPatch(patch, null);
-+ // we have to reset the fields because this should be like a "new" block entity is being used
-+ this.blockEntityTag = CustomData.EMPTY;
-+ this.components = DataComponentMap.EMPTY;
-+ this.updateBlockState(patch);
-+ // Paper end
- }
-
- private static Material shieldToBannerHack() {
-diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBook.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBook.java
-index a395c7ce952f4a60a5edf80e8731afa6388d18ea..e213ac74f8f4a62b7b8b2b7889250f5cdeb348fe 100644
---- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBook.java
-+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBook.java
-@@ -34,7 +34,7 @@ public class CraftMetaBook extends CraftMetaItem implements BookMeta, WritableBo
- @ItemMetaKey.Specific(ItemMetaKey.Specific.To.NBT)
- static final ItemMetaKeyType<WritableBookContent> BOOK_CONTENT = new ItemMetaKeyType<>(DataComponents.WRITABLE_BOOK_CONTENT);
- static final ItemMetaKey BOOK_PAGES = new ItemMetaKey("pages");
-- static final int MAX_PAGES = Integer.MAX_VALUE; // SPIGOT-6911: Use Minecraft limits
-+ static final int MAX_PAGES = WritableBookContent.MAX_PAGES; // SPIGOT-6911: Use Minecraft limits // Paper
- static final int MAX_PAGE_LENGTH = WritableBookContent.PAGE_EDIT_LENGTH; // SPIGOT-6911: Use Minecraft limits
-
- // We store the pages in their raw original text representation. See SPIGOT-5063, SPIGOT-5350, SPIGOT-3206
-diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBookSigned.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBookSigned.java
-index 3f78a0935d738854182254b345064e3c225dcd5f..218df87c596d47b431dbbf2aa42822ef174f948f 100644
---- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBookSigned.java
-+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBookSigned.java
-@@ -116,8 +116,8 @@ public class CraftMetaBookSigned extends CraftMetaItem implements BookMeta {
- }
- }
-
-- this.resolved = SerializableMeta.getObject(Boolean.class, map, CraftMetaBookSigned.RESOLVED.BUKKIT, true);
-- this.generation = SerializableMeta.getObject(Integer.class, map, CraftMetaBookSigned.GENERATION.BUKKIT, true);
-+ this.resolved = SerializableMeta.getBoolean(map, CraftMetaBookSigned.RESOLVED.BUKKIT); // Paper - General ItemMeta fixes
-+ this.generation = SerializableMeta.getObjectOptionally(Integer.class, map, CraftMetaBookSigned.GENERATION.BUKKIT, true).orElse(0); // Paper - General ItemMeta Fixes
- }
-
- @Override
-@@ -129,7 +129,7 @@ public class CraftMetaBookSigned extends CraftMetaItem implements BookMeta {
- for (Component page : this.pages) {
- list.add(Filterable.passThrough(page));
- }
-- itemData.put(CraftMetaBookSigned.BOOK_CONTENT, new WrittenBookContent(Filterable.from(FilteredText.passThrough(this.title)), this.author, this.generation, list, this.resolved));
-+ itemData.put(CraftMetaBookSigned.BOOK_CONTENT, new WrittenBookContent(Filterable.from(FilteredText.passThrough(this.title == null ? "" : this.title)), this.author == null ? "" : this.author, this.generation, list, this.resolved));
- }
- }
-
-@@ -358,7 +358,13 @@ public class CraftMetaBookSigned extends CraftMetaItem implements BookMeta {
- }
-
- if (this.pages != null) {
-- builder.put(CraftMetaBookSigned.BOOK_PAGES.BUKKIT, ImmutableList.copyOf(this.pages));
-+ // Paper start - deserialization expects json
-+ final List<String> jsonPages = new ArrayList<>(this.pages.size());
-+ for (final Component page : this.pages) {
-+ jsonPages.add(CraftChatMessage.toJSON(page));
-+ }
-+ builder.put(CraftMetaBookSigned.BOOK_PAGES.BUKKIT, ImmutableList.copyOf(jsonPages));
-+ // Paper end - deserialization expects json
- }
-
- if (this.resolved) {
-diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBundle.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBundle.java
-index f8c02fe01fd95aa5de8523c9ad452d91f5d3c16f..4447c754458e9fdead003b4044bf2bee6fcbaaa2 100644
---- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBundle.java
-+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaBundle.java
-@@ -42,7 +42,7 @@ public class CraftMetaBundle extends CraftMetaItem implements BundleMeta {
- bundle.items().forEach((item) -> {
- ItemStack itemStack = CraftItemStack.asCraftMirror(item);
-
-- if (!itemStack.getType().isAir()) { // SPIGOT-7174 - Avoid adding air
-+ if (!itemStack.isEmpty()) { // SPIGOT-7174 - Avoid adding air // Paper
- this.addItem(itemStack);
- }
- });
-@@ -55,7 +55,7 @@ public class CraftMetaBundle extends CraftMetaItem implements BundleMeta {
- Iterable<?> items = SerializableMeta.getObject(Iterable.class, map, CraftMetaBundle.ITEMS.BUKKIT, true);
- if (items != null) {
- for (Object stack : items) {
-- if (stack instanceof ItemStack itemStack && !itemStack.getType().isAir()) { // SPIGOT-7174 - Avoid adding air
-+ if (stack instanceof ItemStack itemStack && !itemStack.isEmpty()) { // SPIGOT-7174 - Avoid adding air // Paper
- this.addItem(itemStack);
- }
- }
-@@ -116,7 +116,7 @@ public class CraftMetaBundle extends CraftMetaItem implements BundleMeta {
-
- @Override
- public void addItem(ItemStack item) {
-- Preconditions.checkArgument(item != null && !item.getType().isAir(), "item is null or air");
-+ Preconditions.checkArgument(item != null && !item.isEmpty(), "item is null or empty"); // Paper
-
- if (this.items == null) {
- this.items = new ArrayList<>();
-diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaColorableArmor.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaColorableArmor.java
-index 2c9ca54267579a210d4ea192517fc0fbce8e467a..4e423c4790d7b03c283c9a5fa94bce4a1153445e 100644
---- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaColorableArmor.java
-+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaColorableArmor.java
-@@ -22,16 +22,30 @@ public class CraftMetaColorableArmor extends CraftMetaArmor implements Colorable
- Material.WOLF_ARMOR
- );
-
-- private Color color = DEFAULT_LEATHER_COLOR;
-+ private Integer color; // Paper - keep color component consistent with vanilla (top byte is ignored)
-
- CraftMetaColorableArmor(CraftMetaItem meta) {
- super(meta);
-- CraftMetaLeatherArmor.readColor(this, meta);
-+ // Paper start
-+ if (!(meta instanceof CraftMetaColorableArmor armorMeta)) {
-+ return;
-+ }
-+
-+ this.color = armorMeta.color;
-+ // Paper end
- }
-
- CraftMetaColorableArmor(DataComponentPatch tag, java.util.Set<net.minecraft.core.component.DataComponentType<?>> extraHandledDcts) { // Paper
- super(tag, extraHandledDcts); // Paper
-- CraftMetaLeatherArmor.readColor(this, tag);
-+ // Paper start
-+ getOrEmpty(tag, CraftMetaLeatherArmor.COLOR).ifPresent((dyedItemColor) -> {
-+ if (!dyedItemColor.showInTooltip()) {
-+ this.addItemFlags(org.bukkit.inventory.ItemFlag.HIDE_DYE);
-+ }
-+
-+ this.color = dyedItemColor.rgb();
-+ });
-+ // Paper end
- }
-
- CraftMetaColorableArmor(Map<String, Object> map) {
-@@ -42,7 +56,11 @@ public class CraftMetaColorableArmor extends CraftMetaArmor implements Colorable
- @Override
- void applyToItem(CraftMetaItem.Applicator itemTag) {
- super.applyToItem(itemTag);
-- CraftMetaLeatherArmor.applyColor(this, itemTag);
-+ // Paper start
-+ if (this.hasColor()) {
-+ itemTag.put(CraftMetaLeatherArmor.COLOR, new net.minecraft.world.item.component.DyedItemColor(this.color, !this.hasItemFlag(org.bukkit.inventory.ItemFlag.HIDE_DYE)));
-+ }
-+ // Paper end
- }
-
- @Override
-@@ -68,16 +86,16 @@ public class CraftMetaColorableArmor extends CraftMetaArmor implements Colorable
-
- @Override
- public Color getColor() {
-- return this.color;
-+ return this.color == null ? DEFAULT_LEATHER_COLOR : Color.fromRGB(this.color & 0xFFFFFF); // Paper - this should really be nullable
- }
-
- @Override
- public void setColor(Color color) {
-- this.color = color == null ? DEFAULT_LEATHER_COLOR : color;
-+ this.color = color == null ? null : color.asRGB(); // Paper
- }
-
- boolean hasColor() {
-- return CraftMetaLeatherArmor.hasColor(this);
-+ return this.color != null; // Paper
- }
-
- @Override
-@@ -97,7 +115,7 @@ public class CraftMetaColorableArmor extends CraftMetaArmor implements Colorable
- if (meta instanceof CraftMetaColorableArmor) {
- CraftMetaColorableArmor that = (CraftMetaColorableArmor) meta;
-
-- return this.color.equals(that.color);
-+ return this.hasColor() ? that.hasColor() && this.color.equals(that.color) : !that.hasColor(); // Paper - allow null
- }
- return true;
- }
-diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaCompass.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaCompass.java
-index bbca26f5debb263b04516e68f6e49f68a38fa5b1..aacc4d010f4dfa4d9d11332b802205a6f35b6de3 100644
---- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaCompass.java
-+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaCompass.java
-@@ -36,7 +36,7 @@ public class CraftMetaCompass extends CraftMetaItem implements CompassMeta {
- private int lodestoneX;
- private int lodestoneY;
- private int lodestoneZ;
-- private boolean tracked = true;
-+ private Boolean tracked = null; // Paper - tri-state
-
- CraftMetaCompass(CraftMetaItem meta) {
- super(meta);
-@@ -80,7 +80,7 @@ public class CraftMetaCompass extends CraftMetaItem implements CompassMeta {
- this.setLodestone(lodestone);
- }
- }
-- this.tracked = SerializableMeta.getBoolean(map, CraftMetaCompass.LODESTONE_TRACKED.BUKKIT);
-+ this.tracked = SerializableMeta.getObjectOptionally(Boolean.class, map, CraftMetaCompass.LODESTONE_TRACKED.BUKKIT, true).orElse(null); // Paper - tri-state
- }
-
- @Override
-@@ -146,12 +146,12 @@ public class CraftMetaCompass extends CraftMetaItem implements CompassMeta {
- }
-
- boolean hasLodestoneTracked() {
-- return !this.tracked;
-+ return this.tracked != null; // Paper - tri-state
- }
-
- @Override
- public boolean isLodestoneTracked() {
-- return this.tracked;
-+ return this.tracked != null && this.tracked; // Paper - tri-state
- }
-
- @Override
-diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaCrossbow.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaCrossbow.java
-index a3fa95377e083e51ad7596d21eeb08172bdb18b2..0f5a64fc5eb619e18f5eeb97f377c9d593e4a38f 100644
---- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaCrossbow.java
-+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaCrossbow.java
-@@ -122,7 +122,7 @@ public class CraftMetaCrossbow extends CraftMetaItem implements CrossbowMeta {
- @Override
- public void addChargedProjectile(ItemStack item) {
- Preconditions.checkArgument(item != null, "item");
-- Preconditions.checkArgument(item.getType() == Material.FIREWORK_ROCKET || CraftItemType.bukkitToMinecraft(item.getType()) instanceof ArrowItem, "Item %s is not an arrow or firework rocket", item);
-+ Preconditions.checkArgument(!item.isEmpty(), "Item cannot be empty"); // Paper
-
- if (this.chargedProjectiles == null) {
- this.chargedProjectiles = new ArrayList<>();
-diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaEntityTag.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaEntityTag.java
-index 3f6c5cbbf63631e4b72dc43558651ea94f31ca78..da474a5b963d8e6769d120e9091e60ed0a468a9f 100644
---- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaEntityTag.java
-+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaEntityTag.java
-@@ -98,7 +98,7 @@ public class CraftMetaEntityTag extends CraftMetaItem {
- if (meta instanceof CraftMetaEntityTag) {
- CraftMetaEntityTag that = (CraftMetaEntityTag) meta;
-
-- return this.entityTag != null ? that.entityTag != null && this.entityTag.equals(that.entityTag) : this.entityTag == null;
-+ return this.entityTag != null ? that.entityTag != null && this.entityTag.equals(that.entityTag) : that.entityTag == null; // Paper
- }
- return true;
- }
-diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaFirework.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaFirework.java
-index 8e0dd4b7a7a25a8beb27b507047bc48d8227627c..77489c3ffaa3a72d4cf105499a77150fca6d8526 100644
---- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaFirework.java
-+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaFirework.java
-@@ -55,7 +55,7 @@ class CraftMetaFirework extends CraftMetaItem implements FireworkMeta {
-
- this.power = that.power;
-
-- if (that.hasEffects()) {
-+ if (that.effects != null) { // Paper
- this.effects = new ArrayList<>(that.effects);
- }
- }
-@@ -86,19 +86,14 @@ class CraftMetaFirework extends CraftMetaItem implements FireworkMeta {
- .with(CraftMetaFirework.getEffectType(explosion.shape()));
-
- IntList colors = explosion.colors();
-- // People using buggy command generators specify a list rather than an int here, so recover with dummy data.
-- // Wrong: Colors: [1234]
-- // Right: Colors: [I;1234]
-- if (colors.isEmpty()) {
-- effect.withColor(Color.WHITE);
-- }
-+ // Paper - this is no longer needed
-
- for (int color : colors) {
-- effect.withColor(Color.fromRGB(color));
-+ effect.withColor(Color.fromRGB(color & 0xFFFFFF)); // Paper - try to keep color component consistent with vanilla (top byte is ignored), this will however change the color component for out of bound color
- }
-
- for (int color : explosion.fadeColors()) {
-- effect.withFade(Color.fromRGB(color));
-+ effect.withFade(Color.fromRGB(color & 0xFFFFFF)); // Paper
- }
-
- return effect.build();
-@@ -154,7 +149,7 @@ class CraftMetaFirework extends CraftMetaItem implements FireworkMeta {
- }
-
- Iterable<?> effects = SerializableMeta.getObject(Iterable.class, map, CraftMetaFirework.EXPLOSIONS.BUKKIT, true);
-- this.safelyAddEffects(effects);
-+ this.safelyAddEffects(effects, false); // Paper - limit firework effects
- }
-
- @Override
-@@ -162,7 +157,7 @@ class CraftMetaFirework extends CraftMetaItem implements FireworkMeta {
- return !(this.effects == null || this.effects.isEmpty());
- }
-
-- void safelyAddEffects(Iterable<?> collection) {
-+ void safelyAddEffects(Iterable<?> collection, final boolean throwOnOversize) { // Paper
- if (collection == null || (collection instanceof Collection && ((Collection<?>) collection).isEmpty())) {
- return;
- }
-@@ -174,6 +169,15 @@ class CraftMetaFirework extends CraftMetaItem implements FireworkMeta {
-
- for (Object obj : collection) {
- Preconditions.checkArgument(obj instanceof FireworkEffect, "%s in %s is not a FireworkEffect", obj, collection);
-+ // Paper start - limit firework effects
-+ if (effects.size() + 1 > Fireworks.MAX_EXPLOSIONS) {
-+ if (throwOnOversize) {
-+ throw new IllegalArgumentException("Cannot have more than " + Fireworks.MAX_EXPLOSIONS + " firework effects");
-+ } else {
-+ continue;
-+ }
-+ }
-+ // Paper end - limit firework effects
- effects.add((FireworkEffect) obj);
- }
- }
-@@ -186,9 +190,13 @@ class CraftMetaFirework extends CraftMetaItem implements FireworkMeta {
- }
-
- List<FireworkExplosion> effects = new ArrayList<>();
-- for (FireworkEffect effect : this.effects) {
-- effects.add(CraftMetaFirework.getExplosion(effect));
-+ // Paper start - fix NPE with effects list being null
-+ if (this.effects != null) {
-+ for (FireworkEffect effect : this.effects) {
-+ effects.add(CraftMetaFirework.getExplosion(effect));
-+ }
- }
-+ // Paper end
-
- itemTag.put(CraftMetaFirework.FIREWORKS, new Fireworks(this.power, effects));
- }
-@@ -218,7 +226,7 @@ class CraftMetaFirework extends CraftMetaItem implements FireworkMeta {
- }
-
- boolean isFireworkEmpty() {
-- return !(this.hasEffects() || this.hasPower());
-+ return !(this.effects != null || this.hasPower()); // Paper - empty effects list should stay on the item
- }
-
- boolean hasPower() {
-@@ -234,7 +242,7 @@ class CraftMetaFirework extends CraftMetaItem implements FireworkMeta {
- if (meta instanceof CraftMetaFirework that) {
-
- return (this.hasPower() ? that.hasPower() && this.power == that.power : !that.hasPower())
-- && (this.hasEffects() ? that.hasEffects() && this.effects.equals(that.effects) : !that.hasEffects());
-+ && (this.effects != null ? that.effects != null && this.effects.equals(that.effects) : that.effects == null); // Paper
- }
-
- return true;
-@@ -252,7 +260,7 @@ class CraftMetaFirework extends CraftMetaItem implements FireworkMeta {
- if (this.hasPower()) {
- hash = 61 * hash + this.power;
- }
-- if (this.hasEffects()) {
-+ if (this.effects != null) { // Paper
- hash = 61 * hash + 13 * this.effects.hashCode();
- }
- return hash != original ? CraftMetaFirework.class.hashCode() ^ hash : hash;
-@@ -262,7 +270,7 @@ class CraftMetaFirework extends CraftMetaItem implements FireworkMeta {
- Builder<String, Object> serialize(Builder<String, Object> builder) {
- super.serialize(builder);
-
-- if (this.hasEffects()) {
-+ if (this.effects != null) { // Paper
- builder.put(CraftMetaFirework.EXPLOSIONS.BUKKIT, ImmutableList.copyOf(this.effects));
- }
-
-@@ -287,6 +295,7 @@ class CraftMetaFirework extends CraftMetaItem implements FireworkMeta {
- @Override
- public void addEffect(FireworkEffect effect) {
- Preconditions.checkArgument(effect != null, "FireworkEffect cannot be null");
-+ Preconditions.checkArgument(this.effects == null || this.effects.size() + 1 <= Fireworks.MAX_EXPLOSIONS, "cannot have more than %s firework effects", Fireworks.MAX_EXPLOSIONS); // Paper - limit firework effects
- if (this.effects == null) {
- this.effects = new ArrayList<FireworkEffect>();
- }
-@@ -296,6 +305,10 @@ class CraftMetaFirework extends CraftMetaItem implements FireworkMeta {
- @Override
- public void addEffects(FireworkEffect... effects) {
- Preconditions.checkArgument(effects != null, "effects cannot be null");
-+ // Paper start - limit firework effects
-+ final int initialSize = this.effects == null ? 0 : this.effects.size();
-+ Preconditions.checkArgument(initialSize + effects.length <= Fireworks.MAX_EXPLOSIONS, "Cannot have more than %s firework effects", Fireworks.MAX_EXPLOSIONS);
-+ // Paper end - limit firework effects
- if (effects.length == 0) {
- return;
- }
-@@ -314,7 +327,7 @@ class CraftMetaFirework extends CraftMetaItem implements FireworkMeta {
- @Override
- public void addEffects(Iterable<FireworkEffect> effects) {
- Preconditions.checkArgument(effects != null, "effects cannot be null");
-- this.safelyAddEffects(effects);
-+ this.safelyAddEffects(effects, true); // Paper - limit firework effects
- }
-
- @Override
-@@ -349,7 +362,7 @@ class CraftMetaFirework extends CraftMetaItem implements FireworkMeta {
- @Override
- public void setPower(int power) {
- Preconditions.checkArgument(power >= 0, "power cannot be less than zero: %s", power);
-- Preconditions.checkArgument(power < 0x80, "power cannot be more than 127: %s", power);
-+ Preconditions.checkArgument(power <= 0xFF, "power cannot be more than 255: %s", power); // Paper - set correct limit
- this.power = power;
- }
- }
-diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java
-index 5d86861a0df7308ae9b8440e5d9136fa7c8f1835..60aa9dd1617d302c3b49af21d581a32313c171cc 100644
---- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java
-+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java
-@@ -175,9 +175,10 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
- }
- }
-
-- static final class Applicator {
-+ static abstract class Applicator { // Paper - support updating profile after resolving it
-
-- private final DataComponentPatch.Builder builder = DataComponentPatch.builder();
-+ final DataComponentPatch.Builder builder = DataComponentPatch.builder(); // Paper - private -> package-private
-+ void skullCallback(com.mojang.authlib.GameProfile gameProfile) {} // Paper - support updating profile after resolving it
-
- <T> Applicator put(ItemMetaKeyType<T> key, T value) {
- this.builder.set(key.TYPE, value);
-@@ -299,7 +300,7 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
- this.enchantments = new EnchantmentMap(meta.enchantments); // Paper
- }
-
-- if (meta.hasAttributeModifiers()) {
-+ if (meta.attributeModifiers != null) { // Paper
- this.attributeModifiers = LinkedHashMultimap.create(meta.attributeModifiers);
- }
-
-@@ -332,6 +333,11 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
- }
-
- CraftMetaItem(DataComponentPatch tag, Set<DataComponentType<?>> extraHandledTags) { // Paper - improve handled tags on type changes
-+ // Paper start - properly support data components in BlockEntity
-+ this.updateFromPatch(tag, extraHandledTags);
-+ }
-+ protected final void updateFromPatch(DataComponentPatch tag, Set<DataComponentType<?>> extraHandledTags) {
-+ // Paper end - properly support data components in BlockEntity
- CraftMetaItem.getOrEmpty(tag, CraftMetaItem.NAME).ifPresent((component) -> {
- this.displayName = component;
- });
-@@ -750,7 +756,7 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
- Map<?, ?> mods = SerializableMeta.getObject(Map.class, map, key.BUKKIT, true);
- Multimap<Attribute, AttributeModifier> result = LinkedHashMultimap.create();
- if (mods == null) {
-- return result;
-+ return null; // Paper - null is different from an empty map
- }
-
- for (Object obj : mods.keySet()) {
-@@ -908,10 +914,8 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
- }
-
- void applyModifiers(Multimap<Attribute, AttributeModifier> modifiers, CraftMetaItem.Applicator tag) {
-- if (modifiers == null || modifiers.isEmpty()) {
-- if (this.hasItemFlag(ItemFlag.HIDE_ATTRIBUTES)) {
-- tag.put(CraftMetaItem.ATTRIBUTES, new ItemAttributeModifiers(Collections.emptyList(), false));
-- }
-+ if (modifiers == null/* || modifiers.isEmpty()*/) { // Paper - empty modifiers has a specific meaning, they should still be saved
-+ // Paper - don't save ItemFlag if the underlying data isn't present
- return;
- }
-
-@@ -940,7 +944,7 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
-
- @Overridden
- boolean isEmpty() {
-- return !(this.hasDisplayName() || this.hasItemName() || this.hasLocalizedName() || this.hasEnchants() || (this.lore != null) || this.hasCustomModelData() || this.hasBlockData() || this.hasRepairCost() || !this.unhandledTags.build().isEmpty() || !this.persistentDataContainer.isEmpty() || this.hideFlag != 0 || this.isHideTooltip() || this.isUnbreakable() || this.hasEnchantmentGlintOverride() || this.isFireResistant() || this.hasMaxStackSize() || this.hasRarity() || this.hasFood() || this.hasTool() || this.hasDamage() || this.hasMaxDamage() || this.hasAttributeModifiers() || this.customTag != null || this.canPlaceOnPredicates != null || this.canBreakPredicates != null); // Paper
-+ return !(this.hasDisplayName() || this.hasItemName() || this.hasLocalizedName() || this.hasEnchants() || (this.lore != null) || this.hasCustomModelData() || this.hasBlockData() || this.hasRepairCost() || !this.unhandledTags.build().isEmpty() || !this.persistentDataContainer.isEmpty() || this.hideFlag != 0 || this.isHideTooltip() || this.isUnbreakable() || this.hasEnchantmentGlintOverride() || this.isFireResistant() || this.hasMaxStackSize() || this.hasRarity() || this.hasFood() || this.hasTool() || this.hasDamage() || this.hasMaxDamage() || this.attributeModifiers != null || this.customTag != null || this.canPlaceOnPredicates != null || this.canBreakPredicates != null); // Paper
- }
-
- // Paper start
-@@ -1036,6 +1040,7 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
-
- @Override
- public void lore(final List<? extends net.kyori.adventure.text.Component> lore) {
-+ Preconditions.checkArgument(lore == null || lore.size() <= ItemLore.MAX_LINES, "lore cannot have more than %s lines", ItemLore.MAX_LINES); // Paper - limit lore lines
- this.lore = lore != null ? io.papermc.paper.adventure.PaperAdventure.asVanilla(lore) : null;
- }
- // Paper end
-@@ -1160,6 +1165,7 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
- // Paper end
- @Override
- public void setLore(List<String> lore) {
-+ Preconditions.checkArgument(lore == null || lore.size() <= ItemLore.MAX_LINES, "lore cannot have more than %s lines", ItemLore.MAX_LINES); // Paper - limit lore lines
- if (lore == null || lore.isEmpty()) {
- this.lore = null;
- } else {
-@@ -1175,6 +1181,7 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
- // Paper start
- @Override
- public void setLoreComponents(List<net.md_5.bungee.api.chat.BaseComponent[]> lore) {
-+ Preconditions.checkArgument(lore == null || lore.size() <= ItemLore.MAX_LINES, "lore cannot have more than %s lines", ItemLore.MAX_LINES); // Paper - limit lore lines
- if (lore == null) {
- this.lore = null;
- } else {
-@@ -1316,7 +1323,7 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
-
- @Override
- public FoodComponent getFood() {
-- return (this.hasFood()) ? new CraftFoodComponent(this.food) : new CraftFoodComponent(new FoodProperties(0, 0, false, 0, Collections.emptyList()));
-+ return (this.hasFood()) ? new CraftFoodComponent(this.food) : new CraftFoodComponent(new FoodProperties(0, 0, false, FoodProperties.DEFAULT_EAT_SECONDS, Collections.emptyList())); // Paper - create a valid food properties
- }
-
- @Override
-@@ -1357,7 +1364,7 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
-
- @Override
- public Multimap<Attribute, AttributeModifier> getAttributeModifiers(@Nullable EquipmentSlot slot) {
-- this.checkAttributeList();
-+ if (this.attributeModifiers == null) return LinkedHashMultimap.create(); // Paper - don't change the components
- SetMultimap<Attribute, AttributeModifier> result = LinkedHashMultimap.create();
- for (Map.Entry<Attribute, AttributeModifier> entry : this.attributeModifiers.entries()) {
- if (entry.getValue().getSlot() == null || entry.getValue().getSlot() == slot) {
-@@ -1370,6 +1377,7 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
- @Override
- public Collection<AttributeModifier> getAttributeModifiers(@Nonnull Attribute attribute) {
- Preconditions.checkNotNull(attribute, "Attribute cannot be null");
-+ if (this.attributeModifiers == null) return null; // Paper - fix NPE
- return this.attributeModifiers.containsKey(attribute) ? ImmutableList.copyOf(this.attributeModifiers.get(attribute)) : null;
- }
-
-@@ -1377,10 +1385,12 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
- public boolean addAttributeModifier(@Nonnull Attribute attribute, @Nonnull AttributeModifier modifier) {
- Preconditions.checkNotNull(attribute, "Attribute cannot be null");
- Preconditions.checkNotNull(modifier, "AttributeModifier cannot be null");
-- this.checkAttributeList();
-+ if (this.attributeModifiers != null) { // Paper
- for (Map.Entry<Attribute, AttributeModifier> entry : this.attributeModifiers.entries()) {
- Preconditions.checkArgument(!(entry.getValue().getUniqueId().equals(modifier.getUniqueId()) && entry.getKey() == attribute), "Cannot register AttributeModifier. Modifier is already applied! %s", modifier); // Paper
- }
-+ } // Paper
-+ this.checkAttributeList(); // Paper - moved down
- return this.attributeModifiers.put(attribute, modifier);
- }
-
-@@ -1391,8 +1401,11 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
- return;
- }
-
-- this.checkAttributeList();
-- this.attributeModifiers.clear();
-+ // Paper start - fix modifiers meta
-+ if (this.attributeModifiers != null) {
-+ this.attributeModifiers.clear();
-+ }
-+ // Paper end
-
- Iterator<Map.Entry<Attribute, AttributeModifier>> iterator = attributeModifiers.entries().iterator();
- while (iterator.hasNext()) {
-@@ -1402,6 +1415,7 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
- iterator.remove();
- continue;
- }
-+ this.checkAttributeList(); // Paper - moved down
- this.attributeModifiers.put(next.getKey(), next.getValue());
- }
- }
-@@ -1409,13 +1423,13 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
- @Override
- public boolean removeAttributeModifier(@Nonnull Attribute attribute) {
- Preconditions.checkNotNull(attribute, "Attribute cannot be null");
-- this.checkAttributeList();
-+ if (this.attributeModifiers == null) return false; // Paper
- return !this.attributeModifiers.removeAll(attribute).isEmpty();
- }
-
- @Override
- public boolean removeAttributeModifier(@Nullable EquipmentSlot slot) {
-- this.checkAttributeList();
-+ if (this.attributeModifiers == null) return false; // Paper
- int removed = 0;
- Iterator<Map.Entry<Attribute, AttributeModifier>> iter = this.attributeModifiers.entries().iterator();
-
-@@ -1435,7 +1449,7 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
- public boolean removeAttributeModifier(@Nonnull Attribute attribute, @Nonnull AttributeModifier modifier) {
- Preconditions.checkNotNull(attribute, "Attribute cannot be null");
- Preconditions.checkNotNull(modifier, "AttributeModifier cannot be null");
-- this.checkAttributeList();
-+ if (this.attributeModifiers == null) return false; // Paper
- int removed = 0;
- Iterator<Map.Entry<Attribute, AttributeModifier>> iter = this.attributeModifiers.entries().iterator();
-
-@@ -1457,7 +1471,7 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
-
- @Override
- public String getAsString() {
-- CraftMetaItem.Applicator tag = new CraftMetaItem.Applicator();
-+ CraftMetaItem.Applicator tag = new CraftMetaItem.Applicator() {}; // Paper - support updating profile after resolving it
- this.applyToItem(tag);
- DataComponentPatch patch = tag.build();
- Tag nbt = DataComponentPatch.CODEC.encodeStart(MinecraftServer.getDefaultRegistryAccess().createSerializationContext(NbtOps.INSTANCE), patch).getOrThrow();
-@@ -1466,7 +1480,7 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
-
- @Override
- public String getAsComponentString() {
-- CraftMetaItem.Applicator tag = new CraftMetaItem.Applicator();
-+ CraftMetaItem.Applicator tag = new CraftMetaItem.Applicator() {}; // Paper
- this.applyToItem(tag);
- DataComponentPatch patch = tag.build();
-
-@@ -1506,6 +1520,7 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
- if (first == null || second == null) {
- return false;
- }
-+ if (first.isEmpty() && second.isEmpty()) return true; // Paper - empty modifiers are equivalent
- for (Map.Entry<Attribute, AttributeModifier> entry : first.entries()) {
- if (!second.containsEntry(entry.getKey(), entry.getValue())) {
- return false;
-@@ -1531,6 +1546,8 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
-
- @Override
- public void setDamage(int damage) {
-+ Preconditions.checkArgument(damage >= 0, "Damage cannot be negative"); // Paper
-+ Preconditions.checkArgument(!this.hasMaxDamage() || damage <= this.maxDamage, "Damage cannot exceed max damage"); // Paper
- this.damage = damage;
- }
-
-@@ -1547,6 +1564,7 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
-
- @Override
- public void setMaxDamage(Integer maxDamage) {
-+ Preconditions.checkArgument(maxDamage == null || maxDamage > 0, "Max damage should be positive"); // Paper
- this.maxDamage = maxDamage;
- }
-
-@@ -1578,7 +1596,7 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
- && (this.hasCustomModelData() ? that.hasCustomModelData() && this.customModelData.equals(that.customModelData) : !that.hasCustomModelData())
- && (this.hasBlockData() ? that.hasBlockData() && this.blockData.equals(that.blockData) : !that.hasBlockData())
- && (this.hasRepairCost() ? that.hasRepairCost() && this.repairCost == that.repairCost : !that.hasRepairCost())
-- && (this.hasAttributeModifiers() ? that.hasAttributeModifiers() && CraftMetaItem.compareModifiers(this.attributeModifiers, that.attributeModifiers) : !that.hasAttributeModifiers())
-+ && (this.attributeModifiers != null ? that.attributeModifiers != null && CraftMetaItem.compareModifiers(this.attributeModifiers, that.attributeModifiers) : that.attributeModifiers == null) // Paper - track only null modifiers
- && (this.unhandledTags.equals(that.unhandledTags))
- && (Objects.equals(this.customTag, that.customTag))
- && (this.persistentDataContainer.equals(that.persistentDataContainer))
-@@ -1636,8 +1654,8 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
- hash = 61 * hash + (this.hasFood() ? this.food.hashCode() : 0);
- hash = 61 * hash + (this.hasTool() ? this.tool.hashCode() : 0);
- hash = 61 * hash + (this.hasDamage() ? this.damage : 0);
-- hash = 61 * hash + (this.hasMaxDamage() ? 1231 : 1237);
-- hash = 61 * hash + (this.hasAttributeModifiers() ? this.attributeModifiers.hashCode() : 0);
-+ hash = 61 * hash + (this.hasMaxDamage() ? this.maxDamage.hashCode() : 0); // Paper - max damage is not a boolean
-+ hash = 61 * hash + (this.attributeModifiers != null ? this.attributeModifiers.hashCode() : 0); // Paper - track only null attributes
- hash = 61 * hash + (this.canPlaceOnPredicates != null ? this.canPlaceOnPredicates.hashCode() : 0); // Paper
- hash = 61 * hash + (this.canBreakPredicates != null ? this.canBreakPredicates.hashCode() : 0); // Paper
- hash = 61 * hash + this.version;
-@@ -1657,7 +1675,7 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
- if (this.enchantments != null) {
- clone.enchantments = new EnchantmentMap(this.enchantments); // Paper
- }
-- if (this.hasAttributeModifiers()) {
-+ if (this.attributeModifiers != null) { // Paper
- clone.attributeModifiers = LinkedHashMultimap.create(this.attributeModifiers);
- }
- if (this.customTag != null) {
-@@ -1864,7 +1882,7 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
- }
-
- static void serializeModifiers(Multimap<Attribute, AttributeModifier> modifiers, ImmutableMap.Builder<String, Object> builder, ItemMetaKey key) {
-- if (modifiers == null || modifiers.isEmpty()) {
-+ if (modifiers == null/* || modifiers.isEmpty()*/) { // Paper - null and an empty map have different behaviors
- return;
- }
-
-@@ -1946,7 +1964,7 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
- // Paper start - improve checking handled tags
- @org.jetbrains.annotations.VisibleForTesting
- public static final Map<Class<? extends CraftMetaItem>, Set<DataComponentType<?>>> HANDLED_DCTS_PER_TYPE = new HashMap<>();
-- private static final Set<DataComponentType<?>> DEFAULT_HANDLED_DCTS = Set.of(
-+ protected static final Set<DataComponentType<?>> DEFAULT_HANDLED_DCTS = Set.of(
- CraftMetaItem.NAME.TYPE,
- CraftMetaItem.ITEM_NAME.TYPE,
- CraftMetaItem.LORE.TYPE,
-@@ -2013,7 +2031,12 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
- // Paper end - improve checking handled data component types
-
- protected static <T> Optional<? extends T> getOrEmpty(DataComponentPatch tag, ItemMetaKeyType<T> type) {
-- Optional<? extends T> result = tag.get(type.TYPE);
-+ // Paper start
-+ return getOrEmpty(tag, type.TYPE);
-+ }
-+ protected static <T> Optional<? extends T> getOrEmpty(final DataComponentPatch tag, final DataComponentType<T> type) {
-+ Optional<? extends T> result = tag.get(type);
-+ // Paper end
-
- return (result != null) ? result : Optional.empty();
- }
-diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaLeatherArmor.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaLeatherArmor.java
-index 157a7b7351f48e68d2923c72ed3bbe3dcae21383..70c0d4cc85c045d040a35cd406f3f7ce9b6a58fa 100644
---- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaLeatherArmor.java
-+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaLeatherArmor.java
-@@ -28,16 +28,30 @@ class CraftMetaLeatherArmor extends CraftMetaItem implements LeatherArmorMeta {
-
- static final ItemMetaKeyType<DyedItemColor> COLOR = new ItemMetaKeyType<>(DataComponents.DYED_COLOR, "color");
-
-- private Color color = DEFAULT_LEATHER_COLOR;
-+ private Integer color; // Paper - keep color component consistent with vanilla (top byte is ignored)
-
- CraftMetaLeatherArmor(CraftMetaItem meta) {
- super(meta);
-- CraftMetaLeatherArmor.readColor(this, meta);
-+ // Paper start
-+ if (!(meta instanceof CraftMetaLeatherArmor leatherMeta)) {
-+ return;
-+ }
-+
-+ this.color = leatherMeta.color;
-+ // Paper end
- }
-
- CraftMetaLeatherArmor(DataComponentPatch tag, java.util.Set<net.minecraft.core.component.DataComponentType<?>> extraHandledDcts) { // Paper
- super(tag, extraHandledDcts); // Paper
-- CraftMetaLeatherArmor.readColor(this, tag);
-+ // Paper start
-+ getOrEmpty(tag, CraftMetaLeatherArmor.COLOR).ifPresent((dyedItemColor) -> {
-+ if (!dyedItemColor.showInTooltip()) {
-+ this.addItemFlags(ItemFlag.HIDE_DYE);
-+ }
-+
-+ this.color = dyedItemColor.rgb();
-+ });
-+ // Paper end
- }
-
- CraftMetaLeatherArmor(Map<String, Object> map) {
-@@ -48,7 +62,11 @@ class CraftMetaLeatherArmor extends CraftMetaItem implements LeatherArmorMeta {
- @Override
- void applyToItem(CraftMetaItem.Applicator itemTag) {
- super.applyToItem(itemTag);
-- CraftMetaLeatherArmor.applyColor(this, itemTag);
-+ // Paper start
-+ if (this.hasColor()) {
-+ itemTag.put(CraftMetaLeatherArmor.COLOR, new DyedItemColor(this.color, !this.hasItemFlag(ItemFlag.HIDE_DYE)));
-+ }
-+ // Paper end
- }
-
- @Override
-@@ -72,16 +90,16 @@ class CraftMetaLeatherArmor extends CraftMetaItem implements LeatherArmorMeta {
-
- @Override
- public Color getColor() {
-- return this.color;
-+ return this.color == null ? DEFAULT_LEATHER_COLOR : Color.fromRGB(this.color & 0xFFFFFF); // Paper
- }
-
- @Override
- public void setColor(Color color) {
-- this.color = color == null ? DEFAULT_LEATHER_COLOR : color;
-+ this.color = color == null ? null : color.asRGB(); // Paper
- }
-
- boolean hasColor() {
-- return CraftMetaLeatherArmor.hasColor(this);
-+ return this.color != null; // Paper
- }
-
- @Override
-@@ -101,7 +119,7 @@ class CraftMetaLeatherArmor extends CraftMetaItem implements LeatherArmorMeta {
- if (meta instanceof CraftMetaLeatherArmor) {
- CraftMetaLeatherArmor that = (CraftMetaLeatherArmor) meta;
-
-- return this.color.equals(that.color);
-+ return this.hasColor() ? that.hasColor() && this.color.equals(that.color) : !that.hasColor(); // Paper - allow null
- }
- return true;
- }
-@@ -121,14 +139,16 @@ class CraftMetaLeatherArmor extends CraftMetaItem implements LeatherArmorMeta {
- return original != hash ? CraftMetaLeatherArmor.class.hashCode() ^ hash : hash;
- }
-
-+ @io.papermc.paper.annotation.DoNotUse // Paper
- static void readColor(LeatherArmorMeta meta, CraftMetaItem other) {
- if (!(other instanceof CraftMetaLeatherArmor armorMeta)) {
- return;
- }
-
-- meta.setColor(armorMeta.color);
-+ // meta.setColor(armorMeta.color); // Paper - commented out, color is now an integer and cannot be passed to setColor
- }
-
-+ @io.papermc.paper.annotation.DoNotUse // Paper
- static void readColor(LeatherArmorMeta meta, DataComponentPatch tag) {
- getOrEmpty(tag, CraftMetaLeatherArmor.COLOR).ifPresent((dyedItemColor) -> {
- if (!dyedItemColor.showInTooltip()) {
-@@ -151,6 +171,7 @@ class CraftMetaLeatherArmor extends CraftMetaItem implements LeatherArmorMeta {
- return !DEFAULT_LEATHER_COLOR.equals(meta.getColor());
- }
-
-+ @io.papermc.paper.annotation.DoNotUse // Paper
- static void applyColor(LeatherArmorMeta meta, CraftMetaItem.Applicator tag) {
- if (CraftMetaLeatherArmor.hasColor(meta)) {
- tag.put(CraftMetaLeatherArmor.COLOR, new DyedItemColor(meta.getColor().asRGB(), !meta.hasItemFlag(ItemFlag.HIDE_DYE)));
-diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaMap.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaMap.java
-index d829f4da371b44e7480896118547734be400a314..0ff40e10ce8bab8010222ef7697d585430aeda03 100644
---- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaMap.java
-+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaMap.java
-@@ -30,7 +30,7 @@ class CraftMetaMap extends CraftMetaItem implements MapMeta {
-
- private Integer mapId;
- private byte scaling = CraftMetaMap.SCALING_EMPTY;
-- private Color color;
-+ private Integer color; // Paper - keep color component consistent with vanilla (top byte is ignored)
-
- CraftMetaMap(CraftMetaItem meta) {
- super(meta);
-@@ -58,7 +58,7 @@ class CraftMetaMap extends CraftMetaItem implements MapMeta {
-
- getOrEmpty(tag, CraftMetaMap.MAP_COLOR).ifPresent((mapColor) -> {
- try {
-- this.color = Color.fromRGB(mapColor.rgb());
-+ this.color = mapColor.rgb(); // Paper
- } catch (IllegalArgumentException ex) {
- // Invalid colour
- }
-@@ -102,7 +102,7 @@ class CraftMetaMap extends CraftMetaItem implements MapMeta {
- }
-
- if (this.hasColor()) {
-- tag.put(CraftMetaMap.MAP_COLOR, new MapItemColor(this.color.asRGB()));
-+ tag.put(CraftMetaMap.MAP_COLOR, new MapItemColor(this.color)); // Paper
- }
- }
-
-@@ -127,6 +127,7 @@ class CraftMetaMap extends CraftMetaItem implements MapMeta {
-
- @Override
- public int getMapId() {
-+ Preconditions.checkState(this.hasMapId(), "Item does not have map associated - check hasMapId() first!"); // Paper - fix NPE
- return this.mapId;
- }
-
-@@ -187,12 +188,12 @@ class CraftMetaMap extends CraftMetaItem implements MapMeta {
-
- @Override
- public Color getColor() {
-- return this.color;
-+ return this.color == null ? null : Color.fromRGB(this.color & 0xFFFFFF); // Paper
- }
-
- @Override
- public void setColor(Color color) {
-- this.color = color;
-+ this.color = color == null ? null : color.asRGB(); // Paper
- }
-
- @Override
-diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaOminousBottle.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaOminousBottle.java
-index 7197c4f5698fd041c4db6d0f6a80c55f77661789..062ef890b42075adb5663453806dda3ad89a6aa0 100644
---- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaOminousBottle.java
-+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaOminousBottle.java
-@@ -75,6 +75,7 @@ public class CraftMetaOminousBottle extends CraftMetaItem implements OminousBott
-
- @Override
- public int getAmplifier() {
-+ Preconditions.checkState(this.hasAmplifier(), "'ominous_bottle_amplifier' data component is absent. Check hasAmplifier first!"); // Paper - fix NPE
- return this.ominousBottleAmplifier;
- }
-
-diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaPotion.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaPotion.java
-index db7f71af22d904de08d4badaa7f66d1286d5bf16..e32143e140db50a11cf602cf622d68b787c0d647 100644
---- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaPotion.java
-+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaPotion.java
-@@ -47,7 +47,7 @@ class CraftMetaPotion extends CraftMetaItem implements PotionMeta {
-
- private PotionType type;
- private List<PotionEffect> customEffects;
-- private Color color;
-+ private Integer color; // Paper - keep color component consistent with vanilla (top byte is ignored)
-
- CraftMetaPotion(CraftMetaItem meta) {
- super(meta);
-@@ -70,7 +70,7 @@ class CraftMetaPotion extends CraftMetaItem implements PotionMeta {
-
- potionContents.customColor().ifPresent((customColor) -> {
- try {
-- this.color = Color.fromRGB(customColor);
-+ this.color = customColor; // Paper
- } catch (IllegalArgumentException ex) {
- // Invalid colour
- }
-@@ -126,7 +126,7 @@ class CraftMetaPotion extends CraftMetaItem implements PotionMeta {
- super.applyToItem(tag);
-
- Optional<Holder<Potion>> defaultPotion = (this.hasBasePotionType()) ? Optional.of(CraftPotionType.bukkitToMinecraftHolder(this.type)) : Optional.empty();
-- Optional<Integer> potionColor = (this.hasColor()) ? Optional.of(this.color.asRGB()) : Optional.empty();
-+ Optional<Integer> potionColor = (this.hasColor()) ? Optional.of(this.color) : Optional.empty(); // Paper
-
- List<MobEffectInstance> effectList = new ArrayList<>();
- if (this.customEffects != null) {
-@@ -295,12 +295,12 @@ class CraftMetaPotion extends CraftMetaItem implements PotionMeta {
-
- @Override
- public Color getColor() {
-- return this.color;
-+ return this.color == null ? null : Color.fromRGB(this.color & 0xFFFFFF); // Paper
- }
-
- @Override
- public void setColor(Color color) {
-- this.color = color;
-+ this.color = color == null ? null : color.asRGB(); // Paper
- }
-
- @Override
-diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSkull.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSkull.java
-index 7bdc94c3ba7d8a0d74c2d88edbb32112a90c5980..ab9b3279db914fb4d6666a4dacbf5e401d2bcb87 100644
---- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSkull.java
-+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSkull.java
-@@ -137,10 +137,10 @@ class CraftMetaSkull extends CraftMetaItem implements SkullMeta {
- // Fill in textures
- PlayerProfile ownerProfile = new CraftPlayerProfile(this.profile); // getOwnerProfile may return null
- if (ownerProfile.getTextures().isEmpty()) {
-- ownerProfile.update().thenAccept((filledProfile) -> {
-+ ownerProfile.update().thenAcceptAsync((filledProfile) -> { // Paper - run on main thread
- this.setOwnerProfile(filledProfile);
-- tag.put(CraftMetaSkull.SKULL_PROFILE, new ResolvableProfile(this.profile));
-- });
-+ tag.skullCallback(this.profile); // Paper - actually set profile on itemstack
-+ }, ((org.bukkit.craftbukkit.CraftServer) org.bukkit.Bukkit.getServer()).getServer()); // Paper - run on main thread
- }
- }
-
-diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSpawnEgg.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSpawnEgg.java
-index b98e656c0bb382667bd186a500c5505f1ed3f7cd..63088d50c1ec69d1a829fd0783cf109f7981f43f 100644
---- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSpawnEgg.java
-+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaSpawnEgg.java
-@@ -212,6 +212,7 @@ public class CraftMetaSpawnEgg extends CraftMetaItem implements SpawnEggMeta {
-
- @Override
- public EntitySnapshot getSpawnedEntity() {
-+ if (this.entityTag == null) return null; // Paper - fix NPE
- return CraftEntitySnapshot.create(this.entityTag);
- }
-
-@@ -229,7 +230,7 @@ public class CraftMetaSpawnEgg extends CraftMetaItem implements SpawnEggMeta {
- if (meta instanceof CraftMetaSpawnEgg) {
- CraftMetaSpawnEgg that = (CraftMetaSpawnEgg) meta;
-
-- return this.entityTag != null ? that.entityTag != null && this.entityTag.equals(that.entityTag) : this.entityTag == null;
-+ return this.entityTag != null ? that.entityTag != null && this.entityTag.equals(that.entityTag) : that.entityTag == null; // Paper
- }
- return true;
- }
-diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaTropicalFishBucket.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaTropicalFishBucket.java
-index b5392a3a6f6f3d0a54549e6bb93f28590ee048f0..7514aa6f206c4b82fecd112783f96bb9dd73c01f 100644
---- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaTropicalFishBucket.java
-+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaTropicalFishBucket.java
-@@ -126,6 +126,7 @@ class CraftMetaTropicalFishBucket extends CraftMetaItem implements TropicalFishB
-
- @Override
- public DyeColor getPatternColor() {
-+ com.google.common.base.Preconditions.checkState(this.hasVariant(), "This bucket doesn't have variant, check hasVariant first!"); // Paper - fix NPE
- return CraftTropicalFish.getPatternColor(this.variant);
- }
-
-@@ -139,6 +140,7 @@ class CraftMetaTropicalFishBucket extends CraftMetaItem implements TropicalFishB
-
- @Override
- public DyeColor getBodyColor() {
-+ com.google.common.base.Preconditions.checkState(this.hasVariant(), "This bucket doesn't have variant, check hasVariant first!"); // Paper - fix NPE
- return CraftTropicalFish.getBodyColor(this.variant);
- }
-
-@@ -152,6 +154,7 @@ class CraftMetaTropicalFishBucket extends CraftMetaItem implements TropicalFishB
-
- @Override
- public TropicalFish.Pattern getPattern() {
-+ com.google.common.base.Preconditions.checkState(this.hasVariant(), "This bucket doesn't have variant, check hasVariant first!"); // Paper - fix NPE
- return CraftTropicalFish.getPattern(this.variant);
- }
-
-diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/SerializableMeta.java b/src/main/java/org/bukkit/craftbukkit/inventory/SerializableMeta.java
-index 8b407a33b04af6ae396ada0b8aca7dc246d314ef..d204845cf0b9de00589593469755cb8e42e0aa67 100644
---- a/src/main/java/org/bukkit/craftbukkit/inventory/SerializableMeta.java
-+++ b/src/main/java/org/bukkit/craftbukkit/inventory/SerializableMeta.java
-@@ -123,4 +123,21 @@ public final class SerializableMeta implements ConfigurationSerializable {
- }
- throw new IllegalArgumentException(field + "(" + object + ") is not a valid " + clazz);
- }
-+
-+ // Paper start - General ItemMeta Fixes
-+ public static <T> java.util.Optional<T> getObjectOptionally(Class<T> clazz, Map<?, ?> map, Object field, boolean nullable) {
-+ final Object object = map.get(field);
-+
-+ if (clazz.isInstance(object)) {
-+ return java.util.Optional.of(clazz.cast(object));
-+ }
-+ if (object == null) {
-+ if (!nullable) {
-+ throw new NoSuchElementException(map + " does not contain " + field);
-+ }
-+ return java.util.Optional.empty();
-+ }
-+ throw new IllegalArgumentException(field + "(" + object + ") is not a valid " + clazz);
-+ }
-+ // Paper end - General ItemMeta Fixes
- }
-diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/components/CraftFoodComponent.java b/src/main/java/org/bukkit/craftbukkit/inventory/components/CraftFoodComponent.java
-index c68e85cca0f532a94545c0b7f6ed54451ce5a47e..b647b5205b9c54ccb83e09a9410c722e33e5378d 100644
---- a/src/main/java/org/bukkit/craftbukkit/inventory/components/CraftFoodComponent.java
-+++ b/src/main/java/org/bukkit/craftbukkit/inventory/components/CraftFoodComponent.java
-@@ -103,6 +103,7 @@ public final class CraftFoodComponent implements FoodComponent {
-
- @Override
- public void setEatSeconds(float eatSeconds) {
-+ Preconditions.checkArgument(eatSeconds > 0, "Eat seconds must be positive"); // Paper - validate eat_seconds
- this.handle = new FoodProperties(this.handle.nutrition(), this.handle.saturation(), this.handle.canAlwaysEat(), eatSeconds, this.handle.effects());
- }
-
-@@ -118,6 +119,7 @@ public final class CraftFoodComponent implements FoodComponent {
-
- @Override
- public FoodEffect addEffect(PotionEffect effect, float probability) {
-+ Preconditions.checkArgument(0 <= probability && probability <= 1, "Probability cannot be outside range [0,1]"); // Paper
- List<FoodProperties.PossibleEffect> effects = new ArrayList<>(this.handle.effects());
-
- FoodProperties.PossibleEffect newEffect = new net.minecraft.world.food.FoodProperties.PossibleEffect(CraftPotionUtil.fromBukkit(effect), probability);
-diff --git a/src/test/java/org/bukkit/craftbukkit/inventory/DeprecatedItemMetaCustomValueTest.java b/src/test/java/org/bukkit/craftbukkit/inventory/DeprecatedItemMetaCustomValueTest.java
-index 6bed0a5c8d9f1ca72678cdf4699128e441a24541..8e03e14d0e65bfdf2196a08220d1408b1297aa0d 100644
---- a/src/test/java/org/bukkit/craftbukkit/inventory/DeprecatedItemMetaCustomValueTest.java
-+++ b/src/test/java/org/bukkit/craftbukkit/inventory/DeprecatedItemMetaCustomValueTest.java
-@@ -93,7 +93,7 @@ public class DeprecatedItemMetaCustomValueTest extends AbstractTestingBase {
- public void testNBTTagStoring() {
- CraftMetaItem itemMeta = this.createComplexItemMeta();
-
-- CraftMetaItem.Applicator compound = new CraftMetaItem.Applicator();
-+ CraftMetaItem.Applicator compound = new CraftMetaItem.Applicator() {}; // Paper
- itemMeta.applyToItem(compound);
-
- assertEquals(itemMeta, new CraftMetaItem(compound.build(), null)); // Paper
-diff --git a/src/test/java/org/bukkit/craftbukkit/inventory/ItemMetaTest.java b/src/test/java/org/bukkit/craftbukkit/inventory/ItemMetaTest.java
-index d6018439015583fa0344c7c01b2e60a13de29795..aabe3730fa582f442ee0544dd1a9f3123f719c68 100644
---- a/src/test/java/org/bukkit/craftbukkit/inventory/ItemMetaTest.java
-+++ b/src/test/java/org/bukkit/craftbukkit/inventory/ItemMetaTest.java
-@@ -66,7 +66,7 @@ import org.junit.jupiter.api.Test;
-
- public class ItemMetaTest extends AbstractTestingBase {
-
-- static final int MAX_FIREWORK_POWER = 127; // Please update ItemStackFireworkTest if/when this gets changed.
-+ static final int MAX_FIREWORK_POWER = 255; // Please update ItemStackFireworkTest if/when this gets changed. // Paper - it changed
-
- @Test
- public void testPowerLimitExact() {
-diff --git a/src/test/java/org/bukkit/craftbukkit/inventory/PersistentDataContainerTest.java b/src/test/java/org/bukkit/craftbukkit/inventory/PersistentDataContainerTest.java
-index 6f94c7a19f2f598a836ec7db30332dd95f8675a6..54ffbfd91a03efa2d0d271ed10db4209a2309638 100644
---- a/src/test/java/org/bukkit/craftbukkit/inventory/PersistentDataContainerTest.java
-+++ b/src/test/java/org/bukkit/craftbukkit/inventory/PersistentDataContainerTest.java
-@@ -127,7 +127,7 @@ public class PersistentDataContainerTest extends AbstractTestingBase {
- public void testNBTTagStoring() {
- CraftMetaItem itemMeta = this.createComplexItemMeta();
-
-- CraftMetaItem.Applicator compound = new CraftMetaItem.Applicator();
-+ CraftMetaItem.Applicator compound = new CraftMetaItem.Applicator() {}; // Paper
- itemMeta.applyToItem(compound);
-
- assertEquals(itemMeta, new CraftMetaItem(compound.build(), null)); // Paper
-@@ -473,7 +473,7 @@ public class PersistentDataContainerTest extends AbstractTestingBase {
- assertEquals(List.of(), container.get(PersistentDataContainerTest.requestKey("list"), PersistentDataType.LIST.strings()));
-
- // Write and read the entire container to NBT
-- final CraftMetaItem.Applicator storage = new CraftMetaItem.Applicator();
-+ final CraftMetaItem.Applicator storage = new CraftMetaItem.Applicator() {}; // Paper
- craftItem.applyToItem(storage);
-
- final CraftMetaItem readItem = new CraftMetaItem(storage.build(), null); // Paper
diff --git a/patches/unapplied/server/1038-Expose-hasColor-to-leather-armor.patch b/patches/unapplied/server/1038-Expose-hasColor-to-leather-armor.patch
deleted file mode 100644
index 0cdf66fe04..0000000000
--- a/patches/unapplied/server/1038-Expose-hasColor-to-leather-armor.patch
+++ /dev/null
@@ -1,38 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: SoSeDiK <[email protected]>
-Date: Wed, 1 May 2024 10:58:50 +0300
-Subject: [PATCH] Expose #hasColor to leather armor
-
-
-diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaColorableArmor.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaColorableArmor.java
-index 4e423c4790d7b03c283c9a5fa94bce4a1153445e..1e8a76f6dd54931eec261653a7bd51b6d18d3c68 100644
---- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaColorableArmor.java
-+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaColorableArmor.java
-@@ -134,4 +134,11 @@ public class CraftMetaColorableArmor extends CraftMetaArmor implements Colorable
- }
- return original != hash ? CraftMetaColorableArmor.class.hashCode() ^ hash : hash;
- }
-+
-+ // Paper start - Expose #hasColor to leather armor
-+ @Override
-+ public boolean isDyed() {
-+ return hasColor();
-+ }
-+ // Paper end - Expose #hasColor to leather armor
- }
-diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaLeatherArmor.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaLeatherArmor.java
-index 70c0d4cc85c045d040a35cd406f3f7ce9b6a58fa..0860d85fb9c6d0567f678569efb3c560f58612a9 100644
---- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaLeatherArmor.java
-+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaLeatherArmor.java
-@@ -183,4 +183,11 @@ class CraftMetaLeatherArmor extends CraftMetaItem implements LeatherArmorMeta {
- builder.put(CraftMetaLeatherArmor.COLOR.BUKKIT, meta.getColor());
- }
- }
-+
-+ // Paper start - Expose #hasColor to leather armor
-+ @Override
-+ public boolean isDyed() {
-+ return hasColor();
-+ }
-+ // Paper end - Expose #hasColor to leather armor
- }
diff --git a/patches/unapplied/server/1039-Added-API-to-get-player-ha-proxy-address.patch b/patches/unapplied/server/1039-Added-API-to-get-player-ha-proxy-address.patch
deleted file mode 100644
index 0c70ba8179..0000000000
--- a/patches/unapplied/server/1039-Added-API-to-get-player-ha-proxy-address.patch
+++ /dev/null
@@ -1,56 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: nostalfinals <[email protected]>
-Date: Mon, 8 Apr 2024 23:24:38 +0800
-Subject: [PATCH] Added API to get player ha proxy address
-
-
-diff --git a/src/main/java/net/minecraft/network/Connection.java b/src/main/java/net/minecraft/network/Connection.java
-index 58d28b6c1cc7da7d786f78308db971f7502ad844..02833deaa2bb7e5abc655bc1bdbe15c4b3ac7119 100644
---- a/src/main/java/net/minecraft/network/Connection.java
-+++ b/src/main/java/net/minecraft/network/Connection.java
-@@ -157,6 +157,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
- this.stopReadingPackets = true;
- }
- // Paper end - packet limiter
-+ @Nullable public SocketAddress haProxyAddress; // Paper - Add API to get player's proxy address
-
- public Connection(PacketFlow side) {
- this.receiving = side;
-diff --git a/src/main/java/net/minecraft/server/network/ServerConnectionListener.java b/src/main/java/net/minecraft/server/network/ServerConnectionListener.java
-index 52f537b7bfbdeaad9d17c0e88a1ed1c8925a833f..8aff5129f85ab5729b3da2e465871be62d15bdf2 100644
---- a/src/main/java/net/minecraft/server/network/ServerConnectionListener.java
-+++ b/src/main/java/net/minecraft/server/network/ServerConnectionListener.java
-@@ -146,6 +146,13 @@ public class ServerConnectionListener {
-
- Connection connection = (Connection) channel.pipeline().get("packet_handler");
- connection.address = socketaddr;
-+
-+ // Paper start - Add API to get player's proxy address
-+ final String proxyAddress = message.destinationAddress();
-+ final int proxyPort = message.destinationPort();
-+
-+ connection.haProxyAddress = new java.net.InetSocketAddress(proxyAddress, proxyPort);
-+ // Paper end - Add API to get player's proxy address
- }
- } else {
- super.channelRead(ctx, msg);
-diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
-index 815bcfd90218b932ca004c0f18db8b4de5d35c19..40155cc70ba959eea8011626a30e26f44298c99d 100644
---- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
-+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
-@@ -273,6 +273,15 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
- }
- }
-
-+ // Paper start - Add API to get player's proxy address
-+ @Override
-+ public @Nullable InetSocketAddress getHAProxyAddress() {
-+ if (this.getHandle().connection == null) return null;
-+
-+ return this.getHandle().connection.connection.haProxyAddress instanceof final InetSocketAddress inetSocketAddress ? inetSocketAddress : null;
-+ }
-+ // Paper end - Add API to get player's proxy address
-+
- public interface TransferCookieConnection {
-
- boolean isTransferred();
diff --git a/patches/unapplied/server/1040-More-Chest-Block-API.patch b/patches/unapplied/server/1040-More-Chest-Block-API.patch
deleted file mode 100644
index 020d97d086..0000000000
--- a/patches/unapplied/server/1040-More-Chest-Block-API.patch
+++ /dev/null
@@ -1,73 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: SoSeDiK <[email protected]>
-Date: Wed, 1 May 2024 08:22:13 +0300
-Subject: [PATCH] More Chest Block API
-
-== AT ==
-public net.minecraft.world.level.block.ChestBlock isBlockedChestByBlock(Lnet/minecraft/world/level/BlockGetter;Lnet/minecraft/core/BlockPos;)Z
-
-diff --git a/src/main/java/net/minecraft/world/level/block/EnderChestBlock.java b/src/main/java/net/minecraft/world/level/block/EnderChestBlock.java
-index 5f9858ef8d0ec1a74d469ab4426eb1db068873fd..ca92d49ef2010ba00c623491671dcde8ebe697c1 100644
---- a/src/main/java/net/minecraft/world/level/block/EnderChestBlock.java
-+++ b/src/main/java/net/minecraft/world/level/block/EnderChestBlock.java
-@@ -83,7 +83,7 @@ public class EnderChestBlock extends AbstractChestBlock<EnderChestBlockEntity> i
- BlockEntity blockEntity = world.getBlockEntity(pos);
- if (playerEnderChestContainer != null && blockEntity instanceof EnderChestBlockEntity) {
- BlockPos blockPos = pos.above();
-- if (world.getBlockState(blockPos).isRedstoneConductor(world, blockPos)) {
-+ if (world.getBlockState(blockPos).isRedstoneConductor(world, blockPos)) { // Paper - diff on change; make sure that EnderChest#isBlocked uses the same logic
- return InteractionResult.sidedSuccess(world.isClientSide);
- } else if (world.isClientSide) {
- return InteractionResult.SUCCESS;
-diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftChest.java b/src/main/java/org/bukkit/craftbukkit/block/CraftChest.java
-index 6e98a00d526b734992ce39b15768c5820dce4ca8..cc7bf4d39b834fba472bc163226a01a0cd4b6010 100644
---- a/src/main/java/org/bukkit/craftbukkit/block/CraftChest.java
-+++ b/src/main/java/org/bukkit/craftbukkit/block/CraftChest.java
-@@ -99,4 +99,29 @@ public class CraftChest extends CraftLootable<ChestBlockEntity> implements Chest
- return getTileEntity().openersCounter.opened;
- }
- // Paper end - More Lidded Block API
-+
-+ // Paper start - More Chest Block API
-+ @Override
-+ public boolean isBlocked() {
-+ // Method mimics vanilla logic in ChestBlock and DoubleBlockCombiner when trying to open chest's container
-+ if (!isPlaced()) {
-+ return false;
-+ }
-+ net.minecraft.world.level.LevelAccessor world = getWorldHandle();
-+ if (ChestBlock.isChestBlockedAt(world, getPosition())) {
-+ return true;
-+ }
-+ if (ChestBlock.getBlockType(this.data) == net.minecraft.world.level.block.DoubleBlockCombiner.BlockType.SINGLE) {
-+ return false;
-+ }
-+ net.minecraft.core.Direction direction = ChestBlock.getConnectedDirection(this.data);
-+ net.minecraft.core.BlockPos neighbourBlockPos = getPosition().relative(direction);
-+ BlockState neighbourBlockState = world.getBlockStateIfLoaded(neighbourBlockPos);
-+ return neighbourBlockState != null
-+ && neighbourBlockState.is(this.data.getBlock())
-+ && ChestBlock.getBlockType(neighbourBlockState) != net.minecraft.world.level.block.DoubleBlockCombiner.BlockType.SINGLE
-+ && ChestBlock.getConnectedDirection(neighbourBlockState) == direction.getOpposite()
-+ && ChestBlock.isChestBlockedAt(world, neighbourBlockPos);
-+ }
-+ // Paper end - More Chest Block API
- }
-diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftEnderChest.java b/src/main/java/org/bukkit/craftbukkit/block/CraftEnderChest.java
-index b64adbba3e52d32d439e64a243cb74f3fbca2ce3..f45ee675a10729845bf376fa95e648b23b9aac12 100644
---- a/src/main/java/org/bukkit/craftbukkit/block/CraftEnderChest.java
-+++ b/src/main/java/org/bukkit/craftbukkit/block/CraftEnderChest.java
-@@ -58,4 +58,13 @@ public class CraftEnderChest extends CraftBlockEntityState<EnderChestBlockEntity
- return getTileEntity().openersCounter.opened;
- }
- // Paper end - More Lidded Block API
-+
-+ // Paper start - More Chest Block API
-+ @Override
-+ public boolean isBlocked() {
-+ // Uses the same logic as EnderChestBlock's check for opening container
-+ final net.minecraft.core.BlockPos abovePos = this.getPosition().above();
-+ return this.isPlaced() && this.getWorldHandle().getBlockState(abovePos).isRedstoneConductor(this.getWorldHandle(), abovePos);
-+ }
-+ // Paper end - More Chest Block API
- }
diff --git a/patches/unapplied/server/1041-Print-data-component-type-on-encoding-error.patch b/patches/unapplied/server/1041-Print-data-component-type-on-encoding-error.patch
deleted file mode 100644
index 78cb82fa00..0000000000
--- a/patches/unapplied/server/1041-Print-data-component-type-on-encoding-error.patch
+++ /dev/null
@@ -1,24 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Nassim Jahnke <[email protected]>
-Date: Thu, 9 May 2024 15:11:34 +0200
-Subject: [PATCH] Print data component type on encoding error
-
-
-diff --git a/src/main/java/net/minecraft/core/component/DataComponentPatch.java b/src/main/java/net/minecraft/core/component/DataComponentPatch.java
-index 33340e8ebe23a1a9ce587be34551fb929c41d0fd..b8977749d35dd7343021425f477445bec470d46b 100644
---- a/src/main/java/net/minecraft/core/component/DataComponentPatch.java
-+++ b/src/main/java/net/minecraft/core/component/DataComponentPatch.java
-@@ -143,7 +143,13 @@ public final class DataComponentPatch {
- }
-
- private static <T> void encodeComponent(RegistryFriendlyByteBuf buf, DataComponentType<T> type, Object value) {
-+ // Paper start - codec errors of random anonymous classes are useless
-+ try {
- type.streamCodec().encode(buf, (T) value); // CraftBukkit - decompile error
-+ } catch (final Exception e) {
-+ throw new RuntimeException("Error encoding component " + type, e);
-+ }
-+ // Paper end - codec errors of random anonymous classes are useless
- }
- };
- private static final String REMOVED_PREFIX = "!";
diff --git a/patches/unapplied/server/1043-Brigadier-based-command-API.patch b/patches/unapplied/server/1043-Brigadier-based-command-API.patch
deleted file mode 100644
index 53a3f8b498..0000000000
--- a/patches/unapplied/server/1043-Brigadier-based-command-API.patch
+++ /dev/null
@@ -1,2831 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Owen1212055 <[email protected]>
-Date: Mon, 1 Aug 2022 22:50:34 -0400
-Subject: [PATCH] Brigadier based command API
-
-== AT ==
-public net.minecraft.commands.arguments.blocks.BlockInput tag
-public net.minecraft.commands.arguments.DimensionArgument ERROR_INVALID_VALUE
-public net.minecraft.server.ReloadableServerResources registryLookup
-public net.minecraft.server.ReloadableServerResources
-
-Co-authored-by: Jake Potrebic <[email protected]>
-
-diff --git a/src/main/java/com/mojang/brigadier/CommandDispatcher.java b/src/main/java/com/mojang/brigadier/CommandDispatcher.java
-index 4b4f812eb13d5f03bcf3f8724d8aa8dbbc724e8b..a4d5d7017e0be79844b996de85a63cad5f8488bc 100644
---- a/src/main/java/com/mojang/brigadier/CommandDispatcher.java
-+++ b/src/main/java/com/mojang/brigadier/CommandDispatcher.java
-@@ -459,7 +459,7 @@ public class CommandDispatcher<S> {
- }
-
- private String getSmartUsage(final CommandNode<S> node, final S source, final boolean optional, final boolean deep) {
-- if (!node.canUse(source)) {
-+ if (source != null && !node.canUse(source)) { // Paper
- return null;
- }
-
-@@ -473,7 +473,7 @@ public class CommandDispatcher<S> {
- final String redirect = node.getRedirect() == this.root ? "..." : "-> " + node.getRedirect().getUsageText();
- return self + CommandDispatcher.ARGUMENT_SEPARATOR + redirect;
- } else {
-- final Collection<CommandNode<S>> children = node.getChildren().stream().filter(c -> c.canUse(source)).collect(Collectors.toList());
-+ final Collection<CommandNode<S>> children = node.getChildren().stream().filter(c -> source == null || c.canUse(source)).collect(Collectors.toList()); // Paper
- if (children.size() == 1) {
- final String usage = this.getSmartUsage(children.iterator().next(), source, childOptional, childOptional);
- if (usage != null) {
-diff --git a/src/main/java/com/mojang/brigadier/tree/CommandNode.java b/src/main/java/com/mojang/brigadier/tree/CommandNode.java
-index 1f4963bf4681a771130abc1da179819626ecfc1f..03ce8a2abb6dceaa922dcce7f3adbc228bbde4bc 100644
---- a/src/main/java/com/mojang/brigadier/tree/CommandNode.java
-+++ b/src/main/java/com/mojang/brigadier/tree/CommandNode.java
-@@ -35,6 +35,8 @@ public abstract class CommandNode<S> implements Comparable<CommandNode<S>> {
- private final boolean forks;
- private Command<S> command;
- public CommandNode<CommandSourceStack> clientNode; // Paper - Brigadier API
-+ public CommandNode<io.papermc.paper.command.brigadier.CommandSourceStack> unwrappedCached = null; // Paper - Brigadier Command API
-+ public CommandNode<io.papermc.paper.command.brigadier.CommandSourceStack> wrappedCached = null; // Paper - Brigadier Command API
- // CraftBukkit start
- public void removeCommand(String name) {
- this.children.remove(name);
-@@ -203,4 +205,11 @@ public abstract class CommandNode<S> implements Comparable<CommandNode<S>> {
- }
-
- public abstract Collection<String> getExamples();
-+ // Paper start - Brigadier Command API
-+ public void clearAll() {
-+ this.children.clear();
-+ this.literals.clear();
-+ this.arguments.clear();
-+ }
-+ // Paper end - Brigadier Command API
- }
-diff --git a/src/main/java/io/papermc/paper/brigadier/NullCommandSender.java b/src/main/java/io/papermc/paper/brigadier/NullCommandSender.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..367ef7e0769537e8c13c7fd818a1249e15a28a65
---- /dev/null
-+++ b/src/main/java/io/papermc/paper/brigadier/NullCommandSender.java
-@@ -0,0 +1,151 @@
-+package io.papermc.paper.brigadier;
-+
-+import java.util.Set;
-+import java.util.UUID;
-+import net.kyori.adventure.text.Component;
-+import net.md_5.bungee.api.chat.BaseComponent;
-+import org.bukkit.Bukkit;
-+import org.bukkit.Server;
-+import org.bukkit.command.CommandSender;
-+import org.bukkit.permissions.PermissibleBase;
-+import org.bukkit.permissions.Permission;
-+import org.bukkit.permissions.PermissionAttachment;
-+import org.bukkit.permissions.PermissionAttachmentInfo;
-+import org.bukkit.plugin.Plugin;
-+import org.checkerframework.checker.nullness.qual.NonNull;
-+import org.checkerframework.framework.qual.DefaultQualifier;
-+import org.jetbrains.annotations.NotNull;
-+import org.jetbrains.annotations.Nullable;
-+
-+@DefaultQualifier(NonNull.class)
-+public final class NullCommandSender implements CommandSender {
-+
-+ public static final CommandSender INSTANCE = new NullCommandSender();
-+
-+ private NullCommandSender() {
-+ }
-+
-+ @Override
-+ public void sendMessage(final String message) {
-+ }
-+
-+ @Override
-+ public void sendMessage(final String... messages) {
-+ }
-+
-+ @Override
-+ public void sendMessage(@Nullable final UUID sender, final String message) {
-+ }
-+
-+ @Override
-+ public void sendMessage(@Nullable final UUID sender, final String... messages) {
-+ }
-+
-+ @SuppressWarnings("ConstantValue")
-+ @Override
-+ public Server getServer() {
-+ final @Nullable Server server = Bukkit.getServer();
-+ if (server == null) {
-+ throw new UnsupportedOperationException("The server has not been created yet, you cannot access it at this time from the 'null' CommandSender");
-+ }
-+ return server;
-+ }
-+
-+ @Override
-+ public String getName() {
-+ return "";
-+ }
-+
-+ private final Spigot spigot = new Spigot();
-+ @Override
-+ public Spigot spigot() {
-+ return this.spigot;
-+ }
-+
-+ public final class Spigot extends CommandSender.Spigot {
-+
-+ @Override
-+ public void sendMessage(@NotNull final BaseComponent component) {
-+ }
-+
-+ @Override
-+ public void sendMessage(@NonNull final @NotNull BaseComponent... components) {
-+ }
-+
-+ @Override
-+ public void sendMessage(@Nullable final UUID sender, @NotNull final BaseComponent component) {
-+ }
-+
-+ @Override
-+ public void sendMessage(@Nullable final UUID sender, @NonNull final @NotNull BaseComponent... components) {
-+ }
-+ }
-+
-+ @Override
-+ public Component name() {
-+ return Component.empty();
-+ }
-+
-+ @Override
-+ public boolean isPermissionSet(final String name) {
-+ return false;
-+ }
-+
-+ @Override
-+ public boolean isPermissionSet(final Permission perm) {
-+ return false;
-+ }
-+
-+ @Override
-+ public boolean hasPermission(final String name) {
-+ return true;
-+ }
-+
-+ @Override
-+ public boolean hasPermission(final Permission perm) {
-+ return true;
-+ }
-+
-+ @Override
-+ public PermissionAttachment addAttachment(final Plugin plugin, final String name, final boolean value) {
-+ throw new UnsupportedOperationException("Cannot add attachments to the 'null' CommandSender");
-+ }
-+
-+ @Override
-+ public PermissionAttachment addAttachment(final Plugin plugin) {
-+ throw new UnsupportedOperationException("Cannot add attachments to the 'null' CommandSender");
-+ }
-+
-+ @Override
-+ public @Nullable PermissionAttachment addAttachment(final Plugin plugin, final String name, final boolean value, final int ticks) {
-+ throw new UnsupportedOperationException("Cannot add attachments to the 'null' CommandSender");
-+ }
-+
-+ @Override
-+ public @Nullable PermissionAttachment addAttachment(final Plugin plugin, final int ticks) {
-+ throw new UnsupportedOperationException("Cannot add attachments to the 'null' CommandSender");
-+ }
-+
-+ @Override
-+ public void removeAttachment(final PermissionAttachment attachment) {
-+ throw new UnsupportedOperationException("Cannot add attachments to the 'null' CommandSender");
-+ }
-+
-+ @Override
-+ public void recalculatePermissions() {
-+ }
-+
-+ @Override
-+ public Set<PermissionAttachmentInfo> getEffectivePermissions() {
-+ throw new UnsupportedOperationException("Cannot remove attachments from the 'null' CommandSender");
-+ }
-+
-+ @Override
-+ public boolean isOp() {
-+ return true;
-+ }
-+
-+ @Override
-+ public void setOp(final boolean value) {
-+ }
-+}
-diff --git a/src/main/java/io/papermc/paper/brigadier/PaperBrigadierProviderImpl.java b/src/main/java/io/papermc/paper/brigadier/PaperBrigadierProviderImpl.java
-deleted file mode 100644
-index dd6012b6a097575b2d1471be5069eccee4537c0a..0000000000000000000000000000000000000000
---- a/src/main/java/io/papermc/paper/brigadier/PaperBrigadierProviderImpl.java
-+++ /dev/null
-@@ -1,30 +0,0 @@
--package io.papermc.paper.brigadier;
--
--import com.mojang.brigadier.Message;
--import io.papermc.paper.adventure.PaperAdventure;
--import net.kyori.adventure.text.Component;
--import net.kyori.adventure.text.ComponentLike;
--import net.minecraft.network.chat.ComponentUtils;
--import org.checkerframework.checker.nullness.qual.NonNull;
--
--import static java.util.Objects.requireNonNull;
--
--public enum PaperBrigadierProviderImpl implements PaperBrigadierProvider {
-- INSTANCE;
--
-- PaperBrigadierProviderImpl() {
-- PaperBrigadierProvider.initialize(this);
-- }
--
-- @Override
-- public @NonNull Message message(final @NonNull ComponentLike componentLike) {
-- requireNonNull(componentLike, "componentLike");
-- return PaperAdventure.asVanilla(componentLike.asComponent());
-- }
--
-- @Override
-- public @NonNull Component componentFromMessage(final @NonNull Message message) {
-- requireNonNull(message, "message");
-- return PaperAdventure.asAdventure(ComponentUtils.fromMessage(message));
-- }
--}
-diff --git a/src/main/java/io/papermc/paper/command/brigadier/ApiMirrorRootNode.java b/src/main/java/io/papermc/paper/command/brigadier/ApiMirrorRootNode.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..23525592d880f340745a28c956fa38d3e4057231
---- /dev/null
-+++ b/src/main/java/io/papermc/paper/command/brigadier/ApiMirrorRootNode.java
-@@ -0,0 +1,253 @@
-+package io.papermc.paper.command.brigadier;
-+
-+import com.google.common.collect.Collections2;
-+import com.mojang.brigadier.CommandDispatcher;
-+import com.mojang.brigadier.arguments.ArgumentType;
-+import com.mojang.brigadier.arguments.BoolArgumentType;
-+import com.mojang.brigadier.arguments.DoubleArgumentType;
-+import com.mojang.brigadier.arguments.FloatArgumentType;
-+import com.mojang.brigadier.arguments.IntegerArgumentType;
-+import com.mojang.brigadier.arguments.LongArgumentType;
-+import com.mojang.brigadier.arguments.StringArgumentType;
-+import com.mojang.brigadier.context.CommandContext;
-+import com.mojang.brigadier.suggestion.SuggestionProvider;
-+import com.mojang.brigadier.suggestion.SuggestionsBuilder;
-+import com.mojang.brigadier.tree.ArgumentCommandNode;
-+import com.mojang.brigadier.tree.CommandNode;
-+import com.mojang.brigadier.tree.LiteralCommandNode;
-+import com.mojang.brigadier.tree.RootCommandNode;
-+import io.papermc.paper.command.brigadier.argument.CustomArgumentType;
-+import io.papermc.paper.command.brigadier.argument.VanillaArgumentProviderImpl;
-+import io.papermc.paper.command.brigadier.argument.WrappedArgumentCommandNode;
-+import java.lang.reflect.Method;
-+import java.util.Collection;
-+import java.util.Set;
-+import net.minecraft.commands.synchronization.ArgumentTypeInfos;
-+import org.jetbrains.annotations.NotNull;
-+import org.jetbrains.annotations.Nullable;
-+
-+/**
-+ * This root command node is responsible for wrapping around vanilla's dispatcher.
-+ * <p>
-+ * The reason for this is conversion is we do NOT want there to be NMS types
-+ * in the api. This allows us to reconstruct the nodes to be more api friendly, while
-+ * we can then unwrap it when needed and convert them to NMS types.
-+ * <p>
-+ * Command nodes such as vanilla (those without a proper "api node")
-+ * will be assigned a {@link ShadowBrigNode}.
-+ * This prevents certain parts of it (children) from being accessed by the api.
-+ */
-+public abstract class ApiMirrorRootNode extends RootCommandNode<CommandSourceStack> {
-+
-+ /**
-+ * Represents argument types that are allowed to exist in the api.
-+ * These typically represent primitives that don't need to be wrapped
-+ * by NMS.
-+ */
-+ private static final Set<Class<? extends ArgumentType<?>>> ARGUMENT_WHITELIST = Set.of(
-+ BoolArgumentType.class,
-+ DoubleArgumentType.class,
-+ FloatArgumentType.class,
-+ IntegerArgumentType.class,
-+ LongArgumentType.class,
-+ StringArgumentType.class
-+ );
-+
-+ public static void validatePrimitiveType(ArgumentType<?> type) {
-+ if (ARGUMENT_WHITELIST.contains(type.getClass())) {
-+ if (!ArgumentTypeInfos.isClassRecognized(type.getClass())) {
-+ throw new IllegalArgumentException("This whitelisted primitive argument type is not recognized by the server!");
-+ }
-+ } else if (!(type instanceof VanillaArgumentProviderImpl.NativeWrapperArgumentType<?,?> nativeWrapperArgumentType) || !ArgumentTypeInfos.isClassRecognized(nativeWrapperArgumentType.nativeNmsArgumentType().getClass())) {
-+ throw new IllegalArgumentException("Custom argument type was passed, this was not a recognized type to send to the client! You must only pass vanilla arguments or primitive brig args in the wrapper!");
-+ }
-+ }
-+
-+ public abstract CommandDispatcher<net.minecraft.commands.CommandSourceStack> getDispatcher();
-+
-+ /**
-+ * This logic is responsible for unwrapping an API node to be supported by NMS.
-+ * See the method implementation for detailed steps.
-+ *
-+ * @param maybeWrappedNode api provided node / node to be "wrapped"
-+ * @return wrapped node
-+ */
-+ @SuppressWarnings({"rawtypes", "unchecked"})
-+ private @NotNull CommandNode<CommandSourceStack> unwrapNode(final CommandNode<CommandSourceStack> maybeWrappedNode) {
-+ /*
-+ If the type is a shadow node we can assume that the type that it represents is an already supported NMS node.
-+ This is because these are typically minecraft command nodes.
-+ */
-+ if (maybeWrappedNode instanceof final ShadowBrigNode shadowBrigNode) {
-+ return (CommandNode) shadowBrigNode.getHandle();
-+ }
-+
-+ /*
-+ This node already has had an unwrapped node created, so we can assume that it's safe to reuse that cached copy.
-+ */
-+ if (maybeWrappedNode.unwrappedCached != null) {
-+ return maybeWrappedNode.unwrappedCached;
-+ }
-+
-+ // convert the pure brig node into one compatible with the nms dispatcher
-+ return this.convertFromPureBrigNode(maybeWrappedNode);
-+ }
-+
-+ private @NotNull CommandNode<CommandSourceStack> convertFromPureBrigNode(final CommandNode<CommandSourceStack> pureNode) {
-+ /*
-+ Logic for converting a node.
-+ */
-+ final CommandNode<CommandSourceStack> converted;
-+ if (pureNode instanceof final LiteralCommandNode<CommandSourceStack> node) {
-+ /*
-+ Remap the literal node, we only have to account
-+ for the redirect in this case.
-+ */
-+ converted = this.simpleUnwrap(node);
-+ } else if (pureNode instanceof final ArgumentCommandNode<CommandSourceStack, ?> pureArgumentNode) {
-+ final ArgumentType<?> pureArgumentType = pureArgumentNode.getType();
-+ /*
-+ Check to see if this argument type is a wrapped type, if so we know that
-+ we can unwrap the node to get an NMS type.
-+ */
-+ if (pureArgumentType instanceof final CustomArgumentType<?, ?> customArgumentType) {
-+ final SuggestionProvider<?> suggestionProvider;
-+ try {
-+ final Method listSuggestions = customArgumentType.getClass().getMethod("listSuggestions", CommandContext.class, SuggestionsBuilder.class);
-+ if (listSuggestions.getDeclaringClass() != CustomArgumentType.class) {
-+ suggestionProvider = customArgumentType::listSuggestions;
-+ } else {
-+ suggestionProvider = null;
-+ }
-+ } catch (final NoSuchMethodException ex) {
-+ throw new IllegalStateException("Could not determine if the custom argument type " + customArgumentType + " overrides listSuggestions", ex);
-+ }
-+
-+ converted = this.unwrapArgumentWrapper(pureArgumentNode, customArgumentType, customArgumentType.getNativeType(), suggestionProvider);
-+ } else if (pureArgumentType instanceof final VanillaArgumentProviderImpl.NativeWrapperArgumentType<?,?> nativeWrapperArgumentType) {
-+ converted = this.unwrapArgumentWrapper(pureArgumentNode, nativeWrapperArgumentType, nativeWrapperArgumentType, null); // "null" for suggestion provider so it uses the argument type's suggestion provider
-+
-+ /*
-+ If it's not a wrapped type, it either has to be a primitive or an already
-+ defined NMS type.
-+ This method allows us to check if this is recognized by vanilla.
-+ */
-+ } else if (ArgumentTypeInfos.isClassRecognized(pureArgumentType.getClass())) {
-+ // Allow any type of argument, as long as it's recognized by the client (but in most cases, this should be API only types)
-+ // Previously we only allowed whitelisted types.
-+ converted = this.simpleUnwrap(pureArgumentNode);
-+ } else {
-+ // Unknown argument type was passed
-+ throw new IllegalArgumentException("Custom unknown argument type was passed, should be wrapped inside an CustomArgumentType.");
-+ }
-+ } else {
-+ throw new IllegalArgumentException("Unknown command node passed. Don't know how to unwrap this.");
-+ }
-+
-+ // Store unwrapped node before unwrapping children to avoid infinite recursion in cyclic redirects.
-+ converted.wrappedCached = pureNode;
-+ pureNode.unwrappedCached = converted;
-+
-+ /*
-+ Add the children to the node, unwrapping each child in the process.
-+ */
-+ for (final CommandNode<CommandSourceStack> child : pureNode.getChildren()) {
-+ converted.addChild(this.unwrapNode(child));
-+ }
-+
-+ return converted;
-+ }
-+
-+ /**
-+ * This logic is responsible for rewrapping a node.
-+ * If a node was unwrapped in the past, it should have a wrapped type
-+ * stored in its cache.
-+ * <p>
-+ * However, if it doesn't seem to have a wrapped version we will return
-+ * a {@link ShadowBrigNode} instead. This supports being unwrapped/wrapped while
-+ * preventing the API from accessing it unsafely.
-+ *
-+ * @param unwrapped argument node
-+ * @return wrapped node
-+ */
-+ private @Nullable CommandNode<CommandSourceStack> wrapNode(@Nullable final CommandNode<net.minecraft.commands.CommandSourceStack> unwrapped) {
-+ if (unwrapped == null) {
-+ return null;
-+ }
-+
-+ /*
-+ This was most likely created by API and has a wrapped variant,
-+ so we can return this safely.
-+ */
-+ if (unwrapped.wrappedCached != null) {
-+ return unwrapped.wrappedCached;
-+ }
-+
-+ /*
-+ We don't know the type of this, or where this came from.
-+ Return a shadow, where we will allow the api to handle this but have
-+ restrictive access.
-+ */
-+ CommandNode<CommandSourceStack> shadow = new ShadowBrigNode(unwrapped);
-+ unwrapped.wrappedCached = shadow;
-+ return shadow;
-+ }
-+
-+ /**
-+ * Nodes added to this dispatcher must be unwrapped
-+ * in order to be added to the NMS dispatcher.
-+ *
-+ * @param node node to add
-+ */
-+ @SuppressWarnings({"rawtypes", "unchecked"})
-+ @Override
-+ public void addChild(CommandNode<CommandSourceStack> node) {
-+ CommandNode convertedNode = this.unwrapNode(node);
-+ this.getDispatcher().getRoot().addChild(convertedNode);
-+ }
-+
-+ /**
-+ * Gets the children for the vanilla dispatcher,
-+ * ensuring that all are wrapped.
-+ *
-+ * @return wrapped children
-+ */
-+ @Override
-+ public Collection<CommandNode<CommandSourceStack>> getChildren() {
-+ return Collections2.transform(this.getDispatcher().getRoot().getChildren(), this::wrapNode);
-+ }
-+
-+ @Override
-+ public CommandNode<CommandSourceStack> getChild(String name) {
-+ return this.wrapNode(this.getDispatcher().getRoot().getChild(name));
-+ }
-+
-+ // These are needed for bukkit... we should NOT allow this
-+ @Override
-+ public void removeCommand(String name) {
-+ this.getDispatcher().getRoot().removeCommand(name);
-+ }
-+
-+ @Override
-+ public void clearAll() {
-+ this.getDispatcher().getRoot().clearAll();
-+ }
-+
-+ @SuppressWarnings({"rawtypes", "unchecked"})
-+ private CommandNode<CommandSourceStack> unwrapArgumentWrapper(final ArgumentCommandNode pureNode, final ArgumentType pureArgumentType, final ArgumentType possiblyWrappedNativeArgumentType, @Nullable SuggestionProvider argumentTypeSuggestionProvider) {
-+ validatePrimitiveType(possiblyWrappedNativeArgumentType);
-+ final CommandNode redirectNode = pureNode.getRedirect() == null ? null : this.unwrapNode(pureNode.getRedirect());
-+ // If there is already a custom suggestion provider, ignore the suggestion provider from the argument type
-+ final SuggestionProvider suggestionProvider = pureNode.getCustomSuggestions() != null ? pureNode.getCustomSuggestions() : argumentTypeSuggestionProvider;
-+
-+ final ArgumentType nativeArgumentType = possiblyWrappedNativeArgumentType instanceof final VanillaArgumentProviderImpl.NativeWrapperArgumentType<?,?> nativeWrapperArgumentType ? nativeWrapperArgumentType.nativeNmsArgumentType() : possiblyWrappedNativeArgumentType;
-+ return new WrappedArgumentCommandNode<>(pureNode.getName(), pureArgumentType, nativeArgumentType, pureNode.getCommand(), pureNode.getRequirement(), redirectNode, pureNode.getRedirectModifier(), pureNode.isFork(), suggestionProvider);
-+ }
-+
-+ private CommandNode<CommandSourceStack> simpleUnwrap(final CommandNode<CommandSourceStack> node) {
-+ return node.createBuilder()
-+ .redirect(node.getRedirect() == null ? null : this.unwrapNode(node.getRedirect()))
-+ .build();
-+ }
-+
-+}
-diff --git a/src/main/java/io/papermc/paper/command/brigadier/MessageComponentSerializerImpl.java b/src/main/java/io/papermc/paper/command/brigadier/MessageComponentSerializerImpl.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..0b33c6cf2366568641e6f2fd7f74fb74f6ea0145
---- /dev/null
-+++ b/src/main/java/io/papermc/paper/command/brigadier/MessageComponentSerializerImpl.java
-@@ -0,0 +1,20 @@
-+package io.papermc.paper.command.brigadier;
-+
-+import com.mojang.brigadier.Message;
-+import io.papermc.paper.adventure.PaperAdventure;
-+import net.kyori.adventure.text.Component;
-+import net.minecraft.network.chat.ComponentUtils;
-+import org.jetbrains.annotations.NotNull;
-+
-+public final class MessageComponentSerializerImpl implements MessageComponentSerializer {
-+
-+ @Override
-+ public @NotNull Component deserialize(@NotNull Message input) {
-+ return PaperAdventure.asAdventure(ComponentUtils.fromMessage(input));
-+ }
-+
-+ @Override
-+ public @NotNull Message serialize(@NotNull Component component) {
-+ return PaperAdventure.asVanilla(component);
-+ }
-+}
-diff --git a/src/main/java/io/papermc/paper/command/brigadier/PaperBrigadier.java b/src/main/java/io/papermc/paper/command/brigadier/PaperBrigadier.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..4acf7c3bcfbe61431bfbfa3c8addb33f671eb498
---- /dev/null
-+++ b/src/main/java/io/papermc/paper/command/brigadier/PaperBrigadier.java
-@@ -0,0 +1,73 @@
-+package io.papermc.paper.command.brigadier;
-+
-+import com.mojang.brigadier.CommandDispatcher;
-+import com.mojang.brigadier.tree.CommandNode;
-+import com.mojang.brigadier.tree.LiteralCommandNode;
-+import io.papermc.paper.command.brigadier.bukkit.BukkitBrigForwardingMap;
-+import io.papermc.paper.command.brigadier.bukkit.BukkitCommandNode;
-+import net.minecraft.commands.CommandSource;
-+import net.minecraft.commands.Commands;
-+import net.minecraft.network.chat.CommonComponents;
-+import net.minecraft.server.MinecraftServer;
-+import net.minecraft.world.phys.Vec2;
-+import net.minecraft.world.phys.Vec3;
-+import org.bukkit.Bukkit;
-+import org.bukkit.Server;
-+import org.bukkit.command.Command;
-+import org.bukkit.command.CommandMap;
-+import org.bukkit.craftbukkit.command.VanillaCommandWrapper;
-+
-+import java.util.Map;
-+
-+public final class PaperBrigadier {
-+
-+ @SuppressWarnings("DataFlowIssue")
-+ static final net.minecraft.commands.CommandSourceStack DUMMY = new net.minecraft.commands.CommandSourceStack(
-+ CommandSource.NULL,
-+ Vec3.ZERO,
-+ Vec2.ZERO,
-+ null,
-+ 4,
-+ "",
-+ CommonComponents.EMPTY,
-+ null,
-+ null
-+ );
-+
-+ @SuppressWarnings({"unchecked", "rawtypes"})
-+ public static Command wrapNode(CommandNode node) {
-+ if (!(node instanceof LiteralCommandNode)) {
-+ throw new IllegalArgumentException("Unsure how to wrap a " + node);
-+ }
-+
-+ if (!(node instanceof PluginCommandNode pluginCommandNode)) {
-+ return new VanillaCommandWrapper(null, node);
-+ }
-+ CommandNode<CommandSourceStack> argumentCommandNode = node;
-+ if (argumentCommandNode.getRedirect() != null) {
-+ argumentCommandNode = argumentCommandNode.getRedirect();
-+ }
-+
-+ Map<CommandNode<CommandSourceStack>, String> map = PaperCommands.INSTANCE.getDispatcherInternal().getSmartUsage(argumentCommandNode, DUMMY);
-+ String usage = map.isEmpty() ? pluginCommandNode.getUsageText() : pluginCommandNode.getUsageText() + " " + String.join("\n" + pluginCommandNode.getUsageText() + " ", map.values());
-+ return new PluginVanillaCommandWrapper(pluginCommandNode.getName(), pluginCommandNode.getDescription(), usage, pluginCommandNode.getAliases(), node, pluginCommandNode.getPlugin());
-+ }
-+
-+ /*
-+ Previously, Bukkit used one command dispatcher and ignored minecraft's reloading logic.
-+
-+ In order to allow for legacy commands to be properly added, we will iterate through previous bukkit commands
-+ in the old dispatcher and re-register them.
-+ */
-+ @SuppressWarnings({"unchecked", "rawtypes"})
-+ public static void moveBukkitCommands(Commands before, Commands after) {
-+ CommandDispatcher erasedDispatcher = before.getDispatcher();
-+
-+ for (Object node : erasedDispatcher.getRoot().getChildren()) {
-+ if (node instanceof CommandNode<?> commandNode && commandNode.getCommand() instanceof BukkitCommandNode.BukkitBrigCommand) {
-+ after.getDispatcher().getRoot().removeCommand(((CommandNode<?>) node).getName()); // Remove already existing commands
-+ after.getDispatcher().getRoot().addChild((CommandNode<net.minecraft.commands.CommandSourceStack>) node);
-+ }
-+ }
-+ }
-+}
-diff --git a/src/main/java/io/papermc/paper/command/brigadier/PaperCommandSourceStack.java b/src/main/java/io/papermc/paper/command/brigadier/PaperCommandSourceStack.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..1b1642f306771f029e6214a2e2ebebb6ae6abc3e
---- /dev/null
-+++ b/src/main/java/io/papermc/paper/command/brigadier/PaperCommandSourceStack.java
-@@ -0,0 +1,63 @@
-+package io.papermc.paper.command.brigadier;
-+
-+import com.destroystokyo.paper.brigadier.BukkitBrigadierCommandSource;
-+import net.minecraft.world.level.Level;
-+import net.minecraft.world.phys.Vec2;
-+import net.minecraft.world.phys.Vec3;
-+import org.bukkit.Location;
-+import org.bukkit.command.CommandSender;
-+import org.bukkit.entity.Entity;
-+import org.jetbrains.annotations.NotNull;
-+import org.jetbrains.annotations.Nullable;
-+
-+public interface PaperCommandSourceStack extends CommandSourceStack, BukkitBrigadierCommandSource {
-+
-+ net.minecraft.commands.CommandSourceStack getHandle();
-+
-+ @Override
-+ default @NotNull Location getLocation() {
-+ Vec2 rot = this.getHandle().getRotation();
-+ Vec3 pos = this.getHandle().getPosition();
-+ Level level = this.getHandle().getLevel();
-+
-+ return new Location(level.getWorld(), pos.x, pos.y, pos.z, rot.y, rot.x);
-+ }
-+
-+ @Override
-+ @NotNull
-+ default CommandSender getSender() {
-+ return this.getHandle().getBukkitSender();
-+ }
-+
-+ @Override
-+ @Nullable
-+ default Entity getExecutor() {
-+ net.minecraft.world.entity.Entity nmsEntity = this.getHandle().getEntity();
-+ if (nmsEntity == null) {
-+ return null;
-+ }
-+
-+ return nmsEntity.getBukkitEntity();
-+ }
-+
-+ // OLD METHODS
-+ @Override
-+ default org.bukkit.entity.Entity getBukkitEntity() {
-+ return this.getExecutor();
-+ }
-+
-+ @Override
-+ default org.bukkit.World getBukkitWorld() {
-+ return this.getLocation().getWorld();
-+ }
-+
-+ @Override
-+ default org.bukkit.Location getBukkitLocation() {
-+ return this.getLocation();
-+ }
-+
-+ @Override
-+ default CommandSender getBukkitSender() {
-+ return this.getSender();
-+ }
-+}
-diff --git a/src/main/java/io/papermc/paper/command/brigadier/PaperCommands.java b/src/main/java/io/papermc/paper/command/brigadier/PaperCommands.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..27509813a90980be1dfc7bde27d0eba60adfc820
---- /dev/null
-+++ b/src/main/java/io/papermc/paper/command/brigadier/PaperCommands.java
-@@ -0,0 +1,193 @@
-+package io.papermc.paper.command.brigadier;
-+
-+import com.google.common.base.Preconditions;
-+import com.mojang.brigadier.CommandDispatcher;
-+import com.mojang.brigadier.arguments.StringArgumentType;
-+import com.mojang.brigadier.builder.LiteralArgumentBuilder;
-+import com.mojang.brigadier.suggestion.SuggestionsBuilder;
-+import com.mojang.brigadier.tree.CommandNode;
-+import com.mojang.brigadier.tree.LiteralCommandNode;
-+import io.papermc.paper.plugin.configuration.PluginMeta;
-+import io.papermc.paper.plugin.lifecycle.event.LifecycleEventOwner;
-+import io.papermc.paper.plugin.lifecycle.event.registrar.PaperRegistrar;
-+import java.util.ArrayList;
-+import java.util.Collection;
-+import java.util.Collections;
-+import java.util.HashSet;
-+import java.util.List;
-+import java.util.Locale;
-+import java.util.Set;
-+import net.minecraft.commands.CommandBuildContext;
-+import org.apache.commons.lang3.StringUtils;
-+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.NotNull;
-+import org.jetbrains.annotations.Unmodifiable;
-+
-+import static java.util.Objects.requireNonNull;
-+
-+@DefaultQualifier(NonNull.class)
-+public class PaperCommands implements Commands, PaperRegistrar<LifecycleEventOwner> {
-+
-+ public static final PaperCommands INSTANCE = new PaperCommands();
-+
-+ private @Nullable LifecycleEventOwner currentContext;
-+ private @MonotonicNonNull CommandDispatcher<CommandSourceStack> dispatcher;
-+ private @MonotonicNonNull CommandBuildContext buildContext;
-+ private boolean invalid = false;
-+
-+ @Override
-+ public void setCurrentContext(final @Nullable LifecycleEventOwner context) {
-+ this.currentContext = context;
-+ }
-+
-+ public void setDispatcher(final net.minecraft.commands.Commands commands, final CommandBuildContext commandBuildContext) {
-+ this.invalid = false;
-+ this.dispatcher = new CommandDispatcher<>(new ApiMirrorRootNode() {
-+ @Override
-+ public CommandDispatcher<net.minecraft.commands.CommandSourceStack> getDispatcher() {
-+ return commands.getDispatcher();
-+ }
-+ });
-+ this.buildContext = commandBuildContext;
-+ }
-+
-+ public void setValid() {
-+ this.invalid = false;
-+ }
-+
-+ @Override
-+ public void invalidate() {
-+ this.invalid = true;
-+ }
-+
-+ // use this method internally as it bypasses the valid check
-+ public CommandDispatcher<CommandSourceStack> getDispatcherInternal() {
-+ Preconditions.checkState(this.dispatcher != null, "the dispatcher hasn't been set yet");
-+ return this.dispatcher;
-+ }
-+
-+ public CommandBuildContext getBuildContext() {
-+ Preconditions.checkState(this.buildContext != null, "the build context hasn't been set yet");
-+ return this.buildContext;
-+ }
-+
-+ @Override
-+ public CommandDispatcher<CommandSourceStack> getDispatcher() {
-+ Preconditions.checkState(!this.invalid && this.dispatcher != null, "cannot access the dispatcher in this context");
-+ return this.dispatcher;
-+ }
-+
-+ @Override
-+ public @Unmodifiable Set<String> register(final LiteralCommandNode<CommandSourceStack> node, final @Nullable String description, final Collection<String> aliases) {
-+ return this.register(requireNonNull(this.currentContext, "No lifecycle owner context is set").getPluginMeta(), node, description, aliases);
-+ }
-+
-+ @Override
-+ public @Unmodifiable Set<String> register(final PluginMeta pluginMeta, final LiteralCommandNode<CommandSourceStack> node, final @Nullable String description, final Collection<String> aliases) {
-+ return this.registerWithFlags(pluginMeta, node, description, aliases, Set.of());
-+ }
-+
-+ @Override
-+ public @Unmodifiable Set<String> registerWithFlags(@NotNull final PluginMeta pluginMeta, @NotNull final LiteralCommandNode<CommandSourceStack> node, @org.jetbrains.annotations.Nullable final String description, @NotNull final Collection<String> aliases, @NotNull final Set<CommandRegistrationFlag> flags) {
-+ final boolean hasFlattenRedirectFlag = flags.contains(CommandRegistrationFlag.FLATTEN_ALIASES);
-+ final String identifier = pluginMeta.getName().toLowerCase(Locale.ROOT);
-+ final String literal = node.getLiteral();
-+ final PluginCommandNode pluginLiteral = new PluginCommandNode(identifier + ":" + literal, pluginMeta, node, description); // Treat the keyed version of the command as the root
-+
-+ final Set<String> registeredLabels = new HashSet<>(aliases.size() * 2 + 2);
-+
-+ if (this.registerIntoDispatcher(pluginLiteral, true)) {
-+ registeredLabels.add(pluginLiteral.getLiteral());
-+ }
-+ if (this.registerRedirect(literal, pluginMeta, pluginLiteral, description, true, hasFlattenRedirectFlag)) { // Plugin commands should override vanilla commands
-+ registeredLabels.add(literal);
-+ }
-+
-+ // Add aliases
-+ final List<String> registeredAliases = new ArrayList<>(aliases.size() * 2);
-+ for (final String alias : aliases) {
-+ if (this.registerRedirect(alias, pluginMeta, pluginLiteral, description, false, hasFlattenRedirectFlag)) {
-+ registeredAliases.add(alias);
-+ }
-+ if (this.registerRedirect(identifier + ":" + alias, pluginMeta, pluginLiteral, description, false, hasFlattenRedirectFlag)) {
-+ registeredAliases.add(identifier + ":" + alias);
-+ }
-+ }
-+
-+ if (!registeredAliases.isEmpty()) {
-+ pluginLiteral.setAliases(registeredAliases);
-+ }
-+
-+ registeredLabels.addAll(registeredAliases);
-+ return registeredLabels.isEmpty() ? Collections.emptySet() : Collections.unmodifiableSet(registeredLabels);
-+ }
-+
-+ private boolean registerRedirect(final String aliasLiteral, final PluginMeta plugin, final PluginCommandNode redirectTo, final @Nullable String description, final boolean override, boolean hasFlattenRedirectFlag) {
-+ final LiteralCommandNode<CommandSourceStack> redirect;
-+ if (redirectTo.getChildren().isEmpty() || hasFlattenRedirectFlag) {
-+ redirect = Commands.literal(aliasLiteral)
-+ .executes(redirectTo.getCommand())
-+ .requires(redirectTo.getRequirement())
-+ .build();
-+
-+ for (final CommandNode<CommandSourceStack> child : redirectTo.getChildren()) {
-+ redirect.addChild(child);
-+ }
-+ } else {
-+ redirect = Commands.literal(aliasLiteral)
-+ .executes(redirectTo.getCommand())
-+ .redirect(redirectTo)
-+ .requires(redirectTo.getRequirement())
-+ .build();
-+ }
-+
-+ return this.registerIntoDispatcher(new PluginCommandNode(aliasLiteral, plugin, redirect, description), override);
-+ }
-+
-+ private boolean registerIntoDispatcher(final PluginCommandNode node, final boolean override) {
-+ final boolean hasChild = this.getDispatcher().getRoot().getChild(node.getLiteral()) != null;
-+ if (!hasChild || override) { // Avoid merging behavior. Maybe something to look into in the future
-+ if (override) {
-+ this.getDispatcher().getRoot().removeCommand(node.getLiteral());
-+ }
-+ this.getDispatcher().getRoot().addChild(node);
-+ return true;
-+ }
-+
-+ return false;
-+ }
-+
-+ @Override
-+ public @Unmodifiable Set<String> register(final String label, final @Nullable String description, final Collection<String> aliases, final BasicCommand basicCommand) {
-+ return this.register(requireNonNull(this.currentContext, "No lifecycle owner context is set").getPluginMeta(), label, description, aliases, basicCommand);
-+ }
-+
-+ @Override
-+ public @Unmodifiable Set<String> register(final PluginMeta pluginMeta, final String label, final @Nullable String description, final Collection<String> aliases, final BasicCommand basicCommand) {
-+ final LiteralArgumentBuilder<CommandSourceStack> builder = Commands.literal(label)
-+ .then(
-+ Commands.argument("args", StringArgumentType.greedyString())
-+ .suggests((context, suggestionsBuilder) -> {
-+ final String[] args = StringUtils.split(suggestionsBuilder.getRemaining());
-+ final SuggestionsBuilder offsetSuggestionsBuilder = suggestionsBuilder.createOffset(suggestionsBuilder.getInput().lastIndexOf(' ') + 1);;
-+
-+ final Collection<String> suggestions = basicCommand.suggest(context.getSource(), args);
-+ suggestions.forEach(offsetSuggestionsBuilder::suggest);
-+ return offsetSuggestionsBuilder.buildFuture();
-+ })
-+ .executes((stack) -> {
-+ basicCommand.execute(stack.getSource(), StringUtils.split(stack.getArgument("args", String.class), ' '));
-+ return com.mojang.brigadier.Command.SINGLE_SUCCESS;
-+ })
-+ )
-+ .executes((stack) -> {
-+ basicCommand.execute(stack.getSource(), new String[0]);
-+ return com.mojang.brigadier.Command.SINGLE_SUCCESS;
-+ });
-+
-+ return this.register(pluginMeta, builder.build(), description, aliases);
-+ }
-+}
-diff --git a/src/main/java/io/papermc/paper/command/brigadier/PluginCommandNode.java b/src/main/java/io/papermc/paper/command/brigadier/PluginCommandNode.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..3a9f58873b83f10ba354ae4968c4ab0632662439
---- /dev/null
-+++ b/src/main/java/io/papermc/paper/command/brigadier/PluginCommandNode.java
-@@ -0,0 +1,50 @@
-+package io.papermc.paper.command.brigadier;
-+
-+import com.mojang.brigadier.tree.CommandNode;
-+import com.mojang.brigadier.tree.LiteralCommandNode;
-+import java.util.Collections;
-+import java.util.List;
-+import java.util.Objects;
-+import io.papermc.paper.plugin.configuration.PluginMeta;
-+import org.bukkit.Bukkit;
-+import org.bukkit.plugin.Plugin;
-+import org.jetbrains.annotations.NotNull;
-+import org.jetbrains.annotations.Nullable;
-+
-+public class PluginCommandNode extends LiteralCommandNode<CommandSourceStack> {
-+
-+ private final PluginMeta plugin;
-+ private final String description;
-+ private List<String> aliases = Collections.emptyList();
-+
-+ public PluginCommandNode(final @NotNull String literal, final @NotNull PluginMeta plugin, final @NotNull LiteralCommandNode<CommandSourceStack> rootLiteral, final @Nullable String description) {
-+ super(
-+ literal, rootLiteral.getCommand(), rootLiteral.getRequirement(),
-+ rootLiteral.getRedirect(), rootLiteral.getRedirectModifier(), rootLiteral.isFork()
-+ );
-+ this.plugin = plugin;
-+ this.description = description;
-+
-+ for (CommandNode<CommandSourceStack> argument : rootLiteral.getChildren()) {
-+ this.addChild(argument);
-+ }
-+ }
-+
-+ @NotNull
-+ public Plugin getPlugin() {
-+ return Objects.requireNonNull(Bukkit.getPluginManager().getPlugin(this.plugin.getName()));
-+ }
-+
-+ @NotNull
-+ public String getDescription() {
-+ return this.description;
-+ }
-+
-+ public void setAliases(List<String> aliases) {
-+ this.aliases = aliases;
-+ }
-+
-+ public List<String> getAliases() {
-+ return this.aliases;
-+ }
-+}
-diff --git a/src/main/java/io/papermc/paper/command/brigadier/PluginVanillaCommandWrapper.java b/src/main/java/io/papermc/paper/command/brigadier/PluginVanillaCommandWrapper.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..cf8359af60601d7917e77fd06a00b64992a85953
---- /dev/null
-+++ b/src/main/java/io/papermc/paper/command/brigadier/PluginVanillaCommandWrapper.java
-@@ -0,0 +1,46 @@
-+package io.papermc.paper.command.brigadier;
-+
-+import com.mojang.brigadier.tree.CommandNode;
-+import net.minecraft.commands.CommandSourceStack;
-+import net.minecraft.commands.Commands;
-+import org.bukkit.command.Command;
-+import org.bukkit.command.PluginIdentifiableCommand;
-+import org.bukkit.craftbukkit.command.VanillaCommandWrapper;
-+import org.bukkit.plugin.Plugin;
-+import org.jetbrains.annotations.NotNull;
-+
-+import java.util.List;
-+
-+// Exists to that /help can show the plugin
-+public class PluginVanillaCommandWrapper extends VanillaCommandWrapper implements PluginIdentifiableCommand {
-+
-+ private final Plugin plugin;
-+ private final List<String> alises;
-+
-+ public PluginVanillaCommandWrapper(String name, String description, String usageMessage, List<String> aliases, CommandNode<CommandSourceStack> vanillaCommand, Plugin plugin) {
-+ super(name, description, usageMessage, aliases, vanillaCommand);
-+ this.plugin = plugin;
-+ this.alises = aliases;
-+ }
-+
-+ @Override
-+ public @NotNull List<String> getAliases() {
-+ return this.alises;
-+ }
-+
-+ @Override
-+ public @NotNull Command setAliases(@NotNull List<String> aliases) {
-+ return this;
-+ }
-+
-+ @Override
-+ public @NotNull Plugin getPlugin() {
-+ return this.plugin;
-+ }
-+
-+ // Show in help menu!
-+ @Override
-+ public boolean isRegistered() {
-+ return true;
-+ }
-+}
-diff --git a/src/main/java/io/papermc/paper/command/brigadier/ShadowBrigNode.java b/src/main/java/io/papermc/paper/command/brigadier/ShadowBrigNode.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..895addef908e09d527e4bc9210599e8827c53807
---- /dev/null
-+++ b/src/main/java/io/papermc/paper/command/brigadier/ShadowBrigNode.java
-@@ -0,0 +1,35 @@
-+package io.papermc.paper.command.brigadier;
-+
-+import com.mojang.brigadier.tree.CommandNode;
-+import com.mojang.brigadier.tree.LiteralCommandNode;
-+
-+import java.util.Collection;
-+
-+public class ShadowBrigNode extends LiteralCommandNode<CommandSourceStack> {
-+
-+ private final CommandNode<net.minecraft.commands.CommandSourceStack> handle;
-+
-+ public ShadowBrigNode(CommandNode<net.minecraft.commands.CommandSourceStack> node) {
-+ super(node.getName(), context -> 0, (s) -> false, node.getRedirect() == null ? null : new ShadowBrigNode(node.getRedirect()), null, node.isFork());
-+ this.handle = node;
-+ }
-+
-+ @Override
-+ public Collection<CommandNode<CommandSourceStack>> getChildren() {
-+ throw new UnsupportedOperationException("Cannot retrieve children from this node.");
-+ }
-+
-+ @Override
-+ public CommandNode<CommandSourceStack> getChild(String name) {
-+ throw new UnsupportedOperationException("Cannot retrieve children from this node.");
-+ }
-+
-+ @Override
-+ public void addChild(CommandNode<CommandSourceStack> node) {
-+ throw new UnsupportedOperationException("Cannot modify children for this node.");
-+ }
-+
-+ public CommandNode<net.minecraft.commands.CommandSourceStack> getHandle() {
-+ return this.handle;
-+ }
-+}
-diff --git a/src/main/java/io/papermc/paper/command/brigadier/argument/SignedMessageResolverImpl.java b/src/main/java/io/papermc/paper/command/brigadier/argument/SignedMessageResolverImpl.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..72966584089d3fee9778f572727c9b7f4a4d4302
---- /dev/null
-+++ b/src/main/java/io/papermc/paper/command/brigadier/argument/SignedMessageResolverImpl.java
-@@ -0,0 +1,30 @@
-+package io.papermc.paper.command.brigadier.argument;
-+
-+import com.mojang.brigadier.context.CommandContext;
-+import com.mojang.brigadier.exceptions.CommandSyntaxException;
-+import net.kyori.adventure.chat.SignedMessage;
-+import net.minecraft.commands.CommandSourceStack;
-+import net.minecraft.commands.arguments.MessageArgument;
-+import org.jetbrains.annotations.NotNull;
-+
-+import java.util.concurrent.CompletableFuture;
-+
-+public record SignedMessageResolverImpl(MessageArgument.Message message) implements SignedMessageResolver {
-+
-+ @Override
-+ public String content() {
-+ return this.message.text();
-+ }
-+
-+ @Override
-+ public @NotNull CompletableFuture<SignedMessage> resolveSignedMessage(final String argumentName, final CommandContext erased) throws CommandSyntaxException {
-+ final CommandContext<CommandSourceStack> type = erased;
-+ final CompletableFuture<SignedMessage> future = new CompletableFuture<>();
-+
-+ final MessageArgument.Message response = type.getArgument(argumentName, SignedMessageResolverImpl.class).message;
-+ MessageArgument.resolveChatMessage(response, type, argumentName, (message) -> {
-+ future.complete(message.adventureView());
-+ });
-+ return future;
-+ }
-+}
-diff --git a/src/main/java/io/papermc/paper/command/brigadier/argument/VanillaArgumentProviderImpl.java b/src/main/java/io/papermc/paper/command/brigadier/argument/VanillaArgumentProviderImpl.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..93edb22c8500e79f86b101ef38955bca45a8d3a9
---- /dev/null
-+++ b/src/main/java/io/papermc/paper/command/brigadier/argument/VanillaArgumentProviderImpl.java
-@@ -0,0 +1,354 @@
-+package io.papermc.paper.command.brigadier.argument;
-+
-+import com.destroystokyo.paper.profile.CraftPlayerProfile;
-+import com.google.common.collect.Collections2;
-+import com.google.common.collect.Lists;
-+import com.google.common.collect.Range;
-+import com.mojang.brigadier.StringReader;
-+import com.mojang.brigadier.arguments.ArgumentType;
-+import com.mojang.brigadier.context.CommandContext;
-+import com.mojang.brigadier.exceptions.CommandSyntaxException;
-+import com.mojang.brigadier.suggestion.Suggestions;
-+import com.mojang.brigadier.suggestion.SuggestionsBuilder;
-+import io.papermc.paper.adventure.PaperAdventure;
-+import io.papermc.paper.command.brigadier.PaperCommands;
-+import io.papermc.paper.command.brigadier.argument.predicate.ItemStackPredicate;
-+import io.papermc.paper.command.brigadier.argument.range.DoubleRangeProvider;
-+import io.papermc.paper.command.brigadier.argument.range.IntegerRangeProvider;
-+import io.papermc.paper.command.brigadier.argument.range.RangeProvider;
-+import io.papermc.paper.command.brigadier.argument.resolvers.BlockPositionResolver;
-+import io.papermc.paper.command.brigadier.argument.resolvers.PlayerProfileListResolver;
-+import io.papermc.paper.command.brigadier.argument.resolvers.selector.EntitySelectorArgumentResolver;
-+import io.papermc.paper.command.brigadier.argument.resolvers.selector.PlayerSelectorArgumentResolver;
-+import io.papermc.paper.entity.LookAnchor;
-+import io.papermc.paper.math.Position;
-+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 java.util.Collection;
-+import java.util.Collections;
-+import java.util.List;
-+import java.util.UUID;
-+import java.util.concurrent.CompletableFuture;
-+import java.util.function.Function;
-+import net.kyori.adventure.key.Key;
-+import net.kyori.adventure.text.Component;
-+import net.kyori.adventure.text.format.NamedTextColor;
-+import net.kyori.adventure.text.format.Style;
-+import net.minecraft.advancements.critereon.MinMaxBounds;
-+import net.minecraft.commands.CommandSourceStack;
-+import net.minecraft.commands.arguments.ColorArgument;
-+import net.minecraft.commands.arguments.ComponentArgument;
-+import net.minecraft.commands.arguments.DimensionArgument;
-+import net.minecraft.commands.arguments.EntityAnchorArgument;
-+import net.minecraft.commands.arguments.EntityArgument;
-+import net.minecraft.commands.arguments.GameModeArgument;
-+import net.minecraft.commands.arguments.GameProfileArgument;
-+import net.minecraft.commands.arguments.HeightmapTypeArgument;
-+import net.minecraft.commands.arguments.MessageArgument;
-+import net.minecraft.commands.arguments.ObjectiveCriteriaArgument;
-+import net.minecraft.commands.arguments.RangeArgument;
-+import net.minecraft.commands.arguments.ResourceArgument;
-+import net.minecraft.commands.arguments.ResourceKeyArgument;
-+import net.minecraft.commands.arguments.ResourceLocationArgument;
-+import net.minecraft.commands.arguments.ScoreboardSlotArgument;
-+import net.minecraft.commands.arguments.StyleArgument;
-+import net.minecraft.commands.arguments.TemplateMirrorArgument;
-+import net.minecraft.commands.arguments.TemplateRotationArgument;
-+import net.minecraft.commands.arguments.TimeArgument;
-+import net.minecraft.commands.arguments.UuidArgument;
-+import net.minecraft.commands.arguments.blocks.BlockStateArgument;
-+import net.minecraft.commands.arguments.coordinates.BlockPosArgument;
-+import net.minecraft.commands.arguments.item.ItemArgument;
-+import net.minecraft.commands.arguments.item.ItemPredicateArgument;
-+import net.minecraft.core.BlockPos;
-+import net.minecraft.core.registries.Registries;
-+import net.minecraft.resources.ResourceKey;
-+import net.minecraft.server.MinecraftServer;
-+import net.minecraft.server.level.ServerLevel;
-+import net.minecraft.server.level.ServerPlayer;
-+import net.minecraft.world.level.Level;
-+import org.bukkit.GameMode;
-+import org.bukkit.HeightMap;
-+import org.bukkit.Keyed;
-+import org.bukkit.NamespacedKey;
-+import org.bukkit.World;
-+import org.bukkit.block.BlockState;
-+import org.bukkit.block.structure.Mirror;
-+import org.bukkit.block.structure.StructureRotation;
-+import org.bukkit.craftbukkit.CraftHeightMap;
-+import org.bukkit.craftbukkit.CraftRegistry;
-+import org.bukkit.craftbukkit.block.CraftBlockStates;
-+import org.bukkit.craftbukkit.inventory.CraftItemStack;
-+import org.bukkit.craftbukkit.scoreboard.CraftCriteria;
-+import org.bukkit.craftbukkit.scoreboard.CraftScoreboardTranslations;
-+import org.bukkit.craftbukkit.util.CraftNamespacedKey;
-+import org.bukkit.inventory.ItemStack;
-+import org.bukkit.scoreboard.Criteria;
-+import org.bukkit.scoreboard.DisplaySlot;
-+import org.checkerframework.checker.nullness.qual.NonNull;
-+import org.checkerframework.checker.nullness.qual.Nullable;
-+import org.checkerframework.framework.qual.DefaultQualifier;
-+
-+import static java.util.Objects.requireNonNull;
-+
-+@DefaultQualifier(NonNull.class)
-+public class VanillaArgumentProviderImpl implements VanillaArgumentProvider {
-+
-+ @Override
-+ public ArgumentType<EntitySelectorArgumentResolver> entity() {
-+ return this.wrap(EntityArgument.entity(), (result) -> sourceStack -> {
-+ return List.of(result.findSingleEntity((CommandSourceStack) sourceStack).getBukkitEntity());
-+ });
-+ }
-+
-+ @Override
-+ public ArgumentType<EntitySelectorArgumentResolver> entities() {
-+ return this.wrap(EntityArgument.entities(), (result) -> sourceStack -> {
-+ return Lists.transform(result.findEntities((CommandSourceStack) sourceStack), net.minecraft.world.entity.Entity::getBukkitEntity);
-+ });
-+ }
-+
-+ @Override
-+ public ArgumentType<PlayerSelectorArgumentResolver> player() {
-+ return this.wrap(EntityArgument.player(), (result) -> sourceStack -> {
-+ return List.of(result.findSinglePlayer((CommandSourceStack) sourceStack).getBukkitEntity());
-+ });
-+ }
-+
-+ @Override
-+ public ArgumentType<PlayerSelectorArgumentResolver> players() {
-+ return this.wrap(EntityArgument.players(), (result) -> sourceStack -> {
-+ return Lists.transform(result.findPlayers((CommandSourceStack) sourceStack), ServerPlayer::getBukkitEntity);
-+ });
-+ }
-+
-+ @Override
-+ public ArgumentType<PlayerProfileListResolver> playerProfiles() {
-+ return this.wrap(GameProfileArgument.gameProfile(), result -> {
-+ if (result instanceof GameProfileArgument.SelectorResult) {
-+ return sourceStack -> Collections.unmodifiableCollection(Collections2.transform(result.getNames((CommandSourceStack) sourceStack), CraftPlayerProfile::new));
-+ } else {
-+ return sourceStack -> Collections.unmodifiableCollection(Collections2.transform(result.getNames((CommandSourceStack) sourceStack), CraftPlayerProfile::new));
-+ }
-+ });
-+ }
-+
-+ @Override
-+ public ArgumentType<BlockPositionResolver> blockPosition() {
-+ return this.wrap(BlockPosArgument.blockPos(), (result) -> sourceStack -> {
-+ final BlockPos pos = result.getBlockPos((CommandSourceStack) sourceStack);
-+
-+ return Position.block(pos.getX(), pos.getY(), pos.getZ());
-+ });
-+ }
-+
-+ @Override
-+ public ArgumentType<BlockState> blockState() {
-+ return this.wrap(BlockStateArgument.block(PaperCommands.INSTANCE.getBuildContext()), (result) -> {
-+ return CraftBlockStates.getBlockState(CraftRegistry.getMinecraftRegistry(), BlockPos.ZERO, result.getState(), result.tag);
-+ });
-+ }
-+
-+ @Override
-+ public ArgumentType<ItemStack> itemStack() {
-+ return this.wrap(ItemArgument.item(PaperCommands.INSTANCE.getBuildContext()), (result) -> {
-+ return CraftItemStack.asBukkitCopy(result.createItemStack(1, true));
-+ });
-+ }
-+
-+ @Override
-+ public ArgumentType<ItemStackPredicate> itemStackPredicate() {
-+ return this.wrap(ItemPredicateArgument.itemPredicate(PaperCommands.INSTANCE.getBuildContext()), type -> itemStack -> type.test(CraftItemStack.asNMSCopy(itemStack)));
-+ }
-+
-+ @Override
-+ public ArgumentType<NamedTextColor> namedColor() {
-+ return this.wrap(ColorArgument.color(), result ->
-+ requireNonNull(
-+ NamedTextColor.namedColor(
-+ requireNonNull(result.getColor(), () -> result + " didn't have a color")
-+ ),
-+ () -> result.getColor() + " didn't map to an adventure named color"
-+ )
-+ );
-+ }
-+
-+ @Override
-+ public ArgumentType<Component> component() {
-+ return this.wrap(ComponentArgument.textComponent(PaperCommands.INSTANCE.getBuildContext()), PaperAdventure::asAdventure);
-+ }
-+
-+ @Override
-+ public ArgumentType<Style> style() {
-+ return this.wrap(StyleArgument.style(PaperCommands.INSTANCE.getBuildContext()), PaperAdventure::asAdventure);
-+ }
-+
-+ @Override
-+ public ArgumentType<SignedMessageResolver> signedMessage() {
-+ return this.wrap(MessageArgument.message(), SignedMessageResolverImpl::new);
-+ }
-+
-+ @Override
-+ public ArgumentType<DisplaySlot> scoreboardDisplaySlot() {
-+ return this.wrap(ScoreboardSlotArgument.displaySlot(), CraftScoreboardTranslations::toBukkitSlot);
-+ }
-+
-+ @Override
-+ public ArgumentType<NamespacedKey> namespacedKey() {
-+ return this.wrap(ResourceLocationArgument.id(), CraftNamespacedKey::fromMinecraft);
-+ }
-+
-+ @Override
-+ public ArgumentType<Key> key() {
-+ return this.wrap(ResourceLocationArgument.id(), CraftNamespacedKey::fromMinecraft);
-+ }
-+
-+ @Override
-+ public ArgumentType<IntegerRangeProvider> integerRange() {
-+ return this.wrap(RangeArgument.intRange(), type -> VanillaArgumentProviderImpl.convertToRange(type, integerRange -> () -> integerRange));
-+ }
-+
-+ @Override
-+ public ArgumentType<DoubleRangeProvider> doubleRange() {
-+ return this.wrap(RangeArgument.floatRange(), type -> VanillaArgumentProviderImpl.convertToRange(type, doubleRange -> () -> doubleRange));
-+ }
-+
-+ private static <C extends Number & Comparable<C>, T extends RangeProvider<C>> T convertToRange(final MinMaxBounds<C> bounds, final Function<Range<C>, T> converter) {
-+ if (bounds.isAny()) {
-+ return converter.apply(Range.all());
-+ } else if (bounds.min().isPresent() && bounds.max().isPresent()) {
-+ return converter.apply(Range.closed(bounds.min().get(), bounds.max().get()));
-+ } else if (bounds.max().isPresent()) {
-+ return converter.apply(Range.atMost(bounds.max().get()));
-+ } else if (bounds.min().isPresent()) {
-+ return converter.apply(Range.atLeast(bounds.min().get()));
-+ }
-+ throw new IllegalStateException("This is a bug: " + bounds);
-+ }
-+
-+ @Override
-+ public ArgumentType<World> world() {
-+ return this.wrap(DimensionArgument.dimension(), dimensionLocation -> {
-+ // based on DimensionArgument#getDimension
-+ final ResourceKey<Level> resourceKey = ResourceKey.create(Registries.DIMENSION, dimensionLocation);
-+ @Nullable final ServerLevel serverLevel = MinecraftServer.getServer().getLevel(resourceKey);
-+ if (serverLevel == null) {
-+ throw DimensionArgument.ERROR_INVALID_VALUE.create(dimensionLocation);
-+ } else {
-+ return serverLevel.getWorld();
-+ }
-+ });
-+ }
-+
-+ @Override
-+ public ArgumentType<GameMode> gameMode() {
-+ return this.wrap(GameModeArgument.gameMode(), type -> requireNonNull(GameMode.getByValue(type.getId())));
-+ }
-+
-+ @Override
-+ public ArgumentType<HeightMap> heightMap() {
-+ return this.wrap(HeightmapTypeArgument.heightmap(), CraftHeightMap::fromNMS);
-+ }
-+
-+ @Override
-+ public ArgumentType<UUID> uuid() {
-+ return this.wrap(UuidArgument.uuid());
-+ }
-+
-+ @Override
-+ public ArgumentType<Criteria> objectiveCriteria() {
-+ return this.wrap(ObjectiveCriteriaArgument.criteria(), CraftCriteria::getFromNMS);
-+ }
-+
-+ @Override
-+ public ArgumentType<LookAnchor> entityAnchor() {
-+ return this.wrap(EntityAnchorArgument.anchor(), type -> LookAnchor.valueOf(type.name()));
-+ }
-+
-+ @Override
-+ public ArgumentType<Integer> time(final int minTicks) {
-+ return this.wrap(TimeArgument.time(minTicks));
-+ }
-+
-+ @Override
-+ public ArgumentType<Mirror> templateMirror() {
-+ return this.wrap(TemplateMirrorArgument.templateMirror(), mirror -> Mirror.valueOf(mirror.name()));
-+ }
-+
-+ @Override
-+ public ArgumentType<StructureRotation> templateRotation() {
-+ return this.wrap(TemplateRotationArgument.templateRotation(), mirror -> StructureRotation.valueOf(mirror.name()));
-+ }
-+
-+ @Override
-+ public <T> ArgumentType<TypedKey<T>> resourceKey(final RegistryKey<T> registryKey) {
-+ return this.wrap(
-+ ResourceKeyArgument.key(PaperRegistries.toNms(registryKey)),
-+ nmsRegistryKey -> TypedKey.create(registryKey, CraftNamespacedKey.fromMinecraft(nmsRegistryKey.location()))
-+ );
-+ }
-+
-+ @Override
-+ public <T> ArgumentType<T> resource(final RegistryKey<T> registryKey) {
-+ return this.resourceRaw(registryKey);
-+ }
-+
-+ @SuppressWarnings({"unchecked", "rawtypes", "UnnecessaryLocalVariable"})
-+ private <T, K extends Keyed> ArgumentType<T> resourceRaw(final RegistryKey registryKeyRaw) { // TODO remove Keyed
-+ final RegistryKey<K> registryKey = registryKeyRaw;
-+ return (ArgumentType<T>) this.wrap(
-+ ResourceArgument.resource(PaperCommands.INSTANCE.getBuildContext(), PaperRegistries.toNms(registryKey)),
-+ resource -> requireNonNull(
-+ RegistryAccess.registryAccess()
-+ .getRegistry(registryKey)
-+ .get(CraftNamespacedKey.fromMinecraft(resource.key().location()))
-+ )
-+ );
-+ }
-+
-+ private <T> ArgumentType<T> wrap(final ArgumentType<T> base) {
-+ return this.wrap(base, identity -> identity);
-+ }
-+
-+ private <B, C> ArgumentType<C> wrap(final ArgumentType<B> base, final ResultConverter<B, C> converter) {
-+ return new NativeWrapperArgumentType<>(base, converter);
-+ }
-+
-+ @FunctionalInterface
-+ interface ResultConverter<T, R> {
-+
-+ R convert(T type) throws CommandSyntaxException;
-+ }
-+
-+ public static final class NativeWrapperArgumentType<M, P> implements ArgumentType<P> {
-+
-+ private final ArgumentType<M> nmsBase;
-+ private final ResultConverter<M, P> converter;
-+
-+ private NativeWrapperArgumentType(final ArgumentType<M> nmsBase, final ResultConverter<M, P> converter) {
-+ this.nmsBase = nmsBase;
-+ this.converter = converter;
-+ }
-+
-+ public ArgumentType<M> nativeNmsArgumentType() {
-+ return this.nmsBase;
-+ }
-+
-+ @Override
-+ public P parse(final StringReader reader) throws CommandSyntaxException {
-+ return this.converter.convert(this.nmsBase.parse(reader));
-+ }
-+
-+ @Override
-+ public <S> CompletableFuture<Suggestions> listSuggestions(final CommandContext<S> context, final SuggestionsBuilder builder) {
-+ return this.nmsBase.listSuggestions(context, builder);
-+ }
-+
-+ @Override
-+ public Collection<String> getExamples() {
-+ return this.nmsBase.getExamples();
-+ }
-+ }
-+}
-diff --git a/src/main/java/io/papermc/paper/command/brigadier/argument/WrappedArgumentCommandNode.java b/src/main/java/io/papermc/paper/command/brigadier/argument/WrappedArgumentCommandNode.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..c59bbd90fdf04db837366218b312e7fb80366707
---- /dev/null
-+++ b/src/main/java/io/papermc/paper/command/brigadier/argument/WrappedArgumentCommandNode.java
-@@ -0,0 +1,55 @@
-+package io.papermc.paper.command.brigadier.argument;
-+
-+import com.mojang.brigadier.Command;
-+import com.mojang.brigadier.RedirectModifier;
-+import com.mojang.brigadier.StringReader;
-+import com.mojang.brigadier.arguments.ArgumentType;
-+import com.mojang.brigadier.context.CommandContextBuilder;
-+import com.mojang.brigadier.context.ParsedArgument;
-+import com.mojang.brigadier.exceptions.CommandSyntaxException;
-+import com.mojang.brigadier.suggestion.SuggestionProvider;
-+import com.mojang.brigadier.tree.ArgumentCommandNode;
-+import com.mojang.brigadier.tree.CommandNode;
-+import io.papermc.paper.command.brigadier.CommandSourceStack;
-+import net.minecraft.commands.synchronization.ArgumentTypeInfos;
-+
-+import java.util.function.Predicate;
-+
-+/*
-+Basically this converts the argument to a different type when parsing.
-+ */
-+public class WrappedArgumentCommandNode<NMS, API> extends ArgumentCommandNode<CommandSourceStack, NMS> {
-+
-+ private final ArgumentType<API> pureArgumentType;
-+
-+ public WrappedArgumentCommandNode(
-+ final String name,
-+ final ArgumentType<API> pureArgumentType,
-+ final ArgumentType<NMS> nmsNativeType,
-+ final Command<CommandSourceStack> command,
-+ final Predicate<CommandSourceStack> requirement,
-+ final CommandNode<CommandSourceStack> redirect,
-+ final RedirectModifier<CommandSourceStack> modifier,
-+ final boolean forks,
-+ final SuggestionProvider<CommandSourceStack> customSuggestions
-+ ) {
-+ super(name, nmsNativeType, command, requirement, redirect, modifier, forks, customSuggestions);
-+ if (!ArgumentTypeInfos.isClassRecognized(nmsNativeType.getClass())) {
-+ // Is this argument an NMS argument?
-+ throw new IllegalArgumentException("Unexpected argument type was passed: " + nmsNativeType.getClass() + ". This should be an NMS type!");
-+ }
-+
-+ this.pureArgumentType = pureArgumentType;
-+ }
-+
-+ // See ArgumentCommandNode#parse
-+ @Override
-+ public void parse(final StringReader reader, final CommandContextBuilder<CommandSourceStack> contextBuilder) throws CommandSyntaxException {
-+ final int start = reader.getCursor();
-+ final API result = this.pureArgumentType.parse(reader); // Use the api argument parser
-+ final ParsedArgument<CommandSourceStack, API> parsed = new ParsedArgument<>(start, reader.getCursor(), result); // Return an API parsed argument instead.
-+
-+ contextBuilder.withArgument(this.getName(), parsed);
-+ contextBuilder.withNode(this, parsed.getRange());
-+ }
-+}
-diff --git a/src/main/java/io/papermc/paper/command/brigadier/bukkit/BukkitBrigForwardingMap.java b/src/main/java/io/papermc/paper/command/brigadier/bukkit/BukkitBrigForwardingMap.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..f0cc27640bb3db275295a298d608c9d9f88df617
---- /dev/null
-+++ b/src/main/java/io/papermc/paper/command/brigadier/bukkit/BukkitBrigForwardingMap.java
-@@ -0,0 +1,332 @@
-+package io.papermc.paper.command.brigadier.bukkit;
-+
-+import com.google.common.collect.Iterators;
-+import com.mojang.brigadier.CommandDispatcher;
-+import com.mojang.brigadier.tree.CommandNode;
-+import com.mojang.brigadier.tree.LiteralCommandNode;
-+import io.papermc.paper.command.brigadier.CommandSourceStack;
-+import io.papermc.paper.command.brigadier.PaperBrigadier;
-+import io.papermc.paper.command.brigadier.PaperCommands;
-+import java.util.AbstractCollection;
-+import java.util.AbstractSet;
-+import java.util.ArrayList;
-+import java.util.Collection;
-+import java.util.HashMap;
-+import java.util.Iterator;
-+import java.util.Map;
-+import java.util.Objects;
-+import java.util.Set;
-+import java.util.Spliterator;
-+import java.util.function.Consumer;
-+import java.util.stream.Stream;
-+import org.bukkit.command.Command;
-+import org.jetbrains.annotations.NotNull;
-+import org.jetbrains.annotations.Nullable;
-+
-+/*
-+This map is supposed to act as a legacy bridge for the command map and the command dispatcher.
-+ */
-+public class BukkitBrigForwardingMap extends HashMap<String, Command> {
-+
-+ public static BukkitBrigForwardingMap INSTANCE = new BukkitBrigForwardingMap();
-+
-+ private final EntrySet entrySet = new EntrySet();
-+ private final KeySet keySet = new KeySet();
-+ private final Values values = new Values();
-+
-+ // Previous dispatcher used to get commands to migrate to another dispatcher
-+
-+ public CommandDispatcher<CommandSourceStack> getDispatcher() {
-+ return PaperCommands.INSTANCE.getDispatcherInternal();
-+ }
-+
-+ @Override
-+ public int size() {
-+ return this.getDispatcher().getRoot().getChildren().size();
-+ }
-+
-+ @Override
-+ public boolean isEmpty() {
-+ return this.size() != 0;
-+ }
-+
-+ @Override
-+ public boolean containsKey(Object key) {
-+ if (!(key instanceof String stringKey)) {
-+ return false;
-+ }
-+
-+ // Do any children match?
-+ return this.getDispatcher().getRoot().getChild(stringKey) != null;
-+ }
-+
-+ @Override
-+ public boolean containsValue(@Nullable final Object value) {
-+ if (!(value instanceof Command)) {
-+ return false;
-+ }
-+
-+ for (CommandNode<CommandSourceStack> child : this.getDispatcher().getRoot().getChildren()) {
-+ // If child is a bukkit command node, we can convert it!
-+ if (child instanceof BukkitCommandNode bukkitCommandNode) {
-+ return bukkitCommandNode.getBukkitCommand().equals(value);
-+ }
-+ }
-+
-+ return false;
-+ }
-+
-+ @Override
-+ public Command get(Object key) {
-+ CommandNode<?> node = this.getDispatcher().getRoot().getChild((String) key);
-+ if (node == null) {
-+ return null;
-+ }
-+
-+ if (node instanceof BukkitCommandNode bukkitCommandNode) {
-+ return bukkitCommandNode.getBukkitCommand();
-+ }
-+
-+ return PaperBrigadier.wrapNode(node);
-+ }
-+
-+ @Nullable
-+ @Override
-+ public Command put(String key, Command value) {
-+ Command old = this.get(key);
-+ this.getDispatcher().getRoot().removeCommand(key); // Override previous command
-+ this.getDispatcher().getRoot().addChild(BukkitCommandNode.of(key, value));
-+ return old;
-+ }
-+
-+ @Override
-+ public Command remove(Object key) {
-+ if (!(key instanceof String string)) {
-+ return null;
-+ }
-+
-+ Command old = this.get(key);
-+ if (old != null) {
-+ this.getDispatcher().getRoot().removeCommand(string);
-+ }
-+
-+ return old;
-+ }
-+
-+ @Override
-+ public boolean remove(Object key, Object value) {
-+ Command old = this.get(key);
-+ if (Objects.equals(old, value)) {
-+ this.remove(key);
-+ return true;
-+ }
-+
-+ return false;
-+ }
-+
-+ @Override
-+ public void putAll(@NotNull Map<? extends String, ? extends Command> m) {
-+ for (Entry<? extends String, ? extends Command> entry : m.entrySet()) {
-+ this.put(entry.getKey(), entry.getValue());
-+ }
-+ }
-+
-+ @Override
-+ public void clear() {
-+ this.getDispatcher().getRoot().clearAll();
-+ }
-+
-+ @NotNull
-+ @Override
-+ public Set<String> keySet() {
-+ return this.keySet;
-+ }
-+
-+ @NotNull
-+ @Override
-+ public Collection<Command> values() {
-+ return this.values;
-+ }
-+
-+ @NotNull
-+ @Override
-+ public Set<Entry<String, Command>> entrySet() {
-+ return this.entrySet;
-+ }
-+
-+ final class Values extends AbstractCollection<Command> {
-+
-+ @Override
-+ public Iterator<Command> iterator() {
-+ // AVOID CME since commands can modify multiple commands now through alises, which means it may appear in the iterator even if removed.
-+ // Oh well!
-+ Iterator<CommandNode<CommandSourceStack>> iterator = new ArrayList<>(BukkitBrigForwardingMap.this.getDispatcher().getRoot().getChildren()).iterator();
-+
-+ return new Iterator<>() {
-+
-+ private CommandNode<CommandSourceStack> lastFetched;
-+
-+ @Override
-+ public void remove() {
-+ if (this.lastFetched == null) {
-+ throw new IllegalStateException("next not yet called");
-+ }
-+
-+ BukkitBrigForwardingMap.this.remove(this.lastFetched.getName());
-+ iterator.remove();
-+ }
-+
-+ @Override
-+ public boolean hasNext() {
-+ return iterator.hasNext();
-+ }
-+
-+ @Override
-+ public Command next() {
-+ CommandNode<CommandSourceStack> next = iterator.next();
-+ this.lastFetched = next;
-+ if (next instanceof BukkitCommandNode bukkitCommandNode) {
-+ return bukkitCommandNode.getBukkitCommand();
-+ } else {
-+ return PaperBrigadier.wrapNode(next);
-+ }
-+ }
-+ };
-+ }
-+
-+ @Override
-+ public int size() {
-+ return BukkitBrigForwardingMap.this.getDispatcher().getRoot().getChildren().size();
-+ }
-+
-+ @Override
-+ public void clear() {
-+ BukkitBrigForwardingMap.this.clear();
-+ }
-+ }
-+
-+
-+ final class KeySet extends AbstractSet<String> {
-+
-+ @Override
-+ public int size() {
-+ return BukkitBrigForwardingMap.this.size();
-+ }
-+
-+ @Override
-+ public void clear() {
-+ BukkitBrigForwardingMap.this.clear();
-+ }
-+
-+ @Override
-+ public Iterator<String> iterator() {
-+ return Iterators.transform(BukkitBrigForwardingMap.this.values.iterator(), Command::getName); // Wrap around the values iterator for consistancy
-+ }
-+
-+ @Override
-+ public boolean contains(Object o) {
-+ return BukkitBrigForwardingMap.this.containsKey(o);
-+ }
-+
-+ @Override
-+ public boolean remove(Object o) {
-+ return BukkitBrigForwardingMap.this.remove(o) != null;
-+ }
-+
-+ @Override
-+ public Spliterator<String> spliterator() {
-+ return this.entryStream().spliterator();
-+ }
-+
-+ @Override
-+ public void forEach(Consumer<? super String> action) {
-+ this.entryStream().forEach(action);
-+ }
-+
-+ private Stream<String> entryStream() {
-+ return BukkitBrigForwardingMap.this.getDispatcher().getRoot().getChildren().stream().map(CommandNode::getName);
-+ }
-+ }
-+
-+ final class EntrySet extends AbstractSet<Entry<String, Command>> {
-+ @Override
-+ public int size() {
-+ return BukkitBrigForwardingMap.this.size();
-+ }
-+
-+
-+ @Override
-+ public void clear() {
-+ BukkitBrigForwardingMap.this.clear();
-+ }
-+
-+ @Override
-+ public Iterator<Entry<String, Command>> iterator() {
-+ return this.entryStream().iterator();
-+ }
-+
-+ @Override
-+ public boolean contains(Object o) {
-+ if (!(o instanceof Map.Entry<?, ?> entry)) {
-+ return false;
-+ }
-+
-+ Object key = entry.getKey();
-+ Command candidate = get(key);
-+ return candidate != null && candidate.equals(entry.getValue());
-+ }
-+
-+ @Override
-+ public boolean remove(Object o) {
-+ if (o instanceof Map.Entry<?, ?> e) {
-+ Object key = e.getKey();
-+ Object value = e.getValue();
-+ return BukkitBrigForwardingMap.this.remove(key, value);
-+ }
-+ return false;
-+ }
-+
-+ @Override
-+ public Spliterator<Entry<String, Command>> spliterator() {
-+ return this.entryStream().spliterator();
-+ }
-+
-+ @Override
-+ public void forEach(Consumer<? super Entry<String, Command>> action) {
-+ this.entryStream().forEach(action);
-+ }
-+
-+ private Stream<Map.Entry<String, Command>> entryStream() {
-+ return BukkitBrigForwardingMap.this.getDispatcher().getRoot().getChildren().stream().map(BukkitBrigForwardingMap.this::nodeToEntry);
-+ }
-+ }
-+
-+ private Map.Entry<String, Command> nodeToEntry(CommandNode<?> node) {
-+ if (node instanceof BukkitCommandNode bukkitCommandNode) {
-+ return this.mutableEntry(bukkitCommandNode.getName(), bukkitCommandNode.getBukkitCommand());
-+ } else {
-+ Command wrapped = PaperBrigadier.wrapNode(node);
-+ return this.mutableEntry(node.getName(), wrapped);
-+ }
-+ }
-+
-+ private Map.Entry<String, Command> mutableEntry(String key, Command command) {
-+ return new Entry<>() {
-+ @Override
-+ public String getKey() {
-+ return key;
-+ }
-+
-+ @Override
-+ public Command getValue() {
-+ return command;
-+ }
-+
-+ @Override
-+ public Command setValue(Command value) {
-+ return BukkitBrigForwardingMap.this.put(key, value);
-+ }
-+ };
-+ }
-+
-+}
-diff --git a/src/main/java/io/papermc/paper/command/brigadier/bukkit/BukkitCommandNode.java b/src/main/java/io/papermc/paper/command/brigadier/bukkit/BukkitCommandNode.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..0c3c82b28e581286b798ee58ca4193efc2faff4a
---- /dev/null
-+++ b/src/main/java/io/papermc/paper/command/brigadier/bukkit/BukkitCommandNode.java
-@@ -0,0 +1,148 @@
-+package io.papermc.paper.command.brigadier.bukkit;
-+
-+import co.aikar.timings.Timing;
-+import com.mojang.brigadier.arguments.StringArgumentType;
-+import com.mojang.brigadier.builder.RequiredArgumentBuilder;
-+import com.mojang.brigadier.context.CommandContext;
-+import com.mojang.brigadier.exceptions.CommandSyntaxException;
-+import com.mojang.brigadier.suggestion.SuggestionProvider;
-+import com.mojang.brigadier.suggestion.Suggestions;
-+import com.mojang.brigadier.suggestion.SuggestionsBuilder;
-+import com.mojang.brigadier.tree.LiteralCommandNode;
-+import io.papermc.paper.command.brigadier.CommandSourceStack;
-+import java.util.ArrayList;
-+import java.util.Collections;
-+import net.minecraft.commands.CommandSource;
-+import org.bukkit.Bukkit;
-+import org.bukkit.ChatColor;
-+import org.bukkit.Location;
-+import org.bukkit.command.Command;
-+import org.bukkit.command.CommandException;
-+import org.bukkit.command.CommandSender;
-+
-+import java.util.Arrays;
-+import java.util.List;
-+import java.util.concurrent.CompletableFuture;
-+import java.util.logging.Level;
-+import org.bukkit.entity.Entity;
-+import org.bukkit.entity.Player;
-+import org.bukkit.event.server.TabCompleteEvent;
-+
-+public class BukkitCommandNode extends LiteralCommandNode<CommandSourceStack> {
-+
-+ private final Command command;
-+
-+ private BukkitCommandNode(String literal, Command command, BukkitBrigCommand bukkitBrigCommand) {
-+ super(
-+ literal, bukkitBrigCommand, source -> {
-+ // If the source is null, assume it's true.
-+ // As bukkit doesn't really map the command sender well in all cases
-+ if (source instanceof net.minecraft.commands.CommandSourceStack commandSourceStack && commandSourceStack.source == CommandSource.NULL) {
-+ return true;
-+ } else {
-+ return command.testPermissionSilent(source.getSender());
-+ }
-+ },
-+ null, null, false
-+ );
-+ this.command = command;
-+ }
-+
-+ public static BukkitCommandNode of(String name, Command command) {
-+ BukkitBrigCommand bukkitBrigCommand = new BukkitBrigCommand(command, name);
-+ BukkitCommandNode commandNode = new BukkitCommandNode(name, command, bukkitBrigCommand);
-+ commandNode.addChild(
-+ RequiredArgumentBuilder.<CommandSourceStack, String>argument("args", StringArgumentType.greedyString())
-+ .suggests(new BukkitBrigSuggestionProvider(command, name))
-+ .executes(bukkitBrigCommand).build()
-+ );
-+
-+ return commandNode;
-+ }
-+
-+ public Command getBukkitCommand() {
-+ return this.command;
-+ }
-+
-+ public static class BukkitBrigCommand implements com.mojang.brigadier.Command<CommandSourceStack> {
-+
-+ private final org.bukkit.command.Command command;
-+ private final String literal;
-+
-+ BukkitBrigCommand(org.bukkit.command.Command command, String literal) {
-+ this.command = command;
-+ this.literal = literal;
-+ }
-+
-+ @Override
-+ public int run(CommandContext<CommandSourceStack> context) throws CommandSyntaxException {
-+ CommandSender sender = context.getSource().getSender();
-+
-+ // Plugins do weird things to workaround normal registration
-+ if (this.command.timings == null) {
-+ this.command.timings = co.aikar.timings.TimingsManager.getCommandTiming(null, this.command);
-+ }
-+
-+ String content = context.getRange().get(context.getInput());
-+ String[] args = org.apache.commons.lang3.StringUtils.split(content, ' '); // fix adjacent spaces (from console/plugins) causing empty array elements
-+
-+ try (Timing ignored = this.command.timings.startTiming()) {
-+ // Note: we don't return the result of target.execute as thats success / failure, we return handled (true) or not handled (false)
-+ this.command.execute(sender, this.literal, Arrays.copyOfRange(args, 1, args.length));
-+ }
-+
-+ // return true as command was handled
-+ return 1;
-+ }
-+ }
-+
-+ static class BukkitBrigSuggestionProvider implements SuggestionProvider<CommandSourceStack> {
-+
-+ private final org.bukkit.command.Command command;
-+ private final String literal;
-+
-+ BukkitBrigSuggestionProvider(org.bukkit.command.Command command, String literal) {
-+ this.command = command;
-+ this.literal = literal;
-+ }
-+
-+ @Override
-+ public CompletableFuture<Suggestions> getSuggestions(CommandContext<CommandSourceStack> context, SuggestionsBuilder builder) throws CommandSyntaxException {
-+ // Paper start
-+ org.bukkit.command.CommandSender sender = context.getSource().getSender();
-+ String[] args = builder.getRemaining().split(" ", -1); // We need the command included -- Set limit to -1, allow for trailing spaces
-+
-+ List<String> results = null;
-+ Location pos = context.getSource().getLocation();
-+ try {
-+ results = this.command.tabComplete(sender, this.literal, args, pos.clone());
-+ } catch (CommandException ex) {
-+ sender.sendMessage(ChatColor.RED + "An internal error occurred while attempting to tab-complete this command");
-+ Bukkit.getServer().getLogger().log(Level.SEVERE, "Exception when " + sender.getName() + " attempted to tab complete " + builder.getRemaining(), ex);
-+ }
-+
-+ if (sender instanceof final Player player) {
-+ TabCompleteEvent tabEvent = new org.bukkit.event.server.TabCompleteEvent(player, builder.getInput(), results != null ? results : new ArrayList<>(), true, pos); // Paper - AsyncTabCompleteEvent
-+ if (!tabEvent.callEvent()) {
-+ results = null;
-+ } else {
-+ results = tabEvent.getCompletions();
-+ }
-+ }
-+ // Paper end
-+ if (results == null) {
-+ return builder.buildFuture();
-+ }
-+
-+ // Defaults to sub nodes, but we have just one giant args node, so offset accordingly
-+ builder = builder.createOffset(builder.getInput().lastIndexOf(' ') + 1);
-+
-+ for (String s : results) {
-+ builder.suggest(s);
-+ }
-+
-+ return builder.buildFuture();
-+ }
-+ }
-+
-+}
-diff --git a/src/main/java/io/papermc/paper/plugin/lifecycle/event/LifecycleEventRunner.java b/src/main/java/io/papermc/paper/plugin/lifecycle/event/LifecycleEventRunner.java
-index f84c9c80e701231e5c33ac3c5573f1093e80f38b..6c072e44a8144de6658b4eb818c996f0eac5805b 100644
---- a/src/main/java/io/papermc/paper/plugin/lifecycle/event/LifecycleEventRunner.java
-+++ b/src/main/java/io/papermc/paper/plugin/lifecycle/event/LifecycleEventRunner.java
-@@ -9,6 +9,7 @@ import io.papermc.paper.plugin.lifecycle.event.registrar.RegistrarEventImpl;
- import io.papermc.paper.plugin.lifecycle.event.registrar.ReloadableRegistrarEvent;
- import io.papermc.paper.plugin.lifecycle.event.types.AbstractLifecycleEventType;
- import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEventType;
-+import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents;
- import io.papermc.paper.plugin.lifecycle.event.types.OwnerAwareLifecycleEvent;
- import java.util.ArrayList;
- import java.util.List;
-@@ -26,6 +27,7 @@ public class LifecycleEventRunner {
-
- private static final Logger LOGGER = LogUtils.getClassLogger();
- private static final Supplier<Set<LifecycleEventType<?, ?, ?>>> BLOCKS_RELOADING = Suppliers.memoize(() -> Set.of( // lazy due to cyclic initialization
-+ LifecycleEvents.COMMANDS
- ));
- public static final LifecycleEventRunner INSTANCE = new LifecycleEventRunner();
-
-diff --git a/src/main/java/net/minecraft/commands/CommandSource.java b/src/main/java/net/minecraft/commands/CommandSource.java
-index 5ba0ef6eda157c4e61d1de99c6b017ceb34430ec..bc5fc57018e347caa5ca453430a45669e086bb22 100644
---- a/src/main/java/net/minecraft/commands/CommandSource.java
-+++ b/src/main/java/net/minecraft/commands/CommandSource.java
-@@ -26,7 +26,7 @@ public interface CommandSource {
- // CraftBukkit start
- @Override
- public org.bukkit.command.CommandSender getBukkitSender(CommandSourceStack wrapper) {
-- throw new UnsupportedOperationException("Not supported yet.");
-+ return io.papermc.paper.brigadier.NullCommandSender.INSTANCE; // Paper - expose a no-op CommandSender
- }
- // CraftBukkit end
- };
-diff --git a/src/main/java/net/minecraft/commands/CommandSourceStack.java b/src/main/java/net/minecraft/commands/CommandSourceStack.java
-index e6c7f62ed379a78645933670299e4fcda8540ed1..59d7e8a3d83d3ab7aa28606401bb129ccaeff240 100644
---- a/src/main/java/net/minecraft/commands/CommandSourceStack.java
-+++ b/src/main/java/net/minecraft/commands/CommandSourceStack.java
-@@ -45,8 +45,7 @@ import net.minecraft.world.phys.Vec2;
- import net.minecraft.world.phys.Vec3;
- import com.mojang.brigadier.tree.CommandNode; // CraftBukkit
-
--public class CommandSourceStack implements ExecutionCommandSource<CommandSourceStack>, SharedSuggestionProvider, com.destroystokyo.paper.brigadier.BukkitBrigadierCommandSource { // Paper - Brigadier API
--
-+public class CommandSourceStack implements ExecutionCommandSource<CommandSourceStack>, SharedSuggestionProvider, io.papermc.paper.command.brigadier.PaperCommandSourceStack { // Paper - Brigadier API
- public static final SimpleCommandExceptionType ERROR_NOT_PLAYER = new SimpleCommandExceptionType(Component.translatable("permissions.requires.player"));
- public static final SimpleCommandExceptionType ERROR_NOT_ENTITY = new SimpleCommandExceptionType(Component.translatable("permissions.requires.entity"));
- public final CommandSource source;
-@@ -170,26 +169,6 @@ public class CommandSourceStack implements ExecutionCommandSource<CommandSourceS
- return this.textName;
- }
-
-- // Paper start - Brigadier API
-- @Override
-- public org.bukkit.entity.Entity getBukkitEntity() {
-- return getEntity() != null ? getEntity().getBukkitEntity() : null;
-- }
--
-- @Override
-- public org.bukkit.World getBukkitWorld() {
-- return getLevel() != null ? getLevel().getWorld() : null;
-- }
--
-- @Override
-- public org.bukkit.Location getBukkitLocation() {
-- Vec3 pos = getPosition();
-- org.bukkit.World world = getBukkitWorld();
-- Vec2 rot = getRotation();
-- return world != null && pos != null ? new org.bukkit.Location(world, pos.x, pos.y, pos.z, rot != null ? rot.y : 0, rot != null ? rot.x : 0) : null;
-- }
-- // Paper end - Brigadier API
--
- @Override
- public boolean hasPermission(int level) {
- // CraftBukkit start
-@@ -457,6 +436,12 @@ public class CommandSourceStack implements ExecutionCommandSource<CommandSourceS
- return this.silent;
- }
-
-+ // Paper start
-+ @Override
-+ public CommandSourceStack getHandle() {
-+ return this;
-+ }
-+ // Paper end
- // CraftBukkit start
- public org.bukkit.command.CommandSender getBukkitSender() {
- return this.source.getBukkitSender(this);
-diff --git a/src/main/java/net/minecraft/commands/Commands.java b/src/main/java/net/minecraft/commands/Commands.java
-index aa2fca6917fb67fe0e9ba067d11487c3a274f675..0126906e2afc8dd525f27a0c5e82116075c9d352 100644
---- a/src/main/java/net/minecraft/commands/Commands.java
-+++ b/src/main/java/net/minecraft/commands/Commands.java
-@@ -156,7 +156,7 @@ public class Commands {
- private final com.mojang.brigadier.CommandDispatcher<CommandSourceStack> dispatcher = new com.mojang.brigadier.CommandDispatcher();
-
- public Commands(Commands.CommandSelection environment, CommandBuildContext commandRegistryAccess) {
-- this(); // CraftBukkit
-+ // Paper
- AdvancementCommands.register(this.dispatcher);
- AttributeCommand.register(this.dispatcher, commandRegistryAccess);
- ExecuteCommand.register(this.dispatcher, commandRegistryAccess);
-@@ -265,11 +265,24 @@ public class Commands {
- }
- }
- // Paper end - Vanilla command permission fixes
-- // CraftBukkit start
-- }
--
-- public Commands() {
-- // CraftBukkkit end
-+ // Paper start - Brigadier Command API
-+ // Create legacy minecraft namespace commands
-+ for (final CommandNode<CommandSourceStack> node : new java.util.ArrayList<>(this.dispatcher.getRoot().getChildren())) {
-+ // The brigadier dispatcher is not able to resolve nested redirects.
-+ // E.g. registering the alias minecraft:tp cannot redirect to tp, as tp itself redirects to teleport.
-+ // Instead, target the first none redirecting node.
-+ CommandNode<CommandSourceStack> flattenedAliasTarget = node;
-+ while (flattenedAliasTarget.getRedirect() != null) flattenedAliasTarget = flattenedAliasTarget.getRedirect();
-+
-+ this.dispatcher.register(
-+ com.mojang.brigadier.builder.LiteralArgumentBuilder.<CommandSourceStack>literal("minecraft:" + node.getName())
-+ .executes(flattenedAliasTarget.getCommand())
-+ .requires(flattenedAliasTarget.getRequirement())
-+ .redirect(flattenedAliasTarget)
-+ );
-+ }
-+ // Paper end - Brigadier Command API
-+ // Paper - remove public constructor, no longer needed
- this.dispatcher.setConsumer(ExecutionCommandSource.resultConsumer());
- }
-
-@@ -325,6 +338,11 @@ public class Commands {
- }
-
- public void performCommand(ParseResults<CommandSourceStack> parseresults, String s, String label) { // CraftBukkit
-+ // Paper start
-+ this.performCommand(parseresults, s, label, false);
-+ }
-+ public void performCommand(ParseResults<CommandSourceStack> parseresults, String s, String label, boolean throwCommandError) {
-+ // Paper end
- CommandSourceStack commandlistenerwrapper = (CommandSourceStack) parseresults.getContext().getSource();
-
- commandlistenerwrapper.getServer().getProfiler().push(() -> {
-@@ -339,10 +357,11 @@ public class Commands {
- });
- }
- } catch (Exception exception) {
-+ if (throwCommandError) throw exception;
- MutableComponent ichatmutablecomponent = Component.literal(exception.getMessage() == null ? exception.getClass().getName() : exception.getMessage());
-
-+ Commands.LOGGER.error("Command exception: /{}", s, exception); // Paper - always show execution exception in console log
- if (commandlistenerwrapper.getServer().isDebugging() || Commands.LOGGER.isDebugEnabled()) { // Paper - Debugging
-- Commands.LOGGER.error("Command exception: /{}", s, exception);
- StackTraceElement[] astacktraceelement = exception.getStackTrace();
-
- for (int i = 0; i < Math.min(astacktraceelement.length, 3); ++i) {
-@@ -477,7 +496,7 @@ public class Commands {
- Map<CommandNode<CommandSourceStack>, CommandNode<SharedSuggestionProvider>> map = Maps.newIdentityHashMap(); // Use identity to prevent aliasing issues
- RootCommandNode vanillaRoot = new RootCommandNode();
-
-- RootCommandNode<CommandSourceStack> vanilla = player.server.vanillaCommandDispatcher.getDispatcher().getRoot();
-+ RootCommandNode<CommandSourceStack> vanilla = player.server.getCommands().getDispatcher().getRoot(); // Paper
- map.put(vanilla, vanillaRoot);
- this.fillUsableCommands(vanilla, vanillaRoot, player.createCommandSourceStack(), (Map) map);
-
-@@ -515,6 +534,7 @@ public class Commands {
- }
-
- private void fillUsableCommands(CommandNode<CommandSourceStack> tree, CommandNode<SharedSuggestionProvider> result, CommandSourceStack source, Map<CommandNode<CommandSourceStack>, CommandNode<SharedSuggestionProvider>> resultNodes) {
-+ resultNodes.keySet().removeIf((node) -> !org.spigotmc.SpigotConfig.sendNamespaced && node.getName().contains( ":" )); // Paper - Remove namedspaced from result nodes to prevent redirect trimming ~ see comment below
- Iterator iterator = tree.getChildren().iterator();
-
- boolean registeredAskServerSuggestionsForTree = false; // Paper - tell clients to ask server for suggestions for EntityArguments
-@@ -529,6 +549,42 @@ public class Commands {
-
- if (commandnode2.canUse(source)) {
- ArgumentBuilder argumentbuilder = commandnode2.createBuilder(); // CraftBukkit - decompile error
-+ // Paper start
-+ /*
-+ Because of how commands can be yeeted right left and center due to bad bukkit practices
-+ we need to be able to ensure that ALL commands are registered (even redirects).
-+
-+ What this will do is IF the redirect seems to be "dead" it will create a builder and essentially populate (flatten)
-+ all the children from the dead redirect to the node.
-+
-+ So, if minecraft:msg redirects to msg but the original msg node has been overriden minecraft:msg will now act as msg and will explicilty inherit its children.
-+
-+ The only way to fix this is to either:
-+ - Send EVERYTHING flattened, don't use redirects
-+ - Don't allow command nodes to be deleted
-+ - Do this :)
-+ */
-+
-+ // Is there an invalid command redirect?
-+ if (argumentbuilder.getRedirect() != null && (CommandNode) resultNodes.get(argumentbuilder.getRedirect()) == null) {
-+ // Create the argument builder with the same values as the specified node, but with a different literal and populated children
-+
-+ CommandNode<CommandSourceStack> redirect = argumentbuilder.getRedirect();
-+ // Diff copied from LiteralCommand#createBuilder
-+ final com.mojang.brigadier.builder.LiteralArgumentBuilder<CommandSourceStack> builder = com.mojang.brigadier.builder.LiteralArgumentBuilder.literal(commandnode2.getName());
-+ builder.requires(redirect.getRequirement());
-+ // builder.forward(redirect.getRedirect(), redirect.getRedirectModifier(), redirect.isFork()); We don't want to migrate the forward, since it's invalid.
-+ if (redirect.getCommand() != null) {
-+ builder.executes(redirect.getCommand());
-+ }
-+ // Diff copied from LiteralCommand#createBuilder
-+ for (CommandNode<CommandSourceStack> child : redirect.getChildren()) {
-+ builder.then(child);
-+ }
-+
-+ argumentbuilder = builder;
-+ }
-+ // Paper end
-
- argumentbuilder.requires((icompletionprovider) -> {
- return true;
-diff --git a/src/main/java/net/minecraft/commands/arguments/MessageArgument.java b/src/main/java/net/minecraft/commands/arguments/MessageArgument.java
-index 982b2bab27e3d55d0ba07060862c0c3183ad91b0..5fa8a3343ffc11e82c20b78a73205fd8a42d3c5d 100644
---- a/src/main/java/net/minecraft/commands/arguments/MessageArgument.java
-+++ b/src/main/java/net/minecraft/commands/arguments/MessageArgument.java
-@@ -39,6 +39,11 @@ public class MessageArgument implements SignedArgument<MessageArgument.Message>
-
- public static void resolveChatMessage(CommandContext<CommandSourceStack> context, String name, Consumer<PlayerChatMessage> callback) throws CommandSyntaxException {
- MessageArgument.Message message = context.getArgument(name, MessageArgument.Message.class);
-+ // Paper start
-+ resolveChatMessage(message, context, name, callback);
-+ }
-+ public static void resolveChatMessage(MessageArgument.Message message, CommandContext<CommandSourceStack> context, String name, Consumer<PlayerChatMessage> callback) throws CommandSyntaxException {
-+ // Paper end
- CommandSourceStack commandSourceStack = context.getSource();
- Component component = message.resolveComponent(commandSourceStack);
- CommandSigningContext commandSigningContext = commandSourceStack.getSigningContext();
-diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
-index 5df535aae94bbba940da5d21eb72afc945915f4c..3751c2a077bd13bac330b93c6efc2a640a17f4f2 100644
---- a/src/main/java/net/minecraft/server/MinecraftServer.java
-+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
-@@ -299,7 +299,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
- public static int currentTick; // Paper - improve tick loop
- public java.util.Queue<Runnable> processQueue = new java.util.concurrent.ConcurrentLinkedQueue<Runnable>();
- public int autosavePeriod;
-- public Commands vanillaCommandDispatcher;
-+ // Paper - don't store the vanilla dispatcher
- public boolean forceTicks; // Paper
- // CraftBukkit end
- // Spigot start
-@@ -390,7 +390,6 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
- // CraftBukkit start
- this.options = options;
- this.worldLoader = worldLoader;
-- this.vanillaCommandDispatcher = worldstem.dataPackResources().commands; // CraftBukkit
- // Paper start - Handled by TerminalConsoleAppender
- // Try to see if we're actually running in a terminal, disable jline if not
- /*
-@@ -675,6 +674,9 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
-
- this.server.enablePlugins(org.bukkit.plugin.PluginLoadOrder.POSTWORLD);
- if (io.papermc.paper.plugin.PluginInitializerManager.instance().pluginRemapper != null) io.papermc.paper.plugin.PluginInitializerManager.instance().pluginRemapper.pluginsEnabled(); // Paper - Remap plugins
-+ io.papermc.paper.command.brigadier.PaperCommands.INSTANCE.setValid(); // Paper - reset invalid state for event fire below
-+ io.papermc.paper.plugin.lifecycle.event.LifecycleEventRunner.INSTANCE.callReloadableRegistrarEvent(io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents.COMMANDS, io.papermc.paper.command.brigadier.PaperCommands.INSTANCE, org.bukkit.plugin.Plugin.class, io.papermc.paper.plugin.lifecycle.event.registrar.ReloadableRegistrarEvent.Cause.INITIAL); // Paper - call commands event for regular plugins
-+ ((org.bukkit.craftbukkit.help.SimpleHelpMap) this.server.getHelpMap()).initializeCommands();
- this.server.getPluginManager().callEvent(new ServerLoadEvent(ServerLoadEvent.LoadType.STARTUP));
- this.connection.acceptConnections();
- }
-@@ -2296,9 +2298,9 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
- return new MinecraftServer.ReloadableResources(resourcemanager, datapackresources);
- });
- }).thenAcceptAsync((minecraftserver_reloadableresources) -> {
-+ io.papermc.paper.command.brigadier.PaperBrigadier.moveBukkitCommands(this.resources.managers().getCommands(), minecraftserver_reloadableresources.managers().commands); // Paper
- this.resources.close();
- this.resources = minecraftserver_reloadableresources;
-- this.server.syncCommands(); // SPIGOT-5884: Lost on reload
- this.packRepository.setSelected(dataPacks);
- WorldDataConfiguration worlddataconfiguration = new WorldDataConfiguration(MinecraftServer.getSelectedPacks(this.packRepository, true), this.worldData.enabledFeatures());
-
-@@ -2317,8 +2319,17 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
- this.getPlayerList().reloadResources();
- this.functionManager.replaceLibrary(this.resources.managers.getFunctionLibrary());
- this.structureTemplateManager.onResourceManagerReload(this.resources.resourceManager);
-- org.bukkit.craftbukkit.block.data.CraftBlockData.reloadCache(); // Paper - cache block data strings; they can be defined by datapacks so refresh it here
-- new io.papermc.paper.event.server.ServerResourcesReloadedEvent(cause).callEvent(); // Paper - Add ServerResourcesReloadedEvent; fire after everything has been reloaded
-+ org.bukkit.craftbukkit.block.data.CraftBlockData.reloadCache(); // Paper - cache block data strings, they can be defined by datapacks so refresh it here
-+ // Paper start - brigadier command API
-+ io.papermc.paper.command.brigadier.PaperCommands.INSTANCE.setValid(); // reset invalid state for event fire below
-+ io.papermc.paper.plugin.lifecycle.event.LifecycleEventRunner.INSTANCE.callReloadableRegistrarEvent(io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents.COMMANDS, io.papermc.paper.command.brigadier.PaperCommands.INSTANCE, org.bukkit.plugin.Plugin.class, io.papermc.paper.plugin.lifecycle.event.registrar.ReloadableRegistrarEvent.Cause.RELOAD); // call commands event for regular plugins
-+ final org.bukkit.craftbukkit.help.SimpleHelpMap helpMap = (org.bukkit.craftbukkit.help.SimpleHelpMap) this.server.getHelpMap();
-+ helpMap.clear();
-+ helpMap.initializeGeneralTopics();
-+ helpMap.initializeCommands();
-+ this.server.syncCommands(); // Refresh commands after event
-+ // Paper end
-+ new io.papermc.paper.event.server.ServerResourcesReloadedEvent(cause).callEvent(); // Paper - fire after everything has been reloaded
- }, this);
-
- if (this.isSameThread()) {
-diff --git a/src/main/java/net/minecraft/server/ReloadableServerResources.java b/src/main/java/net/minecraft/server/ReloadableServerResources.java
-index 84b4bfe8363adc015821e9cabedfabed98c0336c..6de563b7adea957a7ead1c00c4900060fa5df277 100644
---- a/src/main/java/net/minecraft/server/ReloadableServerResources.java
-+++ b/src/main/java/net/minecraft/server/ReloadableServerResources.java
-@@ -48,6 +48,7 @@ public class ReloadableServerResources {
- this.recipes = new RecipeManager(this.registryLookup);
- this.tagManager = new TagManager(dynamicRegistryManager);
- this.commands = new Commands(environment, CommandBuildContext.simple(this.registryLookup, enabledFeatures));
-+ io.papermc.paper.command.brigadier.PaperCommands.INSTANCE.setDispatcher(this.commands, CommandBuildContext.simple(this.registryLookup, enabledFeatures)); // Paper - Brigadier Command API
- this.advancements = new ServerAdvancementManager(this.registryLookup);
- this.functionLibrary = new ServerFunctionLibrary(functionPermissionLevel, this.commands.getDispatcher());
- }
-@@ -91,6 +92,14 @@ public class ReloadableServerResources {
- ReloadableServerResources reloadableServerResources = new ReloadableServerResources(
- reloadedDynamicRegistries.compositeAccess(), enabledFeatures, environment, functionPermissionLevel
- );
-+ // Paper start - call commands event for bootstraps
-+ //noinspection ConstantValue
-+ io.papermc.paper.plugin.lifecycle.event.LifecycleEventRunner.INSTANCE.callReloadableRegistrarEvent(
-+ io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents.COMMANDS,
-+ io.papermc.paper.command.brigadier.PaperCommands.INSTANCE,
-+ io.papermc.paper.plugin.bootstrap.BootstrapContext.class,
-+ MinecraftServer.getServer() == null ? io.papermc.paper.plugin.lifecycle.event.registrar.ReloadableRegistrarEvent.Cause.INITIAL : io.papermc.paper.plugin.lifecycle.event.registrar.ReloadableRegistrarEvent.Cause.RELOAD);
-+ // Paper end - call commands event
- return SimpleReloadInstance.create(
- manager, reloadableServerResources.listeners(), prepareExecutor, applyExecutor, DATA_RELOAD_INITIAL_TASK, LOGGER.isDebugEnabled()
- )
-diff --git a/src/main/java/net/minecraft/server/ServerFunctionManager.java b/src/main/java/net/minecraft/server/ServerFunctionManager.java
-index a0ec6c3d122ad28d65d37f1b9f82541997b37d37..c6d7ee0d498bd92d4321acd30ade10abea611e42 100644
---- a/src/main/java/net/minecraft/server/ServerFunctionManager.java
-+++ b/src/main/java/net/minecraft/server/ServerFunctionManager.java
-@@ -36,7 +36,7 @@ public class ServerFunctionManager {
- }
-
- public CommandDispatcher<CommandSourceStack> getDispatcher() {
-- return this.server.vanillaCommandDispatcher.getDispatcher(); // CraftBukkit
-+ return this.server.getCommands().getDispatcher(); // CraftBukkit // Paper - Don't override command dispatcher
- }
-
- public void tick() {
-diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
-index 5678414a608623a7698f72173fef87bde44f6aaa..ad967cc2283f902c76e48a59d59cf0dbdacbb5e3 100644
---- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
-+++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
-@@ -233,7 +233,6 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
- io.papermc.paper.command.PaperCommands.registerCommands(this); // Paper - setup /paper command
- com.destroystokyo.paper.Metrics.PaperMetrics.startMetrics(); // Paper - start metrics
- com.destroystokyo.paper.VersionHistoryManager.INSTANCE.getClass(); // Paper - load version history now
-- io.papermc.paper.brigadier.PaperBrigadierProviderImpl.INSTANCE.getClass(); // Paper - init PaperBrigadierProvider
-
- this.setPvpAllowed(dedicatedserverproperties.pvp);
- this.setFlightAllowed(dedicatedserverproperties.allowFlight);
-diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
-index 3faf80fca51d66480265eaf3cc89149e53ceb215..b9b3277c8ed94e0cd30b20b9c00a33eaad48e5ac 100644
---- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
-+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
-@@ -2464,33 +2464,20 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
- }
- }
-
-+ @Deprecated // Paper
- public void handleCommand(String s) { // Paper - private -> public
-- org.spigotmc.AsyncCatcher.catchOp("Command Dispatched Async: " + s); // Paper - Add async catcher
-- co.aikar.timings.MinecraftTimings.playerCommandTimer.startTiming(); // Paper
-- if ( org.spigotmc.SpigotConfig.logCommands ) // Spigot
-- this.LOGGER.info(this.player.getScoreboardName() + " issued server command: " + s);
--
-- CraftPlayer player = this.getCraftPlayer();
--
-- PlayerCommandPreprocessEvent event = new PlayerCommandPreprocessEvent(player, s, new LazyPlayerSet(this.server));
-- this.cserver.getPluginManager().callEvent(event);
--
-- if (event.isCancelled()) {
-- co.aikar.timings.MinecraftTimings.playerCommandTimer.stopTiming(); // Paper
-- return;
-- }
--
-- try {
-- if (this.cserver.dispatchCommand(event.getPlayer(), event.getMessage().substring(1))) {
-- return;
-- }
-- } catch (org.bukkit.command.CommandException ex) {
-- player.sendMessage(org.bukkit.ChatColor.RED + "An internal error occurred while attempting to perform this command");
-- java.util.logging.Logger.getLogger(ServerGamePacketListenerImpl.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
-- return;
-- } finally {
-- co.aikar.timings.MinecraftTimings.playerCommandTimer.stopTiming(); // Paper
-+ // Paper start - Remove all this old duplicated logic
-+ if (s.startsWith("/")) {
-+ s = s.substring(1);
- }
-+ /*
-+ It should be noted that this represents the "legacy" command execution path.
-+ Api can call commands even if there is no additional context provided.
-+ This method should ONLY be used if you need to execute a command WITHOUT
-+ an actual player's input.
-+ */
-+ this.performUnsignedChatCommand(s);
-+ // Paper end
- }
- // CraftBukkit end
-
-diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-index 3eea023d24c8e1b991f548632564508bfc565d8a..5db08432b6afd3639688830e717f40ceaf599248 100644
---- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-@@ -271,11 +271,11 @@ public final class CraftServer implements Server {
- private final Logger logger = Logger.getLogger("Minecraft");
- private final ServicesManager servicesManager = new SimpleServicesManager();
- private final CraftScheduler scheduler = new CraftScheduler();
-- private final CraftCommandMap commandMap = new CraftCommandMap(this);
-+ private final CraftCommandMap commandMap; // Paper - Move down
- private final SimpleHelpMap helpMap = new SimpleHelpMap(this);
- private final StandardMessenger messenger = new StandardMessenger();
-- private final SimplePluginManager pluginManager = new SimplePluginManager(this, commandMap);
-- public final io.papermc.paper.plugin.manager.PaperPluginManagerImpl paperPluginManager = new io.papermc.paper.plugin.manager.PaperPluginManagerImpl(this, this.commandMap, pluginManager); {this.pluginManager.paperPluginManager = this.paperPluginManager;} // Paper
-+ private final SimplePluginManager pluginManager; // Paper - Move down
-+ public final io.papermc.paper.plugin.manager.PaperPluginManagerImpl paperPluginManager; // Paper
- private final StructureManager structureManager;
- protected final DedicatedServer console;
- protected final DedicatedPlayerList playerList;
-@@ -401,6 +401,12 @@ public final class CraftServer implements Server {
- this.serverTickManager = new CraftServerTickManager(console.tickRateManager());
-
- Bukkit.setServer(this);
-+ // Paper start
-+ this.commandMap = new CraftCommandMap(this);
-+ this.pluginManager = new SimplePluginManager(this, commandMap);
-+ this.paperPluginManager = new io.papermc.paper.plugin.manager.PaperPluginManagerImpl(this, this.commandMap, pluginManager);
-+ this.pluginManager.paperPluginManager = this.paperPluginManager;
-+ // Paper end
-
- CraftRegistry.setMinecraftRegistry(console.registryAccess());
-
-@@ -590,48 +596,11 @@ public final class CraftServer implements Server {
- }
-
- private void setVanillaCommands(boolean first) { // Spigot
-- Commands dispatcher = this.console.vanillaCommandDispatcher;
--
-- // Build a list of all Vanilla commands and create wrappers
-- for (CommandNode<CommandSourceStack> cmd : dispatcher.getDispatcher().getRoot().getChildren()) {
-- // Spigot start
-- VanillaCommandWrapper wrapper = new VanillaCommandWrapper(dispatcher, cmd);
-- if (org.spigotmc.SpigotConfig.replaceCommands.contains( wrapper.getName() ) ) {
-- if (first) {
-- this.commandMap.register("minecraft", wrapper);
-- }
-- } else if (!first) {
-- this.commandMap.register("minecraft", wrapper);
-- }
-- // Spigot end
-- }
-+ // Paper - Replace implementation
- }
-
- public void syncCommands() {
-- // Clear existing commands
-- Commands dispatcher = this.console.resources.managers().commands = new Commands();
--
-- // Register all commands, vanilla ones will be using the old dispatcher references
-- for (Map.Entry<String, Command> entry : this.commandMap.getKnownCommands().entrySet()) {
-- String label = entry.getKey();
-- Command command = entry.getValue();
--
-- if (command instanceof VanillaCommandWrapper) {
-- LiteralCommandNode<CommandSourceStack> node = (LiteralCommandNode<CommandSourceStack>) ((VanillaCommandWrapper) command).vanillaCommand;
-- if (!node.getLiteral().equals(label)) {
-- LiteralCommandNode<CommandSourceStack> clone = new LiteralCommandNode(label, node.getCommand(), node.getRequirement(), node.getRedirect(), node.getRedirectModifier(), node.isFork());
--
-- for (CommandNode<CommandSourceStack> child : node.getChildren()) {
-- clone.addChild(child);
-- }
-- node = clone;
-- }
--
-- dispatcher.getDispatcher().getRoot().addChild(node);
-- } else {
-- new BukkitCommandWrapper(this, entry.getValue()).register(dispatcher.getDispatcher(), label);
-- }
-- }
-+ Commands dispatcher = this.getHandle().getServer().getCommands(); // Paper - We now register directly to the dispatcher.
-
- // Refresh commands
- for (ServerPlayer player : this.getHandle().players) {
-@@ -1018,17 +987,31 @@ public final class CraftServer implements Server {
- return true;
- }
-
-- // Spigot start
-- if (!org.spigotmc.SpigotConfig.unknownCommandMessage.isEmpty()) {
-- // Paper start
-- org.bukkit.event.command.UnknownCommandEvent event = new org.bukkit.event.command.UnknownCommandEvent(sender, commandLine, net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(org.spigotmc.SpigotConfig.unknownCommandMessage));
-- this.getPluginManager().callEvent(event);
-- if (event.message() != null) {
-- sender.sendMessage(event.message());
-- }
-- // Paper end
-+ return this.dispatchCommand(VanillaCommandWrapper.getListener(sender), commandLine);
-+ }
-+
-+ public boolean dispatchCommand(CommandSourceStack sourceStack, String commandLine) {
-+ net.minecraft.commands.Commands commands = this.getHandle().getServer().getCommands();
-+ com.mojang.brigadier.CommandDispatcher<CommandSourceStack> dispatcher = commands.getDispatcher();
-+ com.mojang.brigadier.ParseResults<CommandSourceStack> results = dispatcher.parse(commandLine, sourceStack);
-+
-+ CommandSender sender = sourceStack.getBukkitSender();
-+ String[] args = org.apache.commons.lang3.StringUtils.split(commandLine, ' '); // Paper - fix adjacent spaces (from console/plugins) causing empty array elements
-+ Command target = this.commandMap.getCommand(args[0].toLowerCase(java.util.Locale.ENGLISH));
-+
-+ try {
-+ commands.performCommand(results, commandLine, commandLine, true);
-+ } catch (CommandException ex) {
-+ this.pluginManager.callEvent(new com.destroystokyo.paper.event.server.ServerExceptionEvent(new com.destroystokyo.paper.exception.ServerCommandException(ex, target, sender, args))); // Paper
-+ //target.timings.stopTiming(); // Spigot // Paper
-+ throw ex;
-+ } catch (Throwable ex) {
-+ //target.timings.stopTiming(); // Spigot // Paper
-+ String msg = "Unhandled exception executing '" + commandLine + "' in " + target;
-+ this.pluginManager.callEvent(new com.destroystokyo.paper.event.server.ServerExceptionEvent(new com.destroystokyo.paper.exception.ServerCommandException(ex, target, sender, args))); // Paper
-+ throw new CommandException(msg, ex);
- }
-- // Spigot end
-+ // Paper end
-
- return false;
- }
-@@ -1037,7 +1020,7 @@ public final class CraftServer implements Server {
- public void reload() {
- // Paper start - lifecycle events
- if (io.papermc.paper.plugin.lifecycle.event.LifecycleEventRunner.INSTANCE.blocksPluginReloading()) {
-- throw new IllegalStateException("A lifecycle event handler has been registered which makes reloading plugins not possible");
-+ throw new IllegalStateException(org.bukkit.command.defaults.ReloadCommand.RELOADING_DISABLED_MESSAGE);
- }
- // Paper end - lifecycle events
- org.spigotmc.WatchdogThread.hasStarted = false; // Paper - Disable watchdog early timeout on reload
-@@ -1091,8 +1074,9 @@ public final class CraftServer implements Server {
- }
-
- Plugin[] pluginClone = pluginManager.getPlugins().clone(); // Paper
-+ this.commandMap.clearCommands(); // Paper - Move command reloading up
- this.pluginManager.clearPlugins();
-- this.commandMap.clearCommands();
-+ // Paper - move up
- // Paper start
- for (Plugin plugin : pluginClone) {
- entityMetadata.removeAll(plugin);
-@@ -1132,6 +1116,12 @@ public final class CraftServer implements Server {
- this.enablePlugins(PluginLoadOrder.STARTUP);
- this.enablePlugins(PluginLoadOrder.POSTWORLD);
- if (io.papermc.paper.plugin.PluginInitializerManager.instance().pluginRemapper != null) io.papermc.paper.plugin.PluginInitializerManager.instance().pluginRemapper.pluginsEnabled(); // Paper - Remap plugins
-+ // Paper start - brigadier command API
-+ io.papermc.paper.command.brigadier.PaperCommands.INSTANCE.setValid(); // to clear invalid state for event fire below
-+ io.papermc.paper.plugin.lifecycle.event.LifecycleEventRunner.INSTANCE.callReloadableRegistrarEvent(io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents.COMMANDS, io.papermc.paper.command.brigadier.PaperCommands.INSTANCE, org.bukkit.plugin.Plugin.class, io.papermc.paper.plugin.lifecycle.event.registrar.ReloadableRegistrarEvent.Cause.RELOAD); // call commands event for regular plugins
-+ this.helpMap.initializeCommands();
-+ this.syncCommands(); // Refresh commands after event
-+ // Paper end - brigadier command API
- this.getPluginManager().callEvent(new ServerLoadEvent(ServerLoadEvent.LoadType.RELOAD));
- org.spigotmc.WatchdogThread.hasStarted = true; // Paper - Disable watchdog early timeout on reload
- }
-diff --git a/src/main/java/org/bukkit/craftbukkit/command/BukkitCommandWrapper.java b/src/main/java/org/bukkit/craftbukkit/command/BukkitCommandWrapper.java
-index 21b6f90cf5bd7087d1a0f512289d971f2c3e1afa..a3c02200da9e793de79a74fe7e0cd72634150f64 100644
---- a/src/main/java/org/bukkit/craftbukkit/command/BukkitCommandWrapper.java
-+++ b/src/main/java/org/bukkit/craftbukkit/command/BukkitCommandWrapper.java
-@@ -20,6 +20,7 @@ import org.bukkit.command.CommandException;
- import org.bukkit.command.CommandSender;
- import org.bukkit.craftbukkit.CraftServer;
-
-+@Deprecated(forRemoval = true) // Paper - Don't use
- public class BukkitCommandWrapper implements com.mojang.brigadier.Command<CommandSourceStack>, Predicate<CommandSourceStack>, SuggestionProvider<CommandSourceStack>, com.destroystokyo.paper.brigadier.BukkitBrigadierCommand<CommandSourceStack> { // Paper
-
- private final CraftServer server;
-diff --git a/src/main/java/org/bukkit/craftbukkit/command/CraftCommandMap.java b/src/main/java/org/bukkit/craftbukkit/command/CraftCommandMap.java
-index 4b1ac1fe7ea07f419ae2818251900e7ba434ee16..90ed57a7fbcd0625b64084347460e9864216f610 100644
---- a/src/main/java/org/bukkit/craftbukkit/command/CraftCommandMap.java
-+++ b/src/main/java/org/bukkit/craftbukkit/command/CraftCommandMap.java
-@@ -8,7 +8,7 @@ import org.bukkit.command.SimpleCommandMap;
- public class CraftCommandMap extends SimpleCommandMap {
-
- public CraftCommandMap(Server server) {
-- super(server);
-+ super(server, io.papermc.paper.command.brigadier.bukkit.BukkitBrigForwardingMap.INSTANCE);
- }
-
- public Map<String, Command> getKnownCommands() {
-diff --git a/src/main/java/org/bukkit/craftbukkit/command/VanillaCommandWrapper.java b/src/main/java/org/bukkit/craftbukkit/command/VanillaCommandWrapper.java
-index dd1507f65a7f1d84bc7f236f81a60ac1302a13b8..5b70f53bc4b27a715b8b7aa13586082adbc4bd16 100644
---- a/src/main/java/org/bukkit/craftbukkit/command/VanillaCommandWrapper.java
-+++ b/src/main/java/org/bukkit/craftbukkit/command/VanillaCommandWrapper.java
-@@ -23,14 +23,26 @@ import org.bukkit.craftbukkit.entity.CraftMinecartCommand;
- import org.bukkit.entity.Entity;
- import org.bukkit.entity.minecart.CommandMinecart;
-
--public final class VanillaCommandWrapper extends BukkitCommand {
-+public class VanillaCommandWrapper extends BukkitCommand { // Paper
-
-- private final Commands dispatcher;
-+ //private final Commands dispatcher; // Paper
- public final CommandNode<CommandSourceStack> vanillaCommand;
-
-+ // Paper start
-+ public VanillaCommandWrapper(String name, String description, String usageMessage, List<String> aliases, CommandNode<CommandSourceStack> vanillaCommand) {
-+ super(name, description, usageMessage, aliases);
-+ //this.dispatcher = dispatcher; // Paper
-+ this.vanillaCommand = vanillaCommand;
-+ }
-+
-+ Commands commands() {
-+ return net.minecraft.server.MinecraftServer.getServer().getCommands();
-+ }
-+
-+ // Paper end
- public VanillaCommandWrapper(Commands dispatcher, CommandNode<CommandSourceStack> vanillaCommand) {
- super(vanillaCommand.getName(), "A Mojang provided command.", vanillaCommand.getUsageText(), Collections.EMPTY_LIST);
-- this.dispatcher = dispatcher;
-+ // this.dispatcher = dispatcher; // Paper
- this.vanillaCommand = vanillaCommand;
- this.setPermission(VanillaCommandWrapper.getPermission(vanillaCommand));
- }
-@@ -40,7 +52,7 @@ public final class VanillaCommandWrapper extends BukkitCommand {
- if (!this.testPermission(sender)) return true;
-
- CommandSourceStack icommandlistener = VanillaCommandWrapper.getListener(sender);
-- this.dispatcher.performPrefixedCommand(icommandlistener, this.toDispatcher(args, this.getName()), this.toDispatcher(args, commandLabel));
-+ this.commands().performPrefixedCommand(icommandlistener, this.toDispatcher(args, this.getName()), this.toDispatcher(args, commandLabel)); // Paper
- return true;
- }
-
-@@ -51,10 +63,10 @@ public final class VanillaCommandWrapper extends BukkitCommand {
- Preconditions.checkArgument(alias != null, "Alias cannot be null");
-
- CommandSourceStack icommandlistener = VanillaCommandWrapper.getListener(sender);
-- ParseResults<CommandSourceStack> parsed = this.dispatcher.getDispatcher().parse(this.toDispatcher(args, this.getName()), icommandlistener);
-+ ParseResults<CommandSourceStack> parsed = this.commands().getDispatcher().parse(this.toDispatcher(args, this.getName()), icommandlistener); // Paper
-
- List<String> results = new ArrayList<>();
-- this.dispatcher.getDispatcher().getCompletionSuggestions(parsed).thenAccept((suggestions) -> {
-+ this.commands().getDispatcher().getCompletionSuggestions(parsed).thenAccept((suggestions) -> { // Paper
- suggestions.getList().forEach((s) -> results.add(s.getText()));
- });
-
-@@ -114,4 +126,15 @@ public final class VanillaCommandWrapper extends BukkitCommand {
- private String toDispatcher(String[] args, String name) {
- return name + ((args.length > 0) ? " " + Joiner.on(' ').join(args) : "");
- }
-+ // Paper start
-+ @Override
-+ public boolean canBeOverriden() {
-+ return true;
-+ }
-+
-+ @Override
-+ public boolean isRegistered() {
-+ return true;
-+ }
-+ // Paper end
- }
-diff --git a/src/main/java/org/bukkit/craftbukkit/help/SimpleHelpMap.java b/src/main/java/org/bukkit/craftbukkit/help/SimpleHelpMap.java
-index 05d3aecd4abaab6a94effcb1ab35c1b82410865f..97141968f36b3ef88bd6e520c2ccc37c97e4adb1 100644
---- a/src/main/java/org/bukkit/craftbukkit/help/SimpleHelpMap.java
-+++ b/src/main/java/org/bukkit/craftbukkit/help/SimpleHelpMap.java
-@@ -200,15 +200,18 @@ public class SimpleHelpMap implements HelpMap {
- }
-
- private String getCommandPluginName(Command command) {
-+ // Paper start - Move up
-+ if (command instanceof PluginIdentifiableCommand) {
-+ return ((PluginIdentifiableCommand) command).getPlugin().getName();
-+ }
-+ // Paper end
- if (command instanceof VanillaCommandWrapper) {
- return "Minecraft";
- }
- if (command instanceof BukkitCommand) {
- return "Bukkit";
- }
-- if (command instanceof PluginIdentifiableCommand) {
-- return ((PluginIdentifiableCommand) command).getPlugin().getName();
-- }
-+ // Paper - Move PluginIdentifiableCommand instanceof check to allow brig commands
- return null;
- }
-
-diff --git a/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftCriteria.java b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftCriteria.java
-index 8464531a4ee400834d25c23b1eb723f49be8689e..4a0b1587180381123eb843819cd10630e49c7a02 100644
---- a/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftCriteria.java
-+++ b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftCriteria.java
-@@ -53,7 +53,13 @@ public final class CraftCriteria implements Criteria {
- return RenderType.values()[this.criteria.getDefaultRenderType().ordinal()];
- }
-
-- static CraftCriteria getFromNMS(Objective objective) {
-+ // Paper start
-+ public static CraftCriteria getFromNMS(ObjectiveCriteria criteria) {
-+ return java.util.Objects.requireNonNullElseGet(CraftCriteria.DEFAULTS.get(criteria.getName()), () -> new CraftCriteria(criteria));
-+ }
-+ // Paper end
-+
-+ public static CraftCriteria getFromNMS(Objective objective) {
- return java.util.Objects.requireNonNullElseGet(CraftCriteria.DEFAULTS.get(objective.getCriteria().getName()), () -> new CraftCriteria(objective.getCriteria())); // Paper
- }
-
-diff --git a/src/main/resources/META-INF/services/io.papermc.paper.command.brigadier.CommandBuilderProvider b/src/main/resources/META-INF/services/io.papermc.paper.command.brigadier.CommandBuilderProvider
-new file mode 100644
-index 0000000000000000000000000000000000000000..2f0b1f0ed9ca3605cd24a75466973e1a0a745ee5
---- /dev/null
-+++ b/src/main/resources/META-INF/services/io.papermc.paper.command.brigadier.CommandBuilderProvider
-@@ -0,0 +1 @@
-+io.papermc.paper.command.brigadier.CommandBuilderImpl$ProviderImpl
-diff --git a/src/main/resources/META-INF/services/io.papermc.paper.command.brigadier.MessageComponentSerializer b/src/main/resources/META-INF/services/io.papermc.paper.command.brigadier.MessageComponentSerializer
-new file mode 100644
-index 0000000000000000000000000000000000000000..2428b577b9bf0eac6947f5d919cbb51f7aca3d50
---- /dev/null
-+++ b/src/main/resources/META-INF/services/io.papermc.paper.command.brigadier.MessageComponentSerializer
-@@ -0,0 +1 @@
-+io.papermc.paper.command.brigadier.MessageComponentSerializerImpl
-diff --git a/src/main/resources/META-INF/services/io.papermc.paper.command.brigadier.argument.VanillaArgumentProvider b/src/main/resources/META-INF/services/io.papermc.paper.command.brigadier.argument.VanillaArgumentProvider
-new file mode 100644
-index 0000000000000000000000000000000000000000..b2fdb8351c2abb55283850a929d2a87aa6ecb80f
---- /dev/null
-+++ b/src/main/resources/META-INF/services/io.papermc.paper.command.brigadier.argument.VanillaArgumentProvider
-@@ -0,0 +1 @@
-+io.papermc.paper.command.brigadier.argument.VanillaArgumentProviderImpl
-diff --git a/src/test/java/io/papermc/paper/command/brigadier/BukkitCommandConversionTest.java b/src/test/java/io/papermc/paper/command/brigadier/BukkitCommandConversionTest.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..6475510ea1084a003fb2c8645cb4538b3f48e1c7
---- /dev/null
-+++ b/src/test/java/io/papermc/paper/command/brigadier/BukkitCommandConversionTest.java
-@@ -0,0 +1,113 @@
-+package io.papermc.paper.command.brigadier;
-+
-+import com.mojang.brigadier.CommandDispatcher;
-+import com.mojang.brigadier.ResultConsumer;
-+import com.mojang.brigadier.context.CommandContext;
-+import com.mojang.brigadier.exceptions.CommandSyntaxException;
-+import com.mojang.brigadier.suggestion.Suggestions;
-+import com.mojang.brigadier.tree.CommandNode;
-+import io.papermc.paper.command.brigadier.bukkit.BukkitBrigForwardingMap;
-+import net.minecraft.server.MinecraftServer;
-+import net.minecraft.world.flag.FeatureFlags;
-+import org.apache.logging.log4j.core.util.Assert;
-+import org.bukkit.Bukkit;
-+import org.bukkit.Location;
-+import org.bukkit.World;
-+import org.bukkit.command.Command;
-+import org.bukkit.command.CommandMap;
-+import org.bukkit.command.CommandSender;
-+import org.bukkit.command.SimpleCommandMap;
-+import org.bukkit.craftbukkit.command.CraftCommandMap;
-+import org.bukkit.craftbukkit.command.VanillaCommandWrapper;
-+import org.bukkit.entity.Entity;
-+import org.bukkit.plugin.PluginManager;
-+import org.bukkit.support.AbstractTestingBase;
-+import org.jetbrains.annotations.NotNull;
-+import org.jetbrains.annotations.Nullable;
-+import org.junit.jupiter.api.Assertions;
-+import org.junit.jupiter.api.Test;
-+import org.mockito.Mockito;
-+
-+import java.util.List;
-+import java.util.Map;
-+import java.util.logging.Logger;
-+
-+public class BukkitCommandConversionTest extends AbstractTestingBase {
-+
-+ private CommandSender getSender() {
-+ return Mockito.mock(CommandSender.class);
-+ }
-+
-+ @Test
-+ public void test() throws CommandSyntaxException {
-+ CommandSender sender = this.getSender();
-+ CommandSourceStack object = Mockito.mock(CommandSourceStack.class);
-+ Mockito.when(object.getLocation()).thenReturn(new Location(null, 0, 0, 0));;
-+
-+ CommandDispatcher dispatcher = DATA_PACK.commands.getDispatcher();
-+ dispatcher.setConsumer((context, success, result) -> {});
-+ CommandMap commandMap = new SimpleCommandMap(Bukkit.getServer(), new BukkitBrigForwardingMap());
-+ Map<String, Command> stringCommandMap = commandMap.getKnownCommands();
-+ // All commands should be mirrored -- or equal
-+ int commandMapSize = stringCommandMap.values().size();
-+ ExampleCommand exampleCommand = new ExampleCommand();
-+
-+ Assertions.assertEquals(commandMapSize, dispatcher.getRoot().getChildren().size());
-+
-+ // Register a new command
-+ commandMap.register("test", exampleCommand);
-+ Assertions.assertEquals(commandMapSize + (3 * 2), stringCommandMap.values().size()); // Make sure commands are accounted for, including those with namespaced keys
-+
-+ // Test Registration
-+ for (String alias : exampleCommand.getAliases()) {
-+ Assertions.assertEquals(stringCommandMap.get(alias), exampleCommand);
-+ Assertions.assertEquals(stringCommandMap.get("test:" + alias), exampleCommand);
-+ }
-+ // Test command instance equality
-+ Assertions.assertEquals(stringCommandMap.get(exampleCommand.getName()), exampleCommand);
-+ Assertions.assertEquals(stringCommandMap.get("test:" + exampleCommand.getName()), exampleCommand);
-+
-+ // Test command map execution
-+ commandMap.dispatch(sender, "main-example example");
-+ Assertions.assertEquals(exampleCommand.invocations, 1);
-+ Assertions.assertEquals(commandMap.tabComplete(sender, "main-example 1 2"), List.of("complete"));
-+
-+ // Test dispatcher execution
-+ dispatcher.execute("main-example example", object);
-+ Assertions.assertEquals(exampleCommand.invocations, 2);
-+
-+ dispatcher.execute("test:example2 example", object);
-+ Assertions.assertEquals(exampleCommand.invocations, 3);
-+
-+ Suggestions suggestions = (Suggestions) dispatcher.getCompletionSuggestions(dispatcher.parse("main-example 1 2", object)).join();
-+ Assertions.assertEquals(suggestions.getList().get(0).getText(), "complete");
-+
-+
-+ // Test command map removal
-+ commandMap.getKnownCommands().remove("test");
-+ Assertions.assertNull(commandMap.getCommand("test"));
-+ Assertions.assertNull(dispatcher.getRoot().getChild("test"));
-+ }
-+
-+ private static class ExampleCommand extends Command {
-+
-+ int invocations;
-+
-+ protected ExampleCommand() {
-+ super("main-example", "This is an example.", "", List.of("example", "example2"));
-+ }
-+
-+ @Override
-+ public boolean execute(@NotNull CommandSender sender, @NotNull String commandLabel, @NotNull String[] args) {
-+ Assertions.assertEquals(args[0], "example");
-+ this.invocations++;
-+ return true;
-+ }
-+
-+ @Override
-+ public @NotNull List<String> tabComplete(@NotNull CommandSender sender, @NotNull String alias, @NotNull String[] args) throws IllegalArgumentException {
-+ Assertions.assertEquals(args.length, 2);
-+ return List.of("complete");
-+ }
-+ }
-+}
-diff --git a/src/test/java/org/bukkit/support/DummyServer.java b/src/test/java/org/bukkit/support/DummyServer.java
-index f8e363e9c10e30598185a91cbb5cafc00b79ad3d..d7e24766f383f75ed46123fff1bd0ec926a635b4 100644
---- a/src/test/java/org/bukkit/support/DummyServer.java
-+++ b/src/test/java/org/bukkit/support/DummyServer.java
-@@ -51,7 +51,7 @@ public final class DummyServer {
- final Thread currentThread = Thread.currentThread();
- when(instance.isPrimaryThread()).thenAnswer(ignored -> Thread.currentThread().equals(currentThread));
-
-- final org.bukkit.plugin.PluginManager pluginManager = new io.papermc.paper.plugin.manager.PaperPluginManagerImpl(instance, new org.bukkit.command.SimpleCommandMap(instance), null);
-+ final org.bukkit.plugin.PluginManager pluginManager = new io.papermc.paper.plugin.manager.PaperPluginManagerImpl(instance, new org.bukkit.command.SimpleCommandMap(instance, new java.util.HashMap<>()), null); // Paper
- when(instance.getPluginManager()).thenReturn(pluginManager);
- when(instance.getTag(anyString(), any(org.bukkit.NamespacedKey.class), any())).thenAnswer(ignored -> new io.papermc.paper.util.EmptyTag());
- // paper end - testing additions
diff --git a/patches/unapplied/server/1044-Fix-issues-with-Recipe-API.patch b/patches/unapplied/server/1044-Fix-issues-with-Recipe-API.patch
deleted file mode 100644
index a5312e5dc7..0000000000
--- a/patches/unapplied/server/1044-Fix-issues-with-Recipe-API.patch
+++ /dev/null
@@ -1,116 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Jake Potrebic <[email protected]>
-Date: Sun, 12 May 2024 15:49:36 -0700
-Subject: [PATCH] Fix issues with Recipe API
-
-
-diff --git a/src/main/java/net/minecraft/world/item/crafting/ShapedRecipe.java b/src/main/java/net/minecraft/world/item/crafting/ShapedRecipe.java
-index 482d7b12b80328fba97a01bcfeb974b7ac4bcdb7..fb4c8a2a15e8e3e26454b7da920454e9861336c6 100644
---- a/src/main/java/net/minecraft/world/item/crafting/ShapedRecipe.java
-+++ b/src/main/java/net/minecraft/world/item/crafting/ShapedRecipe.java
-@@ -91,7 +91,7 @@ public class ShapedRecipe extends io.papermc.paper.inventory.recipe.RecipeBookEx
- char c = 'a';
- for (Ingredient list : this.pattern.ingredients()) {
- RecipeChoice choice = CraftRecipe.toBukkit(list);
-- if (choice != null) {
-+ if (choice != RecipeChoice.empty()) { // Paper
- recipe.setIngredient(c, choice);
- }
-
-diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftRecipe.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftRecipe.java
-index 6ba29875d78ede4aa7978ff689e588f7fed11528..c76c78bb7757d407102271463e14716a1b012deb 100644
---- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftRecipe.java
-+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftRecipe.java
-@@ -29,6 +29,10 @@ public interface CraftRecipe extends Recipe {
- } else if (bukkit instanceof RecipeChoice.ExactChoice) {
- stack = new Ingredient(((RecipeChoice.ExactChoice) bukkit).getChoices().stream().map((mat) -> new net.minecraft.world.item.crafting.Ingredient.ItemValue(CraftItemStack.asNMSCopy(mat))));
- stack.exact = true;
-+ // Paper start - support "empty" choices
-+ } else if (bukkit == RecipeChoice.empty()) {
-+ stack = Ingredient.EMPTY;
-+ // Paper end
- } else {
- throw new IllegalArgumentException("Unknown recipe stack instance " + bukkit);
- }
-@@ -45,7 +49,7 @@ public interface CraftRecipe extends Recipe {
- list.getItems();
-
- if (list.itemStacks.length == 0) {
-- return null;
-+ return RecipeChoice.empty(); // Paper - null breaks API contracts
- }
-
- if (list.exact) {
-diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftSmithingTransformRecipe.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftSmithingTransformRecipe.java
-index 38690b28b6f67624d68877c1e89ebe30b402b233..3aec771478a6b17353d57e82baac53dd24779e7b 100644
---- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftSmithingTransformRecipe.java
-+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftSmithingTransformRecipe.java
-@@ -30,6 +30,6 @@ public class CraftSmithingTransformRecipe extends SmithingTransformRecipe implem
- public void addToCraftingManager() {
- ItemStack result = this.getResult();
-
-- MinecraftServer.getServer().getRecipeManager().addRecipe(new RecipeHolder<>(CraftNamespacedKey.toMinecraft(this.getKey()), new net.minecraft.world.item.crafting.SmithingTransformRecipe(this.toNMS(this.getTemplate(), true), this.toNMS(this.getBase(), true), this.toNMS(this.getAddition(), true), CraftItemStack.asNMSCopy(result), this.willCopyDataComponents()))); // Paper - Option to prevent data components copy
-+ MinecraftServer.getServer().getRecipeManager().addRecipe(new RecipeHolder<>(CraftNamespacedKey.toMinecraft(this.getKey()), new net.minecraft.world.item.crafting.SmithingTransformRecipe(this.toNMS(this.getTemplate(), false), this.toNMS(this.getBase(), false), this.toNMS(this.getAddition(), false), CraftItemStack.asNMSCopy(result), this.willCopyDataComponents()))); // Paper - Option to prevent data components copy & support empty RecipeChoice
- }
- }
-diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftSmithingTrimRecipe.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftSmithingTrimRecipe.java
-index 5d7782b168138383c606a2c52fbdebe1732364ac..61af2fe3534ff67f10310c6c7dec39cff0f93ee3 100644
---- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftSmithingTrimRecipe.java
-+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftSmithingTrimRecipe.java
-@@ -28,6 +28,6 @@ public class CraftSmithingTrimRecipe extends SmithingTrimRecipe implements Craft
-
- @Override
- public void addToCraftingManager() {
-- MinecraftServer.getServer().getRecipeManager().addRecipe(new RecipeHolder<>(CraftNamespacedKey.toMinecraft(this.getKey()), new net.minecraft.world.item.crafting.SmithingTrimRecipe(this.toNMS(this.getTemplate(), true), this.toNMS(this.getBase(), true), this.toNMS(this.getAddition(), true), this.willCopyDataComponents()))); // Paper - Option to prevent data components copy
-+ MinecraftServer.getServer().getRecipeManager().addRecipe(new RecipeHolder<>(CraftNamespacedKey.toMinecraft(this.getKey()), new net.minecraft.world.item.crafting.SmithingTrimRecipe(this.toNMS(this.getTemplate(), false), this.toNMS(this.getBase(), false), this.toNMS(this.getAddition(), false), this.willCopyDataComponents()))); // Paper - Option to prevent data components copy & support empty RecipeChoice
- }
- }
-diff --git a/src/test/java/io/papermc/paper/inventory/recipe/TestRecipeChoice.java b/src/test/java/io/papermc/paper/inventory/recipe/TestRecipeChoice.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..b6816485a2360b936c049b398183658ee18813ec
---- /dev/null
-+++ b/src/test/java/io/papermc/paper/inventory/recipe/TestRecipeChoice.java
-@@ -0,0 +1,24 @@
-+package io.papermc.paper.inventory.recipe;
-+
-+import java.util.Iterator;
-+import org.bukkit.Bukkit;
-+import org.bukkit.inventory.Recipe;
-+import org.bukkit.support.AbstractTestingBase;
-+import org.junit.jupiter.api.Test;
-+
-+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
-+import static org.junit.jupiter.api.Assertions.assertTrue;
-+
-+class TestRecipeChoice extends AbstractTestingBase {
-+
-+ @Test
-+ void testRecipeChoices() {
-+ final Iterator<Recipe> iter = Bukkit.recipeIterator();
-+ boolean foundRecipes = false;
-+ while (iter.hasNext()) {
-+ foundRecipes = true;
-+ assertDoesNotThrow(iter::next, "Failed to convert a recipe to Bukkit recipe!");
-+ }
-+ assertTrue(foundRecipes, "No recipes found!");
-+ }
-+}
-diff --git a/src/test/java/org/bukkit/support/DummyServer.java b/src/test/java/org/bukkit/support/DummyServer.java
-index d7e24766f383f75ed46123fff1bd0ec926a635b4..a73c16bb7923957113e688fa6fe46cbd68837d3e 100644
---- a/src/test/java/org/bukkit/support/DummyServer.java
-+++ b/src/test/java/org/bukkit/support/DummyServer.java
-@@ -56,6 +56,14 @@ public final class DummyServer {
- when(instance.getTag(anyString(), any(org.bukkit.NamespacedKey.class), any())).thenAnswer(ignored -> new io.papermc.paper.util.EmptyTag());
- // paper end - testing additions
-
-+ // Paper start - add test for recipe conversion
-+ when(instance.recipeIterator()).thenAnswer(ignored -> {
-+ return com.google.common.collect.Iterators.transform(
-+ AbstractTestingBase.DATA_PACK.getRecipeManager().byType.entries().iterator(),
-+ input -> input.getValue().toBukkitRecipe());
-+ });
-+ // Paper end - add test for recipe conversion
-+
- Bukkit.setServer(instance);
- } catch (Throwable t) {
- throw new Error(t);
diff --git a/patches/unapplied/server/1045-Fix-equipment-slot-and-group-API.patch b/patches/unapplied/server/1045-Fix-equipment-slot-and-group-API.patch
deleted file mode 100644
index 09ab2e3eed..0000000000
--- a/patches/unapplied/server/1045-Fix-equipment-slot-and-group-API.patch
+++ /dev/null
@@ -1,114 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Jake Potrebic <[email protected]>
-Date: Wed, 22 May 2024 10:01:19 -0700
-Subject: [PATCH] Fix equipment slot and group API
-
-Add test for EquipmentSlotGroup
-
-diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryPlayer.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryPlayer.java
-index 9d74577af071954e1e37201a96368c1360076209..eafa54c870c3e2aef30c3f9f96f516607a7cae24 100644
---- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryPlayer.java
-+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryPlayer.java
-@@ -135,6 +135,10 @@ public class CraftInventoryPlayer extends CraftInventory implements org.bukkit.i
- case HEAD:
- this.setHelmet(item);
- break;
-+ // Paper start
-+ case BODY:
-+ throw new IllegalArgumentException("BODY is not valid for players!");
-+ // Paper end
- default:
- throw new IllegalArgumentException("Not implemented. This is a bug");
- }
-@@ -162,6 +166,10 @@ public class CraftInventoryPlayer extends CraftInventory implements org.bukkit.i
- return java.util.Objects.requireNonNullElseGet(this.getChestplate(), () -> new ItemStack(org.bukkit.Material.AIR)); // Paper - make nonnull
- case HEAD:
- return java.util.Objects.requireNonNullElseGet(this.getHelmet(), () -> new ItemStack(org.bukkit.Material.AIR)); // Paper - make nonnull
-+ // Paper start
-+ case BODY:
-+ throw new IllegalArgumentException("BODY is not valid for players!");
-+ // Paper end
- default:
- throw new IllegalArgumentException("Not implemented. This is a bug");
- }
-diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java
-index 60aa9dd1617d302c3b49af21d581a32313c171cc..b6cd6897844aa9c8b9a94e41c56c4cfe4ac78780 100644
---- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java
-+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java
-@@ -1367,7 +1367,7 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
- if (this.attributeModifiers == null) return LinkedHashMultimap.create(); // Paper - don't change the components
- SetMultimap<Attribute, AttributeModifier> result = LinkedHashMultimap.create();
- for (Map.Entry<Attribute, AttributeModifier> entry : this.attributeModifiers.entries()) {
-- if (entry.getValue().getSlot() == null || entry.getValue().getSlot() == slot) {
-+ if (entry.getValue().getSlotGroup().test(slot)) { // Paper - correctly test slot against group
- result.put(entry.getKey(), entry.getValue());
- }
- }
-@@ -1435,9 +1435,7 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta {
-
- while (iter.hasNext()) {
- Map.Entry<Attribute, AttributeModifier> entry = iter.next();
-- // Explicitly match against null because (as of MC 1.13) AttributeModifiers without a -
-- // set slot are active in any slot.
-- if (entry.getValue().getSlot() == null || entry.getValue().getSlot() == slot) {
-+ if (entry.getValue().getSlotGroup().test(slot)) { // Paper - correctly test slot against group
- iter.remove();
- ++removed;
- }
-diff --git a/src/test/java/io/papermc/paper/inventory/item/EquipmentSlotGroupTest.java b/src/test/java/io/papermc/paper/inventory/item/EquipmentSlotGroupTest.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..ee0bfe4edb134d7ea3a3b97f5102a7f3122c3b99
---- /dev/null
-+++ b/src/test/java/io/papermc/paper/inventory/item/EquipmentSlotGroupTest.java
-@@ -0,0 +1,51 @@
-+package io.papermc.paper.inventory.item;
-+
-+import java.lang.reflect.Field;
-+import java.lang.reflect.Modifier;
-+import java.util.ArrayList;
-+import java.util.List;
-+import net.minecraft.world.entity.EquipmentSlot;
-+import org.bukkit.craftbukkit.CraftEquipmentSlot;
-+import org.bukkit.inventory.EquipmentSlotGroup;
-+import org.junit.jupiter.params.ParameterizedTest;
-+import org.junit.jupiter.params.provider.EnumSource;
-+import org.junit.jupiter.params.provider.MethodSource;
-+
-+import static org.junit.jupiter.api.Assertions.assertEquals;
-+import static org.junit.jupiter.api.Assertions.assertNotNull;
-+
-+class EquipmentSlotGroupTest {
-+
-+ static List<EquipmentSlotGroup> apiValues() throws ReflectiveOperationException {
-+ final List<EquipmentSlotGroup> apiValues = new ArrayList<>();
-+ for (final Field field : EquipmentSlotGroup.class.getDeclaredFields()) {
-+ if (!Modifier.isStatic(field.getModifiers()) || !Modifier.isFinal(field.getModifiers()) || !field.getType().equals(EquipmentSlotGroup.class)) {
-+ continue;
-+ }
-+ apiValues.add((EquipmentSlotGroup) field.get(null));
-+ }
-+ if (apiValues.isEmpty()) {
-+ throw new RuntimeException("Didn't find any api " + EquipmentSlotGroup.class.getSimpleName());
-+ }
-+ return apiValues;
-+ }
-+
-+ @ParameterizedTest
-+ @MethodSource("apiValues")
-+ void testBukkitToNms(final EquipmentSlotGroup slotGroup) {
-+ final net.minecraft.world.entity.EquipmentSlotGroup nmsGroup = CraftEquipmentSlot.getNMSGroup(slotGroup);
-+ assertNotNull(nmsGroup, "No nms slot group found for " + slotGroup);
-+ assertEquals(nmsGroup.getSerializedName(), slotGroup.toString(), "slot group name mismatch");
-+ for (final EquipmentSlot slot : EquipmentSlot.values()) {
-+ assertEquals(nmsGroup.test(slot), slotGroup.test(CraftEquipmentSlot.getSlot(slot)));
-+ }
-+ }
-+
-+ @ParameterizedTest
-+ @EnumSource(net.minecraft.world.entity.EquipmentSlotGroup.class)
-+ void testNmsToBukkit(final net.minecraft.world.entity.EquipmentSlotGroup slotGroup) {
-+ final EquipmentSlotGroup apiGroup = CraftEquipmentSlot.getSlot(slotGroup);
-+ assertNotNull(apiGroup, "No api slot group found for " + slotGroup);
-+ assertEquals(apiGroup.toString(), slotGroup.getSerializedName(), "slot group name mismatch");
-+ }
-+}
diff --git a/patches/unapplied/server/1046-Allow-Bukkit-plugin-to-use-Paper-PluginLoader-API.patch b/patches/unapplied/server/1046-Allow-Bukkit-plugin-to-use-Paper-PluginLoader-API.patch
deleted file mode 100644
index 980c3dc8a0..0000000000
--- a/patches/unapplied/server/1046-Allow-Bukkit-plugin-to-use-Paper-PluginLoader-API.patch
+++ /dev/null
@@ -1,130 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Jason Penilla <[email protected]>
-Date: Tue, 21 May 2024 13:18:15 -0700
-Subject: [PATCH] Allow Bukkit plugin to use Paper PluginLoader API
-
-
-diff --git a/src/main/java/io/papermc/paper/plugin/loader/PaperClasspathBuilder.java b/src/main/java/io/papermc/paper/plugin/loader/PaperClasspathBuilder.java
-index f9d4b33050a6fe8c2dabe8e5eec075d95dc513e0..dc106685ecb483c33c06e4f83eda27be58251aad 100644
---- a/src/main/java/io/papermc/paper/plugin/loader/PaperClasspathBuilder.java
-+++ b/src/main/java/io/papermc/paper/plugin/loader/PaperClasspathBuilder.java
-@@ -41,15 +41,7 @@ public class PaperClasspathBuilder implements PluginClasspathBuilder {
- }
-
- public PaperPluginClassLoader buildClassLoader(Logger logger, Path source, JarFile jarFile, PaperPluginMeta configuration) {
-- PaperLibraryStore paperLibraryStore = new PaperLibraryStore();
-- for (ClassPathLibrary library : this.libraries) {
-- library.register(paperLibraryStore);
-- }
--
-- List<Path> paths = paperLibraryStore.getPaths();
-- if (PluginInitializerManager.instance().pluginRemapper != null) {
-- paths = PluginInitializerManager.instance().pluginRemapper.remapLibraries(paths);
-- }
-+ List<Path> paths = this.buildLibraryPaths(true);
- URL[] urls = new URL[paths.size()];
- for (int i = 0; i < paths.size(); i++) {
- Path path = paths.get(i);
-@@ -69,4 +61,17 @@ public class PaperClasspathBuilder implements PluginClasspathBuilder {
- throw new RuntimeException(exception);
- }
- }
-+
-+ public List<Path> buildLibraryPaths(final boolean remap) {
-+ PaperLibraryStore paperLibraryStore = new PaperLibraryStore();
-+ for (ClassPathLibrary library : this.libraries) {
-+ library.register(paperLibraryStore);
-+ }
-+
-+ List<Path> paths = paperLibraryStore.getPaths();
-+ if (remap && PluginInitializerManager.instance().pluginRemapper != null) {
-+ paths = PluginInitializerManager.instance().pluginRemapper.remapLibraries(paths);
-+ }
-+ return paths;
-+ }
- }
-diff --git a/src/main/java/io/papermc/paper/plugin/provider/type/spigot/SpigotPluginProvider.java b/src/main/java/io/papermc/paper/plugin/provider/type/spigot/SpigotPluginProvider.java
-index 75a2b687d58d76b94f8bec111df8613f120ff74b..0fd1040ed376f19c6d5326767baaf3048ce1bfb4 100644
---- a/src/main/java/io/papermc/paper/plugin/provider/type/spigot/SpigotPluginProvider.java
-+++ b/src/main/java/io/papermc/paper/plugin/provider/type/spigot/SpigotPluginProvider.java
-@@ -40,15 +40,17 @@ public class SpigotPluginProvider implements PluginProvider<JavaPlugin>, Provide
- private final PluginDescriptionFile description;
- private final JarFile jarFile;
- private final Logger logger;
-+ private final List<Path> paperLibraryPaths;
- private final ComponentLogger componentLogger;
- private ProviderStatus status;
- private DependencyContext dependencyContext;
-
-- SpigotPluginProvider(Path path, JarFile file, PluginDescriptionFile description) {
-+ SpigotPluginProvider(Path path, JarFile file, PluginDescriptionFile description, List<Path> paperLibraryPaths) {
- this.path = path;
- this.jarFile = file;
- this.description = description;
- this.logger = PaperPluginLogger.getLogger(description);
-+ this.paperLibraryPaths = paperLibraryPaths;
- this.componentLogger = ComponentLogger.logger(this.logger.getName());
- }
-
-@@ -120,7 +122,7 @@ public class SpigotPluginProvider implements PluginProvider<JavaPlugin>, Provide
-
- final PluginClassLoader loader;
- try {
-- loader = new PluginClassLoader(this.getClass().getClassLoader(), this.description, dataFolder, this.path.toFile(), LIBRARY_LOADER.createLoader(this.description), this.jarFile, this.dependencyContext); // Paper
-+ loader = new PluginClassLoader(this.getClass().getClassLoader(), this.description, dataFolder, this.path.toFile(), LIBRARY_LOADER.createLoader(this.description, this.paperLibraryPaths), this.jarFile, this.dependencyContext); // Paper
- } catch (InvalidPluginException ex) {
- throw ex;
- } catch (Throwable ex) {
-diff --git a/src/main/java/io/papermc/paper/plugin/provider/type/spigot/SpigotPluginProviderFactory.java b/src/main/java/io/papermc/paper/plugin/provider/type/spigot/SpigotPluginProviderFactory.java
-index fdb52ad85cfaa1d53aadcad72cec3d3c8c12c058..38075b7348ad7ca3cfece2bfae63e0cce827c694 100644
---- a/src/main/java/io/papermc/paper/plugin/provider/type/spigot/SpigotPluginProviderFactory.java
-+++ b/src/main/java/io/papermc/paper/plugin/provider/type/spigot/SpigotPluginProviderFactory.java
-@@ -1,9 +1,18 @@
- package io.papermc.paper.plugin.provider.type.spigot;
-
-+import com.destroystokyo.paper.utils.PaperPluginLogger;
-+import io.papermc.paper.plugin.bootstrap.PluginProviderContextImpl;
- import io.papermc.paper.plugin.entrypoint.classloader.BytecodeModifyingURLClassLoader;
-+import io.papermc.paper.plugin.entrypoint.classloader.PaperSimplePluginClassLoader;
-+import io.papermc.paper.plugin.loader.PaperClasspathBuilder;
-+import io.papermc.paper.plugin.loader.PluginLoader;
- import io.papermc.paper.plugin.provider.configuration.serializer.constraints.PluginConfigConstraints;
- import io.papermc.paper.plugin.provider.type.PluginTypeFactory;
-+import io.papermc.paper.plugin.provider.util.ProviderUtil;
- import io.papermc.paper.util.MappingEnvironment;
-+import java.util.List;
-+import java.util.logging.Logger;
-+import net.kyori.adventure.text.logger.slf4j.ComponentLogger;
- import org.bukkit.plugin.InvalidDescriptionException;
- import org.bukkit.plugin.PluginDescriptionFile;
- import org.bukkit.plugin.java.LibraryLoader;
-@@ -36,7 +45,28 @@ class SpigotPluginProviderFactory implements PluginTypeFactory<SpigotPluginProvi
- throw new InvalidDescriptionException("Restricted name, cannot use 0x20 (space character) in a plugin name.");
- }
-
-- return new SpigotPluginProvider(source, file, configuration);
-+ final List<Path> paperLibraryPaths;
-+ if (configuration.getPaperPluginLoader() != null) {
-+ final Logger logger = PaperPluginLogger.getLogger(configuration);
-+ PaperClasspathBuilder builder = new PaperClasspathBuilder(PluginProviderContextImpl.create(
-+ configuration, ComponentLogger.logger(logger.getName()), source
-+ ));
-+
-+ try (
-+ PaperSimplePluginClassLoader simplePluginClassLoader = new PaperSimplePluginClassLoader(source, file, configuration, this.getClass().getClassLoader())
-+ ) {
-+ PluginLoader loader = ProviderUtil.loadClass(configuration.getPaperPluginLoader(), PluginLoader.class, simplePluginClassLoader);
-+ loader.classloader(builder);
-+ } catch (IOException e) {
-+ throw new RuntimeException(e);
-+ }
-+
-+ paperLibraryPaths = builder.buildLibraryPaths(false);
-+ } else {
-+ paperLibraryPaths = null;
-+ }
-+
-+ return new SpigotPluginProvider(source, file, configuration, paperLibraryPaths);
- }
-
- @Override
diff --git a/patches/unapplied/server/1047-Prevent-sending-oversized-item-data-in-equipment-and.patch b/patches/unapplied/server/1047-Prevent-sending-oversized-item-data-in-equipment-and.patch
deleted file mode 100644
index ed2e9d9dd8..0000000000
--- a/patches/unapplied/server/1047-Prevent-sending-oversized-item-data-in-equipment-and.patch
+++ /dev/null
@@ -1,228 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Nassim Jahnke <[email protected]>
-Date: Wed, 1 Dec 2021 12:36:25 +0100
-Subject: [PATCH] Prevent sending oversized item data in equipment and metadata
-
-Co-authored-by: Jake Potrebic <[email protected]>
-
-diff --git a/src/main/java/io/papermc/paper/util/DataSanitizationUtil.java b/src/main/java/io/papermc/paper/util/DataSanitizationUtil.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..e9436f8a73ee0a02096d66e14d73edaae28d5a41
---- /dev/null
-+++ b/src/main/java/io/papermc/paper/util/DataSanitizationUtil.java
-@@ -0,0 +1,100 @@
-+package io.papermc.paper.util;
-+
-+import java.util.ArrayList;
-+import java.util.List;
-+import java.util.concurrent.atomic.AtomicBoolean;
-+import java.util.function.UnaryOperator;
-+import net.minecraft.network.RegistryFriendlyByteBuf;
-+import net.minecraft.network.codec.StreamCodec;
-+import net.minecraft.world.item.ItemStack;
-+import net.minecraft.world.item.Items;
-+import net.minecraft.world.item.component.BundleContents;
-+import net.minecraft.world.item.component.ChargedProjectiles;
-+import net.minecraft.world.item.component.ItemContainerContents;
-+import org.checkerframework.checker.nullness.qual.NonNull;
-+import org.checkerframework.framework.qual.DefaultQualifier;
-+
-+@DefaultQualifier(NonNull.class)
-+public final class DataSanitizationUtil {
-+
-+ private static final ThreadLocal<DataSanitizer> DATA_SANITIZER = ThreadLocal.withInitial(DataSanitizer::new);
-+
-+ public static DataSanitizer start(final boolean sanitize) {
-+ final DataSanitizer sanitizer = DATA_SANITIZER.get();
-+ if (sanitize) {
-+ sanitizer.start();
-+ }
-+ return sanitizer;
-+ }
-+
-+ public static final StreamCodec<RegistryFriendlyByteBuf, ChargedProjectiles> CHARGED_PROJECTILES = codec(ChargedProjectiles.STREAM_CODEC, DataSanitizationUtil::sanitizeChargedProjectiles);
-+ public static final StreamCodec<RegistryFriendlyByteBuf, BundleContents> BUNDLE_CONTENTS = codec(BundleContents.STREAM_CODEC, DataSanitizationUtil::sanitizeBundleContents);
-+ public static final StreamCodec<RegistryFriendlyByteBuf, ItemContainerContents> CONTAINER = codec(ItemContainerContents.STREAM_CODEC, contents -> ItemContainerContents.EMPTY);
-+
-+ private static ChargedProjectiles sanitizeChargedProjectiles(final ChargedProjectiles projectiles) {
-+ if (projectiles.isEmpty()) {
-+ return projectiles;
-+ }
-+ final List<ItemStack> items = projectiles.getItems();
-+ final List<ItemStack> sanitized = new ArrayList<>();
-+ for (int i = 0; i < Math.min(items.size(), 3); i++) {
-+ // we want to preserve item type as vanilla client can change visuals based on type
-+ sanitized.add(new ItemStack(items.get(i).getItemHolder()));
-+ }
-+ return ChargedProjectiles.of(sanitized);
-+ }
-+
-+ private static BundleContents sanitizeBundleContents(final BundleContents contents) {
-+ // Bundles change their texture based on their fullness.
-+ int sizeUsed = 0;
-+ for (final ItemStack item : contents.items()) {
-+ final int scale = 64 / item.getMaxStackSize();
-+ sizeUsed += scale * item.getCount();
-+ }
-+ // Now we add a single fake item that uses the same amount of slots as all other items.
-+ final List<ItemStack> items = new ArrayList<>();
-+ items.add(new ItemStack(Items.PAPER, sizeUsed));
-+ return new BundleContents(items);
-+ }
-+
-+ private static <B, A> StreamCodec<B, A> codec(final StreamCodec<B, A> delegate, final UnaryOperator<A> sanitizer) {
-+ return new DataSanitizationCodec<>(delegate, sanitizer);
-+ }
-+
-+ private record DataSanitizationCodec<B, A>(StreamCodec<B, A> delegate, UnaryOperator<A> sanitizer) implements StreamCodec<B, A> {
-+
-+ @Override
-+ public @NonNull A decode(final @NonNull B buf) {
-+ return this.delegate.decode(buf);
-+ }
-+
-+ @Override
-+ public void encode(final @NonNull B buf, final @NonNull A value) {
-+ if (!DATA_SANITIZER.get().value().get()) {
-+ this.delegate.encode(buf, value);
-+ } else {
-+ this.delegate.encode(buf, this.sanitizer.apply(value));
-+ }
-+ }
-+ }
-+
-+ public record DataSanitizer(AtomicBoolean value) implements AutoCloseable {
-+
-+ public DataSanitizer() {
-+ this(new AtomicBoolean(false));
-+ }
-+
-+ public void start() {
-+ this.value.compareAndSet(false, true);
-+ }
-+
-+ @Override
-+ public void close() {
-+ this.value.compareAndSet(true, false);
-+ }
-+ }
-+
-+ private DataSanitizationUtil() {
-+ }
-+
-+}
-diff --git a/src/main/java/net/minecraft/core/component/DataComponents.java b/src/main/java/net/minecraft/core/component/DataComponents.java
-index 5632974af9c603d333ffc30a5a1b1e851821a3bb..9b2a209cda955ef3e5d8ff3ed1b2249888c7d139 100644
---- a/src/main/java/net/minecraft/core/component/DataComponents.java
-+++ b/src/main/java/net/minecraft/core/component/DataComponents.java
-@@ -139,10 +139,10 @@ public class DataComponents {
- "map_post_processing", builder -> builder.networkSynchronized(MapPostProcessing.STREAM_CODEC)
- );
- public static final DataComponentType<ChargedProjectiles> CHARGED_PROJECTILES = register(
-- "charged_projectiles", builder -> builder.persistent(ChargedProjectiles.CODEC).networkSynchronized(ChargedProjectiles.STREAM_CODEC).cacheEncoding()
-+ "charged_projectiles", builder -> builder.persistent(ChargedProjectiles.CODEC).networkSynchronized(io.papermc.paper.util.DataSanitizationUtil.CHARGED_PROJECTILES).cacheEncoding() // Paper - sanitize charged projectiles
- );
- public static final DataComponentType<BundleContents> BUNDLE_CONTENTS = register(
-- "bundle_contents", builder -> builder.persistent(BundleContents.CODEC).networkSynchronized(BundleContents.STREAM_CODEC).cacheEncoding()
-+ "bundle_contents", builder -> builder.persistent(BundleContents.CODEC).networkSynchronized(io.papermc.paper.util.DataSanitizationUtil.BUNDLE_CONTENTS).cacheEncoding() // Paper - sanitize bundle contents
- );
- public static final DataComponentType<PotionContents> POTION_CONTENTS = register(
- "potion_contents", builder -> builder.persistent(PotionContents.CODEC).networkSynchronized(PotionContents.STREAM_CODEC).cacheEncoding()
-@@ -206,7 +206,7 @@ public class DataComponents {
- "pot_decorations", builder -> builder.persistent(PotDecorations.CODEC).networkSynchronized(PotDecorations.STREAM_CODEC).cacheEncoding()
- );
- public static final DataComponentType<ItemContainerContents> CONTAINER = register(
-- "container", builder -> builder.persistent(ItemContainerContents.CODEC).networkSynchronized(ItemContainerContents.STREAM_CODEC).cacheEncoding()
-+ "container", builder -> builder.persistent(ItemContainerContents.CODEC).networkSynchronized(io.papermc.paper.util.DataSanitizationUtil.CONTAINER).cacheEncoding() // Paper - sanitize container contents
- );
- public static final DataComponentType<BlockItemStateProperties> BLOCK_STATE = register(
- "block_state", builder -> builder.persistent(BlockItemStateProperties.CODEC).networkSynchronized(BlockItemStateProperties.STREAM_CODEC).cacheEncoding()
-diff --git a/src/main/java/net/minecraft/network/protocol/game/ClientboundSetEntityDataPacket.java b/src/main/java/net/minecraft/network/protocol/game/ClientboundSetEntityDataPacket.java
-index 59c1c103545f04fd35e6932df64a9910a1d74cd7..56bde49e6b7790155b032d0be40961d566ab89e9 100644
---- a/src/main/java/net/minecraft/network/protocol/game/ClientboundSetEntityDataPacket.java
-+++ b/src/main/java/net/minecraft/network/protocol/game/ClientboundSetEntityDataPacket.java
-@@ -19,9 +19,11 @@ public record ClientboundSetEntityDataPacket(int id, List<SynchedEntityData.Data
- }
-
- private static void pack(List<SynchedEntityData.DataValue<?>> trackedValues, RegistryFriendlyByteBuf buf) {
-+ try (var ignored = io.papermc.paper.util.DataSanitizationUtil.start(true)) { // Paper - data sanitization
- for (SynchedEntityData.DataValue<?> dataValue : trackedValues) {
- dataValue.write(buf);
- }
-+ } // Paper - data sanitization
-
- buf.writeByte(255);
- }
-diff --git a/src/main/java/net/minecraft/network/protocol/game/ClientboundSetEquipmentPacket.java b/src/main/java/net/minecraft/network/protocol/game/ClientboundSetEquipmentPacket.java
-index e092a486c4041ab1cfe9e29c88d0d94528a6e9a6..3945ca04ede578121b370592482ac917f2d4cf96 100644
---- a/src/main/java/net/minecraft/network/protocol/game/ClientboundSetEquipmentPacket.java
-+++ b/src/main/java/net/minecraft/network/protocol/game/ClientboundSetEquipmentPacket.java
-@@ -19,6 +19,13 @@ public class ClientboundSetEquipmentPacket implements Packet<ClientGamePacketLis
- private final List<Pair<EquipmentSlot, ItemStack>> slots;
-
- public ClientboundSetEquipmentPacket(int id, List<Pair<EquipmentSlot, ItemStack>> equipmentList) {
-+ // Paper start - data sanitization
-+ this(id, equipmentList, false);
-+ }
-+ private boolean sanitize = false;
-+ public ClientboundSetEquipmentPacket(int id, List<Pair<EquipmentSlot, ItemStack>> equipmentList, boolean sanitize) {
-+ this.sanitize = sanitize;
-+ // Paper end - data sanitization
- this.entity = id;
- this.slots = equipmentList;
- }
-@@ -41,6 +48,7 @@ public class ClientboundSetEquipmentPacket implements Packet<ClientGamePacketLis
- buf.writeVarInt(this.entity);
- int i = this.slots.size();
-
-+ try (var ignored = io.papermc.paper.util.DataSanitizationUtil.start(this.sanitize)) { // Paper - data sanitization
- for (int j = 0; j < i; j++) {
- Pair<EquipmentSlot, ItemStack> pair = this.slots.get(j);
- EquipmentSlot equipmentSlot = pair.getFirst();
-@@ -49,6 +57,7 @@ public class ClientboundSetEquipmentPacket implements Packet<ClientGamePacketLis
- buf.writeByte(bl ? k | -128 : k);
- ItemStack.OPTIONAL_STREAM_CODEC.encode(buf, pair.getSecond());
- }
-+ } // Paper - data sanitization
- }
-
- @Override
-diff --git a/src/main/java/net/minecraft/server/level/ServerEntity.java b/src/main/java/net/minecraft/server/level/ServerEntity.java
-index f16a69775332a08ed0e87d27acd0fc959359694c..a2279262c93408c11f5d2290b48fd794975e8cfe 100644
---- a/src/main/java/net/minecraft/server/level/ServerEntity.java
-+++ b/src/main/java/net/minecraft/server/level/ServerEntity.java
-@@ -361,7 +361,7 @@ public class ServerEntity {
- }
-
- if (!list.isEmpty()) {
-- sender.accept(new ClientboundSetEquipmentPacket(this.entity.getId(), list));
-+ sender.accept(new ClientboundSetEquipmentPacket(this.entity.getId(), list, true)); // Paper - data sanitization
- }
- ((LivingEntity) this.entity).detectEquipmentUpdatesPublic(); // CraftBukkit - SPIGOT-3789: sync again immediately after sending
- }
-diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
-index b9b3277c8ed94e0cd30b20b9c00a33eaad48e5ac..c450447585af4c8cdc87abe871c229ff895c3e53 100644
---- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
-+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
-@@ -2788,7 +2788,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
- entity.refreshEntityData(ServerGamePacketListenerImpl.this.player);
- // SPIGOT-7136 - Allays
- if (entity instanceof Allay) {
-- ServerGamePacketListenerImpl.this.send(new ClientboundSetEquipmentPacket(entity.getId(), Arrays.stream(net.minecraft.world.entity.EquipmentSlot.values()).map((slot) -> Pair.of(slot, ((LivingEntity) entity).getItemBySlot(slot).copy())).collect(Collectors.toList())));
-+ ServerGamePacketListenerImpl.this.send(new ClientboundSetEquipmentPacket(entity.getId(), Arrays.stream(net.minecraft.world.entity.EquipmentSlot.values()).map((slot) -> Pair.of(slot, ((LivingEntity) entity).getItemBySlot(slot).copy())).collect(Collectors.toList()), true)); // Paper - sanitize
- ServerGamePacketListenerImpl.this.player.containerMenu.sendAllDataToRemote();
- }
- }
-diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java
-index d0b6ade676d94e768c92432dc6cee9f200acf5f2..21e61bb75ac7ce468bc757633ce678b21bcb9deb 100644
---- a/src/main/java/net/minecraft/world/entity/LivingEntity.java
-+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java
-@@ -3302,7 +3302,7 @@ public abstract class LivingEntity extends Entity implements Attackable {
- }
-
- });
-- ((ServerLevel) this.level()).getChunkSource().broadcast(this, new ClientboundSetEquipmentPacket(this.getId(), list));
-+ ((ServerLevel) this.level()).getChunkSource().broadcast(this, new ClientboundSetEquipmentPacket(this.getId(), list, true)); // Paper - data sanitization
- }
-
- private ItemStack getLastArmorItem(EquipmentSlot slot) {
diff --git a/patches/unapplied/server/1048-Prevent-NPE-if-hooked-entity-was-cleared.patch b/patches/unapplied/server/1048-Prevent-NPE-if-hooked-entity-was-cleared.patch
deleted file mode 100644
index 72ff32a552..0000000000
--- a/patches/unapplied/server/1048-Prevent-NPE-if-hooked-entity-was-cleared.patch
+++ /dev/null
@@ -1,24 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Jake Potrebic <[email protected]>
-Date: Sun, 12 May 2024 21:57:23 -0700
-Subject: [PATCH] Prevent NPE if hooked entity was cleared
-
-
-diff --git a/src/main/java/net/minecraft/world/entity/projectile/FishingHook.java b/src/main/java/net/minecraft/world/entity/projectile/FishingHook.java
-index 9962d50ea342cd47428a814519b2d54f547753a4..6ea34a76707d9f60076b7423ac0bb1de200308ae 100644
---- a/src/main/java/net/minecraft/world/entity/projectile/FishingHook.java
-+++ b/src/main/java/net/minecraft/world/entity/projectile/FishingHook.java
-@@ -503,11 +503,13 @@ public class FishingHook extends Projectile {
- if (playerFishEvent.isCancelled()) {
- return 0;
- }
-+ if (this.hookedIn != null) { // Paper - re-check to see if there is a hooked entity
- // CraftBukkit end
- this.pullEntity(this.hookedIn);
- CriteriaTriggers.FISHING_ROD_HOOKED.trigger((ServerPlayer) entityhuman, usedItem, this, Collections.emptyList());
- this.level().broadcastEntityEvent(this, (byte) 31);
- i = this.hookedIn instanceof ItemEntity ? 3 : 5;
-+ } // Paper - re-check to see if there is a hooked entity
- } else if (this.nibble > 0) {
- LootParams lootparams = (new LootParams.Builder((ServerLevel) this.level())).withParameter(LootContextParams.ORIGIN, this.position()).withParameter(LootContextParams.TOOL, usedItem).withParameter(LootContextParams.THIS_ENTITY, this).withLuck((float) this.luck + entityhuman.getLuck()).create(LootContextParamSets.FISHING);
- LootTable loottable = this.level().getServer().reloadableRegistries().getLootTable(BuiltInLootTables.FISHING);
diff --git a/patches/unapplied/server/1049-Fix-cancelling-BlockPlaceEvent-calling-onRemove.patch b/patches/unapplied/server/1049-Fix-cancelling-BlockPlaceEvent-calling-onRemove.patch
deleted file mode 100644
index c740827e5f..0000000000
--- a/patches/unapplied/server/1049-Fix-cancelling-BlockPlaceEvent-calling-onRemove.patch
+++ /dev/null
@@ -1,47 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Tamion <[email protected]>
-Date: Thu, 23 May 2024 11:02:20 +0200
-Subject: [PATCH] Fix cancelling BlockPlaceEvent calling onRemove
-
-
-diff --git a/src/main/java/net/minecraft/world/item/ItemStack.java b/src/main/java/net/minecraft/world/item/ItemStack.java
-index 7c7b9b1e0b604b0164b431873e6753b60421f970..f8589837070039b4911a9532b92fa959c7af6352 100644
---- a/src/main/java/net/minecraft/world/item/ItemStack.java
-+++ b/src/main/java/net/minecraft/world/item/ItemStack.java
-@@ -473,9 +473,11 @@ public final class ItemStack implements DataComponentHolder {
- world.capturedTileEntities.clear(); // Paper - Allow chests to be placed with NBT data; clear out block entities as chests and such will pop loot
- // revert back all captured blocks
- world.preventPoiUpdated = true; // CraftBukkit - SPIGOT-5710
-+ world.isBlockPlaceCancelled = true; // Paper - prevent calling cleanup logic when undoing a block place upon a cancelled BlockPlaceEvent
- for (BlockState blockstate : blocks) {
- blockstate.update(true, false);
- }
-+ world.isBlockPlaceCancelled = false; // Paper - prevent calling cleanup logic when undoing a block place upon a cancelled BlockPlaceEvent
- world.preventPoiUpdated = false;
-
- // Brute force all possible updates
-diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
-index 0a8eeebb2d702ebcefd9f26cc0f41d1eab497902..14281a4e72f49dc4eb2ca3da8479c1f81a3a175d 100644
---- a/src/main/java/net/minecraft/world/level/Level.java
-+++ b/src/main/java/net/minecraft/world/level/Level.java
-@@ -150,6 +150,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
- public boolean preventPoiUpdated = false; // CraftBukkit - SPIGOT-5710
- public boolean captureBlockStates = false;
- public boolean captureTreeGeneration = false;
-+ public boolean isBlockPlaceCancelled = false; // Paper - prevent calling cleanup logic when undoing a block place upon a cancelled BlockPlaceEvent
- public Map<BlockPos, org.bukkit.craftbukkit.block.CraftBlockState> capturedBlockStates = new java.util.LinkedHashMap<>(); // Paper
- public Map<BlockPos, BlockEntity> capturedTileEntities = new java.util.LinkedHashMap<>(); // Paper - Retain block place order when capturing blockstates
- public List<ItemEntity> captureDrops;
-diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
-index 2a8609e33716949ff1877b6d10f64a9d7a7c81e9..14ee7b5b9b804bebd4e2a846b238547a28a36035 100644
---- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
-+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
-@@ -452,7 +452,7 @@ public class LevelChunk extends ChunkAccess {
-
- boolean flag3 = iblockdata1.hasBlockEntity();
-
-- if (!this.level.isClientSide) {
-+ if (!this.level.isClientSide && !this.level.isBlockPlaceCancelled) { // Paper - prevent calling cleanup logic when undoing a block place upon a cancelled BlockPlaceEvent
- iblockdata1.onRemove(this.level, blockposition, iblockdata, flag);
- } else if (!iblockdata1.is(block) && flag3) {
- this.removeBlockEntity(blockposition);
diff --git a/patches/unapplied/server/1050-Add-missing-fishing-event-state.patch b/patches/unapplied/server/1050-Add-missing-fishing-event-state.patch
deleted file mode 100644
index 76fcc3bdcf..0000000000
--- a/patches/unapplied/server/1050-Add-missing-fishing-event-state.patch
+++ /dev/null
@@ -1,26 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: SoSeDiK <[email protected]>
-Date: Wed, 1 May 2024 07:44:50 +0300
-Subject: [PATCH] Add missing fishing event state
-
-
-diff --git a/src/main/java/net/minecraft/world/entity/projectile/FishingHook.java b/src/main/java/net/minecraft/world/entity/projectile/FishingHook.java
-index 6ea34a76707d9f60076b7423ac0bb1de200308ae..7dd5e0b935d98d552c916f8412569ff4aa0e9b04 100644
---- a/src/main/java/net/minecraft/world/entity/projectile/FishingHook.java
-+++ b/src/main/java/net/minecraft/world/entity/projectile/FishingHook.java
-@@ -410,6 +410,15 @@ public class FishingHook extends Projectile {
- this.fishAngle = Mth.nextFloat(this.random, this.minLureAngle, this.maxLureAngle);
- this.timeUntilHooked = Mth.nextInt(this.random, this.minLureTime, this.maxLureTime);
- // CraftBukkit end
-+ // Paper start - Add missing fishing event state
-+ if (this.getPlayerOwner() != null) {
-+ PlayerFishEvent playerFishEvent = new PlayerFishEvent((Player) this.getPlayerOwner().getBukkitEntity(), null, (FishHook) this.getBukkitEntity(), PlayerFishEvent.State.LURED);
-+ if (!playerFishEvent.callEvent()) {
-+ this.timeUntilHooked = 0;
-+ return;
-+ }
-+ }
-+ // Paper end - Add missing fishing event state
- }
- } else {
- // CraftBukkit start - logic to modify fishing wait time
diff --git a/patches/unapplied/server/1051-Deprecate-InvAction-HOTBAR_MOVE_AND_READD.patch b/patches/unapplied/server/1051-Deprecate-InvAction-HOTBAR_MOVE_AND_READD.patch
deleted file mode 100644
index 6be68ca368..0000000000
--- a/patches/unapplied/server/1051-Deprecate-InvAction-HOTBAR_MOVE_AND_READD.patch
+++ /dev/null
@@ -1,26 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Jake Potrebic <[email protected]>
-Date: Sat, 25 May 2024 09:51:13 -0700
-Subject: [PATCH] Deprecate InvAction#HOTBAR_MOVE_AND_READD
-
-
-diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
-index c450447585af4c8cdc87abe871c229ff895c3e53..4ae88bfcead40cd05f9514a48a922a37767cb3cf 100644
---- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
-+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
-@@ -3054,14 +3054,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
- Slot clickedSlot = this.player.containerMenu.getSlot(packet.getSlotNum());
- if (clickedSlot.mayPickup(this.player)) {
- ItemStack hotbar = this.player.getInventory().getItem(packet.getButtonNum());
-- boolean canCleanSwap = hotbar.isEmpty() || (clickedSlot.container == this.player.getInventory() && clickedSlot.mayPlace(hotbar)); // the slot will accept the hotbar item
-- if (clickedSlot.hasItem()) {
-- if (canCleanSwap) {
-- action = InventoryAction.HOTBAR_SWAP;
-- } else {
-- action = InventoryAction.HOTBAR_MOVE_AND_READD;
-- }
-- } else if (!clickedSlot.hasItem() && !hotbar.isEmpty() && clickedSlot.mayPlace(hotbar)) {
-+ if ((!hotbar.isEmpty() && clickedSlot.mayPlace(hotbar)) || (hotbar.isEmpty() && clickedSlot.hasItem())) { // Paper - modernify this logic (no such thing as a "hotbar move and readd"
- action = InventoryAction.HOTBAR_SWAP;
- } else {
- action = InventoryAction.NOTHING;
diff --git a/patches/unapplied/server/1052-Fix-sending-disconnect-packet-in-phases-where-it-doe.patch b/patches/unapplied/server/1052-Fix-sending-disconnect-packet-in-phases-where-it-doe.patch
deleted file mode 100644
index 802fa6c8e4..0000000000
--- a/patches/unapplied/server/1052-Fix-sending-disconnect-packet-in-phases-where-it-doe.patch
+++ /dev/null
@@ -1,21 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Shane Freeder <[email protected]>
-Date: Thu, 30 May 2024 18:46:15 +0100
-Subject: [PATCH] Fix sending disconnect packet in phases where it doesn't
- exist
-
-
-diff --git a/src/main/java/net/minecraft/network/Connection.java b/src/main/java/net/minecraft/network/Connection.java
-index 02833deaa2bb7e5abc655bc1bdbe15c4b3ac7119..f40420a6841f03983b0837e177ea2ae7c3a37ca1 100644
---- a/src/main/java/net/minecraft/network/Connection.java
-+++ b/src/main/java/net/minecraft/network/Connection.java
-@@ -214,7 +214,8 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
- if (player != null) player.quitReason = org.bukkit.event.player.PlayerQuitEvent.QuitReason.ERRONEOUS_STATE; // Paper - Add API for quit reason
- if (flag) {
- Connection.LOGGER.debug("Failed to sent packet", throwable);
-- if (this.getSending() == PacketFlow.CLIENTBOUND) {
-+ boolean doesDisconnectExist = this.packetListener.protocol() != ConnectionProtocol.STATUS && this.packetListener.protocol() != ConnectionProtocol.HANDSHAKING; // Paper
-+ if (this.getSending() == PacketFlow.CLIENTBOUND && doesDisconnectExist) { // Paper
- Packet<?> packet = this.sendLoginDisconnect ? new ClientboundLoginDisconnectPacket(ichatmutablecomponent) : new ClientboundDisconnectPacket(ichatmutablecomponent);
-
- this.send((Packet) packet, PacketSendListener.thenRun(() -> {
diff --git a/patches/unapplied/server/1053-Adopt-MaterialRerouting.patch b/patches/unapplied/server/1053-Adopt-MaterialRerouting.patch
deleted file mode 100644
index c1057b6bc1..0000000000
--- a/patches/unapplied/server/1053-Adopt-MaterialRerouting.patch
+++ /dev/null
@@ -1,134 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Bjarne Koll <[email protected]>
-Date: Thu, 13 Jun 2024 11:02:36 +0200
-Subject: [PATCH] Adopt MaterialRerouting
-
-Adopts the paper-api to the material rerouting infrastructure introduced
-by upstream.
-
-diff --git a/src/main/java/org/bukkit/craftbukkit/legacy/MaterialRerouting.java b/src/main/java/org/bukkit/craftbukkit/legacy/MaterialRerouting.java
-index 3ff0f0e34356cee4c510fdd60af723b1c5df156a..9c004e7cb46841d874ab997bf2e3b63ae763aec7 100644
---- a/src/main/java/org/bukkit/craftbukkit/legacy/MaterialRerouting.java
-+++ b/src/main/java/org/bukkit/craftbukkit/legacy/MaterialRerouting.java
-@@ -600,4 +600,82 @@ public class MaterialRerouting {
- public static void setBlocks(ToolComponent.ToolRule toolRule, Collection<Material> blocks) {
- toolRule.setBlocks(blocks.stream().map(MaterialRerouting::transformToBlockType).toList());
- }
-+
-+ // Paper start - register paper API specific material consumers in rerouting
-+ // A lot of these methods do *not* run through MaterialRerouting to avoid the overhead of a system that
-+ // currently is an effective noop.
-+ // The only downside is that upstream moved the handling of legacy materials into transformFromXType methods.
-+ // As such, methods introduced prior to 1.13 need to run through the transformation to make sure legacy material
-+ // constants still work.
-+
-+ // Utility method for constructing a set from an existing one after mapping each element.
-+ private static <I, O> Set<O> mapSet(final Set<I> input, final java.util.function.Function<I,O> mapper) {
-+ final Set<O> output = new it.unimi.dsi.fastutil.objects.ObjectOpenHashSet<>(input.size());
-+ for (final I i : input) {
-+ output.add(mapper.apply(i));
-+ }
-+ return output;
-+ }
-+
-+ // Method added post-1.13, noop (https://github.com/PaperMC/Paper/pull/4965)
-+ public static org.bukkit.Material getMinecartMaterial(org.bukkit.entity.Minecart minecart, @InjectPluginVersion ApiVersion version) {
-+ return minecart.getMinecartMaterial();
-+ }
-+
-+ // Method added post-1.13, noop (https://github.com/PaperMC/Paper/pull/4965)
-+ public static Material getBoatMaterial(Boat boat, @InjectPluginVersion ApiVersion version) {
-+ return boat.getBoatMaterial();
-+ }
-+
-+ // Method added post-1.13, noop (https://github.com/PaperMC/Paper/pull/3807)
-+ public static Material getType(io.papermc.paper.event.player.PlayerItemCooldownEvent event, @InjectPluginVersion ApiVersion version) {
-+ return event.getType();
-+ }
-+
-+ // Method added post-1.13, noop (https://github.com/PaperMC/Paper/pull/3850)
-+ public static Collection<Material> getInfiniburn(World world, @InjectPluginVersion ApiVersion version) {
-+ return world.getInfiniburn();
-+ }
-+
-+ // Method added pre-1.13, needs legacy rerouting (https://github.com/PaperMC/Paper/commit/3438e96192)
-+ public static Set<Material> getTypes(
-+ final com.destroystokyo.paper.event.player.PlayerArmorChangeEvent.SlotType slotType,
-+ @InjectPluginVersion final ApiVersion apiVersion
-+ ) {
-+ if (apiVersion.isNewerThanOrSameAs(ApiVersion.FLATTENING)) return slotType.getTypes();
-+ else return mapSet(slotType.getTypes(), MaterialRerouting::transformToItemType); // Needed as pre-flattening is hanled by transformToItemType
-+ }
-+
-+ // Method added pre-1.13, needs legacy rerouting (https://github.com/PaperMC/Paper/commit/3438e96192)
-+ @RerouteStatic("com/destroystokyo/paper/event/player/PlayerArmorChangeEvent$SlotType")
-+ public static com.destroystokyo.paper.event.player.PlayerArmorChangeEvent.SlotType getByMaterial(
-+ final Material material
-+ ) {
-+ return com.destroystokyo.paper.event.player.PlayerArmorChangeEvent.SlotType.getByMaterial(MaterialRerouting.transformToItemType(material));
-+ }
-+
-+ // Method added pre-1.13, needs legacy rerouting (https://github.com/PaperMC/Paper/commit/3438e96192)
-+ @RerouteStatic("com/destroystokyo/paper/event/player/PlayerArmorChangeEvent$SlotType")
-+ public static boolean isEquipable(final Material material) {
-+ return com.destroystokyo.paper.event.player.PlayerArmorChangeEvent.SlotType.isEquipable(MaterialRerouting.transformToItemType(material));
-+ }
-+
-+ // Method added post 1.13, no-op (https://github.com/PaperMC/Paper/pull/1244)1
-+ public static Material getMaterial(final com.destroystokyo.paper.event.block.AnvilDamagedEvent.DamageState damageState) {
-+ return damageState.getMaterial();
-+ }
-+
-+ // Method added post 1.13, no-op (https://github.com/PaperMC/Paper/pull/1244)1
-+ @RerouteStatic("com/destroystokyo/paper/event/block/AnvilDamagedEvent$DamageState")
-+ public static com.destroystokyo.paper.event.block.AnvilDamagedEvent.DamageState getState(
-+ final Material material
-+ ) {
-+ return com.destroystokyo.paper.event.block.AnvilDamagedEvent.DamageState.getState(material);
-+ }
-+
-+ // Method added post 1.13, no-op (https://github.com/PaperMC/Paper/pull/10290)
-+ public static ItemStack withType(final ItemStack itemStack, final Material material) {
-+ return itemStack.withType(material);
-+ }
-+ // Paper end - register paper API specific material consumers in rerouting
- }
-diff --git a/src/test/java/org/bukkit/craftbukkit/legacy/MaterialReroutingTest.java b/src/test/java/org/bukkit/craftbukkit/legacy/MaterialReroutingTest.java
-index 26208ca74688be062584824de5d074321b33f1b1..946cd46f3389a4d4ceda86e0115c59c5725a8a0a 100644
---- a/src/test/java/org/bukkit/craftbukkit/legacy/MaterialReroutingTest.java
-+++ b/src/test/java/org/bukkit/craftbukkit/legacy/MaterialReroutingTest.java
-@@ -55,6 +55,9 @@ public class MaterialReroutingTest extends AbstractTestingBase {
- .filter(entry -> !entry.getName().endsWith("ItemType.class"))
- .filter(entry -> !entry.getName().endsWith("Registry.class"))
- .filter(entry -> !entry.getName().startsWith("org/bukkit/material"))
-+ // Paper start - types that cannot be translated to ItemType/BlockType
-+ .filter(entry -> !entry.getName().equals("com/destroystokyo/paper/MaterialSetTag.class"))
-+ // Paper end - types that cannot be translated to ItemType/BlockType
- .map(entry -> {
- try {
- return MaterialReroutingTest.jarFile.getInputStream(entry);
-@@ -92,6 +95,10 @@ public class MaterialReroutingTest extends AbstractTestingBase {
- continue;
- }
- }
-+ // Paper start - filter out more methods from rerouting test
-+ if (methodNode.name.startsWith("lambda$")) continue;
-+ if (isInternal(methodNode.invisibleAnnotations)) continue;
-+ // Paper end - filter out more methods from rerouting test
-
- if (!Commodore.rerouteMethods(Collections.emptySet(), MaterialReroutingTest.MATERIAL_METHOD_REROUTE, (methodNode.access & Opcodes.ACC_STATIC) != 0, classNode.name, methodNode.name, methodNode.desc, a -> { })) {
- missingReroute.add(methodNode.name + " " + methodNode.desc + " " + methodNode.signature);
-@@ -108,6 +115,13 @@ public class MaterialReroutingTest extends AbstractTestingBase {
- }
- }
-
-+ // Paper start - filter out more methods from rerouting test
-+ private static boolean isInternal(final List<org.objectweb.asm.tree.AnnotationNode> annotationNodes) {
-+ return annotationNodes != null
-+ && annotationNodes.stream().anyMatch(a -> a.desc.equals("Lorg/jetbrains/annotations/ApiStatus$Internal;"));
-+ }
-+ // Paper end - filter out more methods from rerouting test
-+
- @AfterAll
- public static void clear() throws IOException {
- if (MaterialReroutingTest.jarFile != null) {