From 285e70ac987165dccdc05794d52e10094f894a79 Mon Sep 17 00:00:00 2001 From: MC_XiaoHei Date: Sat, 23 Aug 2025 13:43:40 +0800 Subject: [PATCH] fix: fix bot action command --- .../bot/agent/actions/ServerBotAction.java | 17 +- .../bot/agent/actions/ServerLookAction.java | 10 ++ .../bot/agent/actions/ServerMountAction.java | 4 +- .../bot/agent/actions/ServerMoveAction.java | 30 +++- .../agent/actions/ServerRotationAction.java | 7 + .../agent/actions/ServerTimerBotAction.java | 16 +- .../bot/agent/actions/ServerUseBotAction.java | 7 + .../leaves/neo_command/CommandContext.java | 11 +- .../leaves/neo_command/CommandNode.java | 3 +- .../neo_command/CustomArgumentNode.java | 3 +- .../neo_command/CustomArgumentType.java | 2 +- .../leaves/neo_command/bot/BotArgument.java | 11 +- .../leaves/neo_command/bot/BotCommand.java | 6 +- .../bot/subcommands/ActionCommand.java | 11 +- .../bot/subcommands/CreateCommand.java | 152 ++++++++++++++++++ .../bot/subcommands/ListCommand.java | 98 +++++++++++ .../bot/subcommands/action/ListCommand.java | 20 ++- .../bot/subcommands/action/StartCommand.java | 33 ++-- .../bot/subcommands/action/StopCommand.java | 57 ++++--- 19 files changed, 413 insertions(+), 85 deletions(-) create mode 100644 leaves-server/src/main/java/org/leavesmc/leaves/neo_command/bot/subcommands/CreateCommand.java create mode 100644 leaves-server/src/main/java/org/leavesmc/leaves/neo_command/bot/subcommands/ListCommand.java diff --git a/leaves-server/src/main/java/org/leavesmc/leaves/bot/agent/actions/ServerBotAction.java b/leaves-server/src/main/java/org/leavesmc/leaves/bot/agent/actions/ServerBotAction.java index c8e45d1e..73e77c56 100644 --- a/leaves-server/src/main/java/org/leavesmc/leaves/bot/agent/actions/ServerBotAction.java +++ b/leaves-server/src/main/java/org/leavesmc/leaves/bot/agent/actions/ServerBotAction.java @@ -59,12 +59,13 @@ public abstract class ServerBotAction> { public abstract Object asCraft(); - public List> provideReadableActionData() { - return new ArrayList<>(); + public void provideActionData(@NotNull ActionData data) { } - public String getReadableActionDataString() { - return provideReadableActionData().stream() + public String getActionDataString() { + ActionData data = new ActionData(new ArrayList<>()); + provideActionData(data); + return data.raw.stream() .map(pair -> pair.getLeft() + "=" + pair.getRight()) .reduce((a, b) -> a + ", " + b) .orElse("No arguments"); @@ -247,4 +248,12 @@ public abstract class ServerBotAction> { public void setOnStop(Consumer onStop) { this.onStop = onStop; } + + public record ActionData( + List> raw + ) { + public void add(String key, String value) { + raw.add(Pair.of(key, value)); + } + } } diff --git a/leaves-server/src/main/java/org/leavesmc/leaves/bot/agent/actions/ServerLookAction.java b/leaves-server/src/main/java/org/leavesmc/leaves/bot/agent/actions/ServerLookAction.java index 3b1eddea..539a0dfb 100644 --- a/leaves-server/src/main/java/org/leavesmc/leaves/bot/agent/actions/ServerLookAction.java +++ b/leaves-server/src/main/java/org/leavesmc/leaves/bot/agent/actions/ServerLookAction.java @@ -98,6 +98,16 @@ public class ServerLookAction extends ServerBotAction { return target; } + @Override + public void provideActionData(@NotNull ActionData data) { + super.provideActionData(data); + if (target != null) { + data.add("target", target.getName().getString()); + } else { + data.add("position", String.format("(%.2f, %.2f, %.2f)", pos.getX(), pos.getY(), pos.getZ())); + } + } + @Override public boolean doTick(@NotNull ServerBot bot) { if (target != null) { diff --git a/leaves-server/src/main/java/org/leavesmc/leaves/bot/agent/actions/ServerMountAction.java b/leaves-server/src/main/java/org/leavesmc/leaves/bot/agent/actions/ServerMountAction.java index 3719200b..464bb761 100644 --- a/leaves-server/src/main/java/org/leavesmc/leaves/bot/agent/actions/ServerMountAction.java +++ b/leaves-server/src/main/java/org/leavesmc/leaves/bot/agent/actions/ServerMountAction.java @@ -22,8 +22,8 @@ public class ServerMountAction extends ServerBotAction { Location center = bot.getBukkitEntity().getLocation(); List vehicles = center.getNearbyEntitiesByType( Vehicle.class, - 3, - vehicle -> manhattanDistance(bot, ((CraftEntity) vehicle).getHandle()) <= 2 + 4, + vehicle -> manhattanDistance(bot, ((CraftEntity) vehicle).getHandle()) <= 3 ).stream().sorted(Comparator.comparingDouble( (vehicle) -> center.distanceSquared(vehicle.getLocation()) )).toList(); diff --git a/leaves-server/src/main/java/org/leavesmc/leaves/bot/agent/actions/ServerMoveAction.java b/leaves-server/src/main/java/org/leavesmc/leaves/bot/agent/actions/ServerMoveAction.java index bd3c2f62..32104ea1 100644 --- a/leaves-server/src/main/java/org/leavesmc/leaves/bot/agent/actions/ServerMoveAction.java +++ b/leaves-server/src/main/java/org/leavesmc/leaves/bot/agent/actions/ServerMoveAction.java @@ -1,6 +1,7 @@ package org.leavesmc.leaves.bot.agent.actions; import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.exceptions.CommandSyntaxException; import org.jetbrains.annotations.NotNull; import org.leavesmc.leaves.bot.ServerBot; import org.leavesmc.leaves.entity.bot.action.MoveAction.MoveDirection; @@ -9,27 +10,36 @@ import org.leavesmc.leaves.event.bot.BotActionStopEvent; import org.leavesmc.leaves.neo_command.CommandContext; 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; public class ServerMoveAction extends ServerStateBotAction { - + private static final Map NAME_TO_DIRECTION = Arrays.stream(MoveDirection.values()).collect(toMap( + it -> it.name, + it -> it + )); private MoveDirection direction = MoveDirection.FORWARD; public ServerMoveAction() { super("move", ServerMoveAction::new); this.addArgument("direction", StringArgumentType.word()) - .suggests(strings(Arrays.stream(MoveDirection.values()).map(MoveDirection::name).toList())); + .suggests(strings( + Arrays.stream(MoveDirection.values()) + .map((it) -> it.name) + .toList() + )); } @Override - public void loadCommand(@NotNull CommandContext context) { + public void loadCommand(@NotNull CommandContext context) throws CommandSyntaxException { String raw = context.getArgument("direction", String.class); - try { - this.direction = MoveDirection.valueOf(raw); - } catch (IllegalArgumentException e) { - throw new IllegalArgumentException("Invalid move direction: " + raw); + MoveDirection direction = NAME_TO_DIRECTION.get(raw); + if (direction == null) { + throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownArgument().create(); } + this.direction = direction; } @Override @@ -54,6 +64,12 @@ public class ServerMoveAction extends ServerStateBotAction { return true; } + @Override + public void provideActionData(@NotNull ActionData data) { + super.provideActionData(data); + data.add("direction", direction.name); + } + public MoveDirection getDirection() { return direction; } diff --git a/leaves-server/src/main/java/org/leavesmc/leaves/bot/agent/actions/ServerRotationAction.java b/leaves-server/src/main/java/org/leavesmc/leaves/bot/agent/actions/ServerRotationAction.java index 5cbc26aa..03c0e8d0 100644 --- a/leaves-server/src/main/java/org/leavesmc/leaves/bot/agent/actions/ServerRotationAction.java +++ b/leaves-server/src/main/java/org/leavesmc/leaves/bot/agent/actions/ServerRotationAction.java @@ -56,6 +56,13 @@ public class ServerRotationAction extends ServerBotAction return this.pitch; } + @Override + public void provideActionData(@NotNull ActionData data) { + super.provideActionData(data); + data.add("yaw", DF.format(this.yaw)); + data.add("pitch", DF.format(this.pitch)); + } + @Override @NotNull public CompoundTag save(@NotNull CompoundTag nbt) { diff --git a/leaves-server/src/main/java/org/leavesmc/leaves/bot/agent/actions/ServerTimerBotAction.java b/leaves-server/src/main/java/org/leavesmc/leaves/bot/agent/actions/ServerTimerBotAction.java index 81edbeb0..58f21986 100644 --- a/leaves-server/src/main/java/org/leavesmc/leaves/bot/agent/actions/ServerTimerBotAction.java +++ b/leaves-server/src/main/java/org/leavesmc/leaves/bot/agent/actions/ServerTimerBotAction.java @@ -1,12 +1,9 @@ package org.leavesmc.leaves.bot.agent.actions; import net.minecraft.network.chat.Component; -import org.apache.commons.lang3.tuple.Pair; import org.jetbrains.annotations.NotNull; import org.leavesmc.leaves.neo_command.CommandContext; -import java.util.ArrayList; -import java.util.List; import java.util.function.Supplier; import static com.mojang.brigadier.arguments.IntegerArgumentType.integer; @@ -35,12 +32,11 @@ public abstract class ServerTimerBotAction> ex } @Override - public List> provideReadableActionData() { - return new ArrayList<>(List.of( - Pair.of("delay", String.valueOf(this.getStartDelayTick())), - Pair.of("interval", String.valueOf(this.getDoIntervalTick())), - Pair.of("do_number", String.valueOf(this.getDoNumber())), - Pair.of("remaining_do_number", String.valueOf(this.getDoNumberRemaining())) - )); + public void provideActionData(@NotNull ActionData data) { + super.provideActionData(data); + data.add("delay", String.valueOf(this.getStartDelayTick())); + data.add("interval", String.valueOf(this.getDoIntervalTick())); + data.add("do_number", String.valueOf(this.getDoNumber())); + data.add("remaining_do_number", String.valueOf(this.getDoNumberRemaining())); } } diff --git a/leaves-server/src/main/java/org/leavesmc/leaves/bot/agent/actions/ServerUseBotAction.java b/leaves-server/src/main/java/org/leavesmc/leaves/bot/agent/actions/ServerUseBotAction.java index a8292447..67657ec1 100644 --- a/leaves-server/src/main/java/org/leavesmc/leaves/bot/agent/actions/ServerUseBotAction.java +++ b/leaves-server/src/main/java/org/leavesmc/leaves/bot/agent/actions/ServerUseBotAction.java @@ -97,6 +97,13 @@ public abstract class ServerUseBotAction> extend this.alreadyUsedTick++; } + @Override + public void provideActionData(@NotNull ActionData data) { + super.provideActionData(data); + data.add("use_timeout", String.valueOf(this.useTickTimeout)); + data.add("already_used_tick", String.valueOf(this.alreadyUsedTick)); + } + @Override @NotNull public CompoundTag save(@NotNull CompoundTag nbt) { diff --git a/leaves-server/src/main/java/org/leavesmc/leaves/neo_command/CommandContext.java b/leaves-server/src/main/java/org/leavesmc/leaves/neo_command/CommandContext.java index 2796ee16..1ff2c7f4 100644 --- a/leaves-server/src/main/java/org/leavesmc/leaves/neo_command/CommandContext.java +++ b/leaves-server/src/main/java/org/leavesmc/leaves/neo_command/CommandContext.java @@ -4,6 +4,7 @@ import com.mojang.brigadier.Command; import com.mojang.brigadier.RedirectModifier; import com.mojang.brigadier.context.ParsedCommandNode; import com.mojang.brigadier.context.StringRange; +import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.tree.CommandNode; import net.minecraft.commands.CommandSourceStack; import org.bukkit.command.CommandSender; @@ -52,13 +53,13 @@ public class CommandContext { } @SuppressWarnings("unchecked") - public @NotNull V getCustomArgument(final Class> nodeClass) { + public @NotNull V getCustomArgument(final Class> nodeClass) throws CommandSyntaxException { String name = getNameForNode(nodeClass); T raw = (T) source.getArgument(name, Object.class); return CustomArgumentNode.transform(nodeClass, raw); } - public @NotNull V getArgumentOrDefault(final Class> nodeClass, final V defaultValue) { + public V getArgumentOrDefault(final Class> nodeClass, final V defaultValue) { try { return getArgument(nodeClass); } catch (IllegalArgumentException e) { @@ -74,7 +75,7 @@ public class CommandContext { } } - public V getCustomArgumentOrDefault(final Class> nodeClass, final V defaultValue) { + public V getCustomArgumentOrDefault(final Class> nodeClass, final V defaultValue) throws CommandSyntaxException { try { return getCustomArgument(nodeClass); } catch (IllegalArgumentException e) { @@ -121,4 +122,8 @@ public class CommandContext { public boolean isForked() { return source.isForked(); } + + public com.mojang.brigadier.context.CommandContext getMojangContext() { + return source; + } } diff --git a/leaves-server/src/main/java/org/leavesmc/leaves/neo_command/CommandNode.java b/leaves-server/src/main/java/org/leavesmc/leaves/neo_command/CommandNode.java index a4971c32..7c0da867 100644 --- a/leaves-server/src/main/java/org/leavesmc/leaves/neo_command/CommandNode.java +++ b/leaves-server/src/main/java/org/leavesmc/leaves/neo_command/CommandNode.java @@ -1,6 +1,7 @@ package org.leavesmc.leaves.neo_command; import com.mojang.brigadier.builder.ArgumentBuilder; +import com.mojang.brigadier.exceptions.CommandSyntaxException; import net.minecraft.commands.CommandSourceStack; import org.jetbrains.annotations.NotNull; @@ -30,7 +31,7 @@ public abstract class CommandNode { protected abstract ArgumentBuilder compileBase(); - protected boolean execute(CommandContext context) { + protected boolean execute(CommandContext context) throws CommandSyntaxException { return true; } diff --git a/leaves-server/src/main/java/org/leavesmc/leaves/neo_command/CustomArgumentNode.java b/leaves-server/src/main/java/org/leavesmc/leaves/neo_command/CustomArgumentNode.java index 9c7a5240..75fdc784 100644 --- a/leaves-server/src/main/java/org/leavesmc/leaves/neo_command/CustomArgumentNode.java +++ b/leaves-server/src/main/java/org/leavesmc/leaves/neo_command/CustomArgumentNode.java @@ -2,6 +2,7 @@ package org.leavesmc.leaves.neo_command; import com.mojang.brigadier.builder.ArgumentBuilder; import com.mojang.brigadier.builder.RequiredArgumentBuilder; +import com.mojang.brigadier.exceptions.CommandSyntaxException; import net.minecraft.commands.CommandSourceStack; import org.jetbrains.annotations.NotNull; @@ -17,7 +18,7 @@ public class CustomArgumentNode extends ArgumentNode { TYPES.put(getClass(), argumentType); } - public static T transform(Class> nodeClass, B base) { + public static T transform(Class> nodeClass, B base) throws CommandSyntaxException { @SuppressWarnings("unchecked") CustomArgumentType type = (CustomArgumentType) TYPES.get(nodeClass); if (type == null) { diff --git a/leaves-server/src/main/java/org/leavesmc/leaves/neo_command/CustomArgumentType.java b/leaves-server/src/main/java/org/leavesmc/leaves/neo_command/CustomArgumentType.java index f8c076b6..458ac711 100644 --- a/leaves-server/src/main/java/org/leavesmc/leaves/neo_command/CustomArgumentType.java +++ b/leaves-server/src/main/java/org/leavesmc/leaves/neo_command/CustomArgumentType.java @@ -11,7 +11,7 @@ public interface CustomArgumentType { ArgumentType getBaseArgumentType(); - T transform(B value); + T transform(B value) throws CommandSyntaxException; CompletableFuture getSuggestions(CommandContext context, SuggestionsBuilder builder) throws CommandSyntaxException; } diff --git a/leaves-server/src/main/java/org/leavesmc/leaves/neo_command/bot/BotArgument.java b/leaves-server/src/main/java/org/leavesmc/leaves/neo_command/bot/BotArgument.java index 08b88409..d65d41bf 100644 --- a/leaves-server/src/main/java/org/leavesmc/leaves/neo_command/bot/BotArgument.java +++ b/leaves-server/src/main/java/org/leavesmc/leaves/neo_command/bot/BotArgument.java @@ -2,8 +2,10 @@ package org.leavesmc.leaves.neo_command.bot; import com.mojang.brigadier.arguments.ArgumentType; 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.minecraft.network.chat.Component; import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.leavesmc.leaves.bot.ServerBot; @@ -22,16 +24,19 @@ public class BotArgument implements CustomArgumentType { } @Override - public ServerBot transform(String value) { + public ServerBot transform(String value) throws CommandSyntaxException { CraftBot craftBot = (CraftBot) Bukkit.getBotManager().getBot(value); if (craftBot == null) { - return null; + throw new CommandSyntaxException( + CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownArgument(), + Component.literal("Bot with name '" + value + "' does not exist") + ); } return craftBot.getHandle(); } @Override - public CompletableFuture getSuggestions(CommandContext context, SuggestionsBuilder builder) { + public CompletableFuture getSuggestions(CommandContext context, SuggestionsBuilder builder) throws CommandSyntaxException { Collection bots = Bukkit.getBotManager().getBots(); if (bots.isEmpty()) { return builder diff --git a/leaves-server/src/main/java/org/leavesmc/leaves/neo_command/bot/BotCommand.java b/leaves-server/src/main/java/org/leavesmc/leaves/neo_command/bot/BotCommand.java index 918ed621..f505d7a9 100644 --- a/leaves-server/src/main/java/org/leavesmc/leaves/neo_command/bot/BotCommand.java +++ b/leaves-server/src/main/java/org/leavesmc/leaves/neo_command/bot/BotCommand.java @@ -6,6 +6,8 @@ import org.jetbrains.annotations.NotNull; import org.leavesmc.leaves.LeavesConfig; import org.leavesmc.leaves.neo_command.LiteralNode; import org.leavesmc.leaves.neo_command.bot.subcommands.ActionCommand; +import org.leavesmc.leaves.neo_command.bot.subcommands.CreateCommand; +import org.leavesmc.leaves.neo_command.bot.subcommands.ListCommand; public class BotCommand extends LiteralNode { private static final String PERM_BASE = "bukkit.command.bot"; @@ -13,7 +15,9 @@ public class BotCommand extends LiteralNode { public BotCommand() { super("bot_neo"); this.children( - ActionCommand::new + ActionCommand::new, + ListCommand::new, + CreateCommand::new ); } diff --git a/leaves-server/src/main/java/org/leavesmc/leaves/neo_command/bot/subcommands/ActionCommand.java b/leaves-server/src/main/java/org/leavesmc/leaves/neo_command/bot/subcommands/ActionCommand.java index ad349d11..81591c28 100644 --- a/leaves-server/src/main/java/org/leavesmc/leaves/neo_command/bot/subcommands/ActionCommand.java +++ b/leaves-server/src/main/java/org/leavesmc/leaves/neo_command/bot/subcommands/ActionCommand.java @@ -1,8 +1,8 @@ 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.jetbrains.annotations.Nullable; import org.leavesmc.leaves.bot.ServerBot; import org.leavesmc.leaves.neo_command.CommandContext; import org.leavesmc.leaves.neo_command.CustomArgumentNode; @@ -35,13 +35,8 @@ public class ActionCommand extends LiteralNode { ); } - public static @Nullable ServerBot getBot(@NotNull CommandContext context) { - try { - return context.getCustomArgument(BotArgument.class); - } catch (IllegalArgumentException e) { - context.getSender().sendMessage("This bot does not exist."); - return null; - } + public static @NotNull ServerBot getBot(@NotNull CommandContext context) throws CommandSyntaxException { + return context.getCustomArgument(BotArgument.class); } } } \ No newline at end of file diff --git a/leaves-server/src/main/java/org/leavesmc/leaves/neo_command/bot/subcommands/CreateCommand.java b/leaves-server/src/main/java/org/leavesmc/leaves/neo_command/bot/subcommands/CreateCommand.java new file mode 100644 index 00000000..5fecbd06 --- /dev/null +++ b/leaves-server/src/main/java/org/leavesmc/leaves/neo_command/bot/subcommands/CreateCommand.java @@ -0,0 +1,152 @@ +package org.leavesmc.leaves.neo_command.bot.subcommands; + +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import net.kyori.adventure.text.format.NamedTextColor; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.arguments.DimensionArgument; +import net.minecraft.commands.arguments.coordinates.Coordinates; +import net.minecraft.commands.arguments.coordinates.Vec3Argument; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.phys.Vec3; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.command.CommandSender; +import org.bukkit.command.ConsoleCommandSender; +import org.bukkit.entity.Entity; +import org.jetbrains.annotations.NotNull; +import org.leavesmc.leaves.LeavesConfig; +import org.leavesmc.leaves.bot.BotCreateState; +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 static net.kyori.adventure.text.Component.text; +import static net.minecraft.commands.arguments.DimensionArgument.getDimension; + +public class CreateCommand extends LiteralNode { + + public CreateCommand() { + super("create"); + children(NameArgument::new); + } + + protected static boolean handleCreateCommand(@NotNull CommandContext context) throws CommandSyntaxException { + CommandSender sender = context.getSender(); + + String name = context.getArgument(NameArgument.class); + if (!canCreate(sender, name)) { + return false; + } + String skinName = context.getArgumentOrDefault(SkinNameArgument.class, name); + + World world; + try { + world = getDimension(context.getMojangContext(), "world").getWorld(); + } catch (IllegalArgumentException e) { + if (!(sender instanceof Entity entity)) { + sender.sendMessage(text("Must specify world and location when executed by console", NamedTextColor.RED)); + return false; + } + world = entity.getWorld(); + } + + Location location = Bukkit.getWorlds().getFirst().getSpawnLocation(); + Coordinates coords = context.getArgumentOrDefault(LocationArgument.class, null); + if (coords != null) { + Vec3 vec3 = coords.getPosition(context.getSource()); + location = new Location(world, vec3.x, vec3.y, vec3.z); + } else if (sender instanceof Entity entity) { + location = entity.getLocation(); + } + + BotCreateState + .builder(name, location) + .createReason(BotCreateEvent.CreateReason.COMMAND) + .skinName(skinName) + .creator(sender) + .spawnWithSkin(null); + + return true; + } + + private static boolean canCreate(CommandSender sender, @NotNull String name) { + BotList botList = BotList.INSTANCE; + if (!name.matches("^[a-zA-Z0-9_]{4,16}$")) { + sender.sendMessage(text("This name is illegal", NamedTextColor.RED)); + return false; + } + + if (Bukkit.getPlayerExact(name) != null || botList.getBotByName(name) != null) { + sender.sendMessage(text("This bot is already in server", NamedTextColor.RED)); + return false; + } + + if (LeavesConfig.modify.fakeplayer.unableNames.contains(name)) { + sender.sendMessage(text("This name is not allowed", NamedTextColor.RED)); + return false; + } + + if (botList.bots.size() >= LeavesConfig.modify.fakeplayer.limit) { + sender.sendMessage(text("Bot number limit exceeded", NamedTextColor.RED)); + return false; + } + + return true; + } + + private static class NameArgument extends ArgumentNode { + + public NameArgument() { + super("name", StringArgumentType.word()); + children(SkinNameArgument::new); + } + + @Override + protected boolean execute(CommandContext context) throws CommandSyntaxException { + return handleCreateCommand(context); + } + } + + private static class SkinNameArgument extends ArgumentNode { + + public SkinNameArgument() { + super("skin_name", StringArgumentType.word()); + children(WorldArgument::new); + } + + @Override + protected boolean execute(CommandContext context) throws CommandSyntaxException { + return handleCreateCommand(context); + } + + } + + private static class WorldArgument extends ArgumentNode { + + public WorldArgument() { + super("world", DimensionArgument.dimension()); + children(LocationArgument::new); + } + + @Override + protected boolean requires(@NotNull CommandSourceStack source) { + return source.getSender() instanceof ConsoleCommandSender; + } + } + + private static class LocationArgument extends ArgumentNode { + + public LocationArgument() { + super("location", Vec3Argument.vec3(true)); + } + + @Override + protected boolean execute(CommandContext context) throws CommandSyntaxException { + return handleCreateCommand(context); + } + } +} diff --git a/leaves-server/src/main/java/org/leavesmc/leaves/neo_command/bot/subcommands/ListCommand.java b/leaves-server/src/main/java/org/leavesmc/leaves/neo_command/bot/subcommands/ListCommand.java new file mode 100644 index 00000000..9a43293f --- /dev/null +++ b/leaves-server/src/main/java/org/leavesmc/leaves/neo_command/bot/subcommands/ListCommand.java @@ -0,0 +1,98 @@ +package org.leavesmc.leaves.neo_command.bot.subcommands; + +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import io.papermc.paper.adventure.PaperAdventure; +import net.kyori.adventure.text.Component; +import net.minecraft.commands.arguments.DimensionArgument; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.player.Player; +import org.bukkit.Bukkit; +import org.bukkit.World; +import org.bukkit.command.CommandSender; +import org.bukkit.craftbukkit.CraftWorld; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +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.LiteralNode; + +import java.util.List; +import java.util.Objects; + +import static net.kyori.adventure.text.Component.join; +import static net.kyori.adventure.text.Component.text; +import static net.kyori.adventure.text.JoinConfiguration.noSeparators; +import static net.kyori.adventure.text.event.HoverEvent.showText; +import static net.kyori.adventure.text.format.NamedTextColor.*; +import static net.minecraft.commands.arguments.DimensionArgument.getDimension; + + +public class ListCommand extends LiteralNode { + + public ListCommand() { + super("list"); + children(WorldArgument::new); + } + + @Override + protected boolean execute(@NotNull CommandContext context) throws CommandSyntaxException { + Component msg = Bukkit.getWorlds().stream() + .map(ListCommand::getBotListMessage) + .filter(Objects::nonNull) + .reduce((a, b) -> a.append(text("\n")).append(b)) + .orElseGet(() -> text("No bots on the server", GRAY)); + context.getSender().sendMessage(join(noSeparators(), + text("Total bot number: ", GRAY), + text(BotList.INSTANCE.bots.size(), AQUA).hoverEvent(showText(text("current bot count"))), + text("/", GRAY), + text(LeavesConfig.modify.fakeplayer.limit, AQUA).hoverEvent(showText(text("bot count limit"))) + )); + context.getSender().sendMessage(msg); + return true; + } + + protected static @Nullable Component getBotListMessage(@NotNull World world) { + BotList botList = BotList.INSTANCE; + List botsInLevel = botList.bots.stream() + .filter((bot) -> bot.getBukkitEntity().getWorld().equals(world)) + .toList(); + if (botsInLevel.isEmpty()) { + return null; + } + Component botsMsg = botsInLevel.stream() + .map(Player::getDisplayName) + .map(PaperAdventure::asAdventure) + .reduce((a, b) -> a.append(text(", ", GRAY)).append(b)) + .get(); + String worldLocation = ((CraftWorld) world).getHandle().dimension().location().toString(); + return join(noSeparators(), + text(world.getName(), AQUA).hoverEvent(showText(text(worldLocation))), + text(" (" + botsInLevel.size() + ")\n", GRAY), + botsMsg + ); + } + + private static class WorldArgument extends ArgumentNode { + + protected WorldArgument() { + super("world", DimensionArgument.dimension()); + } + + @Override + protected boolean execute(@NotNull CommandContext context) throws CommandSyntaxException { + ServerLevel dimension = getDimension(context.getMojangContext(), "world"); + Component botListMessage = getBotListMessage(dimension.getWorld()); + CommandSender sender = context.getSender(); + if (botListMessage == null) { + sender.sendMessage(text("No bots in that world", RED)); + } else { + sender.sendMessage(text("Bot in ").append(botListMessage)); + } + return true; + } + } +} diff --git a/leaves-server/src/main/java/org/leavesmc/leaves/neo_command/bot/subcommands/action/ListCommand.java b/leaves-server/src/main/java/org/leavesmc/leaves/neo_command/bot/subcommands/action/ListCommand.java index 3a32a4b7..754309e0 100644 --- a/leaves-server/src/main/java/org/leavesmc/leaves/neo_command/bot/subcommands/action/ListCommand.java +++ b/leaves-server/src/main/java/org/leavesmc/leaves/neo_command/bot/subcommands/action/ListCommand.java @@ -1,5 +1,7 @@ package org.leavesmc.leaves.neo_command.bot.subcommands.action; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import org.bukkit.command.CommandSender; import org.jetbrains.annotations.NotNull; import org.leavesmc.leaves.bot.ServerBot; import org.leavesmc.leaves.bot.agent.actions.ServerBotAction; @@ -9,6 +11,7 @@ import org.leavesmc.leaves.neo_command.bot.subcommands.ActionCommand; import java.util.List; +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; @@ -23,24 +26,25 @@ public class ListCommand extends LiteralNode { } @Override - protected boolean execute(@NotNull CommandContext context) { + protected boolean execute(@NotNull CommandContext context) throws CommandSyntaxException { ServerBot bot = ActionCommand.BotArgument.getBot(context); - if (bot == null) { - return false; - } + CommandSender sender = context.getSender(); List> actions = bot.getBotActions(); if (actions.isEmpty()) { - context.getSender().sendMessage("This bot has no active actions"); + sender.sendMessage(text("This bot has no active actions", GRAY)); return true; } - context.getSender().sendMessage(bot.getScoreboardName() + "'s action list:"); + sender.sendMessage( + asAdventure(bot.getDisplayName()) + .append(text("'s action list:", GRAY)) + ); for (int i = 0; i < actions.size(); i++) { ServerBotAction action = actions.get(i); - context.getSender().sendMessage(join(spaces(), + sender.sendMessage(join(spaces(), text(i, GRAY), - text(action.getName(), AQUA).hoverEvent(showText(text(action.getReadableActionDataString()))) + text(action.getName(), AQUA).hoverEvent(showText(text(action.getActionDataString()))) )); } diff --git a/leaves-server/src/main/java/org/leavesmc/leaves/neo_command/bot/subcommands/action/StartCommand.java b/leaves-server/src/main/java/org/leavesmc/leaves/neo_command/bot/subcommands/action/StartCommand.java index 0ac8073d..ea1a76ce 100644 --- a/leaves-server/src/main/java/org/leavesmc/leaves/neo_command/bot/subcommands/action/StartCommand.java +++ b/leaves-server/src/main/java/org/leavesmc/leaves/neo_command/bot/subcommands/action/StartCommand.java @@ -38,21 +38,20 @@ public class StartCommand extends LiteralNode { .forEach(this::children); } - private boolean executor(CommandContext context, @NotNull ServerBotAction action) throws CommandSyntaxException { + private boolean handleStartCommand(CommandContext context, @NotNull ServerBotAction action) throws CommandSyntaxException { ServerBot bot = getBot(context); CommandSender sender = context.getSender(); - if (bot == null) { - return false; - } + action.loadCommand(context); if (bot.addBotAction(action, sender)) { sender.sendMessage(join(spaces(), text("Action", GRAY), - text(action.getName(), AQUA).hoverEvent(showText(text(action.getReadableActionDataString()))), + text(action.getName(), AQUA).hoverEvent(showText(text(action.getActionDataString()))), text("has been issued to", GRAY), asAdventure(bot.getDisplayName()) )); } + return true; } @@ -63,16 +62,17 @@ public class StartCommand extends LiteralNode { protected ArgumentBuilder compile() { ArgumentBuilder builder = super.compile(); - for (Map.Entry>>> entry : action.getArguments().entrySet()) { + Map>>> arguments = action.getArguments(); + Command executor = context -> { + if (handleStartCommand(new CommandContext(context), action)) { + return Command.SINGLE_SUCCESS; + } else { + return 0; + } + }; + for (Map.Entry>>> entry : arguments.entrySet()) { List>> value = entry.getValue(); ArgumentBuilder branchArgumentBuilder = null; - Command executor = context -> { - if (executor(new CommandContext(context), action)) { - return Command.SINGLE_SUCCESS; - } else { - return 0; - } - }; for (Pair> stringWrappedArgumentPair : value.reversed()) { WrappedArgument argument = stringWrappedArgumentPair.getRight(); @@ -86,7 +86,7 @@ public class StartCommand extends LiteralNode { } } - if (value.getFirst().getRight().isOptional()) { + if (value.getFirst().getRight().isOptional() || value.isEmpty()) { builder = builder.executes(executor); } @@ -94,6 +94,11 @@ public class StartCommand extends LiteralNode { builder = builder.then(branchArgumentBuilder); } } + + if (arguments.isEmpty()) { + builder = builder.executes(executor); + } + return builder; } }; diff --git a/leaves-server/src/main/java/org/leavesmc/leaves/neo_command/bot/subcommands/action/StopCommand.java b/leaves-server/src/main/java/org/leavesmc/leaves/neo_command/bot/subcommands/action/StopCommand.java index 79c2cfae..e23a077f 100644 --- a/leaves-server/src/main/java/org/leavesmc/leaves/neo_command/bot/subcommands/action/StopCommand.java +++ b/leaves-server/src/main/java/org/leavesmc/leaves/neo_command/bot/subcommands/action/StopCommand.java @@ -1,6 +1,7 @@ package org.leavesmc.leaves.neo_command.bot.subcommands.action; import com.mojang.brigadier.arguments.IntegerArgumentType; +import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.suggestion.Suggestions; import com.mojang.brigadier.suggestion.SuggestionsBuilder; import net.minecraft.network.chat.Component; @@ -15,6 +16,7 @@ import org.leavesmc.leaves.neo_command.LiteralNode; import org.leavesmc.leaves.neo_command.bot.subcommands.ActionCommand; import java.util.HashSet; +import java.util.List; import java.util.Set; import java.util.concurrent.CompletableFuture; @@ -40,29 +42,29 @@ public class StopCommand extends LiteralNode { } @Override - protected CompletableFuture getSuggestions(CommandContext context, SuggestionsBuilder builder) { + protected CompletableFuture getSuggestions(CommandContext context, SuggestionsBuilder builder) throws CommandSyntaxException { ServerBot bot = ActionCommand.BotArgument.getBot(context); - if (bot == null) { - return Suggestions.empty(); - } + for (int i = 0; i < bot.getBotActions().size(); i++) { ServerBotAction action = bot.getBotActions().get(i); builder.suggest(String.valueOf(i), Component.literal(action.getName())); } + return builder.buildFuture(); } @Override - protected boolean execute(CommandContext context) { + protected boolean execute(CommandContext context) throws CommandSyntaxException { ServerBot bot = ActionCommand.BotArgument.getBot(context); CommandSender sender = context.getSender(); - if (bot == null) { - return false; + + int index = context.getArgument(StopIndexArgument.class); + int maxIndex = bot.getBotActions().size() - 1; + if (maxIndex < 0) { + throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownArgument().create(); } - int index = context.getArgument("index", Integer.class); - if (index < 0 || index >= bot.getBotActions().size()) { - sender.sendMessage(text("Invalid index.", RED)); - return false; + if (index > maxIndex) { + throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.integerTooHigh().create(index, maxIndex); } ServerBotAction action = bot.getBotActions().get(index); @@ -77,7 +79,7 @@ public class StopCommand extends LiteralNode { text("Already stopped", GRAY), asAdventure(bot.getDisplayName()).append(text("'s", GRAY)), text("action", GRAY), - text(action.getName(), AQUA).hoverEvent(showText(text(action.getReadableActionDataString()))) + text(action.getName(), AQUA).hoverEvent(showText(text(action.getActionDataString()))) )); } else { sender.sendMessage(text("Action stop cancelled by a plugin", RED)); @@ -93,17 +95,21 @@ public class StopCommand extends LiteralNode { } @Override - protected boolean execute(@NotNull CommandContext context) { + protected boolean execute(@NotNull CommandContext context) throws CommandSyntaxException { ServerBot bot = ActionCommand.BotArgument.getBot(context); - if (bot == null) { - return false; + + List> actions = bot.getBotActions(); + CommandSender sender = context.getSender(); + if (actions.isEmpty()) { + sender.sendMessage(text("This bot has no active actions", GRAY)); + return true; } + Set> canceled = new HashSet<>(); Set> forRemoval = new HashSet<>(); - for (int i = 0; i < bot.getBotActions().size(); i++) { - ServerBotAction action = bot.getBotActions().get(i); + for (ServerBotAction action : actions) { BotActionStopEvent event = new BotActionStopEvent( - bot.getBukkitEntity(), action.getName(), action.getUUID(), BotActionStopEvent.Reason.COMMAND, context.getSender() + bot.getBukkitEntity(), action.getName(), action.getUUID(), BotActionStopEvent.Reason.COMMAND, sender ); event.callEvent(); if (!event.isCancelled()) { @@ -114,14 +120,21 @@ public class StopCommand extends LiteralNode { } } bot.getBotActions().removeAll(forRemoval); + if (canceled.isEmpty()) { - context.getSender().sendMessage(bot.getScoreboardName() + "'s action list cleared."); + sender.sendMessage(join(spaces(), + asAdventure(bot.getDisplayName()).append(text("'s", GRAY)), + text("'s action list cleared", GRAY) + )); } else { - context.getSender().sendMessage("already tried to clear" + bot.getScoreboardName() + "'s action list, but following actions' stop was canceled by plugin:"); + sender.sendMessage(join(spaces(), + text("Tried to clear", GRAY), + asAdventure(bot.getDisplayName()).append(text("'s", GRAY)), + text("'s action list, but following actions' stop was canceled by plugin:", GRAY) + )); for (ServerBotAction action : canceled) { context.getSender().sendMessage( - text(action.getName(), AQUA) - .hoverEvent(showText(text(action.getReadableActionDataString()))) + text(action.getName(), AQUA).hoverEvent(showText(text(action.getActionDataString()))) ); } }