Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 116 additions & 5 deletions patches/api/0469-Brigadier-based-command-API.patch
Original file line number Diff line number Diff line change
Expand Up @@ -502,16 +502,21 @@ index 0000000000000000000000000000000000000000..9df87708206e26167a2c4934deff7fc6
+}
diff --git a/src/main/java/io/papermc/paper/command/brigadier/BasicCommand.java b/src/main/java/io/papermc/paper/command/brigadier/BasicCommand.java
new file mode 100644
index 0000000000000000000000000000000000000000..0f6b921b4bcf983cf25188823f78a061eec5263d
index 0000000000000000000000000000000000000000..2722a855f16d5f5c0104854dedadea3eb54b8bad
--- /dev/null
+++ b/src/main/java/io/papermc/paper/command/brigadier/BasicCommand.java
@@ -0,0 +1,36 @@
@@ -0,0 +1,121 @@
+package io.papermc.paper.command.brigadier;
+
+import io.papermc.paper.plugin.configuration.PluginMeta;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+import org.bukkit.command.CommandSender;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Implementing this interface allows for easily creating "Bukkit-style" {@code String[] args} commands.
Expand Down Expand Up @@ -541,6 +546,86 @@ index 0000000000000000000000000000000000000000..0f6b921b4bcf983cf25188823f78a061
+ default @NotNull Collection<String> suggest(final @NotNull CommandSourceStack commandSourceStack, final @NotNull String[] args) {
+ return Collections.emptyList();
+ }
+
+ /**
+ * Registration builder for {@link BasicCommand} to be used in {@link Commands#register(Consumer)}.
+ * <p>
+ * The command handler and label always have to be specified, the rest will default to {@code null} or an empty collection.
+ * However, <b>it is highly recommended to add a root permission/requirement</b>, which is also used to limit the commands sent to players.
+ * <p>
+ * {@link PluginMeta} may be automatically infered via the registration context, such as during bootstrap,
+ * but will otherwise also have to be defined.
+ */
+ interface RegistrationBuilder {
+
+ /**
+ * Sets the owning plugin's meta. This is not required to be set during plugin bootstrap.
+ *
+ * @param pluginMeta the owning plugin's meta
+ * @return self
+ */
+ @NotNull RegistrationBuilder pluginMeta(@NotNull PluginMeta pluginMeta);
+
+ /**
+ * Sets the command label.
+ *
+ * @param label the label of the to-be-registered command
+ * @return self
+ */
+ @NotNull RegistrationBuilder label(@NotNull String label);
+
+ /**
+ * Sets the command handler.
+ *
+ * @param basicCommand the basic command instance to register
+ * @return self
+ */
+ @NotNull RegistrationBuilder commandHandler(@NotNull BasicCommand basicCommand);
+
+ /**
+ * Sets the root permission node. Null by default.
+ *
+ * @param permission the root permission node
+ * @see #requires(Predicate) for a more flexible permission predicate
+ * @return self
+ */
+ default @NotNull RegistrationBuilder permission(@Nullable String permission) {
+ return this.requires(permission == null ? s -> true : sender -> sender.hasPermission(permission));
+ }
+
+ /**
+ * Sets the root permission predicate. Is always true by default.
+ *
+ * @param requirement the root command permission predicate
+ * @see #permission(String) for a simple permission node
+ * @return self
+ */
+ @NotNull RegistrationBuilder requires(@NotNull Predicate<CommandSender> requirement);
+
+ /**
+ * Sets the help description for the root literal node. Null by default.
+ *
+ * @param description the help description for the root literal node
+ * @return self
+ */
+ @NotNull RegistrationBuilder description(@Nullable String description);
+
+ /**
+ * Sets the aliases to register the basic command under. Empty by default.
+ *
+ * @param aliases a collection of aliases to register the basic command under
+ * @return self
+ */
+ @NotNull RegistrationBuilder aliases(@NotNull String... aliases);
+
+ /**
+ * Sets the aliases to register the basic command under. Empty by default.
+ *
+ * @param aliases a collection of aliases to register the basic command under
+ * @return self
+ */
+ @NotNull RegistrationBuilder aliases(@NotNull Collection<String> aliases);
+ }
+}
diff --git a/src/main/java/io/papermc/paper/command/brigadier/CommandRegistrationFlag.java b/src/main/java/io/papermc/paper/command/brigadier/CommandRegistrationFlag.java
new file mode 100644
Expand Down Expand Up @@ -620,10 +705,10 @@ index 0000000000000000000000000000000000000000..54288dbe7185b875a74184f002ee4de4
+}
diff --git a/src/main/java/io/papermc/paper/command/brigadier/Commands.java b/src/main/java/io/papermc/paper/command/brigadier/Commands.java
new file mode 100644
index 0000000000000000000000000000000000000000..ce60b618de10da7638f5aefa974aebe02600465c
index 0000000000000000000000000000000000000000..9260cf56e62859b1d5b181d10fd1b75f95e1578f
--- /dev/null
+++ b/src/main/java/io/papermc/paper/command/brigadier/Commands.java
@@ -0,0 +1,266 @@
@@ -0,0 +1,292 @@
+package io.papermc.paper.command.brigadier;
+
+import com.mojang.brigadier.CommandDispatcher;
Expand All @@ -639,6 +724,7 @@ index 0000000000000000000000000000000000000000..ce60b618de10da7638f5aefa974aebe0
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Set;
+import java.util.function.Consumer;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
Expand Down Expand Up @@ -838,7 +924,9 @@ index 0000000000000000000000000000000000000000..ce60b618de10da7638f5aefa974aebe0
+ * @param label the label of the to-be-registered command
+ * @param basicCommand the basic command instance to register
+ * @return successfully registered root command labels (including aliases and namespaced variants)
+ * @deprecated use {@link #register(Consumer)}, including its new permission field
+ */
+ @Deprecated(forRemoval = true)
+ default @Unmodifiable @NotNull Set<String> register(final @NotNull String label, final @NotNull BasicCommand basicCommand) {
+ return this.register(label, null, Collections.emptyList(), basicCommand);
+ }
Expand All @@ -850,7 +938,9 @@ index 0000000000000000000000000000000000000000..ce60b618de10da7638f5aefa974aebe0
+ * @param description the help description for the root literal node
+ * @param basicCommand the basic command instance to register
+ * @return successfully registered root command labels (including aliases and namespaced variants)
+ * @deprecated use {@link #register(Consumer)}, including its new permission field
+ */
+ @Deprecated(forRemoval = true)
+ default @Unmodifiable @NotNull Set<String> register(final @NotNull String label, final @Nullable String description, final @NotNull BasicCommand basicCommand) {
+ return this.register(label, description, Collections.emptyList(), basicCommand);
+ }
Expand All @@ -862,7 +952,9 @@ index 0000000000000000000000000000000000000000..ce60b618de10da7638f5aefa974aebe0
+ * @param aliases a collection of aliases to register the basic command under.
+ * @param basicCommand the basic command instance to register
+ * @return successfully registered root command labels (including aliases and namespaced variants)
+ * @deprecated use {@link #register(Consumer)}, including its new permission field
+ */
+ @Deprecated(forRemoval = true)
+ default @Unmodifiable @NotNull Set<String> register(final @NotNull String label, final @NotNull Collection<String> aliases, final @NotNull BasicCommand basicCommand) {
+ return this.register(label, null, aliases, basicCommand);
+ }
Expand All @@ -875,7 +967,9 @@ index 0000000000000000000000000000000000000000..ce60b618de10da7638f5aefa974aebe0
+ * @param aliases a collection of aliases to register the basic command under.
+ * @param basicCommand the basic command instance to register
+ * @return successfully registered root command labels (including aliases and namespaced variants)
+ * @deprecated use {@link #register(Consumer)}, including its new permission field
+ */
+ @Deprecated(forRemoval = true)
+ @Unmodifiable @NotNull Set<String> register(@NotNull String label, @Nullable String description, @NotNull Collection<String> aliases, @NotNull BasicCommand basicCommand);
+
+ /**
Expand All @@ -887,8 +981,25 @@ index 0000000000000000000000000000000000000000..ce60b618de10da7638f5aefa974aebe0
+ * @param aliases a collection of aliases to register the basic command under.
+ * @param basicCommand the basic command instance to register
+ * @return successfully registered root command labels (including aliases and namespaced variants)
+ * @deprecated use {@link #register(Consumer)}, including its new permission field
+ */
+ @Deprecated(forRemoval = true)
+ default @Unmodifiable @NotNull Set<String> register(@NotNull PluginMeta pluginMeta, @NotNull String label, @Nullable String description, @NotNull Collection<String> aliases, @NotNull BasicCommand basicCommand) {
+ return this.register(registrationBuilder -> registrationBuilder.pluginMeta(pluginMeta)
+ .label(label)
+ .commandHandler(basicCommand)
+ .description(description)
+ .aliases(aliases));
+ }
+
+ /**
+ * Registers a command under the same logic as {@link Commands#register(PluginMeta, LiteralCommandNode, String, Collection)}
+ * but with {@link BasicCommand} and the builder parameters as a simplified handler interface.
+ *
+ * @param basicCommandRegistration the registration builder consumer
+ * @return successfully registered root command labels (including aliases and namespaced variants)
+ */
+ @Unmodifiable @NotNull Set<String> register(@NotNull PluginMeta pluginMeta, @NotNull String label, @Nullable String description, @NotNull Collection<String> aliases, @NotNull BasicCommand basicCommand);
+ @Unmodifiable @NotNull Set<String> register(@NotNull Consumer<BasicCommand.RegistrationBuilder> basicCommandRegistration);
+}
diff --git a/src/main/java/io/papermc/paper/command/brigadier/MessageComponentSerializer.java b/src/main/java/io/papermc/paper/command/brigadier/MessageComponentSerializer.java
new file mode 100644
Expand Down
127 changes: 117 additions & 10 deletions patches/server/0975-Brigadier-based-command-API.patch
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,99 @@ index 0000000000000000000000000000000000000000..23525592d880f340745a28c956fa38d3
+ }
+
+}
diff --git a/src/main/java/io/papermc/paper/command/brigadier/BasicCommandRegistration.java b/src/main/java/io/papermc/paper/command/brigadier/BasicCommandRegistration.java
new file mode 100644
index 0000000000000000000000000000000000000000..9a07b3da38df6d38ed31360ab70565365e7f4076
--- /dev/null
+++ b/src/main/java/io/papermc/paper/command/brigadier/BasicCommandRegistration.java
@@ -0,0 +1,87 @@
+package io.papermc.paper.command.brigadier;
+
+import io.papermc.paper.plugin.configuration.PluginMeta;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.function.Predicate;
+import org.bukkit.command.CommandSender;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+public final class BasicCommandRegistration implements BasicCommand.RegistrationBuilder {
+
+ private Collection<String> aliases = Collections.emptyList();
+ private PluginMeta pluginMeta;
+ private String label;
+ private Predicate<CommandSender> requirement;
+ private String description;
+ private BasicCommand commandHandler;
+
+ @Override
+ public BasicCommand.@NotNull RegistrationBuilder pluginMeta(@NotNull final PluginMeta pluginMeta) {
+ this.pluginMeta = pluginMeta;
+ return this;
+ }
+
+ @Override
+ public BasicCommand.@NotNull RegistrationBuilder label(@NotNull final String label) {
+ this.label = Objects.requireNonNull(label);
+ return this;
+ }
+
+ @Override
+ public BasicCommand.@NotNull RegistrationBuilder description(@Nullable final String description) {
+ this.description = description;
+ return this;
+ }
+
+ @Override
+ public BasicCommand.@NotNull RegistrationBuilder aliases(final @NotNull String... aliases) {
+ this.aliases = List.of(aliases);
+ return this;
+ }
+
+ @Override
+ public BasicCommand.@NotNull RegistrationBuilder aliases(@NotNull final Collection<String> aliases) {
+ this.aliases = Collections.unmodifiableCollection(aliases);
+ return this;
+ }
+
+ @Override
+ public BasicCommand.@NotNull RegistrationBuilder commandHandler(@NotNull final BasicCommand basicCommand) {
+ this.commandHandler = Objects.requireNonNull(basicCommand);
+ return this;
+ }
+
+ @Override
+ public BasicCommand.@NotNull RegistrationBuilder requires(@NotNull final Predicate<CommandSender> requirement) {
+ this.requirement = Objects.requireNonNull(requirement);
+ return this;
+ }
+
+ public @Nullable PluginMeta pluginMeta() {
+ return this.pluginMeta;
+ }
+
+ public @NotNull String label() {
+ return Objects.requireNonNull(this.label, "Label missing in registration");
+ }
+
+ public @NotNull Predicate<CommandSender> requirement() {
+ return this.requirement;
+ }
+
+ public @Nullable String description() {
+ return this.description;
+ }
+
+ public @NotNull Collection<String> aliases() {
+ return this.aliases;
+ }
+
+ public @NotNull BasicCommand commandHandler() {
+ return Objects.requireNonNull(this.commandHandler, "BasicCommand handler missing in registration");
+ }
+}
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
Expand Down Expand Up @@ -686,10 +779,10 @@ index 0000000000000000000000000000000000000000..1b1642f306771f029e6214a2e2ebebb6
+}
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
index 0000000000000000000000000000000000000000..9893c6409613471251cf2b2064f1e1e5068b3cc4
--- /dev/null
+++ b/src/main/java/io/papermc/paper/command/brigadier/PaperCommands.java
@@ -0,0 +1,193 @@
@@ -0,0 +1,207 @@
+package io.papermc.paper.command.brigadier;
+
+import com.google.common.base.Preconditions;
Expand All @@ -709,6 +802,7 @@ index 0000000000000000000000000000000000000000..27509813a90980be1dfc7bde27d0eba6
+import java.util.List;
+import java.util.Locale;
+import java.util.Set;
+import java.util.function.Consumer;
+import net.minecraft.commands.CommandBuildContext;
+import org.apache.commons.lang3.StringUtils;
+import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
Expand Down Expand Up @@ -854,33 +948,46 @@ index 0000000000000000000000000000000000000000..27509813a90980be1dfc7bde27d0eba6
+
+ @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);
+ 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)
+ public @Unmodifiable @NotNull Set<String> register(@NotNull final Consumer<BasicCommand.RegistrationBuilder> registrationConsumer) {
+ final BasicCommandRegistration registration = new BasicCommandRegistration();
+ registrationConsumer.accept(registration);
+ final LiteralArgumentBuilder<CommandSourceStack> builder = Commands.literal(registration.label())
+ .requires(stack -> registration.requirement().test(stack.getSender()))
+ .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 SuggestionsBuilder offsetSuggestionsBuilder = suggestionsBuilder.createOffset(suggestionsBuilder.getInput().lastIndexOf(' ') + 1);
+
+ final Collection<String> suggestions = basicCommand.suggest(context.getSource(), args);
+ final Collection<String> suggestions = registration.commandHandler().suggest(context.getSource(), args);
+ suggestions.forEach(offsetSuggestionsBuilder::suggest);
+ return offsetSuggestionsBuilder.buildFuture();
+ })
+ .executes((stack) -> {
+ basicCommand.execute(stack.getSource(), StringUtils.split(stack.getArgument("args", String.class), ' '));
+ registration.commandHandler().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]);
+ registration.commandHandler().execute(stack.getSource(), new String[0]);
+ return com.mojang.brigadier.Command.SINGLE_SUCCESS;
+ });
+
+ return this.register(pluginMeta, builder.build(), description, aliases);
+ PluginMeta pluginMeta = registration.pluginMeta();
+ if (pluginMeta == null) {
+ pluginMeta = requireNonNull(this.currentContext, "No lifecycle owner context is set").getPluginMeta();
+ }
+ return this.register(pluginMeta, builder.build(), registration.description(), registration.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
Expand Down