package com.github.zly2006.carpetslsaddition.command; import carpet.fakes.ServerPlayerInterface; import carpet.helpers.EntityPlayerActionPack; import carpet.patches.EntityPlayerMPFake; import com.github.zly2006.carpetslsaddition.SLSCarpetSettings; import com.github.zly2006.carpetslsaddition.util.access.SLSBotAccessor; import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.arguments.IntegerArgumentType; import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.context.CommandContext; import net.minecraft.commands.CommandSourceStack; import net.minecraft.commands.Commands; import net.minecraft.commands.arguments.coordinates.RotationArgument; import net.minecraft.commands.arguments.coordinates.Vec3Argument; import net.minecraft.core.Direction; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerPlayer; import java.util.ArrayList; import java.util.List; import java.util.function.Consumer; import static net.minecraft.commands.Commands.argument; import static net.minecraft.commands.Commands.literal; /** * /botall —— 批量控制本模组生成的所有假人。 * *

与 {@link BotCommand} 的区别:{@code /bot } 操作单个指定玩家, * 而 {@code /botall} 对其余命令中出现的所有本模组假人(isBot()==true)批量执行同一操作。 */ public class BotAllCommand { public static void register(CommandDispatcher dispatcher) { LiteralArgumentBuilder command = literal("botall") .requires(source -> Commands.LEVEL_OWNERS.check(source.permissions()) || me.lucko.fabric.api.permissions.v0.Permissions.check(source, "slsaddition.command.bot")) .then(literal("stop").executes(batch(EntityPlayerActionPack::stopAll))) .then(actionCommand("use", EntityPlayerActionPack.ActionType.USE)) .then(actionCommand("jump", EntityPlayerActionPack.ActionType.JUMP)) .then(actionCommand("attack", EntityPlayerActionPack.ActionType.ATTACK)) .then(actionCommand("drop", EntityPlayerActionPack.ActionType.DROP_ITEM)) .then(dropCommand("dropStack", true)) .then(actionCommand("swapHands", EntityPlayerActionPack.ActionType.SWAP_HANDS)) .then(literal("hotbar").then(argument("slot", IntegerArgumentType.integer(1, 9)) .executes(c -> batchAp(c, ap -> ap.setSlot(IntegerArgumentType.getInteger(c, "slot")))))) .then(literal("mount").executes(batch(ap -> ap.mount(true))) .then(literal("anything").executes(batch(ap -> ap.mount(false))))) .then(literal("dismount").executes(batch(EntityPlayerActionPack::dismount))) .then(literal("sneak").executes(batch(ap -> ap.setSneaking(true)))) .then(literal("unsneak").executes(batch(ap -> ap.setSneaking(false)))) .then(literal("sprint").executes(batch(ap -> ap.setSprinting(true)))) .then(literal("unsprint").executes(batch(ap -> ap.setSprinting(false)))) .then(literal("look") .then(literal("north").executes(batch(ap -> ap.look(Direction.NORTH)))) .then(literal("south").executes(batch(ap -> ap.look(Direction.SOUTH)))) .then(literal("east").executes(batch(ap -> ap.look(Direction.EAST)))) .then(literal("west").executes(batch(ap -> ap.look(Direction.WEST)))) .then(literal("up").executes(batch(ap -> ap.look(Direction.UP)))) .then(literal("down").executes(batch(ap -> ap.look(Direction.DOWN)))) .then(literal("at").then(argument("position", Vec3Argument.vec3()) .executes(c -> batchAp(c, ap -> ap.lookAt(Vec3Argument.getVec3(c, "position")))))) .then(argument("direction", RotationArgument.rotation()) .executes(c -> batchAp(c, ap -> ap.look(RotationArgument.getRotation(c, "direction").getRotation(c.getSource())))))) .then(literal("turn") .then(literal("left").executes(batch(ap -> ap.turn(-90, 0)))) .then(literal("right").executes(batch(ap -> ap.turn(90, 0)))) .then(literal("back").executes(batch(ap -> ap.turn(180, 0)))) .then(argument("rotation", RotationArgument.rotation()) .executes(c -> batchAp(c, ap -> ap.turn(RotationArgument.getRotation(c, "rotation").getRotation(c.getSource())))))) .then(literal("move").executes(batch(EntityPlayerActionPack::stopMovement)) .then(literal("forward").executes(batch(ap -> ap.setForward(1)))) .then(literal("backward").executes(batch(ap -> ap.setForward(-1)))) .then(literal("left").executes(batch(ap -> ap.setStrafing(1)))) .then(literal("right").executes(batch(ap -> ap.setStrafing(-1))))) .then(literal("sit").executes(BotAllCommand::sitAll)) .then(literal("stand").executes(BotAllCommand::standAll)) .then(literal("spawn") .then(literal("list").executes(BotAllCommand::spawnFromList)) .then(argument("prefix", com.mojang.brigadier.arguments.StringArgumentType.word()) .then(argument("count", IntegerArgumentType.integer(1, 1000)) .executes(BotAllCommand::spawnBatch)))) .then(literal("kill").executes(BotAllCommand::killBatch)); dispatcher.register(command); } private static LiteralArgumentBuilder actionCommand(String name, EntityPlayerActionPack.ActionType type) { return literal(name) .executes(batch(ap -> ap.start(type, EntityPlayerActionPack.Action.once()))) .then(literal("once").executes(batch(ap -> ap.start(type, EntityPlayerActionPack.Action.once())))) .then(literal("continuous").executes(batch(ap -> ap.start(type, EntityPlayerActionPack.Action.continuous())))) .then(literal("interval").then(argument("ticks", IntegerArgumentType.integer(1)) .executes(c -> batchAp(c, ap -> ap.start(type, EntityPlayerActionPack.Action.interval(IntegerArgumentType.getInteger(c, "ticks"))))))); } private static LiteralArgumentBuilder dropCommand(String name, boolean dropAll) { return literal(name) .then(literal("all").executes(batch(ap -> ap.drop(-2, dropAll)))) .then(literal("mainhand").executes(batch(ap -> ap.drop(-1, dropAll)))) .then(literal("offhand").executes(batch(ap -> ap.drop(40, dropAll)))) .then(argument("slot", IntegerArgumentType.integer(0, 40)) .executes(c -> batchAp(c, ap -> ap.drop(IntegerArgumentType.getInteger(c, "slot"), dropAll)))); } /** 批量执行一个 action pack 操作。 */ private static int batchAp(CommandContext context, Consumer action) { List bots = collectBots(context.getSource()); for (ServerPlayer bot : bots) { action.accept(((ServerPlayerInterface) bot).getActionPack()); } final int n = bots.size(); context.getSource().sendSuccess( () -> net.minecraft.network.chat.Component.literal("[SLSA] 已对 " + n + " 个假人执行操作"), false); return n; } private static com.mojang.brigadier.Command batch(Consumer action) { return c -> batchAp(c, action); } /** 收集所有本模组生成的假人(受名单限制:名单非空时只取名单内的)。 */ private static List collectBots(CommandSourceStack source) { MinecraftServer server = source.getServer(); List bots = new ArrayList<>(); boolean filterByList = !com.github.zly2006.carpetslsaddition.util.BotListManager.isEmpty(); for (ServerPlayer player : server.getPlayerList().getPlayers()) { if (player instanceof EntityPlayerMPFake && ((SLSBotAccessor) player).carpet_SLS_Addition$isBot()) { if (filterByList && !com.github.zly2006.carpetslsaddition.util.BotListManager.contains(player.getScoreboardName())) { continue; } bots.add(player); } } return bots; } private static int sitAll(CommandContext context) { if (!SLSCarpetSettings.canUseSitCommand) { context.getSource().sendSuccess(() -> net.minecraft.network.chat.Component.literal( "[SLSA] 坐下功能未启用(canUseSitCommand)"), false); return 0; } List bots = collectBots(context.getSource()); int ok = 0; for (ServerPlayer bot : bots) { if (SitCommand.sitPlayer(bot)) ok++; } final int done = ok; context.getSource().sendSuccess(() -> net.minecraft.network.chat.Component.literal( "[SLSA] 已让 " + done + "/" + bots.size() + " 个假人坐下"), false); return ok; } private static int standAll(CommandContext context) { List bots = collectBots(context.getSource()); int ok = 0; for (ServerPlayer bot : bots) { if (bot.getVehicle() != null) { bot.stopRiding(); ok++; } } final int done = ok; context.getSource().sendSuccess(() -> net.minecraft.network.chat.Component.literal( "[SLSA] 已让 " + done + "/" + bots.size() + " 个假人站起"), false); return ok; } /** 一键召唤名单里的所有假人(执行者位置)。 */ private static int spawnFromList(CommandContext context) { CommandSourceStack source = context.getSource(); java.util.Set names = com.github.zly2006.carpetslsaddition.util.BotListManager.getNames(); if (names.isEmpty()) { source.sendSuccess(() -> net.minecraft.network.chat.Component.literal( "[SLSA] 名单为空,先用 /botlist add 添加名字"), false); return 0; } net.minecraft.world.phys.Vec3 pos = source.getPosition(); net.minecraft.world.phys.Vec2 rot = source.getRotation(); net.minecraft.resources.ResourceKey dim = source.getLevel().dimension(); int ok = 0; for (String name : names) { // 已在线的跳过 if (source.getServer().getPlayerList().getPlayerByName(name) != null) continue; try { var bot = BotCommand.createBot(name, source.getServer(), pos, rot.y, rot.x, dim); if (bot != null) ok++; } catch (Throwable t) { com.github.zly2006.carpetslsaddition.ServerMain.LOGGER.warn("[SLSA] spawn list bot {} failed: {}", name, t.toString()); } } final int done = ok; source.sendSuccess(() -> net.minecraft.network.chat.Component.literal( "[SLSA] 已召唤名单中的 " + done + "/" + names.size() + " 个假人"), false); return ok; } /** 批量召唤:生成 前缀1 ~ 前缀N。 */ private static int spawnBatch(CommandContext context) { CommandSourceStack source = context.getSource(); String prefix = com.mojang.brigadier.arguments.StringArgumentType.getString(context, "prefix"); int count = IntegerArgumentType.getInteger(context, "count"); net.minecraft.world.phys.Vec3 pos = source.getPosition(); net.minecraft.world.phys.Vec2 rot = source.getRotation(); net.minecraft.resources.ResourceKey dim = source.getLevel().dimension(); int ok = 0; for (int i = 1; i <= count; i++) { String name = prefix + i; try { var bot = BotCommand.createBot(name, source.getServer(), pos, rot.y, rot.x, dim); if (bot != null) ok++; } catch (Throwable t) { com.github.zly2006.carpetslsaddition.ServerMain.LOGGER.warn("[SLSA] spawn bot {} failed: {}", name, t.toString()); } } final int done = ok; source.sendSuccess(() -> net.minecraft.network.chat.Component.literal( "[SLSA] 已召唤 " + done + "/" + count + " 个假人(前缀 " + prefix + ")"), false); return ok; } /** 批量移除:杀死受名单限制的假人(名单空则全部)。 */ private static int killBatch(CommandContext context) { List bots = collectBots(context.getSource()); int ok = 0; for (ServerPlayer bot : bots) { bot.kill((net.minecraft.server.level.ServerLevel) bot.level()); ok++; } final int done = ok; context.getSource().sendSuccess(() -> net.minecraft.network.chat.Component.literal( "[SLSA] 已移除 " + done + " 个假人"), false); return ok; } }