diff options
Diffstat (limited to 'patches/unapplied/server')
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) { |