diff --git a/.idea/vcs.xml b/.idea/vcs.xml
index 35eb1dd..0b33847 100644
--- a/.idea/vcs.xml
+++ b/.idea/vcs.xml
@@ -2,5 +2,6 @@
+
\ No newline at end of file
diff --git a/.idea/workspace.xml b/.idea/workspace.xml
index 6c220b6..9b895f6 100644
--- a/.idea/workspace.xml
+++ b/.idea/workspace.xml
@@ -5,57 +5,41 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
@@ -92,17 +76,25 @@
+
+
+
+
+
+
+
+
@@ -125,6 +117,9 @@
+
+
+
@@ -139,38 +134,40 @@
- {
+ "keyToString": {
+ "Gradle./home/a/src/kento2/KenCommandAPI/kencommandapi-core [:kencommandapi-core:de.kentoj.kencommandapi.Main.main()].executor": "Run",
+ "Gradle./home/a/src/kento2/KenCommandAPI/kencommandapi-core [build].executor": "Run",
+ "Gradle./home/a/src/kento2/KenCommandAPI/kencommandapi-core [publish].executor": "Run",
+ "Gradle.Download Sources.executor": "Run",
+ "Gradle.KenCommandAPI [:de.kencommand.Main.main()].executor": "Run",
+ "Gradle.KenCommandAPI [assemble].executor": "Run",
+ "Gradle.KenCommandAPI [build].executor": "Run",
+ "Gradle.KenCommandAPI [clean].executor": "Run",
+ "Gradle.KenCommandAPI [publishAllPublicationsToScrowRepository].executor": "Run",
+ "Gradle.KenCommandAPI [publish].executor": "Run",
+ "Gradle.KenCommandAPI:kencommandapi-bukkit [build].executor": "Run",
+ "Gradle.KenCommandAPI:kencommandapi-bukkit [generatePomFileForKenCommandAPIBukkitPublication].executor": "Run",
+ "Gradle.KenCommandAPI:kencommandapi-bukkit [publishAllPublicationsToScrowRepository].executor": "Run",
+ "Gradle.KenCommandAPI:kencommandapi-bukkit [publish].executor": "Run",
+ "Gradle.KenCommandAPI:kencommandapi-velocity [publish].executor": "Run",
+ "Gradle.kencommandapi-core [build].executor": "Run",
+ "ModuleVcsDetector.initialDetectionPerformed": "true",
+ "RunOnceActivity.ShowReadmeOnStart": "true",
+ "RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252": "true",
+ "RunOnceActivity.git.unshallow": "true",
+ "git-widget-placeholder": "master",
+ "kotlin-language-version-configured": "true",
+ "last_opened_file_path": "/home/a/src/scrow/KenCommandAPI/kencommandapi-core",
+ "project.structure.last.edited": "Modules",
+ "project.structure.proportion": "0.15",
+ "project.structure.side.proportion": "0.6268199",
+ "settings.editor.selected.configurable": "preferences.pluginManager"
}
-}]]>
+}
+
@@ -185,40 +182,20 @@
+
+
-
-
-
-
-
-
-
-
-
-
-
- true
- true
- false
- false
-
-
@@ -241,6 +218,28 @@
false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+ true
+ false
+ false
+
+
@@ -311,9 +310,9 @@
-
-
+
+
@@ -533,7 +532,23 @@
1780445840891
-
+
+
+ 1782751783492
+
+
+
+ 1782751783492
+
+
+
+ 1782751797787
+
+
+
+ 1782751797787
+
+
diff --git a/build.gradle.kts b/build.gradle.kts
index ebe09d8..72a06af 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -1,8 +1,7 @@
group = "de.kentoj.scrow"
-version = "0.10"
+version = "0.21"
subprojects {
group = rootProject.group
version = rootProject.version
-
}
diff --git a/kencommandapi-bukkit/build.gradle.kts b/kencommandapi-bukkit/build.gradle.kts
index f0113cd..69c09dd 100644
--- a/kencommandapi-bukkit/build.gradle.kts
+++ b/kencommandapi-bukkit/build.gradle.kts
@@ -7,14 +7,14 @@ plugins {
}
repositories {
- maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots/") {
- name = "spigotmc-repo"
+ maven("https://repo.papermc.io/repository/maven-public/") {
+ name = "papermc"
}
}
dependencies {
api(project(":kencommandapi-core"))
- compileOnly("org.spigotmc:spigot-api:1.21.11-R0.1-SNAPSHOT")
+ compileOnly("io.papermc.paper:paper-api:1.21.11-rc3-R0.1-SNAPSHOT")
}
publishing {
diff --git a/kencommandapi-bukkit/src/main/java/de/kentoj/kencommandapi/AsyncTabCompleter.java b/kencommandapi-bukkit/src/main/java/de/kentoj/kencommandapi/AsyncTabCompleter.java
new file mode 100644
index 0000000..3c48bcc
--- /dev/null
+++ b/kencommandapi-bukkit/src/main/java/de/kentoj/kencommandapi/AsyncTabCompleter.java
@@ -0,0 +1,25 @@
+package de.kentoj.kencommandapi;
+
+import com.destroystokyo.paper.event.server.AsyncTabCompleteEvent;
+import de.kentoj.kencommandapi.api.CommandHandler;
+import lombok.RequiredArgsConstructor;
+import org.bukkit.command.CommandSender;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+
+@RequiredArgsConstructor
+public class AsyncTabCompleter implements Listener {
+
+ private final CommandAPI commandAPI;
+ private final CommandHandler commandHandler;
+
+ @EventHandler(ignoreCancelled = true)
+ public void onAsyncTabComplete(AsyncTabCompleteEvent ev) {
+ var args = ev.getBuffer().split(" ");
+ var label = args[0];
+ var rootNode = commandAPI.getRegistered(label);
+ if (rootNode == null) return;
+ commandHandler.getSuggestions(rootNode, ev.getSender(), args)
+ .thenAccept(ev::setCompletions);
+ }
+}
diff --git a/kencommandapi-bukkit/src/main/java/de/kentoj/kencommandapi/BukkitKenCommand.java b/kencommandapi-bukkit/src/main/java/de/kentoj/kencommandapi/BukkitKenCommand.java
index 38a9afd..119166e 100644
--- a/kencommandapi-bukkit/src/main/java/de/kentoj/kencommandapi/BukkitKenCommand.java
+++ b/kencommandapi-bukkit/src/main/java/de/kentoj/kencommandapi/BukkitKenCommand.java
@@ -1,9 +1,7 @@
package de.kentoj.kencommandapi;
-import de.kentoj.kencommandapi.api.CommandNode;
-import de.kentoj.kencommandapi.api.invocation.CommandContext;
-import de.kentoj.kencommandapi.internal.parser.CommandParser;
-import de.kentoj.kencommandapi.internal.parser.ParseResult;
+import de.kentoj.kencommandapi.api.CommandHandler;
+import de.kentoj.kencommandapi.api.node.RootCommandNode;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
@@ -12,15 +10,15 @@ import java.util.Arrays;
public class BukkitKenCommand extends Command {
- private final CommandNode rootNode;
- private final CommandParser commandParser;
+ private final RootCommandNode rootNode;
+ private final CommandHandler commandHandler;
protected BukkitKenCommand(
- CommandParser commandParser,
- CommandNode rootNode
+ CommandHandler commandHandler,
+ RootCommandNode rootNode
) {
super(rootNode.getNames()[0]);
- this.commandParser = commandParser;
+ this.commandHandler = commandHandler;
this.rootNode = rootNode;
this.setAliases(Arrays.asList(rootNode.getNames()));
this.setPermission(rootNode.getPermission());
@@ -28,21 +26,7 @@ public class BukkitKenCommand extends Command {
@Override
public boolean execute(@NotNull CommandSender sender, @NotNull String commandLabel, @NotNull String[] args) {
- var parseResult = commandParser.parseLiteral(rootNode, sender, args);
- if (parseResult instanceof ParseResult.Success) {
- var ctx = new CommandContext<>(sender, ((ParseResult.Success) parseResult).getParsedArguments());
- assert parseResult.getLiteral().getExecutor() != null;
- try {
- var result = parseResult.getLiteral().getExecutor().execute(ctx);
- if (result.isError()) {
- sender.sendMessage("§c" + result.getError());
- }
- } catch (Exception e) {
- sender.sendMessage("§4§lERROR " + e.getMessage());
- }
- } else {
- sender.sendMessage("§c" + parseResult.getMessage());
- }
+ commandHandler.invoke(rootNode, sender, args);
return true;
}
}
diff --git a/kencommandapi-bukkit/src/main/java/de/kentoj/kencommandapi/CommandAPI.java b/kencommandapi-bukkit/src/main/java/de/kentoj/kencommandapi/CommandAPI.java
index 8e57675..2671fa2 100644
--- a/kencommandapi-bukkit/src/main/java/de/kentoj/kencommandapi/CommandAPI.java
+++ b/kencommandapi-bukkit/src/main/java/de/kentoj/kencommandapi/CommandAPI.java
@@ -1,27 +1,50 @@
package de.kentoj.kencommandapi;
-import de.kentoj.kencommandapi.api.CommandNode;
-import de.kentoj.kencommandapi.internal.parser.CommandParser;
+import de.kentoj.kencommandapi.api.CommandHandler;
+import de.kentoj.kencommandapi.api.node.RootCommandNode;
+import de.kentoj.kencommandapi.api.parser.CommandParser;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.command.SimpleCommandMap;
import org.bukkit.permissions.Permissible;
+import org.bukkit.plugin.Plugin;
+import org.jetbrains.annotations.Nullable;
import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.List;
public class CommandAPI {
- private final CommandParser commandParser = new CommandParser<>(Permissible::hasPermission);
+ private final CommandHandler commandHandler;
+ private final List> registeredNodes = new ArrayList<>();
+ private final SimpleCommandMap commandMap;
+
+ public CommandAPI(Plugin plugin) {
+ this.commandHandler = new CommandHandler<>(new CommandParser<>(Permissible::hasPermission),
+ CommandSender::sendMessage, Permissible::hasPermission);
+ Bukkit.getPluginManager().registerEvents(new AsyncTabCompleter(this, commandHandler), plugin);
- public void register(CommandNode rootNode) {
- SimpleCommandMap commandMap;
try {
Field field = Bukkit.getPluginManager().getClass().getDeclaredField("commandMap");
field.setAccessible(true);
commandMap = (SimpleCommandMap) field.get(Bukkit.getPluginManager());
} catch (NoSuchFieldException | IllegalAccessException e) {
- throw new IllegalStateException("failed registering command ", e);
+ throw new IllegalStateException("failed getting command map", e);
}
- commandMap.register(rootNode.getNames()[0], new BukkitKenCommand(commandParser, rootNode));
+ }
+
+ @Nullable RootCommandNode getRegistered(String name) {
+ for (var node : registeredNodes) {
+ for (String nodeName : node.getNames()) {
+ if (nodeName.equals(name)) return node;
+ }
+ }
+ return null;
+ }
+
+ public void register(RootCommandNode rootNode) {
+ registeredNodes.add(rootNode);
+ commandMap.register(rootNode.getNames()[0], new BukkitKenCommand(commandHandler, rootNode));
}
}
diff --git a/kencommandapi-bukkit/src/main/java/de/kentoj/kencommandapi/suggestionprovider/PlayerSuggestionProvider.java b/kencommandapi-bukkit/src/main/java/de/kentoj/kencommandapi/suggestionprovider/PlayerSuggestionProvider.java
index b70f287..e62c81f 100644
--- a/kencommandapi-bukkit/src/main/java/de/kentoj/kencommandapi/suggestionprovider/PlayerSuggestionProvider.java
+++ b/kencommandapi-bukkit/src/main/java/de/kentoj/kencommandapi/suggestionprovider/PlayerSuggestionProvider.java
@@ -1,21 +1,19 @@
package de.kentoj.kencommandapi.suggestionprovider;
-import de.kentoj.kencommandapi.api.invocation.CommandContext;
import de.kentoj.kencommandapi.api.suggestion.SuggestionProvider;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
-import java.util.Set;
+import java.util.List;
import java.util.concurrent.CompletableFuture;
-import java.util.stream.Collectors;
public class PlayerSuggestionProvider implements SuggestionProvider {
@Override
- public CompletableFuture> suggest(CommandContext context) {
+ public CompletableFuture> suggest(CommandSender __) {
var list = Bukkit.getOnlinePlayers().stream()
.map(Player::getName)
- .collect(Collectors.toSet());
+ .toList();
return CompletableFuture.completedFuture(list);
}
}
diff --git a/kencommandapi-bukkit/src/main/java/de/kentoj/kencommandapi/type/OfflinePlayerArgumentType.java b/kencommandapi-bukkit/src/main/java/de/kentoj/kencommandapi/type/OfflinePlayerArgumentType.java
index 1e2b423..1b74400 100644
--- a/kencommandapi-bukkit/src/main/java/de/kentoj/kencommandapi/type/OfflinePlayerArgumentType.java
+++ b/kencommandapi-bukkit/src/main/java/de/kentoj/kencommandapi/type/OfflinePlayerArgumentType.java
@@ -26,10 +26,10 @@ public class OfflinePlayerArgumentType implements ArgumentType {
@Override
public Player parseInput(String string) {
var player = Bukkit.getPlayer(string);
- if (player == null) throw new IllegalArgumentException("Player not online -- " + string);
+ if (player == null) throw new IllegalArgumentException("player not online");
return player;
}
diff --git a/kencommandapi-core/build.gradle.kts b/kencommandapi-core/build.gradle.kts
index 0eddbf2..0a2ac13 100644
--- a/kencommandapi-core/build.gradle.kts
+++ b/kencommandapi-core/build.gradle.kts
@@ -6,6 +6,14 @@ plugins {
id("de.kentoj.scrow.java-library")
}
+repositories {
+ mavenCentral()
+}
+
+dependencies {
+ api("net.kyori:adventure-api:5.2.0")
+}
+
publishing {
publications {
create("KenCommandAPI") {
diff --git a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/CommandHandler.java b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/CommandHandler.java
new file mode 100644
index 0000000..6f810aa
--- /dev/null
+++ b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/CommandHandler.java
@@ -0,0 +1,69 @@
+package de.kentoj.kencommandapi.api;
+
+import de.kentoj.kencommandapi.api.invocation.CommandContext;
+import de.kentoj.kencommandapi.api.node.CommandNode;
+import de.kentoj.kencommandapi.api.node.RootCommandNode;
+import de.kentoj.kencommandapi.api.parser.CommandParser;
+import de.kentoj.kencommandapi.api.parser.ParseResult;
+import de.kentoj.kencommandapi.api.platform.HasPermissionMethod;
+import de.kentoj.kencommandapi.api.platform.SendMessageMethod;
+import lombok.RequiredArgsConstructor;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+
+@RequiredArgsConstructor
+public class CommandHandler {
+
+ private final CommandParser commandParser;
+ private final SendMessageMethod sendMessageMethod;
+ private final HasPermissionMethod hasPermissionMethod;
+
+ public void invoke(RootCommandNode rootNode, T sender, String[] args) {
+ var parseResult = commandParser.parseLiteral(rootNode, sender, args, true);
+ if (!(parseResult instanceof ParseResult.Success)) {
+ sendMessageMethod.send(sender, rootNode.getMessageStyle().err(parseResult.getMessage()));
+ return;
+ }
+
+ var ctx = new CommandContext<>(sender, ((ParseResult.Success) parseResult).getParsedArguments());
+ assert parseResult.getLiteral().getExecutor() != null;
+ parseResult.getLiteral().getExecutor().execute(ctx).whenComplete((result, ex) -> {
+ if (ex != null) {
+ sendMessageMethod.send(sender, rootNode.getMessageStyle().exception(ex));
+ }
+ result.ifFailure(failure ->
+ sendMessageMethod.send(sender, rootNode.getMessageStyle().err(failure))
+ );
+ });
+ }
+
+ public CompletableFuture> getSuggestions(RootCommandNode rootNode, T sender, String[] args) {
+ var parseResult = commandParser.parseLiteral(rootNode, sender, args, false);
+
+ if (parseResult instanceof ParseResult.ArgumentExpected) {
+ var arg = ((ParseResult.ArgumentExpected) parseResult).getArgument();
+ return arg.getSuggestionProvider().suggest(sender);
+ }
+
+ String filter = null;
+ if (parseResult instanceof ParseResult.UnknownLiteral)
+ filter = ((ParseResult.UnknownLiteral) parseResult).getName();
+ else if (parseResult instanceof ParseResult.NoPermission)
+ filter = ((ParseResult.NoPermission) parseResult).getName();
+ return getSuggestions(sender, parseResult.getLiteral(), filter);
+ }
+
+ private CompletableFuture> getSuggestions(T sender, CommandNode literal, @Nullable String filter) {
+ var stream = literal.getLiterals().stream()
+ .filter(lit -> lit.getPermission() != null && hasPermissionMethod.hasPermission(sender, lit.getPermission()))
+ .map(CommandNode::getNames)
+ .flatMap(Arrays::stream);
+ if (filter != null)
+ stream = stream.filter(f -> f.toLowerCase().contains(filter.toLowerCase()));
+
+ return CompletableFuture.completedFuture(stream.toList());
+ }
+}
diff --git a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/invocation/CommandExecutor.java b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/invocation/CommandExecutor.java
index f377756..ad4e900 100644
--- a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/invocation/CommandExecutor.java
+++ b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/invocation/CommandExecutor.java
@@ -1,6 +1,10 @@
package de.kentoj.kencommandapi.api.invocation;
+import com.leakyabstractions.result.api.Result;
+
+import java.util.concurrent.CompletableFuture;
+
public interface CommandExecutor {
- Result execute(CommandContext ctx);
+ CompletableFuture> execute(CommandContext ctx);
}
diff --git a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/invocation/Result.java b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/invocation/Result.java
deleted file mode 100644
index 774a286..0000000
--- a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/invocation/Result.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package de.kentoj.kencommandapi.api.invocation;
-
-import lombok.Getter;
-import lombok.RequiredArgsConstructor;
-import org.jetbrains.annotations.Nullable;
-
-@RequiredArgsConstructor
-@Getter
-public class Result {
-
- public static Result success() {
- return new Result(null);
- }
-
- public static Result error(String error) {
- return new Result(error);
- }
-
- private final @Nullable String error;
-
- public boolean isError() {
- return error != null;
- }
-}
diff --git a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/CommandNode.java b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/node/CommandNode.java
similarity index 82%
rename from kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/CommandNode.java
rename to kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/node/CommandNode.java
index 322819e..4b956b8 100644
--- a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/CommandNode.java
+++ b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/node/CommandNode.java
@@ -1,8 +1,10 @@
-package de.kentoj.kencommandapi.api;
+package de.kentoj.kencommandapi.api.node;
+import de.kentoj.kencommandapi.api.platform.MessageStyle;
import de.kentoj.kencommandapi.api.argument.CommandArgument;
import de.kentoj.kencommandapi.api.invocation.CommandExecutor;
import de.kentoj.kencommandapi.internal.CommandNodeImpl;
+import de.kentoj.kencommandapi.internal.RootCommandNodeImpl;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -14,6 +16,10 @@ public interface CommandNode {
return new CommandNodeImpl<>(name);
}
+ static RootCommandNode rootNode(MessageStyle messageStyle, String...name) {
+ return new RootCommandNodeImpl<>(messageStyle, name);
+ }
+
@NotNull String[] getNames();
void setDescription(@Nullable String description);
diff --git a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/node/RootCommandNode.java b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/node/RootCommandNode.java
new file mode 100644
index 0000000..68acae4
--- /dev/null
+++ b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/node/RootCommandNode.java
@@ -0,0 +1,10 @@
+package de.kentoj.kencommandapi.api.node;
+
+import de.kentoj.kencommandapi.api.platform.MessageStyle;
+
+public interface RootCommandNode extends CommandNode {
+
+ MessageStyle getMessageStyle();
+
+ void setMessageStyle(MessageStyle messageStyle);
+}
diff --git a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/internal/parser/CommandParser.java b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/parser/CommandParser.java
similarity index 53%
rename from kencommandapi-core/src/main/java/de/kentoj/kencommandapi/internal/parser/CommandParser.java
rename to kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/parser/CommandParser.java
index 18ea798..3f7a36a 100644
--- a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/internal/parser/CommandParser.java
+++ b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/parser/CommandParser.java
@@ -1,8 +1,12 @@
-package de.kentoj.kencommandapi.internal.parser;
+package de.kentoj.kencommandapi.api.parser;
-import de.kentoj.kencommandapi.api.CommandNode;
+import com.leakyabstractions.result.api.Result;
+import com.leakyabstractions.result.core.Results;
import de.kentoj.kencommandapi.api.argument.CommandArgument;
import de.kentoj.kencommandapi.api.invocation.ParsedArgument;
+import de.kentoj.kencommandapi.api.platform.HasPermissionMethod;
+import de.kentoj.kencommandapi.api.node.CommandNode;
+import de.kentoj.kencommandapi.api.node.RootCommandNode;
import de.kentoj.kencommandapi.internal.ParsedArgumentImpl;
import lombok.RequiredArgsConstructor;
@@ -14,28 +18,25 @@ import java.util.Map;
@RequiredArgsConstructor
public class CommandParser {
- private final PermissionChecker hasPermissionMethod;
+ private final HasPermissionMethod hasPermissionMethod;
- public ParseResult parseLiteral(CommandNode rootNode, T sender, String[] args) {
- var cur = rootNode;
+ public ParseResult parseLiteral(RootCommandNode rootNode, T sender, String[] args, boolean parseArgs) {
+ CommandNode cur = rootNode;
final Iterator argStrIter = Arrays.stream(args).iterator();
final Map> parsedArguments = new HashMap<>();
while (argStrIter.hasNext()) {
for (var nodeArg : cur.getArguments()) {
- ParsedArgument parsed;
- try {
- parsed = parseArgument(argStrIter, sender, nodeArg);
- } catch (IllegalArgumentException ex) {
- return new ParseResult.IllegalArgument<>(cur, ex.getMessage());
- }
- if (parsed == null) return new ParseResult.IncompleteArgument<>(cur, nodeArg);
- parsedArguments.put(nodeArg.getId(), parsed);
+ var parsed = parseArgument(rootNode, nodeArg, argStrIter, sender, !parseArgs);
+ if (parsed.hasFailure())
+ return parsed.getFailure().orElseThrow();
+ if (parseArgs)
+ parsedArguments.put(nodeArg.getId(), parsed.getSuccess().orElseThrow());
}
if (!argStrIter.hasNext()) {
if (cur.getExecutor() == null)
- return new ParseResult.IncompleteCommand<>(cur);
+ return new ParseResult.LiteralExpected<>(cur);
return new ParseResult.Success<>(cur, parsedArguments);
}
@@ -48,23 +49,40 @@ public class CommandParser {
cur = nextNode;
}
- throw new RuntimeException("code reached unexpected line");
+ if (cur.getExecutor() == null)
+ return new ParseResult.LiteralExpected<>(cur);
+ return new ParseResult.Success<>(cur, parsedArguments);
}
@SuppressWarnings("unchecked")
- private ParsedArgument parseArgument(Iterator args, T sender, CommandArgument nodeArg) {
+ private Result, ParseResult> parseArgument(
+ CommandNode node,
+ CommandArgument nodeArg,
+ Iterator args,
+ T sender,
+ boolean fakeParse
+ ) {
Object value;
if (args.hasNext()) {
var argStr = getRawArgument(args, nodeArg);
- value = nodeArg.getType().parseInput(argStr);
+ if (fakeParse) {
+ value = null;
+ } else {
+ try {
+ value = nodeArg.getType().parseInput(argStr);
+ } catch (IllegalArgumentException ex) {
+ return Results.failure(new ParseResult.IllegalArgument<>(node, nodeArg, ex.getMessage(), argStr));
+ }
+ }
} else {
if (nodeArg.getDefaultValueProvider() == null)
- return null;
+ return Results.failure(new ParseResult.ArgumentExpected<>(node, nodeArg));
value = nodeArg.getDefaultValueProvider().apply(sender);
}
- return new ParsedArgumentImpl<>(value, (CommandArgument) nodeArg);
+ var parsedArg = new ParsedArgumentImpl<>(value, (CommandArgument) nodeArg);
+ return Results.success(parsedArg);
}
private String getRawArgument(Iterator args, CommandArgument arg) {
@@ -80,4 +98,4 @@ public class CommandParser {
return rawArgument.toString();
}
-}
+}
\ No newline at end of file
diff --git a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/internal/parser/ParseResult.java b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/parser/ParseResult.java
similarity index 58%
rename from kencommandapi-core/src/main/java/de/kentoj/kencommandapi/internal/parser/ParseResult.java
rename to kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/parser/ParseResult.java
index c2a133a..4453cf3 100644
--- a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/internal/parser/ParseResult.java
+++ b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/parser/ParseResult.java
@@ -1,17 +1,17 @@
-package de.kentoj.kencommandapi.internal.parser;
+package de.kentoj.kencommandapi.api.parser;
-import de.kentoj.kencommandapi.api.CommandNode;
import de.kentoj.kencommandapi.api.argument.CommandArgument;
import de.kentoj.kencommandapi.api.invocation.ParsedArgument;
+import de.kentoj.kencommandapi.api.node.CommandNode;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import java.util.Map;
-public abstract sealed class ParseResult permits ParseResult.IllegalArgument, ParseResult.IncompleteArgument,
- ParseResult.IncompleteCommand, ParseResult.NoPermission, ParseResult.Success, ParseResult.UnknownLiteral {
+public abstract sealed class ParseResult permits ParseResult.IllegalArgument, ParseResult.ArgumentExpected,
+ ParseResult.LiteralExpected, ParseResult.NoPermission, ParseResult.Success, ParseResult.UnknownLiteral {
- /**
+ /**
* @return last successfully literal
*/
public abstract CommandNode getLiteral();
@@ -19,25 +19,27 @@ public abstract sealed class ParseResult permits ParseResult.IllegalArgument,
public abstract String getMessage();
@RequiredArgsConstructor
- public static final class IncompleteArgument extends ParseResult {
+ public static final class ArgumentExpected extends ParseResult {
@Getter
private final CommandNode literal;
+ @Getter
private final CommandArgument argument;
@Override
public String getMessage() {
- return "Missing value for argument: " + argument.getId();
+ return "Argument expected: " + argument.getId();
}
}
@RequiredArgsConstructor
public static final class UnknownLiteral extends ParseResult {
private final CommandNode previousLiteral;
+ @Getter
private final String name;
@Override
public String getMessage() {
- return "Unknown literal: " + name;
+ return "Unknown subcommand: " + name;
}
@Override
@@ -47,13 +49,13 @@ public abstract sealed class ParseResult permits ParseResult.IllegalArgument,
}
@RequiredArgsConstructor
- public static final class IncompleteCommand extends ParseResult {
+ public static final class LiteralExpected extends ParseResult {
@Getter
private final CommandNode literal;
@Override
public String getMessage() {
- return "Incomplete command";
+ return "Subcommand expected";
}
}
@@ -74,11 +76,12 @@ public abstract sealed class ParseResult permits ParseResult.IllegalArgument,
public static final class NoPermission extends ParseResult {
@Getter
private final CommandNode literal;
+ @Getter
private final String name;
@Override
public String getMessage() {
- return "Unknown literal: " + name;
+ return "Unknown subcommand: " + name;
}
}
@@ -86,7 +89,26 @@ public abstract sealed class ParseResult permits ParseResult.IllegalArgument,
public static final class IllegalArgument extends ParseResult {
@Getter
private final CommandNode literal;
- @Getter
+ private final CommandArgument argument;
private final String message;
+ /**
+ * Input passed to the parser
+ */
+ @Getter
+ private final String argStr;
+
+ public IllegalArgument(CommandNode literal, CommandArgument argument, Throwable throwable, String argStr) {
+ this.literal = literal;
+ this.argument = argument;
+ this.argStr = argStr;
+ while (throwable.getMessage() == null && throwable.getCause() != null)
+ throwable = throwable.getCause();
+ message = throwable.getMessage();
+ }
+
+ @Override
+ public String getMessage() {
+ return "Illegal argument '" + argStr + "' for " + argument.getId() + ":" + message;
+ }
}
}
diff --git a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/platform/HasPermissionMethod.java b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/platform/HasPermissionMethod.java
new file mode 100644
index 0000000..1e3fd67
--- /dev/null
+++ b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/platform/HasPermissionMethod.java
@@ -0,0 +1,6 @@
+package de.kentoj.kencommandapi.api.platform;
+
+@FunctionalInterface
+public interface HasPermissionMethod {
+ boolean hasPermission(T sender, String permission);
+}
diff --git a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/platform/MessageStyle.java b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/platform/MessageStyle.java
new file mode 100644
index 0000000..dc246da
--- /dev/null
+++ b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/platform/MessageStyle.java
@@ -0,0 +1,32 @@
+package de.kentoj.kencommandapi.api.platform;
+
+import net.kyori.adventure.text.Component;
+
+public interface MessageStyle {
+
+ MessageStyle PLAIN = new PlainMessageStyle();
+
+ Component ok(Component msg);
+
+ Component err(Component msg);
+
+ Component exception(Component msg);
+
+ default Component ok(String msg) {
+ return ok(Component.text(msg));
+ }
+
+ default Component err(String msg) {
+ return err(Component.text(msg));
+ }
+
+ default Component exception(String msg) {
+ return exception(Component.text(msg));
+ }
+
+ default Component exception(Throwable ex) {
+ while (ex.getMessage() == null && ex.getCause() != null)
+ ex = ex.getCause();
+ return exception(ex.getMessage());
+ }
+}
diff --git a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/platform/PlainMessageStyle.java b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/platform/PlainMessageStyle.java
new file mode 100644
index 0000000..ef35a0c
--- /dev/null
+++ b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/platform/PlainMessageStyle.java
@@ -0,0 +1,20 @@
+package de.kentoj.kencommandapi.api.platform;
+
+import net.kyori.adventure.text.Component;
+
+public class PlainMessageStyle implements MessageStyle {
+ @Override
+ public Component ok(Component msg) {
+ return msg;
+ }
+
+ @Override
+ public Component err(Component msg) {
+ return msg;
+ }
+
+ @Override
+ public Component exception(Component msg) {
+ return Component.text("ERROR: ").append(msg);
+ }
+}
diff --git a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/platform/SendMessageMethod.java b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/platform/SendMessageMethod.java
new file mode 100644
index 0000000..b696317
--- /dev/null
+++ b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/platform/SendMessageMethod.java
@@ -0,0 +1,8 @@
+package de.kentoj.kencommandapi.api.platform;
+
+import net.kyori.adventure.text.Component;
+
+@FunctionalInterface
+public interface SendMessageMethod {
+ void send(T to, Component msg);
+}
diff --git a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/suggestion/NoSuggestionProvider.java b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/suggestion/NoSuggestionProvider.java
index 1cff474..d4c4dcf 100644
--- a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/suggestion/NoSuggestionProvider.java
+++ b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/suggestion/NoSuggestionProvider.java
@@ -1,15 +1,13 @@
package de.kentoj.kencommandapi.api.suggestion;
-import de.kentoj.kencommandapi.api.invocation.CommandContext;
-
import java.util.Collections;
-import java.util.Set;
+import java.util.List;
import java.util.concurrent.CompletableFuture;
public class NoSuggestionProvider implements SuggestionProvider {
@Override
- public CompletableFuture> suggest(CommandContext __) {
- return CompletableFuture.completedFuture(Collections.emptySet());
+ public CompletableFuture> suggest(T __) {
+ return CompletableFuture.completedFuture(Collections.emptyList());
}
}
diff --git a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/suggestion/SuggestionProvider.java b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/suggestion/SuggestionProvider.java
index c0a79ef..64ae4ef 100644
--- a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/suggestion/SuggestionProvider.java
+++ b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/suggestion/SuggestionProvider.java
@@ -1,11 +1,12 @@
package de.kentoj.kencommandapi.api.suggestion;
-import de.kentoj.kencommandapi.api.invocation.CommandContext;
-
-import java.util.Set;
+import java.util.List;
import java.util.concurrent.CompletableFuture;
public interface SuggestionProvider {
- CompletableFuture> suggest(CommandContext context);
+ /**
+ * @return an unfiltered(no permission checks or filtering by input) list of strings that may be used for the argument
+ */
+ CompletableFuture> suggest(T sender);
}
diff --git a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/internal/CommandArgumentImpl.java b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/internal/CommandArgumentImpl.java
index caa0f91..88bf012 100644
--- a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/internal/CommandArgumentImpl.java
+++ b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/internal/CommandArgumentImpl.java
@@ -6,6 +6,7 @@ import de.kentoj.kencommandapi.api.suggestion.SuggestionProvider;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
+import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.function.Function;
@@ -22,7 +23,13 @@ public class CommandArgumentImpl implements CommandArgument {
@Setter
private @Nullable Function defaultValueProvider;
- @Getter
@Setter
private @Nullable SuggestionProvider suggestionProvider;
+
+ @Override
+ public @NotNull SuggestionProvider getSuggestionProvider() {
+ if (suggestionProvider == null)
+ return getType().defaultSuggestionProvider();
+ return this.suggestionProvider;
+ }
}
diff --git a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/internal/CommandNodeImpl.java b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/internal/CommandNodeImpl.java
index e03e2c7..e8f911c 100644
--- a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/internal/CommandNodeImpl.java
+++ b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/internal/CommandNodeImpl.java
@@ -1,6 +1,6 @@
package de.kentoj.kencommandapi.internal;
-import de.kentoj.kencommandapi.api.CommandNode;
+import de.kentoj.kencommandapi.api.node.CommandNode;
import de.kentoj.kencommandapi.api.argument.CommandArgument;
import de.kentoj.kencommandapi.api.invocation.CommandExecutor;
import lombok.Getter;
@@ -65,6 +65,7 @@ public class CommandNodeImpl implements CommandNode {
}
private boolean isGreedy() {
+ if (arguments.isEmpty()) return false;
var arg = arguments.getLast();
return arg != null && arg.getType().isGreedy();
}
diff --git a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/internal/RootCommandNodeImpl.java b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/internal/RootCommandNodeImpl.java
new file mode 100644
index 0000000..45112fa
--- /dev/null
+++ b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/internal/RootCommandNodeImpl.java
@@ -0,0 +1,17 @@
+package de.kentoj.kencommandapi.internal;
+
+import de.kentoj.kencommandapi.api.platform.MessageStyle;
+import de.kentoj.kencommandapi.api.node.RootCommandNode;
+import lombok.Getter;
+import lombok.Setter;
+
+public class RootCommandNodeImpl extends CommandNodeImpl implements RootCommandNode {
+ @Setter
+ @Getter
+ private MessageStyle messageStyle;
+
+ public RootCommandNodeImpl(MessageStyle messageStyle, String[] names) {
+ super(names);
+ this.messageStyle = messageStyle;
+ }
+}
diff --git a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/internal/parser/PermissionChecker.java b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/internal/parser/PermissionChecker.java
deleted file mode 100644
index 9b4ee55..0000000
--- a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/internal/parser/PermissionChecker.java
+++ /dev/null
@@ -1,6 +0,0 @@
-package de.kentoj.kencommandapi.internal.parser;
-
-@FunctionalInterface
-public interface PermissionChecker {
- boolean hasPermission(T sender, String permission);
-}
diff --git a/kencommandapi-velocity/src/main/java/de/kentoj/kencommandapi/CommandAPI.java b/kencommandapi-velocity/src/main/java/de/kentoj/kencommandapi/CommandAPI.java
index be08240..07b5519 100644
--- a/kencommandapi-velocity/src/main/java/de/kentoj/kencommandapi/CommandAPI.java
+++ b/kencommandapi-velocity/src/main/java/de/kentoj/kencommandapi/CommandAPI.java
@@ -2,8 +2,9 @@ package de.kentoj.kencommandapi;
import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.proxy.ProxyServer;
-import de.kentoj.kencommandapi.api.CommandNode;
-import de.kentoj.kencommandapi.internal.parser.CommandParser;
+import de.kentoj.kencommandapi.api.CommandHandler;
+import de.kentoj.kencommandapi.api.node.RootCommandNode;
+import de.kentoj.kencommandapi.api.parser.CommandParser;
import lombok.RequiredArgsConstructor;
import java.util.Arrays;
@@ -12,13 +13,17 @@ import java.util.Arrays;
public class CommandAPI {
private final ProxyServer server;
- private final CommandParser commandParser = new CommandParser<>(CommandSource::hasPermission);
+ private final CommandHandler commandHandler = new CommandHandler<>(
+ new CommandParser<>(CommandSource::hasPermission),
+ CommandSource::sendMessage,
+ CommandSource::hasPermission
+ );
- public void register(CommandNode rootNode) {
+ public void register(RootCommandNode rootNode) {
var cm = server.getCommandManager();
var meta = cm.metaBuilder(rootNode.getNames()[0])
.aliases(Arrays.copyOfRange(rootNode.getNames(), 1, rootNode.getNames().length - 1))
.build();
- cm.register(meta, new VelocityCommand(commandParser, rootNode));
+ cm.register(meta, new VelocityCommand(commandHandler, rootNode));
}
}
diff --git a/kencommandapi-velocity/src/main/java/de/kentoj/kencommandapi/VelocityCommand.java b/kencommandapi-velocity/src/main/java/de/kentoj/kencommandapi/VelocityCommand.java
index 27241ec..e5a4f3c 100644
--- a/kencommandapi-velocity/src/main/java/de/kentoj/kencommandapi/VelocityCommand.java
+++ b/kencommandapi-velocity/src/main/java/de/kentoj/kencommandapi/VelocityCommand.java
@@ -2,42 +2,22 @@ package de.kentoj.kencommandapi;
import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.command.SimpleCommand;
-import de.kentoj.kencommandapi.api.CommandNode;
-import de.kentoj.kencommandapi.api.invocation.CommandContext;
-import de.kentoj.kencommandapi.api.invocation.CommandExecutor;
-import de.kentoj.kencommandapi.internal.parser.CommandParser;
-import de.kentoj.kencommandapi.internal.parser.ParseResult;
+import de.kentoj.kencommandapi.api.CommandHandler;
+import de.kentoj.kencommandapi.api.node.RootCommandNode;
import lombok.RequiredArgsConstructor;
-import net.kyori.adventure.text.Component;
-import net.kyori.adventure.text.format.NamedTextColor;
-import net.kyori.adventure.text.format.TextDecoration;
+
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
@RequiredArgsConstructor
public class VelocityCommand implements SimpleCommand {
- private final CommandParser commandParser;
- private final CommandNode rootNode;
+ private final CommandHandler commandHandler;
+ private final RootCommandNode rootNode;
@Override
public void execute(Invocation invocation) {
- var parseResult = commandParser.parseLiteral(rootNode, invocation.source(), invocation.arguments());
- if (parseResult instanceof ParseResult.Success) {
- var ctx = new CommandContext<>(invocation.source(), ((ParseResult.Success) parseResult).getParsedArguments());
- assert parseResult.getLiteral().getExecutor() != null;
-
- try {
- var result = parseResult.getLiteral().getExecutor().execute(ctx);
- if (result.getError() != null)
- invocation.source().sendMessage(Component.text(result.getError()).color(NamedTextColor.RED));
- } catch (Exception e) {
- invocation.source().sendMessage(Component.text("ERROR ")
- .append(Component.text(e.getMessage()))
- .color(NamedTextColor.DARK_RED)
- .decorate(TextDecoration.BOLD));
- }
- } else {
- invocation.source().sendMessage(Component.text(parseResult.getMessage()).color(NamedTextColor.RED));
- }
+ commandHandler.invoke(rootNode, invocation.source(), invocation.arguments());
}
@Override
@@ -45,4 +25,9 @@ public class VelocityCommand implements SimpleCommand {
if (rootNode.getPermission() == null) return true;
return ctx.source().hasPermission(rootNode.getPermission());
}
-}
+
+ @Override
+ public CompletableFuture> suggestAsync(Invocation invocation) {
+ return commandHandler.getSuggestions(rootNode, invocation.source(), invocation.arguments());
+ }
+}
\ No newline at end of file
diff --git a/kencommandapi-velocity/src/main/java/de/kentoj/kencommandapi/suggestionprovider/PlayerSuggestionProvider.java b/kencommandapi-velocity/src/main/java/de/kentoj/kencommandapi/suggestionprovider/PlayerSuggestionProvider.java
index a7ac325..f925463 100644
--- a/kencommandapi-velocity/src/main/java/de/kentoj/kencommandapi/suggestionprovider/PlayerSuggestionProvider.java
+++ b/kencommandapi-velocity/src/main/java/de/kentoj/kencommandapi/suggestionprovider/PlayerSuggestionProvider.java
@@ -3,13 +3,11 @@ package de.kentoj.kencommandapi.suggestionprovider;
import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.proxy.ProxyServer;
-import de.kentoj.kencommandapi.api.invocation.CommandContext;
import de.kentoj.kencommandapi.api.suggestion.SuggestionProvider;
import lombok.RequiredArgsConstructor;
-import java.util.Set;
+import java.util.List;
import java.util.concurrent.CompletableFuture;
-import java.util.stream.Collectors;
@RequiredArgsConstructor
public class PlayerSuggestionProvider implements SuggestionProvider {
@@ -17,10 +15,10 @@ public class PlayerSuggestionProvider implements SuggestionProvider> suggest(CommandContext context) {
+ public CompletableFuture> suggest(CommandSource __) {
var list = proxyServer.getAllPlayers().stream()
.map(Player::getUsername)
- .collect(Collectors.toSet());
+ .toList();
return CompletableFuture.completedFuture(list);
}
}
diff --git a/kencommandapi-velocity/src/main/java/de/kentoj/kencommandapi/type/PlayerArgumentType.java b/kencommandapi-velocity/src/main/java/de/kentoj/kencommandapi/type/PlayerArgumentType.java
index 335ebb1..3ba3a7e 100644
--- a/kencommandapi-velocity/src/main/java/de/kentoj/kencommandapi/type/PlayerArgumentType.java
+++ b/kencommandapi-velocity/src/main/java/de/kentoj/kencommandapi/type/PlayerArgumentType.java
@@ -16,7 +16,7 @@ public class PlayerArgumentType implements ArgumentType {
@Override
public Player parseInput(String string) {
var player = server.getPlayer(string).orElse(null);
- if (player == null) throw new IllegalArgumentException("Player not online -- " + string);
+ if (player == null) throw new IllegalArgumentException("Player not online");
return player;
}