diff --git a/api/src/main/java/com/velocitypowered/api/command/CustomArgumentType.java b/api/src/main/java/com/velocitypowered/api/command/CustomArgumentType.java new file mode 100644 index 0000000000..166912efb7 --- /dev/null +++ b/api/src/main/java/com/velocitypowered/api/command/CustomArgumentType.java @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2026 Velocity Contributors + * + * The Velocity API is licensed under the terms of the MIT License. For more details, + * reference the LICENSE file in the api top-level directory. + */ + +package com.velocitypowered.api.command; + +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 java.util.Collection; +import java.util.concurrent.CompletableFuture; +import org.jetbrains.annotations.ApiStatus; + +/** + * An argument type that wraps around a native-to-vanilla argument type. + * This argument receives special handling in that the native argument type will + * be sent to the client for possible client-side completions and syntax validation. + * + *
When implementing this class, you have to create your own parsing logic from a
+ * {@link StringReader}. If only want to convert from the native type ({@code N}) to the custom
+ * type ({@code T}), implement {@link Converted} instead.
+ *
+ * @param The parsed native type will be converted via {@link #convert(Object)}.
+ * Implement {@link CustomArgumentType} if you want to handle parsing the type manually.
+ *
+ * @param context type
+ * @return suggestions
+ */
+ @Override
+ default CompletableFuture context, final SuggestionsBuilder builder) {
+ return ArgumentType.super.listSuggestions(context, builder);
+ }
+
+ /**
+ * An argument type that wraps around a native-to-vanilla argument type.
+ * This argument receives special handling in that the native argument type will
+ * be sent to the client for possible client-side completions and syntax validation.
+ *
+ * dest, final S source) {
final LiteralCommandNode asLiteral = (LiteralCommandNode) node;
final LiteralCommandNode copy = asLiteral.createBuilder().build();
- final VelocityArgumentCommandNode argsNode =
+ final CustomArgumentCommandNode argsNode =
VelocityCommands.getArgumentsNode(asLiteral);
if (argsNode == null) {
// This literal is associated to a BrigadierCommand, filter normally.
@@ -117,7 +117,14 @@ public void inject(final RootCommandNode dest, final S source) {
if (!node.canUse(source)) {
return null;
}
- final ArgumentBuilder builder = node.createBuilder();
+
+ final ArgumentBuilder builder;
+ if (node instanceof CustomArgumentCommandNode customArgumentCommandNode) {
+ builder = customArgumentCommandNode.createCustomArgBuilder();
+ } else {
+ builder = node.createBuilder();
+ }
+
if (node.getRedirect() != null) {
// Redirects to non-Brigadier commands are not supported. Luckily,
// we don't expose the root node to API users, so they can't access
diff --git a/proxy/src/main/java/com/velocitypowered/proxy/command/SuggestionsProvider.java b/proxy/src/main/java/com/velocitypowered/proxy/command/SuggestionsProvider.java
index 9c65c023ae..59b200e7f7 100644
--- a/proxy/src/main/java/com/velocitypowered/proxy/command/SuggestionsProvider.java
+++ b/proxy/src/main/java/com/velocitypowered/proxy/command/SuggestionsProvider.java
@@ -34,7 +34,7 @@
import com.velocitypowered.api.command.CommandMeta;
import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.proxy.Player;
-import com.velocitypowered.proxy.command.brigadier.VelocityArgumentCommandNode;
+import com.velocitypowered.proxy.command.brigadier.CustomArgumentCommandNode;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -194,7 +194,7 @@ private CompletableFuture contextSoFar) {
final S source = contextSoFar.getSource();
final String fullInput = reader.getString();
- final VelocityArgumentCommandNode argsNode = VelocityCommands.getArgumentsNode(alias);
+ final CustomArgumentCommandNode argsNode = VelocityCommands.getArgumentsNode(alias);
if (argsNode == null) {
// This is a BrigadierCommand, fallback to regular suggestions
reader.setCursor(0);
@@ -252,7 +252,7 @@ private CompletableFuture node, final StringReader reader,
+ final CustomArgumentCommandNode node, final StringReader reader,
final CommandContextBuilder context) {
final int start = reader.getCursor();
final String fullInput = reader.getString();
diff --git a/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommands.java b/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommands.java
index 1e4a01ce21..8bb1849d5c 100644
--- a/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommands.java
+++ b/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommands.java
@@ -19,6 +19,7 @@
import com.google.common.base.Preconditions;
import com.mojang.brigadier.CommandDispatcher;
+import com.mojang.brigadier.builder.ArgumentBuilder;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.context.CommandContextBuilder;
@@ -31,8 +32,10 @@
import com.velocitypowered.api.command.Command;
import com.velocitypowered.api.command.CommandManager;
import com.velocitypowered.api.command.CommandSource;
+import com.velocitypowered.api.command.CustomArgumentType;
import com.velocitypowered.api.command.InvocableCommand;
-import com.velocitypowered.proxy.command.brigadier.VelocityArgumentCommandNode;
+import com.velocitypowered.proxy.command.brigadier.CustomArgumentBuilder;
+import com.velocitypowered.proxy.command.brigadier.CustomArgumentCommandNode;
import com.velocitypowered.proxy.command.brigadier.VelocityBrigadierCommandWrapper;
import java.util.List;
import java.util.Locale;
@@ -70,34 +73,34 @@ public static CommandNode