Files
CarpetSLSAddition/src/main/java/com/github/zly2006/carpetslsaddition/command/BotAllCommand.java
T
2026-10-05 11:19:24 +08:00

235 lines
13 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 —— 批量控制本模组生成的所有假人。
*
* <p>与 {@link BotCommand} 的区别:{@code /bot <player>} 操作单个指定玩家,
* 而 {@code /botall} 对其余命令中出现的所有本模组假人(isBot()==true)批量执行同一操作。
*/
public class BotAllCommand {
public static void register(CommandDispatcher<CommandSourceStack> dispatcher) {
LiteralArgumentBuilder<CommandSourceStack> 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<CommandSourceStack> 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<CommandSourceStack> 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<CommandSourceStack> context, Consumer<EntityPlayerActionPack> action) {
List<ServerPlayer> 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<CommandSourceStack> batch(Consumer<EntityPlayerActionPack> action) {
return c -> batchAp(c, action);
}
/** 收集所有本模组生成的假人(受名单限制:名单非空时只取名单内的)。 */
private static List<ServerPlayer> collectBots(CommandSourceStack source) {
MinecraftServer server = source.getServer();
List<ServerPlayer> 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<CommandSourceStack> context) {
if (!SLSCarpetSettings.canUseSitCommand) {
context.getSource().sendSuccess(() -> net.minecraft.network.chat.Component.literal(
"[SLSA] 坐下功能未启用(canUseSitCommand)"), false);
return 0;
}
List<ServerPlayer> 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<CommandSourceStack> context) {
List<ServerPlayer> 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<CommandSourceStack> context) {
CommandSourceStack source = context.getSource();
java.util.Set<String> 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<net.minecraft.world.level.Level> 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<CommandSourceStack> 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<net.minecraft.world.level.Level> 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<CommandSourceStack> context) {
List<ServerPlayer> 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;
}
}