From af07ad92a6c0f00a9b6fc23926f8750784820062 Mon Sep 17 00:00:00 2001 From: Kazotaruu_ Date: Fri, 4 Sep 2026 14:42:50 +0200 Subject: [PATCH] fix: do not run commands with missing required arguments Paper-Dispatch attaches an executor to every required argument builder in SubCommand#addRequiredArgument, so each intermediate node of the chain is executable. A command declaring several required arguments therefore runs when only the first ones are typed, and CommandDispatch#getArgument throws for the arguments that were never parsed: /zmenu create villages java.lang.IllegalArgumentException: No such argument 'inventory-size' exists on this command ZSubCommand removes the executor from every required argument but the last one before the node is built, so Brigadier answers with its usual incomplete command error instead of dispatching a half parsed command. Applies to the six commands declaring more than one required argument: create, players add, players get, players remove, players set and players subtract. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01LXoCyL3N3h2wixGJbm5bj5 --- .../maxlego08/menu/command/ZSubCommand.java | 49 +++++++++++++++++++ .../command/commands/CommandMenuCreate.java | 4 +- .../players/CommandMenuPlayersAdd.java | 4 +- .../players/CommandMenuPlayersGet.java | 4 +- .../players/CommandMenuPlayersRemove.java | 4 +- .../players/CommandMenuPlayersSet.java | 4 +- .../players/CommandMenuPlayersSubtract.java | 4 +- 7 files changed, 61 insertions(+), 12 deletions(-) create mode 100644 src/main/java/fr/maxlego08/menu/command/ZSubCommand.java diff --git a/src/main/java/fr/maxlego08/menu/command/ZSubCommand.java b/src/main/java/fr/maxlego08/menu/command/ZSubCommand.java new file mode 100644 index 00000000..dec684c0 --- /dev/null +++ b/src/main/java/fr/maxlego08/menu/command/ZSubCommand.java @@ -0,0 +1,49 @@ +package fr.maxlego08.menu.command; + +import com.mojang.brigadier.builder.ArgumentBuilder; +import com.mojang.brigadier.tree.LiteralCommandNode; +import fr.robie.paperdispatch.command.SubCommand; +import io.papermc.paper.command.brigadier.CommandSourceStack; +import org.bukkit.plugin.Plugin; + +import java.util.ArrayList; +import java.util.List; + +/** + * Paper-Dispatch attaches an executor to every required argument node, so a command declaring + * several required arguments is also executable when only the first ones are typed. The command + * then runs with arguments that were never parsed and Brigadier throws + * {@code No such argument '...' exists on this command}. + *

+ * Only the last required argument may be executable, so this class removes the executor from all + * the previous ones just before the node is built. Brigadier then answers with its usual + * "incomplete command" error instead of running the command. + */ +public abstract class ZSubCommand extends SubCommand { + + private final List> requiredArgumentBuilders = new ArrayList<>(); + + protected ZSubCommand(T plugin, String name) { + super(plugin, name); + } + + protected ZSubCommand(T plugin, String name, String... aliases) { + super(plugin, name, aliases); + } + + @Override + protected void addRequiredArgument(ArgumentBuilder argument, ArgumentExecutor executor) { + super.addRequiredArgument(argument, executor); + // Paper-Dispatch may wrap the builder before this point, so only the instance received here + // is the one that will end up in the command tree. + this.requiredArgumentBuilders.add(argument); + } + + @Override + public LiteralCommandNode build() { + for (int index = 0; index < this.requiredArgumentBuilders.size() - 1; index++) { + this.requiredArgumentBuilders.get(index).executes(null); + } + return super.build(); + } +} diff --git a/src/main/java/fr/maxlego08/menu/command/commands/CommandMenuCreate.java b/src/main/java/fr/maxlego08/menu/command/commands/CommandMenuCreate.java index 5b802570..6dd5fbcc 100644 --- a/src/main/java/fr/maxlego08/menu/command/commands/CommandMenuCreate.java +++ b/src/main/java/fr/maxlego08/menu/command/commands/CommandMenuCreate.java @@ -4,15 +4,15 @@ import com.mojang.brigadier.arguments.StringArgumentType; import fr.maxlego08.menu.ZMenuPlugin; import fr.maxlego08.menu.api.utils.Message; +import fr.maxlego08.menu.command.ZSubCommand; import fr.maxlego08.menu.common.enums.Permission; import fr.maxlego08.menu.common.utils.MessageUtils; import fr.robie.paperdispatch.command.CommandDispatch; import fr.robie.paperdispatch.command.CommandResultType; -import fr.robie.paperdispatch.command.SubCommand; import io.papermc.paper.command.brigadier.Commands; import org.jetbrains.annotations.NotNull; -public class CommandMenuCreate extends SubCommand { +public class CommandMenuCreate extends ZSubCommand { public CommandMenuCreate(ZMenuPlugin plugin) { super(plugin, "create"); diff --git a/src/main/java/fr/maxlego08/menu/command/commands/players/CommandMenuPlayersAdd.java b/src/main/java/fr/maxlego08/menu/command/commands/players/CommandMenuPlayersAdd.java index 4d60a7b6..5b7b132e 100644 --- a/src/main/java/fr/maxlego08/menu/command/commands/players/CommandMenuPlayersAdd.java +++ b/src/main/java/fr/maxlego08/menu/command/commands/players/CommandMenuPlayersAdd.java @@ -5,6 +5,7 @@ import fr.maxlego08.menu.api.players.Data; import fr.maxlego08.menu.api.players.DataManager; import fr.maxlego08.menu.api.utils.Message; +import fr.maxlego08.menu.command.ZSubCommand; import fr.maxlego08.menu.common.enums.Permission; import fr.maxlego08.menu.common.utils.MessageUtils; import fr.maxlego08.menu.common.utils.command.NonSpaceStringArgumentType; @@ -13,14 +14,13 @@ import fr.robie.paperdispatch.cache.OfflinePlayerCache; import fr.robie.paperdispatch.command.CommandDispatch; import fr.robie.paperdispatch.command.CommandResultType; -import fr.robie.paperdispatch.command.SubCommand; import io.papermc.paper.command.brigadier.Commands; import org.jetbrains.annotations.NotNull; import java.util.Optional; import java.util.UUID; -public class CommandMenuPlayersAdd extends SubCommand { +public class CommandMenuPlayersAdd extends ZSubCommand { public CommandMenuPlayersAdd(ZMenuPlugin plugin) { super(plugin, "add"); diff --git a/src/main/java/fr/maxlego08/menu/command/commands/players/CommandMenuPlayersGet.java b/src/main/java/fr/maxlego08/menu/command/commands/players/CommandMenuPlayersGet.java index 529b8c5f..9f0561b3 100644 --- a/src/main/java/fr/maxlego08/menu/command/commands/players/CommandMenuPlayersGet.java +++ b/src/main/java/fr/maxlego08/menu/command/commands/players/CommandMenuPlayersGet.java @@ -5,20 +5,20 @@ import fr.maxlego08.menu.api.players.DataManager; import fr.maxlego08.menu.api.players.PlayerData; import fr.maxlego08.menu.api.utils.Message; +import fr.maxlego08.menu.command.ZSubCommand; import fr.maxlego08.menu.common.enums.Permission; import fr.maxlego08.menu.common.utils.MessageUtils; import fr.maxlego08.menu.common.utils.command.NonSpaceStringArgumentType; import fr.robie.paperdispatch.argument.OfflinePlayerArgument; import fr.robie.paperdispatch.command.CommandDispatch; import fr.robie.paperdispatch.command.CommandResultType; -import fr.robie.paperdispatch.command.SubCommand; import io.papermc.paper.command.brigadier.Commands; import org.jetbrains.annotations.NotNull; import java.util.Optional; import java.util.UUID; -public class CommandMenuPlayersGet extends SubCommand { +public class CommandMenuPlayersGet extends ZSubCommand { public CommandMenuPlayersGet(ZMenuPlugin plugin) { super(plugin, "get"); diff --git a/src/main/java/fr/maxlego08/menu/command/commands/players/CommandMenuPlayersRemove.java b/src/main/java/fr/maxlego08/menu/command/commands/players/CommandMenuPlayersRemove.java index 40a0cddb..88901006 100644 --- a/src/main/java/fr/maxlego08/menu/command/commands/players/CommandMenuPlayersRemove.java +++ b/src/main/java/fr/maxlego08/menu/command/commands/players/CommandMenuPlayersRemove.java @@ -4,13 +4,13 @@ import fr.maxlego08.menu.api.players.DataManager; import fr.maxlego08.menu.api.players.PlayerData; import fr.maxlego08.menu.api.utils.Message; +import fr.maxlego08.menu.command.ZSubCommand; import fr.maxlego08.menu.common.enums.Permission; import fr.maxlego08.menu.common.utils.MessageUtils; import fr.maxlego08.menu.common.utils.command.NonSpaceStringArgumentType; import fr.robie.paperdispatch.argument.OfflinePlayerArgument; import fr.robie.paperdispatch.command.CommandDispatch; import fr.robie.paperdispatch.command.CommandResultType; -import fr.robie.paperdispatch.command.SubCommand; import io.papermc.paper.command.brigadier.Commands; import org.bukkit.OfflinePlayer; import org.jetbrains.annotations.NotNull; @@ -18,7 +18,7 @@ import java.util.Optional; import java.util.UUID; -public class CommandMenuPlayersRemove extends SubCommand { +public class CommandMenuPlayersRemove extends ZSubCommand { public CommandMenuPlayersRemove(ZMenuPlugin plugin) { super(plugin, "remove", "r"); diff --git a/src/main/java/fr/maxlego08/menu/command/commands/players/CommandMenuPlayersSet.java b/src/main/java/fr/maxlego08/menu/command/commands/players/CommandMenuPlayersSet.java index de75eadf..afc74769 100644 --- a/src/main/java/fr/maxlego08/menu/command/commands/players/CommandMenuPlayersSet.java +++ b/src/main/java/fr/maxlego08/menu/command/commands/players/CommandMenuPlayersSet.java @@ -6,6 +6,7 @@ import fr.maxlego08.menu.api.players.Data; import fr.maxlego08.menu.api.players.DataManager; import fr.maxlego08.menu.api.utils.Message; +import fr.maxlego08.menu.command.ZSubCommand; import fr.maxlego08.menu.common.enums.Permission; import fr.maxlego08.menu.common.utils.MessageUtils; import fr.maxlego08.menu.common.utils.command.NonSpaceStringArgumentType; @@ -14,13 +15,12 @@ import fr.robie.paperdispatch.cache.OfflinePlayerCache; import fr.robie.paperdispatch.command.CommandDispatch; import fr.robie.paperdispatch.command.CommandResultType; -import fr.robie.paperdispatch.command.SubCommand; import io.papermc.paper.command.brigadier.Commands; import org.jetbrains.annotations.NotNull; import java.util.UUID; -public class CommandMenuPlayersSet extends SubCommand { +public class CommandMenuPlayersSet extends ZSubCommand { public CommandMenuPlayersSet(ZMenuPlugin plugin) { super(plugin, "set"); diff --git a/src/main/java/fr/maxlego08/menu/command/commands/players/CommandMenuPlayersSubtract.java b/src/main/java/fr/maxlego08/menu/command/commands/players/CommandMenuPlayersSubtract.java index 01c2d37d..13c77116 100644 --- a/src/main/java/fr/maxlego08/menu/command/commands/players/CommandMenuPlayersSubtract.java +++ b/src/main/java/fr/maxlego08/menu/command/commands/players/CommandMenuPlayersSubtract.java @@ -5,6 +5,7 @@ import fr.maxlego08.menu.api.players.Data; import fr.maxlego08.menu.api.players.DataManager; import fr.maxlego08.menu.api.utils.Message; +import fr.maxlego08.menu.command.ZSubCommand; import fr.maxlego08.menu.common.enums.Permission; import fr.maxlego08.menu.common.utils.MessageUtils; import fr.maxlego08.menu.common.utils.command.NonSpaceStringArgumentType; @@ -13,14 +14,13 @@ import fr.robie.paperdispatch.cache.OfflinePlayerCache; import fr.robie.paperdispatch.command.CommandDispatch; import fr.robie.paperdispatch.command.CommandResultType; -import fr.robie.paperdispatch.command.SubCommand; import io.papermc.paper.command.brigadier.Commands; import org.jetbrains.annotations.NotNull; import java.util.Optional; import java.util.UUID; -public class CommandMenuPlayersSubtract extends SubCommand { +public class CommandMenuPlayersSubtract extends ZSubCommand { public CommandMenuPlayersSubtract(ZMenuPlugin plugin) { super(plugin, "subtract", "sub");