9
0
mirror of https://github.com/LeavesMC/Leaves.git synced 2025-12-30 20:39:15 +00:00

feat: finish perms system, fix bugs

This commit is contained in:
MC_XiaoHei
2025-08-23 16:26:23 +08:00
parent 285e70ac98
commit eb86035885
20 changed files with 465 additions and 84 deletions

View File

@@ -6,6 +6,7 @@ import net.kyori.adventure.text.Component;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.player.Player;
import org.bukkit.Bukkit;
import org.bukkit.command.Command;
import org.bukkit.configuration.MemorySection;
import org.bukkit.configuration.file.YamlConfiguration;
@@ -30,7 +31,6 @@ import org.leavesmc.leaves.config.api.impl.ConfigValidatorImpl.IntConfigValidato
import org.leavesmc.leaves.config.api.impl.ConfigValidatorImpl.ListConfigValidator;
import org.leavesmc.leaves.config.api.impl.ConfigValidatorImpl.LongConfigValidator;
import org.leavesmc.leaves.config.api.impl.ConfigValidatorImpl.StringConfigValidator;
import org.leavesmc.leaves.neo_command.LeavesCommands;
import org.leavesmc.leaves.profile.LeavesMinecraftSessionService;
import org.leavesmc.leaves.protocol.CarpetServerProtocol.CarpetRule;
import org.leavesmc.leaves.protocol.CarpetServerProtocol.CarpetRules;
@@ -95,7 +95,7 @@ public final class LeavesConfig {
GlobalConfigManager.init();
registerCommand("leaves", new LeavesCommand());
LeavesCommands.registerLeavesCommands();
org.leavesmc.leaves.neo_command.leaves.LeavesCommand.INSTANCE.register();
}
public static void reload() {
@@ -152,9 +152,16 @@ public final class LeavesConfig {
public void verify(Boolean old, Boolean value) throws IllegalArgumentException {
if (value) {
registerCommand("bot", new BotCommand());
org.leavesmc.leaves.neo_command.bot.BotCommand.INSTANCE.register();
Actions.registerAll();
} else {
unregisterCommand("bot");
org.leavesmc.leaves.neo_command.bot.BotCommand.INSTANCE.unregister();
}
if (old != null && !old.equals(value)) {
Bukkit.getOnlinePlayers().stream()
.filter(org.leavesmc.leaves.neo_command.bot.BotCommand::hasPermission)
.forEach(org.bukkit.entity.Player::updateCommands);
}
}
}
@@ -190,15 +197,58 @@ public final class LeavesConfig {
@GlobalConfig("open-fakeplayer-inventory")
public boolean canOpenInventory = false;
@GlobalConfig("use-action")
@GlobalConfig(value = "use-action", validator = CanUseActionValidator.class)
public boolean canUseAction = true;
@GlobalConfig("modify-config")
private static class CanUseActionValidator extends BooleanConfigValidator {
@Override
public void verify(Boolean old, Boolean value) throws IllegalArgumentException {
if (old != null && !old.equals(value)) {
Bukkit.getOnlinePlayers().stream()
.filter(sender ->
org.leavesmc.leaves.neo_command.bot.BotCommand.hasPermission(sender)
|| org.leavesmc.leaves.neo_command.bot.BotCommand.hasPermission(sender, "action")
)
.forEach(org.bukkit.entity.Player::updateCommands);
}
}
}
@GlobalConfig(value = "modify-config", validator = CanModifyConfigValidator.class)
public boolean canModifyConfig = false;
@GlobalConfig("manual-save-and-load")
private static class CanModifyConfigValidator extends BooleanConfigValidator {
@Override
public void verify(Boolean old, Boolean value) throws IllegalArgumentException {
if (old != null && !old.equals(value)) {
Bukkit.getOnlinePlayers().stream()
.filter(sender ->
org.leavesmc.leaves.neo_command.bot.BotCommand.hasPermission(sender)
|| org.leavesmc.leaves.neo_command.bot.BotCommand.hasPermission(sender, "config")
)
.forEach(org.bukkit.entity.Player::updateCommands);
}
}
}
@GlobalConfig(value = "manual-save-and-load", validator = CanManualSaveAndLoadValidator.class)
public boolean canManualSaveAndLoad = false;
private static class CanManualSaveAndLoadValidator extends BooleanConfigValidator {
@Override
public void verify(Boolean old, Boolean value) throws IllegalArgumentException {
if (old != null && !old.equals(value)) {
Bukkit.getOnlinePlayers().stream()
.filter(sender ->
org.leavesmc.leaves.neo_command.bot.BotCommand.hasPermission(sender)
|| org.leavesmc.leaves.neo_command.bot.BotCommand.hasPermission(sender, "save")
|| org.leavesmc.leaves.neo_command.bot.BotCommand.hasPermission(sender, "load")
)
.forEach(org.bukkit.entity.Player::updateCommands);
}
}
}
@GlobalConfig(value = "cache-skin", lock = true)
public boolean useSkinCache = false;

View File

@@ -13,7 +13,7 @@ import java.util.Arrays;
import java.util.Map;
import static java.util.stream.Collectors.toMap;
import static org.leavesmc.leaves.neo_command.leaves.ArgumentSuggestions.strings;
import static org.leavesmc.leaves.neo_command.ArgumentNode.ArgumentSuggestions.strings;
public class ServerMoveAction extends ServerStateBotAction<ServerMoveAction> {
private static final Map<String, MoveDirection> NAME_TO_DIRECTION = Arrays.stream(MoveDirection.values()).collect(toMap(

View File

@@ -7,7 +7,7 @@ import org.leavesmc.leaves.neo_command.CommandContext;
import java.util.function.Supplier;
import static com.mojang.brigadier.arguments.IntegerArgumentType.integer;
import static org.leavesmc.leaves.neo_command.leaves.ArgumentSuggestions.strings;
import static org.leavesmc.leaves.neo_command.ArgumentNode.ArgumentSuggestions.strings;
public abstract class ServerTimerBotAction<E extends ServerTimerBotAction<E>> extends ServerBotAction<E> {

View File

@@ -8,7 +8,10 @@ import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.Commands;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.concurrent.CompletableFuture;
public abstract class ArgumentNode<T> extends CommandNode {
@@ -36,4 +39,24 @@ public abstract class ArgumentNode<T> extends CommandNode {
return argumentBuilder;
}
public static class ArgumentSuggestions {
@Contract(pure = true)
public static WrappedArgument.@NotNull SuggestionApplier strings(String... values) {
return (context, builder) -> {
for (String s : values) {
builder.suggest(s);
}
};
}
@Contract(pure = true)
public static WrappedArgument.@NotNull SuggestionApplier strings(List<String> values) {
return (context, builder) -> {
for (String s : values) {
builder.suggest(s);
}
};
}
}
}

View File

@@ -35,10 +35,14 @@ public abstract class CommandNode {
return true;
}
protected boolean requires(CommandSourceStack source) {
public boolean requires(CommandSourceStack source) {
return true;
}
public String getName() {
return this.name;
}
protected ArgumentBuilder<CommandSourceStack, ?> compile() {
ArgumentBuilder<CommandSourceStack, ?> builder = compileBase();

View File

@@ -0,0 +1,135 @@
package org.leavesmc.leaves.neo_command;
import com.google.common.base.Functions;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import org.bukkit.Bukkit;
import org.bukkit.permissions.Permission;
import org.bukkit.permissions.PermissionDefault;
import org.bukkit.plugin.PluginManager;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.framework.qual.DefaultQualifier;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class CommandUtils {
public static void registerPermissions(@NotNull List<String> permissions) {
PluginManager pluginManager = Bukkit.getServer().getPluginManager();
for (String perm : permissions) {
if (pluginManager.getPermission(perm) == null) {
pluginManager.addPermission(new Permission(perm, PermissionDefault.OP));
}
}
}
@DefaultQualifier(NonNull.class)
public static @NotNull List<String> getListClosestMatchingLast(
final String last,
final Collection<?> collection
) {
if (collection.isEmpty()) {
return Collections.emptyList();
}
ArrayList<Candidate> candidates = Lists.newArrayList();
String lastLower = last.toLowerCase();
for (String item : Iterables.transform(collection, Functions.toStringFunction())) {
String itemLower = item.toLowerCase();
if (itemLower.startsWith(lastLower)) {
candidates.add(Candidate.of(item, 0));
} else if (itemLower.contains(lastLower)) {
candidates.add(Candidate.of(item, damerauLevenshteinDistance(lastLower, itemLower)));
}
}
candidates.sort(Comparator.comparingInt(c -> c.score));
List<String> results = new ArrayList<>(candidates.size());
for (Candidate candidate : candidates) {
results.add(candidate.item);
}
return results;
}
/**
* Computes the Dameraur-Levenshtein Distance between two strings. Adapted
* from the algorithm at <a href="http://en.wikipedia.org/wiki/Damerau%E2%80%93Levenshtein_distance">Wikipedia: DamerauLevenshtein distance</a>
*
* @param s1 The first string being compared.
* @param s2 The second string being compared.
* @return The number of substitutions, deletions, insertions, and
* transpositions required to get from s1 to s2.
*/
@SuppressWarnings("DuplicatedCode")
private static int damerauLevenshteinDistance(@Nullable String s1, @Nullable String s2) {
if (s1 == null && s2 == null) {
return 0;
}
if (s1 != null && s2 == null) {
return s1.length();
}
if (s1 == null) {
return s2.length();
}
int s1Len = s1.length();
int s2Len = s2.length();
int[][] H = new int[s1Len + 2][s2Len + 2];
int INF = s1Len + s2Len;
H[0][0] = INF;
for (int i = 0; i <= s1Len; i++) {
H[i + 1][1] = i;
H[i + 1][0] = INF;
}
for (int j = 0; j <= s2Len; j++) {
H[1][j + 1] = j;
H[0][j + 1] = INF;
}
Map<Character, Integer> sd = new HashMap<>();
for (char Letter : (s1 + s2).toCharArray()) {
if (!sd.containsKey(Letter)) {
sd.put(Letter, 0);
}
}
for (int i = 1; i <= s1Len; i++) {
int DB = 0;
for (int j = 1; j <= s2Len; j++) {
int i1 = sd.get(s2.charAt(j - 1));
int j1 = DB;
if (s1.charAt(i - 1) == s2.charAt(j - 1)) {
H[i + 1][j + 1] = H[i][j];
DB = j;
} else {
H[i + 1][j + 1] = Math.min(H[i][j], Math.min(H[i + 1][j], H[i][j + 1])) + 1;
}
H[i + 1][j + 1] = Math.min(H[i + 1][j + 1], H[i1][j1] + (i - i1 - 1) + 1 + (j - j1 - 1));
}
sd.put(s1.charAt(i - 1), i);
}
return H[s1Len + 1][s2Len + 1];
}
// Copy from org/bukkit/command/defaults/HelpCommand.java
private record Candidate(String item, int score) {
@Contract("_, _ -> new")
private static @NotNull Candidate of(String item, int score) {
return new Candidate(item, score);
}
}
}

View File

@@ -1,15 +0,0 @@
package org.leavesmc.leaves.neo_command;
import com.mojang.brigadier.CommandDispatcher;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.server.MinecraftServer;
import org.leavesmc.leaves.neo_command.bot.BotCommand;
import org.leavesmc.leaves.neo_command.leaves.LeavesCommand;
public class LeavesCommands {
public static void registerLeavesCommands() {
CommandDispatcher<CommandSourceStack> dispatcher = MinecraftServer.getServer().getCommands().getDispatcher();
new LeavesCommand().register(dispatcher);
new BotCommand().register(dispatcher);
}
}

View File

@@ -5,7 +5,7 @@ import com.mojang.brigadier.builder.ArgumentBuilder;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.Commands;
import org.jetbrains.annotations.NotNull;
import net.minecraft.server.MinecraftServer;
public class LiteralNode extends CommandNode {
@@ -19,7 +19,17 @@ public class LiteralNode extends CommandNode {
}
@SuppressWarnings("unchecked")
public void register(@NotNull CommandDispatcher<CommandSourceStack> dispatcher) {
dispatcher.register((LiteralArgumentBuilder<CommandSourceStack>) compile());
public void register() {
MinecraftServer.getServer()
.getCommands()
.getDispatcher()
.register((LiteralArgumentBuilder<CommandSourceStack>) compile());
}
public void unregister() {
CommandDispatcher<CommandSourceStack> dispatcher = MinecraftServer.getServer()
.getCommands()
.getDispatcher();
dispatcher.getRoot().removeCommand(name);
}
}

View File

@@ -20,7 +20,7 @@ import java.util.concurrent.CompletableFuture;
public class BotArgument implements CustomArgumentType<ServerBot, String> {
@Override
public ArgumentType<String> getBaseArgumentType() {
return StringArgumentType.string();
return StringArgumentType.word();
}
@Override

View File

@@ -1,33 +1,57 @@
package org.leavesmc.leaves.neo_command.bot;
import com.mojang.brigadier.builder.ArgumentBuilder;
import net.minecraft.commands.CommandSourceStack;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
import org.leavesmc.leaves.LeavesConfig;
import org.leavesmc.leaves.neo_command.CommandNode;
import org.leavesmc.leaves.neo_command.CommandUtils;
import org.leavesmc.leaves.neo_command.LiteralNode;
import org.leavesmc.leaves.neo_command.bot.subcommands.ActionCommand;
import org.leavesmc.leaves.neo_command.bot.subcommands.ConfigCommand;
import org.leavesmc.leaves.neo_command.bot.subcommands.CreateCommand;
import org.leavesmc.leaves.neo_command.bot.subcommands.ListCommand;
import org.leavesmc.leaves.neo_command.bot.subcommands.LoadCommand;
import org.leavesmc.leaves.neo_command.bot.subcommands.SaveCommand;
import java.util.ArrayList;
import java.util.List;
public class BotCommand extends LiteralNode {
public static final BotCommand INSTANCE = new BotCommand();
private static final String PERM_BASE = "bukkit.command.bot";
public BotCommand() {
private BotCommand() {
super("bot_neo");
this.children(
ActionCommand::new,
ListCommand::new,
CreateCommand::new
CreateCommand::new,
LoadCommand::new,
SaveCommand::new,
ConfigCommand::new
);
}
@Override
protected boolean requires(CommandSourceStack source) {
return LeavesConfig.modify.fakeplayer.enable && source.getSender().hasPermission(PERM_BASE);
protected ArgumentBuilder<CommandSourceStack, ?> compile() {
List<String> permissions = new ArrayList<>();
permissions.add(PERM_BASE);
permissions.addAll(this.children.stream().map(CommandNode::getName).toList());
CommandUtils.registerPermissions(permissions);
return super.compile();
}
public static boolean hasPermission(@NotNull CommandSourceStack source, String subcommand) {
CommandSender sender = source.getSender();
@Override
public boolean requires(@NotNull CommandSourceStack source) {
return children.stream().anyMatch(child -> child.requires(source));
}
public static boolean hasPermission(@NotNull CommandSender sender) {
return sender.hasPermission(PERM_BASE);
}
public static boolean hasPermission(@NotNull CommandSender sender, String subcommand) {
return sender.hasPermission(PERM_BASE) || sender.hasPermission(PERM_BASE + "." + subcommand);
}
}

View File

@@ -0,0 +1,17 @@
package org.leavesmc.leaves.neo_command.bot;
import net.minecraft.commands.CommandSourceStack;
import org.jetbrains.annotations.NotNull;
import org.leavesmc.leaves.neo_command.LiteralNode;
public abstract class BotSubcommand extends LiteralNode {
protected BotSubcommand(String name) {
super(name);
}
@Override
public boolean requires(@NotNull CommandSourceStack source) {
return BotCommand.hasPermission(source.getSender(), this.name);
}
}

View File

@@ -3,16 +3,16 @@ package org.leavesmc.leaves.neo_command.bot.subcommands;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import net.minecraft.commands.CommandSourceStack;
import org.jetbrains.annotations.NotNull;
import org.leavesmc.leaves.LeavesConfig;
import org.leavesmc.leaves.bot.ServerBot;
import org.leavesmc.leaves.neo_command.CommandContext;
import org.leavesmc.leaves.neo_command.CustomArgumentNode;
import org.leavesmc.leaves.neo_command.LiteralNode;
import org.leavesmc.leaves.neo_command.bot.BotCommand;
import org.leavesmc.leaves.neo_command.bot.BotSubcommand;
import org.leavesmc.leaves.neo_command.bot.subcommands.action.ListCommand;
import org.leavesmc.leaves.neo_command.bot.subcommands.action.StartCommand;
import org.leavesmc.leaves.neo_command.bot.subcommands.action.StopCommand;
public class ActionCommand extends LiteralNode {
public class ActionCommand extends BotSubcommand {
public ActionCommand() {
super("action");
@@ -20,8 +20,8 @@ public class ActionCommand extends LiteralNode {
}
@Override
protected boolean requires(CommandSourceStack source) {
return BotCommand.hasPermission(source, "action");
public boolean requires(@NotNull CommandSourceStack source) {
return LeavesConfig.modify.fakeplayer.canUseAction && super.requires(source);
}
public static class BotArgument extends CustomArgumentNode<ServerBot, String> {

View File

@@ -0,0 +1,10 @@
package org.leavesmc.leaves.neo_command.bot.subcommands;
import org.leavesmc.leaves.neo_command.bot.BotSubcommand;
public class ConfigCommand extends BotSubcommand {
public ConfigCommand() {
super("config");
}
}

View File

@@ -22,12 +22,12 @@ import org.leavesmc.leaves.bot.BotList;
import org.leavesmc.leaves.event.bot.BotCreateEvent;
import org.leavesmc.leaves.neo_command.ArgumentNode;
import org.leavesmc.leaves.neo_command.CommandContext;
import org.leavesmc.leaves.neo_command.LiteralNode;
import org.leavesmc.leaves.neo_command.bot.BotSubcommand;
import static net.kyori.adventure.text.Component.text;
import static net.minecraft.commands.arguments.DimensionArgument.getDimension;
public class CreateCommand extends LiteralNode {
public class CreateCommand extends BotSubcommand {
public CreateCommand() {
super("create");
@@ -133,7 +133,7 @@ public class CreateCommand extends LiteralNode {
}
@Override
protected boolean requires(@NotNull CommandSourceStack source) {
public boolean requires(@NotNull CommandSourceStack source) {
return source.getSender() instanceof ConsoleCommandSender;
}
}

View File

@@ -18,7 +18,7 @@ import org.leavesmc.leaves.bot.BotList;
import org.leavesmc.leaves.bot.ServerBot;
import org.leavesmc.leaves.neo_command.ArgumentNode;
import org.leavesmc.leaves.neo_command.CommandContext;
import org.leavesmc.leaves.neo_command.LiteralNode;
import org.leavesmc.leaves.neo_command.bot.BotSubcommand;
import java.util.List;
import java.util.Objects;
@@ -31,7 +31,7 @@ import static net.kyori.adventure.text.format.NamedTextColor.*;
import static net.minecraft.commands.arguments.DimensionArgument.getDimension;
public class ListCommand extends LiteralNode {
public class ListCommand extends BotSubcommand {
public ListCommand() {
super("list");

View File

@@ -0,0 +1,80 @@
package org.leavesmc.leaves.neo_command.bot.subcommands;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import net.kyori.adventure.text.format.NamedTextColor;
import net.minecraft.commands.CommandSourceStack;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
import org.leavesmc.leaves.LeavesConfig;
import org.leavesmc.leaves.bot.BotList;
import org.leavesmc.leaves.bot.ServerBot;
import org.leavesmc.leaves.neo_command.ArgumentNode;
import org.leavesmc.leaves.neo_command.CommandContext;
import org.leavesmc.leaves.neo_command.bot.BotSubcommand;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import static io.papermc.paper.adventure.PaperAdventure.asAdventure;
import static net.kyori.adventure.text.Component.join;
import static net.kyori.adventure.text.Component.text;
import static net.kyori.adventure.text.JoinConfiguration.spaces;
public class LoadCommand extends BotSubcommand {
public LoadCommand() {
super("load");
children(BotNameArgument::new);
}
@Override
public boolean requires(@NotNull CommandSourceStack source) {
return LeavesConfig.modify.fakeplayer.canManualSaveAndLoad && super.requires(source);
}
private static class BotNameArgument extends ArgumentNode<String> {
public BotNameArgument() {
super("bot_name", StringArgumentType.word());
}
@Override
protected boolean execute(@NotNull CommandContext context) throws CommandSyntaxException {
String botName = context.getArgument(BotNameArgument.class);
BotList botList = BotList.INSTANCE;
CommandSender sender = context.getSender();
if (!botList.getSavedBotList().contains(botName)) {
throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownArgument().create();
}
ServerBot bot = botList.loadNewBot(botName);
if (bot == null) {
sender.sendMessage(text("Failed to load bot, please check log", NamedTextColor.RED));
return false;
}
sender.sendMessage(join(
spaces(),
text("Successfully loaded bot", NamedTextColor.GRAY),
asAdventure(bot.getDisplayName())
));
return true;
}
@Override
protected CompletableFuture<Suggestions> getSuggestions(CommandContext context, @NotNull SuggestionsBuilder builder) {
BotList botList = BotList.INSTANCE;
Set<String> bots = botList.getSavedBotList().keySet();
if (bots.isEmpty()) {
return builder
.suggest("<NO SAVED BOT EXISTS>", net.minecraft.network.chat.Component.literal("There are no bots saved before, save one first."))
.buildFuture();
}
bots.forEach(builder::suggest);
return builder.buildFuture();
}
}
}

View File

@@ -0,0 +1,57 @@
package org.leavesmc.leaves.neo_command.bot.subcommands;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import net.kyori.adventure.text.format.NamedTextColor;
import net.minecraft.commands.CommandSourceStack;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
import org.leavesmc.leaves.LeavesConfig;
import org.leavesmc.leaves.bot.BotList;
import org.leavesmc.leaves.bot.ServerBot;
import org.leavesmc.leaves.event.bot.BotRemoveEvent;
import org.leavesmc.leaves.neo_command.CommandContext;
import org.leavesmc.leaves.neo_command.CustomArgumentNode;
import org.leavesmc.leaves.neo_command.bot.BotSubcommand;
import static io.papermc.paper.adventure.PaperAdventure.asAdventure;
import static net.kyori.adventure.text.Component.join;
import static net.kyori.adventure.text.Component.text;
import static net.kyori.adventure.text.JoinConfiguration.spaces;
public class SaveCommand extends BotSubcommand {
public SaveCommand() {
super("save");
children(BotArgument::new);
}
@Override
public boolean requires(@NotNull CommandSourceStack source) {
return LeavesConfig.modify.fakeplayer.canManualSaveAndLoad && super.requires(source);
}
private static class BotArgument extends CustomArgumentNode<ServerBot, String> {
public BotArgument() {
super("bot", new org.leavesmc.leaves.neo_command.bot.BotArgument());
}
@Override
protected boolean execute(@NotNull CommandContext context) throws CommandSyntaxException {
ServerBot bot = context.getCustomArgument(BotArgument.class);
CommandSender sender = context.getSender();
BotList botList = BotList.INSTANCE;
if (!botList.removeBot(bot, BotRemoveEvent.RemoveReason.COMMAND, sender, true)) {
sender.sendMessage(text("Failed to save bot, please check log", NamedTextColor.RED));
return false;
}
sender.sendMessage(join(spaces(),
text("Successfully saved bot", NamedTextColor.GRAY),
asAdventure(bot.getDisplayName()),
text("as " + bot.createState.realName(), NamedTextColor.GRAY)
));
return true;
}
}
}

View File

@@ -1,27 +0,0 @@
package org.leavesmc.leaves.neo_command.leaves;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.leavesmc.leaves.neo_command.WrappedArgument;
import java.util.List;
public class ArgumentSuggestions {
@Contract(pure = true)
public static WrappedArgument.@NotNull SuggestionApplier strings(String... values) {
return (context, builder) -> {
for (String s : values) {
builder.suggest(s);
}
};
}
@Contract(pure = true)
public static WrappedArgument.@NotNull SuggestionApplier strings(List<String> values) {
return (context, builder) -> {
for (String s : values) {
builder.suggest(s);
}
};
}
}

View File

@@ -1,26 +1,41 @@
package org.leavesmc.leaves.neo_command.leaves;
import com.mojang.brigadier.builder.ArgumentBuilder;
import net.minecraft.commands.CommandSourceStack;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
import org.leavesmc.leaves.neo_command.CommandNode;
import org.leavesmc.leaves.neo_command.CommandUtils;
import org.leavesmc.leaves.neo_command.LiteralNode;
import org.leavesmc.leaves.neo_command.leaves.subcommands.ConfigCommand;
import java.util.ArrayList;
import java.util.List;
public class LeavesCommand extends LiteralNode {
public static final LeavesCommand INSTANCE = new LeavesCommand();
private static final String PERM_BASE = "bukkit.command.leaves";
public LeavesCommand() {
private LeavesCommand() {
super("leaves_new");
children(ConfigCommand::new);
}
@Override
protected boolean requires(@NotNull CommandSourceStack source) {
return source.getSender().hasPermission(PERM_BASE);
protected ArgumentBuilder<CommandSourceStack, ?> compile() {
List<String> permissions = new ArrayList<>();
permissions.add(PERM_BASE);
permissions.addAll(this.children.stream().map(CommandNode::getName).toList());
CommandUtils.registerPermissions(permissions);
return super.compile();
}
public static boolean hasPermission(@NotNull CommandSourceStack source, String subcommand) {
CommandSender sender = source.getSender();
@Override
public boolean requires(@NotNull CommandSourceStack source) {
return children.stream().anyMatch(child -> child.requires(source));
}
public static boolean hasPermission(@NotNull CommandSender sender, String subcommand) {
return sender.hasPermission(PERM_BASE) || sender.hasPermission(PERM_BASE + "." + subcommand);
}
}

View File

@@ -21,6 +21,7 @@ import static net.kyori.adventure.text.Component.join;
import static net.kyori.adventure.text.Component.text;
import static net.kyori.adventure.text.JoinConfiguration.spaces;
import static net.kyori.adventure.text.format.NamedTextColor.*;
import static org.leavesmc.leaves.neo_command.CommandUtils.getListClosestMatchingLast;
public class ConfigCommand extends LiteralNode {
@@ -30,8 +31,8 @@ public class ConfigCommand extends LiteralNode {
}
@Override
protected boolean requires(@NotNull CommandSourceStack source) {
return LeavesCommand.hasPermission(source, "config");
public boolean requires(@NotNull CommandSourceStack source) {
return LeavesCommand.hasPermission(source.getSender(), "config");
}
private static class PathArgument extends ArgumentNode<String> {
@@ -46,13 +47,10 @@ public class ConfigCommand extends LiteralNode {
String path = context.getArgumentOrDefault(PathArgument.class, "");
int dotIndex = path.lastIndexOf(".");
builder = builder.createOffset(builder.getInput().lastIndexOf(' ') + dotIndex + 2);
LeavesCommandUtil.getListClosestMatchingLast(
context.getSender(),
getListClosestMatchingLast(
path.substring(dotIndex + 1),
GlobalConfigManager.getVerifiedConfigSubPaths(path),
"bukkit.command.leaves"
)
.forEach(builder::suggest);
GlobalConfigManager.getVerifiedConfigSubPaths(path)
).forEach(builder::suggest);
return builder.buildFuture();
}