This commit is contained in:
kento2 2026-04-15 21:45:19 +02:00
parent 179f94085f
commit 3345595146
52 changed files with 95 additions and 905 deletions

View file

@ -18,8 +18,11 @@ dependencies {
testImplementation("org.junit.jupiter:junit-jupiter")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
implementation(project(":core-bukkit-api"))
implementation(kotlin("stdlib-jdk8"))
implementation(project(":core-bukkit-api"))
implementation("org.spigotmc:spigot-api:1.21.11-R0.1-SNAPSHOT")
implementation("de.kentoj:kencommandapi-bukkit:1.0.0")
}
tasks {

View file

@ -1,5 +1,6 @@
package de.kentoj.scrow.bukkit;
import de.kentoj.kencommandapi.BukkitKenCommandApi;
import de.kentoj.scrow.bukkit.economy.CoinsCommand;
import de.kentoj.scrow.bukkit.friends.command.FriendCommand;
import org.bukkit.plugin.java.JavaPlugin;
@ -14,6 +15,7 @@ public class CoreImplPlugin extends JavaPlugin {
ScrowAPISurface.initScrowAPI(this, false);
{
BukkitKenCommandApi kenCommandApi = new BukkitKenCommandApi();
ScrowAPI.getCommandManager().register(new CoinsCommand().create(), this);
ScrowAPI.getCommandManager().register(new FriendCommand().create(), this);
}

View file

@ -4,11 +4,12 @@ import com.mongodb.ConnectionString;
import com.mongodb.MongoClientSettings;
import com.mongodb.reactivestreams.client.MongoClient;
import com.mongodb.reactivestreams.client.MongoClients;
import de.kentoj.kencommandapi.BukkitKenCommandApi;
import de.kentoj.scrow.bukkit.cmds2.CommandFactoryImpl;
import de.kentoj.scrow.bukkit.cmds2.bukkit.BukkitCommandManager;
import de.kentoj.scrow.bukkit.economy.InMemoryEconomyService;
import de.kentoj.scrow.bukkit.economy.MongoEconomyService;
import de.kentoj.scrow.bukkit.services.command.Commands;
import de.kentoj.scrow.bukkit.command.Commands;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.bson.UuidRepresentation;
@ -47,9 +48,10 @@ public class ScrowAPISurface {
ScrowAPI.getDatabase() != null ?
new MongoEconomyService(ScrowAPI.getDatabase()) : new InMemoryEconomyService()
);
ScrowAPI.setCommandManager(new BukkitKenCommandApi());
// TODO friends service
Commands.setFactory(new CommandFactoryImpl());
ScrowAPI.setCommandManager(new BukkitCommandManager());
}
public static void destroyScrowAPI() {

View file

@ -1,34 +0,0 @@
package de.kentoj.scrow.bukkit.cmds2;
import de.kentoj.scrow.bukkit.services.command.literal.CommandArgument;
import de.kentoj.scrow.bukkit.services.command.execution.CommandContext;
import de.kentoj.scrow.bukkit.services.command.SuggestionProvider;
import de.kentoj.scrow.bukkit.services.command.type.ArgumentType;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import org.jetbrains.annotations.Nullable;
import java.util.function.Function;
@Getter
@RequiredArgsConstructor
public class CommandArgumentImpl<T> implements CommandArgument<T> {
private final String name;
private final ArgumentType<T> type;
private final Function<CommandContext, T> defaultValueFun;
@Setter
private @Nullable SuggestionProvider suggestionProvider;
@Override
public @Nullable T getDefaultValue(CommandContext ctx) {
return defaultValueFun.apply(ctx);
}
public SuggestionProvider getSuggestionProvider() {
if (suggestionProvider == null)
return type::getDefaultSuggestions;
return suggestionProvider;
}
}

View file

@ -1,40 +0,0 @@
package de.kentoj.scrow.bukkit.cmds2;
import com.google.common.base.Preconditions;
import de.kentoj.scrow.bukkit.services.command.execution.ArgumentData;
import de.kentoj.scrow.bukkit.services.command.execution.CommandContext;
import de.kentoj.scrow.bukkit.services.command.exception.CommandInvocationException;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import java.util.Map;
@RequiredArgsConstructor
public class CommandContextImpl implements CommandContext {
@Getter
private final CommandSender sender;
private final Map<String, ArgumentData<?>> arguments;
@SuppressWarnings("unchecked")
public <T> ArgumentData<T> getArgData(String name) {
final var data = (ArgumentData<T>) arguments.get(name);
Preconditions.checkState(data != null, "no data for '" + name + "'-argument");
return data;
}
@Override
public Player getSenderPlayer() {
if (!(sender instanceof Player)) throw new CommandInvocationException("This command can only be used by players");
return (Player) sender;
}
@Override
public Entity getSenderEntity() {
if (!(sender instanceof Entity)) throw new CommandInvocationException("This command can only be used by entities");
return (Entity) sender;
}
}

View file

@ -1,28 +0,0 @@
package de.kentoj.scrow.bukkit.cmds2;
import de.kentoj.scrow.bukkit.services.command.literal.CommandArgument;
import de.kentoj.scrow.bukkit.services.command.execution.CommandContext;
import de.kentoj.scrow.bukkit.services.command.CommandFactory;
import de.kentoj.scrow.bukkit.services.command.literal.Literal;
import de.kentoj.scrow.bukkit.services.command.literal.RootLiteral;
import de.kentoj.scrow.bukkit.services.command.type.ArgumentType;
import java.util.function.Function;
public class CommandFactoryImpl implements CommandFactory {
@Override
public Literal literal(String name) {
return new LiteralImpl(name);
}
@Override
public RootLiteral root(String name) {
return new RootLiteralImpl(name);
}
@Override
public <T> CommandArgument<T> argument(String name, ArgumentType<T> type, Function<CommandContext, T> defaultValueFun) {
return new CommandArgumentImpl<>(name, type, defaultValueFun);
}
}

View file

@ -1,60 +0,0 @@
package de.kentoj.scrow.bukkit.cmds2;
import de.kentoj.scrow.bukkit.services.command.execution.CommandExecutor;
import de.kentoj.scrow.bukkit.services.command.literal.CommandArgument;
import de.kentoj.scrow.bukkit.services.command.literal.Literal;
import lombok.Getter;
import lombok.Setter;
import org.bukkit.Bukkit;
import org.bukkit.permissions.Permission;
import org.jetbrains.annotations.Nullable;
import java.util.*;
@Getter
public class LiteralImpl implements Literal {
@Setter
private String name;
@Setter
private @Nullable Permission requiredPermission = null;
@Setter
private @Nullable String description = null;
@Setter
private @Nullable CommandExecutor executor = null;
private final List<CommandArgument<?>> arguments = new ArrayList<>();
private final Map<String, Literal> literals = new HashMap<>();
public LiteralImpl(String name) {
this.name = name;
}
@Override
public void addLiteral(Literal node) {
if (literals.containsKey(node.getName())) {
Bukkit.getLogger().warning("Literal " + node.getName() + " is overwritten by literal with same name");
}
literals.put(node.getName(), node);
}
@Override
public @Nullable Literal getLiteral(String name) {
return literals.get(name);
}
@Override
public boolean hasLiteral() {
return !literals.isEmpty();
}
@Override
public Set<Literal> getLiterals() {
return new HashSet<>(literals.values());
}
@Override
public <T> void addArgument(CommandArgument<T> argument) {
arguments.add(argument);
}
}

View file

@ -1,25 +0,0 @@
package de.kentoj.scrow.bukkit.cmds2;
import de.kentoj.scrow.bukkit.services.command.literal.RootLiteral;
import lombok.Getter;
import lombok.Setter;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class RootLiteralImpl extends LiteralImpl implements RootLiteral {
@Getter
@Setter
private Set<String> aliases = new HashSet<>();
public RootLiteralImpl(String name) {
super(name);
}
@Override
public void addAliases(String... aliases) {
this.aliases.addAll(List.of(aliases));
}
}

View file

@ -1,38 +0,0 @@
package de.kentoj.scrow.bukkit.cmds2.bukkit;
import de.kentoj.scrow.bukkit.cmds2.parsing.ArgumentReaderImpl;
import de.kentoj.scrow.bukkit.cmds2.parsing.CommandProcessor;
import de.kentoj.scrow.bukkit.services.command.literal.RootLiteral;
import de.kentoj.scrow.bukkit.services.command.exception.CommandInvocationException;
import org.bukkit.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
public class BukkitCommand extends Command {
private final RootLiteral rootLiteral;
public BukkitCommand(RootLiteral rootLiteral) {
super(rootLiteral.getName());
this.rootLiteral = rootLiteral;
if (rootLiteral.getDescription() != null) {
super.setDescription(rootLiteral.getDescription());
}
if (rootLiteral.getAliases() != null) {
super.setAliases(rootLiteral.getAliases().stream().toList());
}
// TODO usage string?
}
@Override
public boolean execute(@NotNull CommandSender sender, @NotNull String commandLabel, @NotNull String[] args) {
try {
new CommandProcessor(sender, new ArgumentReaderImpl(args)).execute(rootLiteral);
} catch (CommandInvocationException syntaxException) {
sender.sendMessage(ChatColor.RED + syntaxException.getMessage());
}
return true;
}
}

View file

@ -1,28 +0,0 @@
package de.kentoj.scrow.bukkit.cmds2.bukkit;
import de.kentoj.scrow.bukkit.services.command.CommandManager;
import de.kentoj.scrow.bukkit.services.command.literal.RootLiteral;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandMap;
import org.bukkit.plugin.Plugin;
public class BukkitCommandManager implements CommandManager {
private final CommandMap commandMap;
public BukkitCommandManager() {
try {
var field = Bukkit.getPluginManager().getClass().getDeclaredField("commandMap");
field.setAccessible(true);
commandMap = (CommandMap) field.get(Bukkit.getPluginManager());
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new RuntimeException(e);
}
}
@Override
public void register(RootLiteral rootLiteral, Plugin plugin) {
var bukkitCommand = new BukkitCommand(rootLiteral);
commandMap.register(plugin.getName(), bukkitCommand);
}
}

View file

@ -1,23 +0,0 @@
package de.kentoj.scrow.bukkit.cmds2.bukkit;
import de.kentoj.scrow.bukkit.services.command.literal.RootLiteral;
import lombok.RequiredArgsConstructor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabCompleter;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
@RequiredArgsConstructor
public class BukkitTabCompleter implements TabCompleter {
private final RootLiteral rootLiteral;
@Override
public @Nullable List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
return List.of();
}
}

View file

@ -1,13 +0,0 @@
package de.kentoj.scrow.bukkit.cmds2.parsing;
import de.kentoj.scrow.bukkit.services.command.execution.ArgumentData;
import de.kentoj.scrow.bukkit.services.command.literal.CommandArgument;
import lombok.Value;
import org.jetbrains.annotations.Nullable;
@Value
public class ArgumentDataImpl<T> implements ArgumentData<T> {
T value;
CommandArgument<T> argument;
@Nullable String rawInput;
}

View file

@ -1,62 +0,0 @@
package de.kentoj.scrow.bukkit.cmds2.parsing;
import de.kentoj.scrow.bukkit.services.command.literal.ArgumentReader;
import de.kentoj.scrow.bukkit.services.command.exception.CommandInvocationException;
import java.nio.CharBuffer;
public class ArgumentReaderImpl implements ArgumentReader {
private final CharBuffer buf;
public ArgumentReaderImpl(String string) {
buf = CharBuffer.wrap(string);
}
public ArgumentReaderImpl(String[] args) {
this(String.join(" ", args));
}
@Override
public boolean isEOF() {
return buf.position() >= buf.limit();
}
@Override
public String readWord() {
final var str = new StringBuilder();
while (true) {
if (buf.position() >= buf.limit()) break;
final char c = buf.get();
if (c == ' ') break;
str.append(c);
}
return str.toString();
}
@Override
public String readQuotedWord() {
final var str = new StringBuilder();
var quoting = false;
while (true) {
if (buf.position() >= buf.limit()) break;
final char c = buf.get();
if (c == '"') quoting = !quoting;
if (c == ' ' && !quoting) break;
str.append(c);
}
if (quoting) throw new CommandInvocationException("Missing matching \" to terminate quote");
return str.toString();
}
@Override
public int getCursor() {
return buf.position();
}
@Override
public void setCursor(int cursor) {
buf.position(cursor);
}
}

View file

@ -1,76 +0,0 @@
package de.kentoj.scrow.bukkit.cmds2.parsing;
import de.kentoj.scrow.bukkit.cmds2.CommandContextImpl;
import de.kentoj.scrow.bukkit.services.command.exception.CommandInvocationException;
import de.kentoj.scrow.bukkit.services.command.execution.ArgumentData;
import de.kentoj.scrow.bukkit.services.command.literal.ArgumentReader;
import de.kentoj.scrow.bukkit.services.command.literal.CommandArgument;
import de.kentoj.scrow.bukkit.services.command.literal.Literal;
import de.kentoj.scrow.bukkit.services.command.literal.RootLiteral;
import lombok.RequiredArgsConstructor;
import org.bukkit.command.CommandSender;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
@RequiredArgsConstructor
public class CommandProcessor {
private final Map<String, ArgumentData<?>> arguments = new HashMap<>();
private final CommandSender sender;
private final ArgumentReader argumentReader;
public void execute(RootLiteral rootLiteral) {
Literal literal = rootLiteral;
do {
processArguments(literal);
if (argumentReader.isEOF()) {
if (literal.getExecutor() == null) {
throw new CommandInvocationException("Incomplete command -- literal expected");
}
literal.getExecutor().execute(new CommandContextImpl(sender, arguments));
return;
}
final var literalName = argumentReader.readWord();
final var nextLiteral = literal.getLiteral(literalName);
if (nextLiteral == null) {
final String validLiterals = literal.getLiterals().stream().map(Literal::getName).collect(Collectors.joining(", "));
throw new CommandInvocationException("Unknown literal -- " + literalName +
"\nValid literals: " + validLiterals);
}
literal = nextLiteral;
final boolean hasSenderPermission = literal.getRequiredPermission() == null || sender.hasPermission(literal.getRequiredPermission());
if (!hasSenderPermission) throw new CommandInvocationException("No permission");
} while (literal.hasLiteral());
processArguments(literal);
if (literal.getExecutor() == null) throw new CommandInvocationException("Incomplete command.");
literal.getExecutor().execute(new CommandContextImpl(sender, arguments));
}
private void processArguments(Literal node) {
node.getArguments().forEach(arg -> {
var parsed = parseArgument(arg);
arguments.put(arg.getName(), parsed);
});
}
private <T> ArgumentData<T> parseArgument(CommandArgument<T> argument) {
if (argumentReader.isEOF()) {
final var defaultValue = argument.getDefaultValue(new CommandContextImpl(sender, arguments));
if (defaultValue == null) throw new CommandInvocationException("Missing argument -- " + argument.getName());
return new ArgumentDataImpl<>(defaultValue, argument, null);
}
final var rawInput = argument.getType().readRawInput(argumentReader);
final T value = argument.getType().parseInput(rawInput);
argument.getType().checkValue(new CommandContextImpl(sender, arguments), value, rawInput);
return new ArgumentDataImpl<>(value, argument, rawInput);
}
}

View file

@ -1,30 +1,41 @@
package de.kentoj.scrow.bukkit.economy;
import de.kentoj.kencommandapi.BukkitKenCommandApi;
import de.kentoj.kencommandapi.api.processing.CommandContext;
import de.kentoj.kencommandapi.api.structure.argument.types.IntegerArgumentType;
import de.kentoj.kencommandapi.api.structure.node.CommandNode;
import de.kentoj.scrow.bukkit.ScrowAPI;
import de.kentoj.scrow.bukkit.services.command.literal.CommandArgument;
import de.kentoj.scrow.bukkit.services.command.execution.CommandContext;
import de.kentoj.scrow.bukkit.services.command.Commands;
import de.kentoj.scrow.bukkit.services.command.literal.RootLiteral;
import de.kentoj.scrow.bukkit.services.command.type.NumberArgumentType;
import de.kentoj.scrow.bukkit.services.command.type.PlayerArgumentType;
import de.kentoj.scrow.bukkit.command.Commands;
import de.kentoj.scrow.bukkit.command.execution.CommandContext;
import de.kentoj.scrow.bukkit.command.literal.CommandArgument;
import de.kentoj.scrow.bukkit.command.literal.RootLiteral;
import de.kentoj.scrow.bukkit.command.type.NumberArgumentType;
import de.kentoj.scrow.bukkit.command.type.PlayerArgumentType;
import lombok.var;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.CommandSender;
import reactor.core.scheduler.Schedulers;
import java.util.logging.Level;
public class CoinsCommand {
private final RootLiteral rootCommand;
private final CommandArgument<OfflinePlayer> playerArg;
private final CommandArgument<Integer> amountArg;
public void create() {
var rootCommand = ScrowAPI.getCommandManager().createNode("coins");
var playerArg = ScrowAPI.getCommandManager().createArgument("player", )
}
/*
/coins steve
/coins steve set 0
/coins steve get
*/
public CoinsCommand() {
public CoinsCommand(BukkitKenCommandApi commandManager) {
playerArg = Commands.argument0("player", PlayerArgumentType.offlinePlayer(), CommandContext::getSenderPlayer);
amountArg = Commands.argument0("amount", NumberArgumentType.integer(0, Integer.MAX_VALUE));
@ -45,10 +56,6 @@ public class CoinsCommand {
}
}
public RootLiteral create() {
return rootCommand;
}
private void getCoins(CommandContext ctx) {
final var player = ctx.getArg(playerArg);

View file

@ -1,7 +1,7 @@
package de.kentoj.scrow.bukkit.economy;
import com.google.common.base.Preconditions;
import de.kentoj.scrow.bukkit.services.EconomyService;
import de.kentoj.scrow.bukkit.EconomyService;
import org.jetbrains.annotations.NotNull;
import reactor.core.publisher.Mono;

View file

@ -6,9 +6,8 @@ import com.mongodb.client.model.UpdateOptions;
import com.mongodb.client.model.Updates;
import com.mongodb.reactivestreams.client.MongoCollection;
import com.mongodb.reactivestreams.client.MongoDatabase;
import de.kentoj.scrow.bukkit.services.EconomyService;
import de.kentoj.scrow.bukkit.EconomyService;
import org.bson.Document;
import org.bson.types.ObjectId;
import org.jetbrains.annotations.NotNull;
import reactor.core.publisher.Mono;

View file

@ -1,12 +1,10 @@
package de.kentoj.scrow.bukkit.friends.command;
import de.kentoj.scrow.bukkit.services.command.Commands;
import de.kentoj.scrow.bukkit.services.command.literal.RootLiteral;
import de.kentoj.scrow.bukkit.command.Commands;
import de.kentoj.scrow.bukkit.command.literal.RootLiteral;
public class FriendCommand {
private final RootLiteral rootLiteral;
public FriendCommand() {
rootLiteral = Commands.root("friend");
rootLiteral.addAliases("f", "friends");

View file

@ -1,9 +1,9 @@
package de.kentoj.scrow.bukkit.friends.command;
import de.kentoj.scrow.bukkit.ScrowAPI;
import de.kentoj.scrow.bukkit.services.command.Commands;
import de.kentoj.scrow.bukkit.services.command.execution.CommandContext;
import de.kentoj.scrow.bukkit.services.command.literal.Literal;
import de.kentoj.scrow.bukkit.command.Commands;
import de.kentoj.scrow.bukkit.command.execution.CommandContext;
import de.kentoj.scrow.bukkit.command.literal.Literal;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import reactor.core.scheduler.Schedulers;

View file

@ -1,7 +1,7 @@
package de.kentoj.scrow.bukkit.friends.friendship;
import com.google.common.base.Preconditions;
import de.kentoj.scrow.bukkit.services.friends.Friendship;
import de.kentoj.scrow.bukkit.friends.Friendship;
import de.kentoj.scrow.bukkit.util.pair.Tuple2;
import lombok.Value;
import org.bson.codecs.pojo.annotations.BsonCreator;

View file

@ -1,6 +1,6 @@
package de.kentoj.scrow.bukkit.friends.friendship;
import de.kentoj.scrow.bukkit.services.friends.Friendship;
import de.kentoj.scrow.bukkit.friends.Friendship;
import de.kentoj.scrow.bukkit.util.pair.Tuple2;
import org.jetbrains.annotations.NotNull;
import reactor.core.publisher.Flux;

View file

@ -7,7 +7,7 @@ import com.mongodb.client.model.IndexOptions;
import com.mongodb.client.model.Indexes;
import com.mongodb.reactivestreams.client.MongoCollection;
import com.mongodb.reactivestreams.client.MongoDatabase;
import de.kentoj.scrow.bukkit.services.friends.Friendship;
import de.kentoj.scrow.bukkit.friends.Friendship;
import de.kentoj.scrow.bukkit.util.pair.Tuple2;
import org.jetbrains.annotations.NotNull;
import reactor.core.publisher.Flux;

View file

@ -1,7 +1,7 @@
package de.kentoj.scrow.bukkit.friends.request;
import com.google.common.base.Preconditions;
import de.kentoj.scrow.bukkit.services.friends.FriendRequest;
import de.kentoj.scrow.bukkit.friends.FriendRequest;
import lombok.Value;
import org.bson.codecs.pojo.annotations.BsonCreator;
import org.bson.codecs.pojo.annotations.BsonProperty;

View file

@ -1,6 +1,6 @@
package de.kentoj.scrow.bukkit.friends.request;
import de.kentoj.scrow.bukkit.services.friends.FriendRequest;
import de.kentoj.scrow.bukkit.friends.FriendRequest;
import org.jetbrains.annotations.NotNull;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

View file

@ -7,7 +7,7 @@ import com.mongodb.client.model.IndexOptions;
import com.mongodb.client.model.Indexes;
import com.mongodb.reactivestreams.client.MongoCollection;
import com.mongodb.reactivestreams.client.MongoDatabase;
import de.kentoj.scrow.bukkit.services.friends.FriendRequest;
import de.kentoj.scrow.bukkit.friends.FriendRequest;
import org.jetbrains.annotations.NotNull;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

View file

@ -1,5 +1,5 @@
name: "CoreBukkit"
version: "1.0"
main: "de.kentoj.scrow.bukkit.CoreImplPlugin"
api-version: "1.1"
api-version: "1.21"
author: "Kento2 <kento@placeq.com>"