diff --git a/paper-api/build.gradle.kts b/paper-api/build.gradle.kts index 832a2b3e86..5608f8d01d 100644 --- a/paper-api/build.gradle.kts +++ b/paper-api/build.gradle.kts @@ -39,7 +39,7 @@ abstract class MockitoAgentProvider : CommandLineArgumentProvider { // Paper end - configure mockito agent that is needed in newer java versions dependencies { - api("com.mojang:brigadier:1.2.9") // Paper - Brigadier command api + api("com.mojang:brigadier:1.3.10") // Paper - Brigadier command api // api dependencies are listed transitively to API consumers api("com.google.guava:guava:33.3.1-jre") api("com.google.code.gson:gson:2.11.0") diff --git a/paper-api/src/main/java/io/papermc/paper/command/brigadier/argument/CustomArgumentType.java b/paper-api/src/main/java/io/papermc/paper/command/brigadier/argument/CustomArgumentType.java index 91d40ef0bd..14be89fab5 100644 --- a/paper-api/src/main/java/io/papermc/paper/command/brigadier/argument/CustomArgumentType.java +++ b/paper-api/src/main/java/io/papermc/paper/command/brigadier/argument/CustomArgumentType.java @@ -37,10 +37,31 @@ public interface CustomArgumentType extends ArgumentType { * @param reader string reader input * @return parsed value * @throws CommandSyntaxException if an error occurs while parsing + * @see #parse(StringReader, Object) */ @Override T parse(final StringReader reader) throws CommandSyntaxException; + /** + * Parses the argument into the custom type ({@code T}). Keep in mind + * that this parsing will be done on the server. This means that if + * you throw a {@link CommandSyntaxException} during parsing, this + * will only show up to the user after the user has executed the command + * not while they are still entering it. + *

+ * This method provides the command source for additional context when parsing. You + * may have to do your own {@code instanceof} checks for {@link io.papermc.paper.command.brigadier.CommandSourceStack}. + * + * @param reader string reader input + * @param source source of the command + * @return parsed value + * @throws CommandSyntaxException if an error occurs while parsing + */ + @Override + default T parse(final StringReader reader, final S source) throws CommandSyntaxException { + return ArgumentType.super.parse(reader, source); + } + /** * Gets the native type that this argument uses, * the type that is sent to the client. @@ -95,13 +116,35 @@ public interface CustomArgumentType extends ArgumentType { return this.convert(this.getNativeType().parse(reader)); } + @ApiStatus.NonExtendable + @Override + default T parse(final StringReader reader, final S source) throws CommandSyntaxException { + return this.convert(this.getNativeType().parse(reader, source), source); + } + /** * Converts the value from the native type to the custom argument type. * * @param nativeType native argument provided value * @return converted value * @throws CommandSyntaxException if an exception occurs while parsing + * @see #convert(Object, Object) */ T convert(N nativeType) throws CommandSyntaxException; + + /** + * Converts the value from the native type to the custom argument type. + *

+ * This method provides the command source for additional context when converting. You + * may have to do your own {@code instanceof} checks for {@link io.papermc.paper.command.brigadier.CommandSourceStack}. + * + * @param nativeType native argument provided value + * @param source source of the command + * @return converted value + * @throws CommandSyntaxException if an exception occurs while parsing + */ + default T convert(final N nativeType, final S source) throws CommandSyntaxException { + return this.convert(nativeType); + } } } diff --git a/paper-server/patches/features/0012-Fix-entity-type-tags-suggestions-in-selectors.patch b/paper-server/patches/features/0012-Fix-entity-type-tags-suggestions-in-selectors.patch index 0f7eeec9d8..5e8077c3dd 100644 --- a/paper-server/patches/features/0012-Fix-entity-type-tags-suggestions-in-selectors.patch +++ b/paper-server/patches/features/0012-Fix-entity-type-tags-suggestions-in-selectors.patch @@ -35,10 +35,10 @@ index 704a63890a06d793f8ac3452838917e7c7335232..75262c8c9eaecb4a88a94f4076d67119 + // Paper end - tell clients to ask server for suggestions for EntityArguments } diff --git a/net/minecraft/commands/Commands.java b/net/minecraft/commands/Commands.java -index 45fa9cdc34e78613e346138aa92a6d3bbdee374c..e422a266de555bbf77eee201df9e4c5d89f7b801 100644 +index fa8c5ba4e0efd0c36613aaa8eaafba0cb70ceb87..19ccf3abf14c67f72a1ca065e4a304f50e645ef4 100644 --- a/net/minecraft/commands/Commands.java +++ b/net/minecraft/commands/Commands.java -@@ -510,6 +510,7 @@ public class Commands { +@@ -509,6 +509,7 @@ public class Commands { Map, CommandNode> commandNodeToSuggestionNode ) { commandNodeToSuggestionNode.keySet().removeIf((node) -> !org.spigotmc.SpigotConfig.sendNamespaced && node.getName().contains(":")); // Paper - Remove namedspaced from result nodes to prevent redirect trimming ~ see comment below diff --git a/paper-server/patches/sources/net/minecraft/commands/Commands.java.patch b/paper-server/patches/sources/net/minecraft/commands/Commands.java.patch index f16c785fbd..0c4203d037 100644 --- a/paper-server/patches/sources/net/minecraft/commands/Commands.java.patch +++ b/paper-server/patches/sources/net/minecraft/commands/Commands.java.patch @@ -183,7 +183,6 @@ + private void sendAsync(ServerPlayer player, java.util.Collection> dispatcherRootChildren) { + // Paper end - Perf: Async command map building + Map, CommandNode> map = Maps.newIdentityHashMap(); // Use identity to prevent aliasing issues -+ // Paper - brigadier API removes the need to fill the map twice RootCommandNode rootCommandNode = new RootCommandNode<>(); map.put(this.dispatcher.getRoot(), rootCommandNode); - this.fillUsableCommands(this.dispatcher.getRoot(), rootCommandNode, player.createCommandSourceStack(), map); @@ -276,6 +275,7 @@ - argumentBuilder.executes(commandContext -> 0); + // Paper start - fix suggestions due to falsely equal nodes + // Always create a new instance ++ //noinspection Convert2Lambda + argumentBuilder.executes(new com.mojang.brigadier.Command<>() { + @Override + public int run(com.mojang.brigadier.context.CommandContext commandContext) { diff --git a/paper-server/patches/sources/net/minecraft/commands/arguments/MessageArgument.java.patch b/paper-server/patches/sources/net/minecraft/commands/arguments/MessageArgument.java.patch index 9c9cb202be..c2bfa96065 100644 --- a/paper-server/patches/sources/net/minecraft/commands/arguments/MessageArgument.java.patch +++ b/paper-server/patches/sources/net/minecraft/commands/arguments/MessageArgument.java.patch @@ -4,11 +4,11 @@ public static void resolveChatMessage(CommandContext context, String key, Consumer callback) throws CommandSyntaxException { MessageArgument.Message message = context.getArgument(key, MessageArgument.Message.class); -+ // Paper start ++ // Paper start - brig message argument support + resolveChatMessage(message, context, key, callback); + } + public static void resolveChatMessage(MessageArgument.Message message, CommandContext context, String key, Consumer callback) throws CommandSyntaxException { -+ // Paper end ++ // Paper end - brig message argument support CommandSourceStack commandSourceStack = context.getSource(); Component component = message.resolveComponent(commandSourceStack); CommandSigningContext signingContext = commandSourceStack.getSigningContext(); diff --git a/paper-server/patches/sources/net/minecraft/core/component/DataComponentPatch.java.patch b/paper-server/patches/sources/net/minecraft/core/component/DataComponentPatch.java.patch index 272f5afdc5..9a4fca81b5 100644 --- a/paper-server/patches/sources/net/minecraft/core/component/DataComponentPatch.java.patch +++ b/paper-server/patches/sources/net/minecraft/core/component/DataComponentPatch.java.patch @@ -7,7 +7,7 @@ - component.streamCodec().encode(buffer, (T)value); + // Paper start - codec errors of random anonymous classes are useless + try { -+ component.streamCodec().encode(buffer, (T) value); // CraftBukkit - decompile error ++ component.streamCodec().encode(buffer, (T)value); + } catch (final Exception e) { + throw new RuntimeException("Error encoding component " + component, e); + } diff --git a/paper-server/patches/sources/net/minecraft/core/dispenser/BoatDispenseItemBehavior.java.patch b/paper-server/patches/sources/net/minecraft/core/dispenser/BoatDispenseItemBehavior.java.patch index bceaccc7b8..c40f77ee71 100644 --- a/paper-server/patches/sources/net/minecraft/core/dispenser/BoatDispenseItemBehavior.java.patch +++ b/paper-server/patches/sources/net/minecraft/core/dispenser/BoatDispenseItemBehavior.java.patch @@ -5,9 +5,9 @@ } + // CraftBukkit start -+ ItemStack itemstack1 = item.copyWithCount(1); // Paper - shrink at end and single item in event ++ ItemStack singleItemStack = item.copyWithCount(1); // Paper - shrink at end and single item in event + org.bukkit.block.Block block = org.bukkit.craftbukkit.block.CraftBlock.at(serverLevel, blockSource.pos()); -+ org.bukkit.craftbukkit.inventory.CraftItemStack craftItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemstack1); ++ org.bukkit.craftbukkit.inventory.CraftItemStack craftItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(singleItemStack); + + org.bukkit.event.block.BlockDispenseEvent event = new org.bukkit.event.block.BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(d1, d2 + d4, d3)); + if (!DispenserBlock.eventFired) { diff --git a/paper-server/patches/sources/net/minecraft/core/dispenser/DispenseItemBehavior.java.patch b/paper-server/patches/sources/net/minecraft/core/dispenser/DispenseItemBehavior.java.patch index fed8dc6488..c4e6ac4f58 100644 --- a/paper-server/patches/sources/net/minecraft/core/dispenser/DispenseItemBehavior.java.patch +++ b/paper-server/patches/sources/net/minecraft/core/dispenser/DispenseItemBehavior.java.patch @@ -6,9 +6,9 @@ + // CraftBukkit start + ServerLevel serverLevel = blockSource.level(); -+ ItemStack itemstack1 = item.copyWithCount(1); // Paper - shrink below and single item in event ++ ItemStack singleItemStack = item.copyWithCount(1); // Paper - shrink below and single item in event + org.bukkit.block.Block block = org.bukkit.craftbukkit.block.CraftBlock.at(serverLevel, blockSource.pos()); -+ org.bukkit.craftbukkit.inventory.CraftItemStack craftItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemstack1); ++ org.bukkit.craftbukkit.inventory.CraftItemStack craftItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(singleItemStack); + + org.bukkit.event.block.BlockDispenseEvent event = new org.bukkit.event.block.BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(0, 0, 0)); + if (!DispenserBlock.eventFired) { @@ -25,20 +25,20 @@ + shrink = false; // Paper - shrink below + // Chain to handler for new item + ItemStack eventStack = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem()); -+ DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(blockSource, eventStack); // Paper - Fix NPE with equippable and items without behavior -+ if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != this) { -+ idispensebehavior.dispense(blockSource, eventStack); ++ DispenseItemBehavior dispenseBehavior = DispenserBlock.getDispenseBehavior(blockSource, eventStack); // Paper - Fix NPE with equippable and items without behavior ++ if (dispenseBehavior != DispenseItemBehavior.NOOP && dispenseBehavior != this) { ++ dispenseBehavior.dispense(blockSource, eventStack); + return item; + } + // Paper start - track changed items in the dispense event -+ itemstack1 = org.bukkit.craftbukkit.inventory.CraftItemStack.unwrap(event.getItem()); // unwrap is safe because the stack won't be modified -+ type = ((SpawnEggItem) itemstack1.getItem()).getType(serverLevel.registryAccess(), itemstack1); ++ singleItemStack = org.bukkit.craftbukkit.inventory.CraftItemStack.unwrap(event.getItem()); // unwrap is safe because the stack won't be modified ++ type = ((SpawnEggItem) singleItemStack.getItem()).getType(serverLevel.registryAccess(), singleItemStack); + // Paper end - track changed item from dispense event + } try { type.spawn( - blockSource.level(), item, null, blockSource.pos().relative(direction), EntitySpawnReason.DISPENSER, direction != Direction.UP, false -+ blockSource.level(), itemstack1, null, blockSource.pos().relative(direction), EntitySpawnReason.DISPENSER, direction != Direction.UP, false // Paper - track changed item in dispense event ++ blockSource.level(), singleItemStack, null, blockSource.pos().relative(direction), EntitySpawnReason.DISPENSER, direction != Direction.UP, false // Paper - track changed item in dispense event ); } catch (Exception var6) { LOGGER.error("Error while dispensing spawn egg from dispenser at {}", blockSource.pos(), var6); @@ -56,9 +56,9 @@ BlockPos blockPos = blockSource.pos().relative(direction); ServerLevel serverLevel = blockSource.level(); + // CraftBukkit start -+ ItemStack itemstack1 = item.copyWithCount(1); // Paper - shrink below and single item in event ++ ItemStack singleItemStack = item.copyWithCount(1); // Paper - shrink below and single item in event + org.bukkit.block.Block block = org.bukkit.craftbukkit.block.CraftBlock.at(serverLevel, blockSource.pos()); -+ org.bukkit.craftbukkit.inventory.CraftItemStack craftItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemstack1); ++ org.bukkit.craftbukkit.inventory.CraftItemStack craftItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(singleItemStack); + + org.bukkit.event.block.BlockDispenseEvent event = new org.bukkit.event.block.BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(0, 0, 0)); + if (!DispenserBlock.eventFired) { @@ -100,10 +100,10 @@ if (!entitiesOfClass.isEmpty()) { - ((Saddleable)entitiesOfClass.get(0)).equipSaddle(item.split(1), SoundSource.BLOCKS); + // CraftBukkit start -+ ItemStack itemstack1 = item.copyWithCount(1); // Paper - shrink below and single item in event ++ ItemStack singleItemStack = item.copyWithCount(1); // Paper - shrink below and single item in event + ServerLevel world = blockSource.level(); + org.bukkit.block.Block block = org.bukkit.craftbukkit.block.CraftBlock.at(world, blockSource.pos()); -+ org.bukkit.craftbukkit.inventory.CraftItemStack craftItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemstack1); ++ org.bukkit.craftbukkit.inventory.CraftItemStack craftItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(singleItemStack); + + org.bukkit.event.block.BlockDispenseArmorEvent event = new org.bukkit.event.block.BlockDispenseArmorEvent(block, craftItem.clone(), entitiesOfClass.get(0).getBukkitLivingEntity()); + if (!DispenserBlock.eventFired) { @@ -120,9 +120,9 @@ + shrink = false; // Paper - shrink below + // Chain to handler for new item + ItemStack eventStack = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem()); -+ DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(blockSource, eventStack); // Paper - Fix NPE with equippable and items without behavior -+ if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != this) { // Paper - fix possible StackOverflowError -+ idispensebehavior.dispense(blockSource, eventStack); ++ DispenseItemBehavior dispenseBehavior = DispenserBlock.getDispenseBehavior(blockSource, eventStack); // Paper - Fix NPE with equippable and items without behavior ++ if (dispenseBehavior != DispenseItemBehavior.NOOP && dispenseBehavior != this) { // Paper - fix possible StackOverflowError ++ dispenseBehavior.dispense(blockSource, eventStack); + return item; + } + } @@ -379,9 +379,9 @@ BlockPos blockPos = blockSource.pos().relative(blockSource.state().getValue(DispenserBlock.FACING)); - PrimedTnt primedTnt = new PrimedTnt(level, blockPos.getX() + 0.5, blockPos.getY(), blockPos.getZ() + 0.5, null); + // CraftBukkit start -+ ItemStack itemstack1 = item.copyWithCount(1); // Paper - shrink at end and single item in event ++ ItemStack singleItemStack = item.copyWithCount(1); // Paper - shrink at end and single item in event + org.bukkit.block.Block block = org.bukkit.craftbukkit.block.CraftBlock.at(level, blockSource.pos()); -+ org.bukkit.craftbukkit.inventory.CraftItemStack craftItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemstack1); ++ org.bukkit.craftbukkit.inventory.CraftItemStack craftItem = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(singleItemStack); + + org.bukkit.event.block.BlockDispenseEvent event = new org.bukkit.event.block.BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector((double) blockPos.getX() + 0.5D, (double) blockPos.getY(), (double) blockPos.getZ() + 0.5D)); + if (!DispenserBlock.eventFired) { @@ -557,9 +557,9 @@ + if (!event.getItem().equals(craftItem)) { + // Chain to handler for new item + ItemStack eventStack = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem()); -+ DispenseItemBehavior idispensebehavior = DispenserBlock.getDispenseBehavior(blockSource, eventStack); // Paper - Fix NPE with equippable and items without behavior -+ if (idispensebehavior != DispenseItemBehavior.NOOP && idispensebehavior != this) { // Paper - fix possible StackOverflowError -+ idispensebehavior.dispense(blockSource, eventStack); ++ DispenseItemBehavior dispenseBehavior = DispenserBlock.getDispenseBehavior(blockSource, eventStack); // Paper - Fix NPE with equippable and items without behavior ++ if (dispenseBehavior != DispenseItemBehavior.NOOP && dispenseBehavior != this) { // Paper - fix possible StackOverflowError ++ dispenseBehavior.dispense(blockSource, eventStack); + return item; + } + }