diff --git a/src/main/java/org/mvplugins/multiverse/core/command/MVCommandCompletionFilters.java b/src/main/java/org/mvplugins/multiverse/core/command/MVCommandCompletionFilters.java
new file mode 100644
index 000000000..82194b9d8
--- /dev/null
+++ b/src/main/java/org/mvplugins/multiverse/core/command/MVCommandCompletionFilters.java
@@ -0,0 +1,60 @@
+package org.mvplugins.multiverse.core.command;
+
+import co.aikar.commands.CommandCompletionContext;
+import co.aikar.commands.CommandCompletionFilter;
+import co.aikar.commands.apachecommonslang.ApacheCommonsLangUtil;
+import org.jetbrains.annotations.ApiStatus;
+
+import java.util.Locale;
+
+/**
+ * Utility filters for command completion matching.
+ *
+ * @since 5.7
+ */
+@ApiStatus.AvailableSince("5.7")
+public final class MVCommandCompletionFilters {
+
+ /**
+ * Matches namespaced keys (for example, {@code minecraft:stone}) using namespace-aware checks.
+ *
+ * This filter accepts completions that:
+ *
+ * - start with the current input,
+ * - have a namespace starting with the input, or
+ * - have a key value containing the input.
+ *
+ *
+ * @since 5.7
+ */
+ @ApiStatus.AvailableSince("5.7")
+ public static final CommandCompletionFilter NAMESPACED_KEY = (context, completion) -> {
+ String[] split = completion.split(":", 2);
+ if (split.length < 2) {
+ return ApacheCommonsLangUtil.startsWithIgnoreCase(completion, context.getInput());
+ }
+ String lowerCase = context.getInput().toLowerCase(Locale.ROOT);
+ return ApacheCommonsLangUtil.startsWithIgnoreCase(completion, context.getInput())
+ || split[0].toLowerCase(Locale.ROOT).startsWith(lowerCase)
+ || split[1].toLowerCase(Locale.ROOT).contains(lowerCase);
+ };
+
+ /**
+ * Gets the namespaced-key completion filter with a generic type-safe signature.
+ * Use this method instead of {@link MVCommandCompletionFilters#NAMESPACED_KEY}
+ * to avoid raw type unchecked warnings.
+ *
+ * @param completion context type
+ * @return the namespaced key filter
+ *
+ * @since 5.7
+ */
+ @ApiStatus.AvailableSince("5.7")
+ public static CommandCompletionFilter namespacedKey() {
+ return NAMESPACED_KEY;
+ }
+
+ private MVCommandCompletionFilters() {
+ throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
+ }
+}
diff --git a/src/main/java/org/mvplugins/multiverse/core/command/MVCommandCompletions.java b/src/main/java/org/mvplugins/multiverse/core/command/MVCommandCompletions.java
index ae4ffcfb0..9c1b222c8 100644
--- a/src/main/java/org/mvplugins/multiverse/core/command/MVCommandCompletions.java
+++ b/src/main/java/org/mvplugins/multiverse/core/command/MVCommandCompletions.java
@@ -11,6 +11,7 @@
import java.util.stream.Collectors;
import co.aikar.commands.BukkitCommandCompletionContext;
+import co.aikar.commands.CommandCompletionFilter;
import co.aikar.commands.CommandIssuer;
import co.aikar.commands.PaperCommandCompletions;
import co.aikar.commands.RegisteredCommand;
@@ -89,27 +90,27 @@ public class MVCommandCompletions extends PaperCommandCompletions {
this.generatorProvider = generatorProvider;
this.potentialWorldFinder = potentialWorldFinder;
- registerAsyncCompletion("anchornames", this::suggestAnchorNames);
+ registerAsyncCompletion("anchornames", this::suggestAnchorNames, CommandCompletionFilter.contains());
registerAsyncCompletion("commands", this::suggestCommands);
registerAsyncCompletion("destinations", this::suggestDestinations);
registerStaticCompletion("difficulties", suggestEnums(Difficulty.class));
registerStaticCompletion("environments", List.of("normal", "nether", "the_end")); // Don't tab complete the "custom" environment
registerAsyncCompletion("flags", this::suggestFlags);
registerStaticCompletion("gamemodes", suggestEnums(GameMode.class));
- registerStaticCompletion("gamerules", this::suggestGamerules);
+ registerStaticCompletion("gamerules", this::suggestGamerules, MVCommandCompletionFilters.namespacedKey());
registerAsyncCompletion("gamerulesvalues", this::suggestGamerulesValues);
- registerAsyncCompletion("generatorplugins", this::suggestGeneratorPlugins);
- registerStaticCompletion("materials", suggestEnums(Material.class));
- registerStaticCompletion("mvconfigs", config.getStringPropertyHandle().getAllPropertyNames());
+ registerAsyncCompletion("generatorplugins", this::suggestGeneratorPlugins, CommandCompletionFilter.contains());
+ registerStaticCompletion("materials", suggestEnums(Material.class), CommandCompletionFilter.contains());
+ registerStaticCompletion("mvconfigs", config.getStringPropertyHandle().getAllPropertyNames(), CommandCompletionFilter.contains());
registerAsyncCompletion("mvconfigvalues", this::suggestMVConfigValues);
- registerAsyncCompletion("mvworlds", this::suggestMVWorlds);
- registerAsyncCompletion("mvworldmetakey", this::suggestMVWorldMetaKey);
- registerAsyncCompletion("mvworldpropsname", this::suggestMVWorldPropsName);
+ registerAsyncCompletion("mvworlds", this::suggestMVWorlds, MVCommandCompletionFilters.namespacedKey());
+ registerAsyncCompletion("mvworldmetakey", this::suggestMVWorldMetaKey, CommandCompletionFilter.contains());
+ registerAsyncCompletion("mvworldpropsname", this::suggestMVWorldPropsName, CommandCompletionFilter.contains());
registerAsyncCompletion("mvworldpropsvalue", this::suggestMVWorldPropsValue);
- registerCompletion("playersarray", this::suggestPlayersArray); // getting online players cannot be async
+ registerCompletion("playersarray", this::suggestPlayersArray, CommandCompletionFilter.contains()); // getting online players cannot be async
registerStaticCompletion("propsmodifyaction", suggestEnums(PropertyModifyAction.class));
registerStaticCompletion("spawncategories", suggestEnums(SpawnCategory.class));
- registerAsyncCompletion("spawncategorypropsname", this::suggestSpawnCategoryPropsName);
+ registerAsyncCompletion("spawncategorypropsname", this::suggestSpawnCategoryPropsName, CommandCompletionFilter.contains());
registerAsyncCompletion("spawncategorypropsvalue", this::suggestSpawnCategoryPropsValue);
setDefaultCompletion("destinations", DestinationInstance.class);