This commit is contained in:
kento2 2026-06-29 18:49:42 +02:00
parent 254af80fde
commit 72f7625b19
53 changed files with 679 additions and 756 deletions

View file

@ -1,62 +1,8 @@
val scrowToken = providers.gradleProperty("scrowMavenToken")
.orElse(providers.environmentVariable("SCROW_MAVEN_TOKEN")).get()
plugins {
id("java")
id("io.freefair.lombok") version "9.2.0" apply false
}
group = "de.kentoj.scrow" group = "de.kentoj.scrow"
version = "0.9" version = "0.10"
fun RepositoryHandler.scrowRepo() = maven {
name = "scrow"
url = uri("http://forge.kentoj.de/api/packages/scrow/maven")
isAllowInsecureProtocol = true
credentials(HttpHeaderCredentials::class) {
name = "Authorization"
value = "token $scrowToken"
}
authentication {
create<HttpHeaderAuthentication>("header")
}
}
subprojects { subprojects {
apply(plugin = "java") group = rootProject.group
apply(plugin = "maven-publish") version = rootProject.version
apply(plugin = "io.freefair.lombok")
java {
withSourcesJar()
withJavadocJar()
}
tasks.withType<Javadoc>().configureEach {
(options as StandardJavadocDocletOptions).addStringOption("Xdoclint:all,-missing", "-quiet")
}
repositories {
mavenCentral()
scrowRepo()
}
dependencies {
// https://mvnrepository.com/artifact/org.jetbrains/annotations
implementation("org.jetbrains:annotations:26.0.2-1")
}
extensions.configure<PublishingExtension> {
publications {
create<MavenPublication>("KenCommandAPI") {
groupId = rootProject.group.toString()
version = rootProject.version.toString()
artifactId = project.name
from(components["java"])
}
}
repositories {
scrowRepo()
}
}
} }

View file

@ -1,11 +1,11 @@
plugins { plugins {
id("java") id("java")
id("java-library") id("maven-publish")
id("de.kentoj.scrow.scrow-repository")
id("de.kentoj.scrow.base-dependencies")
id("de.kentoj.scrow.java-library")
} }
group = "de.kentoj"
version = rootProject.version
repositories { repositories {
maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots/") { maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots/") {
name = "spigotmc-repo" name = "spigotmc-repo"
@ -16,3 +16,14 @@ dependencies {
api(project(":kencommandapi-core")) api(project(":kencommandapi-core"))
compileOnly("org.spigotmc:spigot-api:1.21.11-R0.1-SNAPSHOT") compileOnly("org.spigotmc:spigot-api:1.21.11-R0.1-SNAPSHOT")
} }
publishing {
publications {
create<MavenPublication>("KenCommandAPI") {
groupId = rootProject.group.toString()
version = rootProject.version.toString()
artifactId = project.name
from(components["java"])
}
}
}

View file

@ -1,22 +0,0 @@
package de.kentoj.kencommandapi;
import de.kentoj.kencommandapi.api.processing.ParsedArgument;
import de.kentoj.kencommandapi.impl.processing.CommandContextImpl;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.Map;
public class BukkitCommandContext extends CommandContextImpl<CommandSender, BukkitCommandContext> {
BukkitCommandContext(
CommandSender sender,
Map<String, ParsedArgument<CommandSender, BukkitCommandContext, ?>> parsedArguments
) {
super(sender, parsedArguments);
}
public Player getPlayerSender() {
return ((Player) getSender());
}
}

View file

@ -1,7 +1,9 @@
package de.kentoj.kencommandapi; package de.kentoj.kencommandapi;
import de.kentoj.kencommandapi.api.KenCommandApi; import de.kentoj.kencommandapi.api.CommandNode;
import de.kentoj.kencommandapi.api.nodes.CommandNode; import de.kentoj.kencommandapi.api.invocation.CommandContext;
import de.kentoj.kencommandapi.internal.parser.CommandParser;
import de.kentoj.kencommandapi.internal.parser.ParseResult;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@ -10,23 +12,37 @@ import java.util.Arrays;
public class BukkitKenCommand extends Command { public class BukkitKenCommand extends Command {
private final CommandNode<CommandSender, BukkitCommandContext> rootNode; private final CommandNode<CommandSender> rootNode;
private final KenCommandApi<CommandSender, BukkitCommandContext> commandManager; private final CommandParser<CommandSender> commandParser;
protected BukkitKenCommand( protected BukkitKenCommand(
KenCommandApi<CommandSender, BukkitCommandContext> commandManager, CommandParser<CommandSender> commandParser,
CommandNode<CommandSender, BukkitCommandContext> rootNode CommandNode<CommandSender> rootNode
) { ) {
super(rootNode.getNames()[0]); super(rootNode.getNames()[0]);
this.commandParser = commandParser;
this.rootNode = rootNode; this.rootNode = rootNode;
this.commandManager = commandManager;
this.setAliases(Arrays.asList(rootNode.getNames())); this.setAliases(Arrays.asList(rootNode.getNames()));
this.setPermission(rootNode.getPermission()); this.setPermission(rootNode.getPermission());
} }
@Override @Override
public boolean execute(@NotNull CommandSender sender, @NotNull String commandLabel, @NotNull String[] args) { public boolean execute(@NotNull CommandSender sender, @NotNull String commandLabel, @NotNull String[] args) {
commandManager.invoke(rootNode, sender, args); var parseResult = commandParser.parseLiteral(rootNode, sender, args);
return false; if (parseResult instanceof ParseResult.Success<CommandSender>) {
var ctx = new CommandContext<>(sender, ((ParseResult.Success<CommandSender>) 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());
}
return true;
} }
} }

View file

@ -1,45 +0,0 @@
package de.kentoj.kencommandapi;
import de.kentoj.kencommandapi.api.processing.CommandException;
import de.kentoj.kencommandapi.api.processing.ParsedArgument;
import de.kentoj.kencommandapi.api.nodes.CommandNode;
import de.kentoj.kencommandapi.impl.AbstractKenCommandApi;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.command.SimpleCommandMap;
import java.lang.reflect.Field;
import java.util.Map;
public class BukkitKenCommandApi extends AbstractKenCommandApi<CommandSender, BukkitCommandContext> {
@Override
public BukkitCommandContext createContext(
CommandSender sender,
Map<String, ParsedArgument<CommandSender, BukkitCommandContext, ?>> parsedArguments
) {
return new BukkitCommandContext(sender, parsedArguments);
}
@Override
public void sendErrorMessage(CommandSender sender, CommandException ex) {
var cause = (ex.getCause() != null) ? ": " + ex.getCause().getMessage() : "";
sender.sendMessage("§c" + ex.getMessage() + cause);
}
@Override
public boolean hasPermission(CommandSender sender, String permission) {
return sender.hasPermission(permission);
}
public void register(CommandNode<CommandSender, BukkitCommandContext> rootNode) {
try {
Field field = Bukkit.getPluginManager().getClass().getDeclaredField("commandMap");
field.setAccessible(true);
SimpleCommandMap commandMap = (SimpleCommandMap) field.get(Bukkit.getPluginManager());
commandMap.register(rootNode.getNames()[0], new BukkitKenCommand(this, rootNode));
} catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}
}
}

View file

@ -0,0 +1,27 @@
package de.kentoj.kencommandapi;
import de.kentoj.kencommandapi.api.CommandNode;
import de.kentoj.kencommandapi.internal.parser.CommandParser;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.command.SimpleCommandMap;
import org.bukkit.permissions.Permissible;
import java.lang.reflect.Field;
public class CommandAPI {
private final CommandParser<CommandSender> commandParser = new CommandParser<>(Permissible::hasPermission);
public void register(CommandNode<CommandSender> 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);
}
commandMap.register(rootNode.getNames()[0], new BukkitKenCommand(commandParser, rootNode));
}
}

View file

@ -1,7 +1,7 @@
package de.kentoj.kencommandapi.suggestionprovider; package de.kentoj.kencommandapi.suggestionprovider;
import de.kentoj.kencommandapi.BukkitCommandContext; import de.kentoj.kencommandapi.api.invocation.CommandContext;
import de.kentoj.kencommandapi.api.argument.SuggestionProvider; import de.kentoj.kencommandapi.api.suggestion.SuggestionProvider;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@ -10,9 +10,9 @@ import java.util.Set;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors; import java.util.stream.Collectors;
public class PlayerSuggestionProvider implements SuggestionProvider<CommandSender, BukkitCommandContext> { public class PlayerSuggestionProvider implements SuggestionProvider<CommandSender> {
@Override @Override
public CompletableFuture<Set<String>> suggest(BukkitCommandContext context) { public CompletableFuture<Set<String>> suggest(CommandContext<CommandSender> context) {
var list = Bukkit.getOnlinePlayers().stream() var list = Bukkit.getOnlinePlayers().stream()
.map(Player::getName) .map(Player::getName)
.collect(Collectors.toSet()); .collect(Collectors.toSet());

View file

@ -1,10 +1,7 @@
package de.kentoj.kencommandapi.type; package de.kentoj.kencommandapi.type;
import de.kentoj.kencommandapi.BukkitCommandContext;
import de.kentoj.kencommandapi.api.processing.CommandException;
import de.kentoj.kencommandapi.api.argument.ArgumentType; import de.kentoj.kencommandapi.api.argument.ArgumentType;
import de.kentoj.kencommandapi.api.argument.SuggestionProvider; import de.kentoj.kencommandapi.api.suggestion.SuggestionProvider;
import de.kentoj.kencommandapi.api.argument.suggestion.NoSuggestionProvider;
import de.kentoj.kencommandapi.suggestionprovider.PlayerSuggestionProvider; import de.kentoj.kencommandapi.suggestionprovider.PlayerSuggestionProvider;
import lombok.AccessLevel; import lombok.AccessLevel;
import lombok.Getter; import lombok.Getter;
@ -16,7 +13,7 @@ import org.bukkit.command.CommandSender;
import java.util.UUID; import java.util.UUID;
@NoArgsConstructor(access = AccessLevel.PRIVATE) @NoArgsConstructor(access = AccessLevel.PRIVATE)
public class OfflinePlayerArgumentType implements ArgumentType<CommandSender, BukkitCommandContext, OfflinePlayer> { public class OfflinePlayerArgumentType implements ArgumentType<CommandSender, OfflinePlayer> {
@Getter(lazy = true) @Getter(lazy = true)
private static final OfflinePlayerArgumentType instance = new OfflinePlayerArgumentType(); private static final OfflinePlayerArgumentType instance = new OfflinePlayerArgumentType();
@ -29,15 +26,15 @@ public class OfflinePlayerArgumentType implements ArgumentType<CommandSender, Bu
try { try {
player = Bukkit.getOfflinePlayer(UUID.fromString(string)); player = Bukkit.getOfflinePlayer(UUID.fromString(string));
} catch (IllegalArgumentException ex) { } catch (IllegalArgumentException ex) {
throw new CommandException("Not a UUID or online player -- " + string); throw new IllegalArgumentException("Not a UUID or online player -- " + string);
} }
} }
if (!player.hasPlayedBefore() && !player.isOnline()) throw new CommandException("Unknown player -- " + string); if (!player.hasPlayedBefore() && !player.isOnline()) throw new IllegalArgumentException("Unknown player -- " + string);
return player; return player;
} }
@Override @Override
public SuggestionProvider<CommandSender, BukkitCommandContext> defaultSuggestionProvider() { public SuggestionProvider<CommandSender> defaultSuggestionProvider() {
return new PlayerSuggestionProvider(); return new PlayerSuggestionProvider();
} }
} }

View file

@ -1,9 +1,7 @@
package de.kentoj.kencommandapi.type; package de.kentoj.kencommandapi.type;
import de.kentoj.kencommandapi.BukkitCommandContext;
import de.kentoj.kencommandapi.api.argument.ArgumentType; import de.kentoj.kencommandapi.api.argument.ArgumentType;
import de.kentoj.kencommandapi.api.argument.SuggestionProvider; import de.kentoj.kencommandapi.api.suggestion.SuggestionProvider;
import de.kentoj.kencommandapi.api.processing.CommandException;
import de.kentoj.kencommandapi.suggestionprovider.PlayerSuggestionProvider; import de.kentoj.kencommandapi.suggestionprovider.PlayerSuggestionProvider;
import lombok.AccessLevel; import lombok.AccessLevel;
import lombok.Getter; import lombok.Getter;
@ -13,7 +11,7 @@ import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@NoArgsConstructor(access = AccessLevel.PRIVATE) @NoArgsConstructor(access = AccessLevel.PRIVATE)
public class PlayerArgumentType implements ArgumentType<CommandSender, BukkitCommandContext, Player> { public class PlayerArgumentType implements ArgumentType<CommandSender, Player> {
@Getter(lazy = true) @Getter(lazy = true)
private static final PlayerArgumentType instance = new PlayerArgumentType(); private static final PlayerArgumentType instance = new PlayerArgumentType();
@ -21,12 +19,12 @@ public class PlayerArgumentType implements ArgumentType<CommandSender, BukkitCom
@Override @Override
public Player parseInput(String string) { public Player parseInput(String string) {
var player = Bukkit.getPlayer(string); var player = Bukkit.getPlayer(string);
if (player == null) throw new CommandException("Player not online -- " + string); if (player == null) throw new IllegalArgumentException("Player not online -- " + string);
return player; return player;
} }
@Override @Override
public SuggestionProvider<CommandSender, BukkitCommandContext> defaultSuggestionProvider() { public SuggestionProvider<CommandSender> defaultSuggestionProvider() {
return new PlayerSuggestionProvider(); return new PlayerSuggestionProvider();
} }
} }

View file

@ -0,0 +1,18 @@
plugins {
id("java")
id("maven-publish")
id("de.kentoj.scrow.scrow-repository")
id("de.kentoj.scrow.base-dependencies")
id("de.kentoj.scrow.java-library")
}
publishing {
publications {
create<MavenPublication>("KenCommandAPI") {
groupId = rootProject.group.toString()
version = rootProject.version.toString()
artifactId = project.name
from(components["java"])
}
}
}

View file

@ -1,57 +0,0 @@
package de.kentoj.kencommandapi;
import de.kentoj.kencommandapi.api.processing.CommandException;
import de.kentoj.kencommandapi.api.processing.ParsedArgument;
import de.kentoj.kencommandapi.api.types.IntegerArgumentType;
import de.kentoj.kencommandapi.impl.AbstractKenCommandApi;
import de.kentoj.kencommandapi.impl.processing.CommandContextImpl;
import java.io.PrintStream;
import java.util.Map;
public class Main {
private static class BasicCommandContext extends CommandContextImpl<PrintStream, BasicCommandContext> {
public BasicCommandContext(PrintStream sender, Map<String, ParsedArgument<PrintStream, BasicCommandContext, ?>> parsedArguments) {
super(sender, parsedArguments);
}
public void respond(String msg) {
getSender().println("|==================|");
getSender().println(msg);
getSender().println("|==================|");
}
}
public static void main(String[] args) {
var api = new AbstractKenCommandApi<PrintStream, BasicCommandContext>() {
@Override
public BasicCommandContext createContext(PrintStream sender, Map<String, ParsedArgument<PrintStream, BasicCommandContext, ?>> parsedArguments) {
return new BasicCommandContext(sender, parsedArguments);
}
@Override
public void sendErrorMessage(PrintStream printStream, CommandException ex) {
System.err.println(ex.getMessage());
}
@Override
public boolean hasPermission(PrintStream printStream, String permission) {
return true;
}
};
var root = api.createNode("coins");
var addNode = api.createNode("add", "deposit");
addNode.setExecutor(ctx -> {
int amount = ctx.getArg("amount");
ctx.respond("adding " + amount + "$ to your balance");
});
addNode.addArgument(api.createArgument("amount", new IntegerArgumentType<>()));
root.addLiteral(addNode);
api.invoke(root, System.out, new String[]{"add", "100"});
}
}

View file

@ -0,0 +1,56 @@
package de.kentoj.kencommandapi.api;
import de.kentoj.kencommandapi.api.argument.CommandArgument;
import de.kentoj.kencommandapi.api.invocation.CommandExecutor;
import de.kentoj.kencommandapi.internal.CommandNodeImpl;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.Set;
public interface CommandNode<T> {
static <T> CommandNode<T> node(String... name) {
return new CommandNodeImpl<>(name);
}
@NotNull String[] getNames();
void setDescription(@Nullable String description);
@Nullable String getDescription();
/*
* arguments
*/
/**
* @throws IllegalArgumentException if there is already an argument with the same id
* @throws IllegalStateException if a greedy argument was added to the node previously
*/
<V> void addArgument(CommandArgument<T, V> argument);
List<CommandArgument<T, ?>> getArguments();
/*
* literals
*/
void setExecutor(@Nullable CommandExecutor<T> executor);
@Nullable CommandExecutor<T> getExecutor();
/**
* @throws IllegalArgumentException if there is already a literal with the same name
* @throws IllegalStateException if a greedy argument was added to the node previously
*/
void addLiteral(@NotNull CommandNode<T> commandNode);
@Nullable CommandNode<T> getLiteral(@NotNull String name);
@NotNull Set<CommandNode<T>> getLiterals();
@Nullable String getPermission();
void setPermission(@Nullable String permission);
}

View file

@ -1,15 +0,0 @@
package de.kentoj.kencommandapi.api;
import de.kentoj.kencommandapi.api.processing.CommandContext;
import de.kentoj.kencommandapi.api.argument.ArgumentType;
import de.kentoj.kencommandapi.api.argument.CommandArgument;
import de.kentoj.kencommandapi.api.nodes.CommandNode;
public interface KenCommandApi<T, S extends CommandContext<T, ?>> {
CommandNode<T, S> createNode(String... name);
<V> CommandArgument<T, S, V> createArgument(String id, ArgumentType<T, S, V> type);
void invoke(CommandNode<T, S> node, T sender, String[] args);
}

View file

@ -1,13 +1,21 @@
package de.kentoj.kencommandapi.api.argument; package de.kentoj.kencommandapi.api.argument;
import de.kentoj.kencommandapi.api.processing.CommandContext; import de.kentoj.kencommandapi.api.suggestion.SuggestionProvider;
public interface ArgumentType<T, S extends CommandContext<T, ?>, V> { public interface ArgumentType<T, V> {
/**
* @throws IllegalArgumentException if string invalid
*/
V parseInput(String string); V parseInput(String string);
SuggestionProvider<T, S> defaultSuggestionProvider(); SuggestionProvider<T> defaultSuggestionProvider();
/**
* @return whether this argument is greedy. Greedy arguments consume all remaining input instead of a single word.
* For example, given the 2 words "hello" "world" as input, a greedy argument parses "hello world" as a single value.
* Greedy arguments may not be followed by another argument or a literal
*/
default boolean isGreedy() { default boolean isGreedy() {
return false; return false;
} }

View file

@ -1,23 +1,30 @@
package de.kentoj.kencommandapi.api.argument; package de.kentoj.kencommandapi.api.argument;
import de.kentoj.kencommandapi.api.processing.CommandContext; import de.kentoj.kencommandapi.api.suggestion.SuggestionProvider;
import de.kentoj.kencommandapi.internal.CommandArgumentImpl;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
public interface CommandArgument<T, S extends CommandContext<T, ?>, V> { import java.util.function.Function;
public interface CommandArgument<T, V> {
static <T, V> CommandArgument<T, V> arg(String id, ArgumentType<T, V> type) {
return new CommandArgumentImpl<>(id, type);
}
String getId(); String getId();
ArgumentType<T, S, V> getType(); ArgumentType<T, V> getType();
void setDefaultValue(@Nullable V defaultValue); void setDefaultValueProvider(@Nullable Function<T, V> getDefaultValueProvider);
@Nullable V getDefaultValue(); @Nullable Function<T, V> getDefaultValueProvider();
void setSuggestionProvider(@Nullable SuggestionProvider<T, S> suggestionProvider); void setSuggestionProvider(@Nullable SuggestionProvider<T> suggestionProvider);
/** /**
* @return the explicitly set SuggestionProvider or the default suggestions for the type if unset/null * @return the explicitly set SuggestionProvider or the default suggestions for the type if unset/null
*/ */
@NotNull SuggestionProvider<T, S> getSuggestionProvider(); @NotNull SuggestionProvider<T> getSuggestionProvider();
} }

View file

@ -1,11 +0,0 @@
package de.kentoj.kencommandapi.api.argument;
import de.kentoj.kencommandapi.api.processing.CommandContext;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
public interface SuggestionProvider<T, S extends CommandContext<T, ?>> {
CompletableFuture<Set<String>> suggest(S context);
}

View file

@ -1,16 +0,0 @@
package de.kentoj.kencommandapi.api.argument.suggestion;
import de.kentoj.kencommandapi.api.processing.CommandContext;
import de.kentoj.kencommandapi.api.argument.SuggestionProvider;
import java.util.Collections;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
public class NoSuggestionProvider<T, S extends CommandContext<T, ?>> implements SuggestionProvider<T, S> {
@Override
public CompletableFuture<Set<String>> suggest(S ctx) {
return CompletableFuture.completedFuture(Collections.emptySet());
}
}

View file

@ -0,0 +1,18 @@
package de.kentoj.kencommandapi.api.argument.types;
import de.kentoj.kencommandapi.api.argument.ArgumentType;
import de.kentoj.kencommandapi.api.suggestion.SuggestionProvider;
import de.kentoj.kencommandapi.api.suggestion.NoSuggestionProvider;
public class IntegerArgumentType<T> implements ArgumentType<T, Integer> {
@Override
public Integer parseInput(String string) {
return Integer.parseInt(string);
}
@Override
public SuggestionProvider<T> defaultSuggestionProvider() {
return new NoSuggestionProvider<>();
}
}

View file

@ -1,14 +1,12 @@
package de.kentoj.kencommandapi.api.types; package de.kentoj.kencommandapi.api.argument.types;
import de.kentoj.kencommandapi.api.argument.ArgumentType; import de.kentoj.kencommandapi.api.argument.ArgumentType;
import de.kentoj.kencommandapi.api.argument.SuggestionProvider; import de.kentoj.kencommandapi.api.suggestion.SuggestionProvider;
import de.kentoj.kencommandapi.api.argument.suggestion.NoSuggestionProvider; import de.kentoj.kencommandapi.api.suggestion.NoSuggestionProvider;
import de.kentoj.kencommandapi.api.processing.CommandContext;
import lombok.AllArgsConstructor;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor @RequiredArgsConstructor
public class StringArgumentType<T, S extends CommandContext<T, ?>> implements ArgumentType<T, S, String> { public class StringArgumentType<T> implements ArgumentType<T, String> {
private final boolean isGreedy; private final boolean isGreedy;
@ -22,7 +20,7 @@ public class StringArgumentType<T, S extends CommandContext<T, ?>> implements Ar
} }
@Override @Override
public SuggestionProvider<T, S> defaultSuggestionProvider() { public SuggestionProvider<T> defaultSuggestionProvider() {
return new NoSuggestionProvider<>(); return new NoSuggestionProvider<>();
} }

View file

@ -0,0 +1,37 @@
package de.kentoj.kencommandapi.api.invocation;
import de.kentoj.kencommandapi.api.argument.CommandArgument;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import java.util.Map;
@RequiredArgsConstructor
public class CommandContext<T> {
@Getter
private final T sender;
@Getter
private final Map<String, ParsedArgument<T, ?>> parsedArguments;
public <V> ParsedArgument<T, V> getParsedArgument(String name) {
try {
//noinspection unchecked
return (ParsedArgument<T, V>) parsedArguments.get(name);
} catch (ClassCastException e) {
throw new RuntimeException("requested argument with wrong type", e);
}
}
@SuppressWarnings("unchecked")
public <V> V getArg(String name) {
return (V) getParsedArgument(name).getValue();
}
public <V> V getArg(CommandArgument<T, V> arg) {
return getArg(arg.getId());
}
public <V> ParsedArgument<T, V> getParsedArgument(CommandArgument<T, V> arg) {
return getParsedArgument(arg.getId());
}
}

View file

@ -0,0 +1,6 @@
package de.kentoj.kencommandapi.api.invocation;
public interface CommandExecutor<T> {
Result execute(CommandContext<T> ctx);
}

View file

@ -0,0 +1,10 @@
package de.kentoj.kencommandapi.api.invocation;
import de.kentoj.kencommandapi.api.argument.CommandArgument;
public interface ParsedArgument<T, V> {
V getValue();
CommandArgument<T, V> getArgument();
}

View file

@ -0,0 +1,24 @@
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;
}
}

View file

@ -1,13 +0,0 @@
package de.kentoj.kencommandapi.api.nodes;
import de.kentoj.kencommandapi.api.argument.CommandArgument;
import de.kentoj.kencommandapi.api.processing.CommandContext;
import java.util.List;
public interface ArgumentNode<T, S extends CommandContext<T, ?>> {
<V> void addArgument(CommandArgument<T, S, V> argument);
List<CommandArgument<T, S, ?>> getArguments();
}

View file

@ -1,22 +0,0 @@
package de.kentoj.kencommandapi.api.nodes;
import de.kentoj.kencommandapi.api.processing.CommandContext;
import de.kentoj.kencommandapi.api.processing.CommandExecutor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @param <S> type of command context
*/
public interface CommandNode<T, S extends CommandContext<T, ?>> extends ArgumentNode<T, S>, LiteralNode<T, S> {
@NotNull String[] getNames();
void setExecutor(@Nullable CommandExecutor<T, S> executor);
@Nullable CommandExecutor<T, S> getExecutor();
void setDescription(@Nullable String description);
@Nullable String getDescription();
}

View file

@ -1,18 +0,0 @@
package de.kentoj.kencommandapi.api.nodes;
import de.kentoj.kencommandapi.api.processing.CommandContext;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Set;
public interface LiteralNode<T, S extends CommandContext<T, ?>> {
void addLiteral(@NotNull CommandNode<T, S> commandNode);
@Nullable CommandNode<T, S> getLiteral(@NotNull String name);
@NotNull Set<CommandNode<T, S>> getLiterals();
@Nullable String getPermission();
}

View file

@ -1,27 +0,0 @@
package de.kentoj.kencommandapi.api.processing;
import de.kentoj.kencommandapi.api.argument.CommandArgument;
public interface CommandContext<T, S extends CommandContext<T, ?>> {
T getSender();
/**
* @return Object that is parsed from the raw argument
* @throws RuntimeException if no value given and no default value supplied
*/
<V> ParsedArgument<T, S, V> getParsedArgument(String name);
@SuppressWarnings("unchecked")
default <V> V getArg(String name) {
return (V) getParsedArgument(name).getValue();
}
default <V> V getArg(CommandArgument<T, S, V> arg) {
return getArg(arg.getId());
}
default <V> ParsedArgument<T, S, V> getParsedArgument(CommandArgument<T, S, V> arg) {
return getParsedArgument(arg.getId());
}
}

View file

@ -1,15 +0,0 @@
package de.kentoj.kencommandapi.api.processing;
public class CommandException extends RuntimeException {
public CommandException(String message) {
super(message);
}
public CommandException(String message, Throwable cause) {
super(message, cause);
}
public CommandException(Throwable cause) {
super(cause);
}
}

View file

@ -1,6 +0,0 @@
package de.kentoj.kencommandapi.api.processing;
public interface CommandExecutor<T, S extends CommandContext<T, ?>> {
void execute(S ctx);
}

View file

@ -1,10 +0,0 @@
package de.kentoj.kencommandapi.api.processing;
import de.kentoj.kencommandapi.api.argument.CommandArgument;
public interface ParsedArgument<T, S extends CommandContext<T, ?>, V> {
V getValue();
CommandArgument<T, S, V> getArgument();
}

View file

@ -0,0 +1,15 @@
package de.kentoj.kencommandapi.api.suggestion;
import de.kentoj.kencommandapi.api.invocation.CommandContext;
import java.util.Collections;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
public class NoSuggestionProvider<T> implements SuggestionProvider<T> {
@Override
public CompletableFuture<Set<String>> suggest(CommandContext<T> __) {
return CompletableFuture.completedFuture(Collections.emptySet());
}
}

View file

@ -0,0 +1,11 @@
package de.kentoj.kencommandapi.api.suggestion;
import de.kentoj.kencommandapi.api.invocation.CommandContext;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
public interface SuggestionProvider<T> {
CompletableFuture<Set<String>> suggest(CommandContext<T> context);
}

View file

@ -1,19 +0,0 @@
package de.kentoj.kencommandapi.api.types;
import de.kentoj.kencommandapi.api.processing.CommandContext;
import de.kentoj.kencommandapi.api.argument.ArgumentType;
import de.kentoj.kencommandapi.api.argument.SuggestionProvider;
import de.kentoj.kencommandapi.api.argument.suggestion.NoSuggestionProvider;
public class IntegerArgumentType<T, S extends CommandContext<T, ?>> implements ArgumentType<T, S, Integer> {
@Override
public Integer parseInput(String string) {
return Integer.parseInt(string);
}
@Override
public SuggestionProvider<T, S> defaultSuggestionProvider() {
return new NoSuggestionProvider<>();
}
}

View file

@ -1,107 +0,0 @@
package de.kentoj.kencommandapi.impl;
import de.kentoj.kencommandapi.api.KenCommandApi;
import de.kentoj.kencommandapi.api.argument.ArgumentType;
import de.kentoj.kencommandapi.api.argument.CommandArgument;
import de.kentoj.kencommandapi.api.nodes.CommandNode;
import de.kentoj.kencommandapi.api.processing.CommandContext;
import de.kentoj.kencommandapi.api.processing.CommandException;
import de.kentoj.kencommandapi.api.processing.ParsedArgument;
import de.kentoj.kencommandapi.impl.processing.ParsedArgumentImpl;
import de.kentoj.kencommandapi.impl.structure.CommandArgumentImpl;
import de.kentoj.kencommandapi.impl.structure.CommandNodeImpl;
import lombok.RequiredArgsConstructor;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
@RequiredArgsConstructor
public abstract class AbstractKenCommandApi<T, S extends CommandContext<T, ?>> implements KenCommandApi<T, S> {
public abstract S createContext(T sender, Map<String, ParsedArgument<T, S, ?>> parsedArguments);
public abstract void sendErrorMessage(T t, CommandException exception);
public abstract boolean hasPermission(T t, String permission);
@Override
public CommandNode<T, S> createNode(String... name) {
return new CommandNodeImpl<>(name);
}
@Override
public <V> CommandArgument<T, S, V> createArgument(String id, ArgumentType<T, S, V> type) {
return new CommandArgumentImpl<>(id, type);
}
@Override
public void invoke(CommandNode<T, S> node, T sender, String[] args) {
var iter = Arrays.stream(args).iterator();
try {
var executableLiteral = processLiterals(sender, node, iter);
var parsedArguments = parseArguments(executableLiteral, iter);
assert executableLiteral.getExecutor() != null;
executableLiteral.getExecutor().execute(createContext(sender, parsedArguments));
} catch (CommandException ex) {
sendErrorMessage(sender, ex);
}
}
private CommandNode<T, S> processLiterals(T sender, CommandNode<T, S> commandNode, Iterator<String> iter) {
if (!iter.hasNext()) {
// literals/arguments are optional if the node itself is executable
if (commandNode.getExecutor() == null) throw new CommandException("no literal given but required");
return commandNode;
}
if (commandNode.getLiterals().isEmpty()) {
return commandNode;
}
final var arg = iter.next();
final var literal = commandNode.getLiteral(arg);
if (literal == null) {
throw new CommandException("Invalid literal: " + arg);
}
if (literal.getPermission() != null && !hasPermission(sender, literal.getPermission())) {
throw new CommandException("No permission");
}
return processLiterals(sender, literal, iter);
}
private Map<String, ParsedArgument<T, S, ?>> parseArguments(CommandNode<T, S> commandNode, Iterator<String> iter) {
Map<String, ParsedArgument<T, S, ?>> parsedArguments = new HashMap<>();
for (var argument : commandNode.getArguments()) {
@SuppressWarnings("unchecked")
var arg = ((CommandArgument<T, S, Object>) argument);
if (!iter.hasNext()) {
var defaultValue = arg.getDefaultValue();
if (defaultValue == null) throw new CommandException("no argument given but required");
parsedArguments.put(arg.getId(), new ParsedArgumentImpl<>(defaultValue, arg));
continue;
}
var rawArgument = getRawArgument(arg, iter);
var parsedArg = arg.getType().parseInput(rawArgument);
parsedArguments.put(arg.getId(), new ParsedArgumentImpl<>(parsedArg, arg));
}
return parsedArguments;
}
private String getRawArgument(CommandArgument<T, S, Object> arg, Iterator<String> iter) {
var rawArgument = "";
if (arg.getType().isGreedy()) {
while (iter.hasNext()) {
//noinspection StringConcatenationInLoop
rawArgument += iter.next();
}
} else {
rawArgument = iter.next();
}
return rawArgument;
}
}

View file

@ -1,26 +0,0 @@
package de.kentoj.kencommandapi.impl.processing;
import de.kentoj.kencommandapi.api.processing.CommandContext;
import de.kentoj.kencommandapi.api.processing.ParsedArgument;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import java.util.Map;
@RequiredArgsConstructor
public class CommandContextImpl<T, S extends CommandContext<T, ?>> implements CommandContext<T, S> {
@Getter
private final T sender;
@Getter
private final Map<String, ParsedArgument<T, S, ?>> parsedArguments;
@Override
public <V> ParsedArgument<T, S, V> getParsedArgument(String name) {
try {
//noinspection unchecked
return (ParsedArgument<T, S, V>) parsedArguments.get(name);
} catch (ClassCastException e) {
throw new RuntimeException("requested argument with wrong type", e);
}
}
}

View file

@ -1,12 +0,0 @@
package de.kentoj.kencommandapi.impl.processing;
import de.kentoj.kencommandapi.api.processing.CommandContext;
import de.kentoj.kencommandapi.api.processing.ParsedArgument;
import de.kentoj.kencommandapi.api.argument.CommandArgument;
import lombok.Value;
@Value
public class ParsedArgumentImpl<T, S extends CommandContext<T, ?>, V> implements ParsedArgument<T, S, V> {
V value;
CommandArgument<T, S, V> argument;
}

View file

@ -1,24 +0,0 @@
package de.kentoj.kencommandapi.impl.processing;
import de.kentoj.kencommandapi.api.processing.CommandContext;
import de.kentoj.kencommandapi.api.nodes.CommandNode;
import java.util.Arrays;
import java.util.List;
public class TabCompleter<T, S extends CommandContext<T, ?>> {
private List<String> tabComplete(CommandNode<T, S> node, String[] args) {
var lastNode = node;
for (int i = 0; i < args.length; i++) {
var tmp = node.getLiteral(args[i]);
if (tmp == null) {
break;
}
lastNode = tmp;
i++;
}
return node.getLiterals().stream().map(CommandNode::getNames).flatMap(Arrays::stream).toList();
}
}

View file

@ -1,27 +0,0 @@
package de.kentoj.kencommandapi.impl.structure;
import de.kentoj.kencommandapi.api.processing.CommandContext;
import de.kentoj.kencommandapi.api.argument.ArgumentType;
import de.kentoj.kencommandapi.api.argument.CommandArgument;
import de.kentoj.kencommandapi.api.argument.SuggestionProvider;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import org.jetbrains.annotations.Nullable;
@RequiredArgsConstructor
public class CommandArgumentImpl<T, S extends CommandContext<T, ?>, V> implements CommandArgument<T, S, V> {
@Getter
private final String id;
@Getter
private final ArgumentType<T, S, V> type;
@Getter
@Setter
private @Nullable V defaultValue;
@Getter
@Setter
private @Nullable SuggestionProvider<T, S> suggestionProvider;
}

View file

@ -1,71 +0,0 @@
package de.kentoj.kencommandapi.impl.structure;
import de.kentoj.kencommandapi.api.argument.CommandArgument;
import de.kentoj.kencommandapi.api.nodes.CommandNode;
import de.kentoj.kencommandapi.api.processing.CommandContext;
import de.kentoj.kencommandapi.api.processing.CommandExecutor;
import lombok.Getter;
import lombok.Setter;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class CommandNodeImpl<T, S extends CommandContext<T, ?>> implements CommandNode<T, S> {
@Getter
private final String[] names;
@Getter
private final Set<CommandNode<T, S>> literals = new HashSet<>();
@Getter
private final List<CommandArgument<T, S, ?>> arguments = new ArrayList<>();
@Getter
@Setter
private @Nullable CommandExecutor<T, S> executor;
@Getter
@Setter
private String description;
@Getter
private @Nullable String permission = null;
public CommandNodeImpl(String[] names) {
if (names.length == 0) throw new IllegalArgumentException("at least one name is required");
this.names = names;
}
@Override
public void addLiteral(@NotNull CommandNode<T, S> commandNode) {
for (String name : commandNode.getNames()) {
if (getLiteral(name) != null) throw new IllegalArgumentException("literal with same name is already added");
}
literals.add(commandNode);
}
@Override
public CommandNode<T, S> getLiteral(@NotNull String name) {
for (var literal : literals) {
for (String literalName : literal.getNames()) {
if (literalName.equalsIgnoreCase(name)) return literal;
}
}
return null;
}
@Override
public <V> void addArgument(CommandArgument<T, S, V> argument) {
if (arguments.stream()
.anyMatch(arg -> arg.getId().equals(argument.getId()))
) throw new IllegalArgumentException("argument with same identifier is already added");
if (!arguments.isEmpty() && arguments.getLast().getType().isGreedy())
throw new IllegalStateException("can't append argument to a node, whose last argument type is greedy: "
+ arguments.getLast().getType().getClass().getCanonicalName());
arguments.add(argument);
}
}

View file

@ -0,0 +1,28 @@
package de.kentoj.kencommandapi.internal;
import de.kentoj.kencommandapi.api.argument.ArgumentType;
import de.kentoj.kencommandapi.api.argument.CommandArgument;
import de.kentoj.kencommandapi.api.suggestion.SuggestionProvider;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import org.jetbrains.annotations.Nullable;
import java.util.function.Function;
@RequiredArgsConstructor
public class CommandArgumentImpl<T, V> implements CommandArgument<T, V> {
@Getter
private final String id;
@Getter
private final ArgumentType<T, V> type;
@Getter
@Setter
private @Nullable Function<T, V> defaultValueProvider;
@Getter
@Setter
private @Nullable SuggestionProvider<T> suggestionProvider;
}

View file

@ -0,0 +1,71 @@
package de.kentoj.kencommandapi.internal;
import de.kentoj.kencommandapi.api.CommandNode;
import de.kentoj.kencommandapi.api.argument.CommandArgument;
import de.kentoj.kencommandapi.api.invocation.CommandExecutor;
import lombok.Getter;
import lombok.Setter;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class CommandNodeImpl<T> implements CommandNode<T> {
@Getter
private final String[] names;
@Getter
private final Set<CommandNode<T>> literals = new HashSet<>();
@Getter
private final List<CommandArgument<T, ?>> arguments = new ArrayList<>();
@Getter
@Setter
private @Nullable CommandExecutor<T> executor;
@Getter
@Setter
private String description;
@Getter
@Setter
private @Nullable String permission = null;
public CommandNodeImpl(String[] names) {
if (names.length == 0) throw new IllegalArgumentException("at least one name is required");
this.names = names;
}
@Override
public void addLiteral(@NotNull CommandNode<T> commandNode) {
if (isGreedy()) throw new IllegalStateException("can't add literal to a node with a greedy argument");
for (String name : commandNode.getNames())
if (getLiteral(name) != null)
throw new IllegalArgumentException("literal with same name is already exists");
literals.add(commandNode);
}
@Override
public CommandNode<T> getLiteral(@NotNull String name) {
for (var literal : literals)
for (String literalName : literal.getNames())
if (literalName.equalsIgnoreCase(name)) return literal;
return null;
}
@Override
public <V> void addArgument(CommandArgument<T, V> argument) {
if (isGreedy()) throw new IllegalStateException("can't add literal to a node with a greedy argument");
if (arguments.stream().anyMatch(arg -> arg.getId().equals(argument.getId())))
throw new IllegalArgumentException("argument with same identifier is already added");
arguments.add(argument);
}
private boolean isGreedy() {
var arg = arguments.getLast();
return arg != null && arg.getType().isGreedy();
}
}

View file

@ -0,0 +1,11 @@
package de.kentoj.kencommandapi.internal;
import de.kentoj.kencommandapi.api.argument.CommandArgument;
import de.kentoj.kencommandapi.api.invocation.ParsedArgument;
import lombok.Value;
@Value
public class ParsedArgumentImpl<T, V> implements ParsedArgument<T, V> {
V value;
CommandArgument<T, V> argument;
}

View file

@ -0,0 +1,83 @@
package de.kentoj.kencommandapi.internal.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.internal.ParsedArgumentImpl;
import lombok.RequiredArgsConstructor;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
@RequiredArgsConstructor
public class CommandParser<T> {
private final PermissionChecker<T> hasPermissionMethod;
public ParseResult<T> parseLiteral(CommandNode<T> rootNode, T sender, String[] args) {
var cur = rootNode;
final Iterator<String> argStrIter = Arrays.stream(args).iterator();
final Map<String, ParsedArgument<T, ?>> parsedArguments = new HashMap<>();
while (argStrIter.hasNext()) {
for (var nodeArg : cur.getArguments()) {
ParsedArgument<T, Object> 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);
}
if (!argStrIter.hasNext()) {
if (cur.getExecutor() == null)
return new ParseResult.IncompleteCommand<>(cur);
return new ParseResult.Success<>(cur, parsedArguments);
}
var argStr = argStrIter.next();
var nextNode = cur.getLiteral(argStr);
if (nextNode == null)
return new ParseResult.UnknownLiteral<>(cur, argStr);
if (nextNode.getPermission() != null && !hasPermissionMethod.hasPermission(sender, nextNode.getPermission()))
return new ParseResult.NoPermission<>(cur, argStr);
cur = nextNode;
}
throw new RuntimeException("code reached unexpected line");
}
@SuppressWarnings("unchecked")
private ParsedArgument<T, Object> parseArgument(Iterator<String> args, T sender, CommandArgument<T, ?> nodeArg) {
Object value;
if (args.hasNext()) {
var argStr = getRawArgument(args, nodeArg);
value = nodeArg.getType().parseInput(argStr);
} else {
if (nodeArg.getDefaultValueProvider() == null)
return null;
value = nodeArg.getDefaultValueProvider().apply(sender);
}
return new ParsedArgumentImpl<>(value, (CommandArgument<T, Object>) nodeArg);
}
private String getRawArgument(Iterator<String> args, CommandArgument<T, ?> arg) {
StringBuilder rawArgument = new StringBuilder();
if (arg.getType().isGreedy()) {
while (args.hasNext()) {
rawArgument.append(args.next());
}
} else {
rawArgument = new StringBuilder(args.next());
}
return rawArgument.toString();
}
}

View file

@ -0,0 +1,92 @@
package de.kentoj.kencommandapi.internal.parser;
import de.kentoj.kencommandapi.api.CommandNode;
import de.kentoj.kencommandapi.api.argument.CommandArgument;
import de.kentoj.kencommandapi.api.invocation.ParsedArgument;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import java.util.Map;
public abstract sealed class ParseResult<T> permits ParseResult.IllegalArgument, ParseResult.IncompleteArgument,
ParseResult.IncompleteCommand, ParseResult.NoPermission, ParseResult.Success, ParseResult.UnknownLiteral {
/**
* @return last successfully literal
*/
public abstract CommandNode<T> getLiteral();
public abstract String getMessage();
@RequiredArgsConstructor
public static final class IncompleteArgument<T> extends ParseResult<T> {
@Getter
private final CommandNode<T> literal;
private final CommandArgument<T, ?> argument;
@Override
public String getMessage() {
return "Missing value for argument: " + argument.getId();
}
}
@RequiredArgsConstructor
public static final class UnknownLiteral<T> extends ParseResult<T> {
private final CommandNode<T> previousLiteral;
private final String name;
@Override
public String getMessage() {
return "Unknown literal: " + name;
}
@Override
public CommandNode<T> getLiteral() {
return previousLiteral;
}
}
@RequiredArgsConstructor
public static final class IncompleteCommand<T> extends ParseResult<T> {
@Getter
private final CommandNode<T> literal;
@Override
public String getMessage() {
return "Incomplete command";
}
}
@RequiredArgsConstructor
public static final class Success<T> extends ParseResult<T> {
@Getter
private final CommandNode<T> literal;
@Getter
private final Map<String, ParsedArgument<T, ?>> parsedArguments;
@Override
public String getMessage() {
return "";
}
}
@RequiredArgsConstructor
public static final class NoPermission<T> extends ParseResult<T> {
@Getter
private final CommandNode<T> literal;
private final String name;
@Override
public String getMessage() {
return "Unknown literal: " + name;
}
}
@RequiredArgsConstructor
public static final class IllegalArgument<T> extends ParseResult<T> {
@Getter
private final CommandNode<T> literal;
@Getter
private final String message;
}
}

View file

@ -0,0 +1,6 @@
package de.kentoj.kencommandapi.internal.parser;
@FunctionalInterface
public interface PermissionChecker<T> {
boolean hasPermission(T sender, String permission);
}

View file

@ -1,13 +1,12 @@
plugins { plugins {
id("java") id("java")
id("java-library") id("maven-publish")
id("de.kentoj.scrow.scrow-repository")
id("de.kentoj.scrow.base-dependencies")
id("de.kentoj.scrow.java-library")
} }
group = "de.kentoj.scrow"
version = rootProject.version
repositories { repositories {
mavenCentral()
maven { maven {
name = "papermc" name = "papermc"
url = uri("https://repo.papermc.io/repository/maven-public/") url = uri("https://repo.papermc.io/repository/maven-public/")
@ -20,4 +19,15 @@ dependencies {
compileOnly("com.velocitypowered:velocity-api:3.5.0-SNAPSHOT") compileOnly("com.velocitypowered:velocity-api:3.5.0-SNAPSHOT")
api(project(":kencommandapi-core")) api(project(":kencommandapi-core"))
} }
publishing {
publications {
create<MavenPublication>("KenCommandAPI") {
groupId = rootProject.group.toString()
version = rootProject.version.toString()
artifactId = project.name
from(components["java"])
}
}
}

View file

@ -0,0 +1,24 @@
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 lombok.RequiredArgsConstructor;
import java.util.Arrays;
@RequiredArgsConstructor
public class CommandAPI {
private final ProxyServer server;
private final CommandParser<CommandSource> commandParser = new CommandParser<>(CommandSource::hasPermission);
public void register(CommandNode<CommandSource> 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));
}
}

View file

@ -2,22 +2,47 @@ package de.kentoj.kencommandapi;
import com.velocitypowered.api.command.CommandSource; import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.command.SimpleCommand; import com.velocitypowered.api.command.SimpleCommand;
import de.kentoj.kencommandapi.api.nodes.CommandNode; 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 lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.TextDecoration;
@RequiredArgsConstructor @RequiredArgsConstructor
public class VelocityCommand implements SimpleCommand { public class VelocityCommand implements SimpleCommand {
private final VelocityKenCommandApi commandApi; private final CommandParser<CommandSource> commandParser;
private final CommandNode<CommandSource, VelocityCommandContext> rootNode; private final CommandNode<CommandSource> rootNode;
@Override @Override
public void execute(Invocation ctx) { public void execute(Invocation invocation) {
commandApi.invoke(rootNode, ctx.source(), ctx.arguments()); var parseResult = commandParser.parseLiteral(rootNode, invocation.source(), invocation.arguments());
if (parseResult instanceof ParseResult.Success<CommandSource>) {
var ctx = new CommandContext<>(invocation.source(), ((ParseResult.Success<CommandSource>) 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));
}
} }
@Override @Override
public boolean hasPermission(Invocation ctx) { public boolean hasPermission(Invocation ctx) {
return commandApi.hasPermission(ctx.source(), rootNode.getPermission()); if (rootNode.getPermission() == null) return true;
return ctx.source().hasPermission(rootNode.getPermission());
} }
} }

View file

@ -1,22 +0,0 @@
package de.kentoj.kencommandapi;
import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.proxy.Player;
import de.kentoj.kencommandapi.api.processing.ParsedArgument;
import de.kentoj.kencommandapi.impl.processing.CommandContextImpl;
import java.util.Map;
public class VelocityCommandContext extends CommandContextImpl<CommandSource, VelocityCommandContext> {
VelocityCommandContext(
CommandSource sender,
Map<String, ParsedArgument<CommandSource, VelocityCommandContext, ?>> parsedArguments
) {
super(sender, parsedArguments);
}
public Player getPlayerSender() {
return (Player) getSender();
}
}

View file

@ -1,44 +0,0 @@
package de.kentoj.kencommandapi;
import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.proxy.ProxyServer;
import de.kentoj.kencommandapi.api.nodes.CommandNode;
import de.kentoj.kencommandapi.api.processing.CommandException;
import de.kentoj.kencommandapi.api.processing.ParsedArgument;
import de.kentoj.kencommandapi.impl.AbstractKenCommandApi;
import lombok.RequiredArgsConstructor;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import java.util.Arrays;
import java.util.Map;
@RequiredArgsConstructor
public class VelocityKenCommandApi extends AbstractKenCommandApi<CommandSource, VelocityCommandContext> {
private final ProxyServer server;
@Override
public VelocityCommandContext createContext(CommandSource sender, Map<String, ParsedArgument<CommandSource, VelocityCommandContext, ?>> parsedArguments) {
return new VelocityCommandContext(sender, parsedArguments);
}
@Override
public void sendErrorMessage(CommandSource sender, CommandException ex) {
var cause = (ex.getCause() != null) ? ": " + ex.getCause().getMessage() : "";
sender.sendMessage(Component.text(ex.getMessage() + cause).color(NamedTextColor.RED));
}
@Override
public boolean hasPermission(CommandSource commandSource, String permission) {
return commandSource.hasPermission(permission);
}
public void register(CommandNode<CommandSource, VelocityCommandContext> 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(this, rootNode));
}
}

View file

@ -3,8 +3,8 @@ package de.kentoj.kencommandapi.suggestionprovider;
import com.velocitypowered.api.command.CommandSource; import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.proxy.Player; import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.proxy.ProxyServer; import com.velocitypowered.api.proxy.ProxyServer;
import de.kentoj.kencommandapi.VelocityCommandContext; import de.kentoj.kencommandapi.api.invocation.CommandContext;
import de.kentoj.kencommandapi.api.argument.SuggestionProvider; import de.kentoj.kencommandapi.api.suggestion.SuggestionProvider;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import java.util.Set; import java.util.Set;
@ -12,12 +12,12 @@ import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@RequiredArgsConstructor @RequiredArgsConstructor
public class PlayerSuggestionProvider implements SuggestionProvider<CommandSource, VelocityCommandContext> { public class PlayerSuggestionProvider implements SuggestionProvider<CommandSource> {
private final ProxyServer proxyServer; private final ProxyServer proxyServer;
@Override @Override
public CompletableFuture<Set<String>> suggest(VelocityCommandContext context) { public CompletableFuture<Set<String>> suggest(CommandContext<CommandSource> context) {
var list = proxyServer.getAllPlayers().stream() var list = proxyServer.getAllPlayers().stream()
.map(Player::getUsername) .map(Player::getUsername)
.collect(Collectors.toSet()); .collect(Collectors.toSet());

View file

@ -3,27 +3,25 @@ package de.kentoj.kencommandapi.type;
import com.velocitypowered.api.command.CommandSource; import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.proxy.Player; import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.proxy.ProxyServer; import com.velocitypowered.api.proxy.ProxyServer;
import de.kentoj.kencommandapi.VelocityCommandContext;
import de.kentoj.kencommandapi.api.argument.ArgumentType; import de.kentoj.kencommandapi.api.argument.ArgumentType;
import de.kentoj.kencommandapi.api.argument.SuggestionProvider; import de.kentoj.kencommandapi.api.suggestion.SuggestionProvider;
import de.kentoj.kencommandapi.api.processing.CommandException;
import de.kentoj.kencommandapi.suggestionprovider.PlayerSuggestionProvider; import de.kentoj.kencommandapi.suggestionprovider.PlayerSuggestionProvider;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor @RequiredArgsConstructor
public class PlayerArgumentType implements ArgumentType<CommandSource, VelocityCommandContext, Player> { public class PlayerArgumentType implements ArgumentType<CommandSource, Player> {
private final ProxyServer server; private final ProxyServer server;
@Override @Override
public Player parseInput(String string) { public Player parseInput(String string) {
var player = server.getPlayer(string).orElse(null); var player = server.getPlayer(string).orElse(null);
if (player == null) throw new CommandException("Player not online -- " + string); if (player == null) throw new IllegalArgumentException("Player not online -- " + string);
return player; return player;
} }
@Override @Override
public SuggestionProvider<CommandSource, VelocityCommandContext> defaultSuggestionProvider() { public SuggestionProvider<CommandSource> defaultSuggestionProvider() {
return new PlayerSuggestionProvider(server); return new PlayerSuggestionProvider(server);
} }
} }

View file

@ -1,5 +1,6 @@
rootProject.name = "KenCommandAPI" rootProject.name = "KenCommandAPI"
includeBuild("../build-logic")
include("kencommandapi-core") include("kencommandapi-core")
include("kencommandapi-bukkit") include("kencommandapi-bukkit")
include("kencommandapi-velocity") include("kencommandapi-velocity")