/* * Super Lead rope mod * Copyright (C) 2025 R3944Realms * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package top.r3944realms.superleadrope.content.command; import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.arguments.DoubleArgumentType; import com.mojang.brigadier.arguments.IntegerArgumentType; import com.mojang.brigadier.arguments.StringArgumentType; import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.builder.RequiredArgumentBuilder; import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.exceptions.CommandSyntaxException; import net.minecraft.commands.CommandSourceStack; import net.minecraft.commands.Commands; import net.minecraft.commands.arguments.EntityArgument; import net.minecraft.commands.arguments.coordinates.BlockPosArgument; import net.minecraft.commands.arguments.selector.EntitySelector; import net.minecraft.core.BlockPos; import net.minecraft.network.chat.Component; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.entity.Entity; import org.jetbrains.annotations.Nullable; import top.r3944realms.superleadrope.CommonEventHandler; import top.r3944realms.superleadrope.SuperLeadRope; import top.r3944realms.superleadrope.api.type.capabilty.LeashInfo; import top.r3944realms.superleadrope.content.entity.SuperLeashKnotEntity; import top.r3944realms.superleadrope.content.gamerule.server.CreateSuperLeashKnotEntityIfAbsent; import top.r3944realms.superleadrope.core.register.SLPGameruleRegistry; import top.r3944realms.superleadrope.util.capability.LeashDataInnerAPI; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Optional; import static top.r3944realms.superleadrope.content.command.Command.*; /** * The type Leash data command. */ public class LeashDataCommand { /** * The constant SLP_LEASH_MESSAGE_. */ public static final String SLP_LEASH_MESSAGE_ = SuperLeadRope.MOD_ID + ".command.leash.message."; /** * The constant LEASH_DATA_GET_. */ public static final String LEASH_DATA_GET_ = SLP_LEASH_MESSAGE_ + ".get.", /** * The Title. */ TITLE = LEASH_DATA_GET_ + "title", /** * The Total. */ TOTAL = LEASH_DATA_GET_ + "total", /** * The Block. */ BLOCK = LEASH_DATA_GET_ + "block", /** * The Uuid. */ UUID = LEASH_DATA_GET_ + "uuid", /** * The Max. */ MAX = LEASH_DATA_GET_ + "max", /** * The Elastic. */ ELASTIC = LEASH_DATA_GET_ + "elastic", /** * The Keep. */ KEEP = LEASH_DATA_GET_ + "keep", /** * The Reserved. */ RESERVED = LEASH_DATA_GET_ + "reserved" ; /** * Register. * * @param dispatcher the dispatcher */ public static void register(CommandDispatcher dispatcher) { @Nullable List> nodeList = SHOULD_USE_PREFIX ? null : new ArrayList<>(); LiteralArgumentBuilder literalArgumentBuilder = Commands.literal(PREFIX); LiteralArgumentBuilder $$leashDataRoot = getLiterArgumentBuilderOfCSS("leashdata", !SHOULD_USE_PREFIX, nodeList); RequiredArgumentBuilder $$$add$holder = Commands.argument("holder", EntityArgument.entity()) .executes(LeashDataCommand::addLeash) .then(Commands.argument("maxDistance", DoubleArgumentType.doubleArg(1.0, 256.0)) .executes(context -> addLeash(context, DoubleArgumentType.getDouble(context, "maxDistance"))) .then(Commands.argument("elasticDistanceScale", DoubleArgumentType.doubleArg(1.0, 128.0)) .executes(context -> addLeash(context, DoubleArgumentType.getDouble(context, "maxDistance"), DoubleArgumentType.getDouble(context, "elasticDistanceScale"))) .then(Commands.argument("keepTicks", IntegerArgumentType.integer(0)) .executes(context -> addLeash(context, DoubleArgumentType.getDouble(context, "maxDistance"), DoubleArgumentType.getDouble(context, "elasticDistanceScale"), IntegerArgumentType.getInteger(context, "keepTicks"))) .then(Commands.argument("reserved", StringArgumentType.string()) .executes(context -> addLeash(context, DoubleArgumentType.getDouble(context, "maxDistance"), DoubleArgumentType.getDouble(context, "elasticDistanceScale"), IntegerArgumentType.getInteger(context, "keepTicks"), StringArgumentType.getString(context, "reserved"))) ) ) ) ); LiteralArgumentBuilder $$$add$pos = Commands.literal("block") .then(Commands.argument("pos", BlockPosArgument.blockPos()) .executes(LeashDataCommand::addBlockLeash) .then(Commands.argument("maxDistance", DoubleArgumentType.doubleArg(1.0, 256.0)) .executes(context -> addBlockLeash(context, DoubleArgumentType.getDouble(context, "maxDistance"))) .then(Commands.argument("elasticDistanceScale", DoubleArgumentType.doubleArg(1.0, 128.0)) .executes(context -> addBlockLeash(context, DoubleArgumentType.getDouble(context, "maxDistance"), DoubleArgumentType.getDouble(context, "elasticDistanceScale"), 0, "")) .then(Commands.argument("keepTicks", IntegerArgumentType.integer(0)) .executes(context -> addBlockLeash(context, DoubleArgumentType.getDouble(context, "maxDistance"), DoubleArgumentType.getDouble(context, "elasticDistanceScale"), IntegerArgumentType.getInteger(context, "keepTicks"))) .then(Commands.argument("reserved", StringArgumentType.string()) .executes(context -> addBlockLeash(context, DoubleArgumentType.getDouble(context, "maxDistance"), DoubleArgumentType.getDouble(context, "elasticDistanceScale"), IntegerArgumentType.getInteger(context, "keepTicks"), StringArgumentType.getString(context, "reserved"))) ) ) ) ) ); LiteralArgumentBuilder $$$add = Commands.literal("addApplyEntity") .then(Commands.argument("target", EntityArgument.entities()) // 实体拴绳 .then($$$add$holder) // 方块拴绳 .then($$$add$pos) ); LiteralArgumentBuilder $$$remove = Commands.literal("removeApplyEntity") .then(Commands.argument("target", EntityArgument.entities()) // 移除特定实体拴绳 .then(Commands.argument("holder", EntityArgument.entity()) .executes(LeashDataCommand::removeLeash) ) // 移除方块拴绳 .then(Commands.literal("block") .then(Commands.argument("pos", BlockPosArgument.blockPos()) .executes(LeashDataCommand::removeBlockLeash) ) ) // 批量移除 .then(Commands.literal("all") .executes(LeashDataCommand::removeAllLeashes) ) .then(Commands.literal("holders") .executes(LeashDataCommand::removeAllHolderLeashes) ) .then(Commands.literal("blocks") .executes(LeashDataCommand::removeAllBlockLeashes) ) ); LiteralArgumentBuilder $$$transfer = Commands.literal("transfer") .then(Commands.argument("target", EntityArgument.entities()) // 实体到实体转移 .then(Commands.argument("from", EntityArgument.entity()) .then(Commands.argument("to", EntityArgument.entity()) .executes(LeashDataCommand::transferLeash) .then(Commands.argument("reserved", StringArgumentType.string()) .executes(context -> transferLeash(context, StringArgumentType.getString(context, "reserved"))) ) ) ) // 方块到实体转移 .then(Commands.literal("fromBlock") .then(Commands.argument("fromPos", BlockPosArgument.blockPos()) .then(Commands.argument("to", EntityArgument.entity()) .executes(LeashDataCommand::transferFromBlock) .then(Commands.argument("reserved", StringArgumentType.string()) .executes(context -> transferFromBlock(context, StringArgumentType.getString(context, "reserved"))) ) ) ) ) ); RequiredArgumentBuilder $$$set$holder = Commands.argument("holder", EntityArgument.entity()) // 设置最大距离 .then(Commands.literal("maxDistance") .then(Commands.argument("distance", DoubleArgumentType.doubleArg(1.0, 256.0)) .executes(LeashDataCommand::setMaxDistance) .then(Commands.argument("keepTicks", IntegerArgumentType.integer(0)) .executes(context -> setMaxDistance(context, IntegerArgumentType.getInteger(context, "keepTicks"))) .then(Commands.argument("reserved", StringArgumentType.string()) .executes(context -> setMaxDistance(context, IntegerArgumentType.getInteger(context, "keepTicks"), StringArgumentType.getString(context, "reserved"))) ) ) ) ) // 设置弹性距离 .then(Commands.literal("elasticDistanceScale") .then(Commands.argument("distance", DoubleArgumentType.doubleArg(1.0, 128.0)) .executes(context -> setElasticDistance(context, 0, "")) .then(Commands.argument("keepTicks", IntegerArgumentType.integer(0)) .executes(context -> setElasticDistance(context, IntegerArgumentType.getInteger(context, "keepTicks"), "")) .then(Commands.argument("reserved", StringArgumentType.string()) .executes(context -> setElasticDistance(context, IntegerArgumentType.getInteger(context, "keepTicks"), StringArgumentType.getString(context, "reserved"))) ) ) ) ); LiteralArgumentBuilder $$$set$pos = Commands.literal("block") .then(Commands.argument("pos", BlockPosArgument.blockPos()) // 设置最大距离 .then(Commands.literal("maxDistance") .then(Commands.argument("distance", DoubleArgumentType.doubleArg(1.0, 256.0)) .executes(LeashDataCommand::setBlockMaxDistance) .then(Commands.argument("keepTicks", IntegerArgumentType.integer(0)) .executes(context -> setBlockMaxDistance(context, IntegerArgumentType.getInteger(context, "keepTicks"))) .then(Commands.argument("reserved", StringArgumentType.string()) .executes(context -> setBlockMaxDistance(context, IntegerArgumentType.getInteger(context, "keepTicks"), StringArgumentType.getString(context, "reserved"))) ) ) ) ) // 设置弹性距离 .then(Commands.literal("elasticDistanceScale") .then(Commands.argument("distance", DoubleArgumentType.doubleArg(1.0, 128.0)) .executes(LeashDataCommand::setBlockElasticDistance) .then(Commands.argument("keepTicks", IntegerArgumentType.integer(0)) .executes(context -> setBlockElasticDistance(context, IntegerArgumentType.getInteger(context, "keepTicks"))) .then(Commands.argument("reserved", StringArgumentType.string()) .executes(context -> setBlockElasticDistance(context, IntegerArgumentType.getInteger(context, "keepTicks"), StringArgumentType.getString(context, "reserved"))) ) ) ) ) ); LiteralArgumentBuilder $$$set = Commands.literal("setApplyEntity") .then(Commands.argument("target", EntityArgument.entities()) // 实体拴绳设置 .then($$$set$holder) // 方块拴绳设置 .then($$$set$pos) ); LiteralArgumentBuilder $$$applayForces = Commands.literal("applyForces") .then(Commands.argument("target", EntityArgument.entities()) .executes(LeashDataCommand::applyForces) ); LiteralArgumentBuilder $$$get = Commands.literal("get") .then(Commands.argument("target", EntityArgument.entities()) .executes(LeashDataCommand::getLeashData) ); $$leashDataRoot .requires(source -> source.hasPermission(2)) // 需要OP权限 // ==================== GET 命令 ==================== .then($$$get) // ==================== ADD 命令 ==================== .then($$$add) // ==================== REMOVE 命令 ==================== .then($$$remove) // ==================== TRANSFER 命令 ==================== .then($$$transfer) // ==================== SET 命令 ==================== .then($$$set) // ==================== APPLY FORCES 命令 ==================== .then($$$applayForces); if(SHOULD_USE_PREFIX){ literalArgumentBuilder.then($$leashDataRoot); dispatcher.register(literalArgumentBuilder); } else { if (nodeList != null) { nodeList.forEach(dispatcher::register); } } } /** * The constant SET_MAX_DISTANCE. */ public static final String SET_MAX_DISTANCE = SLP_LEASH_MESSAGE_ + "set_apply_entity.max_distance"; private static int setMaxDistance(CommandContext context) throws CommandSyntaxException { return setMaxDistance(context, CommonEventHandler.leashConfigManager.getMaxLeashLength(), ""); } private static int setMaxDistance(CommandContext context, double maxDistance) throws CommandSyntaxException { return setMaxDistance(context, maxDistance, ""); } private static int setMaxDistance(CommandContext context, double maxDistance, String reserved) throws CommandSyntaxException { Collection targets = EntityArgument.getEntities(context, "target"); Entity holder = EntityArgument.getEntity(context, "holder"); for (Entity target : targets) { } return -1; } /** * The constant REMOVE_ALL_BLOCK_LEASHES. */ public static final String REMOVE_ALL_BLOCK_LEASHES = SLP_LEASH_MESSAGE_ + "remove_apply_entity.all_block_leashes"; private static int removeAllBlockLeashes(CommandContext context) throws CommandSyntaxException { return -1; } /** * The constant REMOVE_ALL_HOLDER_LEASHES. */ public static final String REMOVE_ALL_HOLDER_LEASHES = SLP_LEASH_MESSAGE_ + "remove_apply_entity.all_holder_leashes"; private static int removeAllHolderLeashes(CommandContext context) throws CommandSyntaxException { return -1; } /** * The constant TRANSFER_FROM_BLOCK. */ public static final String TRANSFER_FROM_BLOCK = SLP_LEASH_MESSAGE_ + "transfer.from_block"; private static int transferFromBlock(CommandContext context) throws CommandSyntaxException { return transferFromBlock(context, ""); } private static int transferFromBlock(CommandContext context, String reserved) throws CommandSyntaxException { return -1; } /** * The constant SET_ELASTIC_DISTANCE. */ public static final String SET_ELASTIC_DISTANCE = SLP_LEASH_MESSAGE_ + "set_apply_entity.elastic_distance"; private static int setElasticDistance(CommandContext context) throws CommandSyntaxException { return setElasticDistance(context, 0 ,""); } private static int setElasticDistance(CommandContext context, int keepTicks) throws CommandSyntaxException { return setElasticDistance(context, keepTicks ,""); } private static int setElasticDistance(CommandContext context, int keepTicks, String reserved) throws CommandSyntaxException { return -1; } /** * The constant SET_BLOCK_MAX_DISTANCE. */ public static final String SET_BLOCK_MAX_DISTANCE = SLP_LEASH_MESSAGE_ + "set_apply_entity.block_max_distance"; private static int setBlockMaxDistance(CommandContext context) throws CommandSyntaxException { return setBlockMaxDistance(context, 0 ,""); } private static int setBlockMaxDistance(CommandContext context, int keepTicks) throws CommandSyntaxException { return setBlockMaxDistance(context, keepTicks ,""); } private static int setBlockMaxDistance(CommandContext context, int keepTicks, String reserved) throws CommandSyntaxException { return -1; } /** * The constant SET_BLOCK_ELASTIC_DISTANCE. */ public static final String SET_BLOCK_ELASTIC_DISTANCE = SLP_LEASH_MESSAGE_ + "set_apply_entity.block_elastic_distance"; private static int setBlockElasticDistance(CommandContext context) throws CommandSyntaxException { return setBlockElasticDistance(context, 0 ,""); } private static int setBlockElasticDistance(CommandContext context, int keepTicks) throws CommandSyntaxException { return setBlockElasticDistance(context, keepTicks ,""); } private static int setBlockElasticDistance(CommandContext context, int keepTicks, String reserved) throws CommandSyntaxException { return -1; } // ==================== 命令执行方法 ==================== private static int getLeashData(CommandContext context) throws CommandSyntaxException { Collection targets = EntityArgument.getEntities(context, "target"); CommandSourceStack source = context.getSource(); for (Entity target : targets) { Collection leashes = LeashDataInnerAPI.QueryOperations.getAllLeashes(target); // +++ source.sendSuccess(() -> Component.literal("=== Leash Data for " + target.getName().getString() + " ==="), false); source.sendSuccess(() -> Component.literal("Total leashes: " + leashes.size()), false); // TODO:翻译支持 HoverTip实现部分信息简化显示 for (LeashInfo leash : leashes) { StringBuilder info = new StringBuilder(); leash.blockPosOpt().ifPresent(pos -> info.append("Block: ").append(pos.toShortString()).append(" ")); leash.holderUUIDOpt().ifPresent(uuid -> info.append("UUID: ").append(uuid).append(" ")); info.append("Max: ").append(leash.maxDistance()).append(" "); info.append("Elastic: ").append(leash.elasticDistanceScale()).append(" "); info.append("Keep: ").append(leash.keepLeashTicks()).append("/").append(leash.maxKeepLeashTicks()); if (!leash.reserved().isEmpty()) { info.append(" Reserved: ").append(leash.reserved()); } source.sendSuccess(() -> Component.literal(info.toString()), false); } } return targets.size(); } private static int addLeash(CommandContext context) throws CommandSyntaxException { return addLeash(context, CommonEventHandler.leashConfigManager.getMaxLeashLength(), CommonEventHandler.leashConfigManager.getElasticDistanceScale(), 0, ""); } private static int addLeash(CommandContext context, double maxDistance) throws CommandSyntaxException { return addLeash(context, maxDistance, CommonEventHandler.leashConfigManager.getElasticDistanceScale(), 0, ""); } private static int addLeash(CommandContext context, double maxDistance, double elasticDistance) throws CommandSyntaxException { return addLeash(context, maxDistance, elasticDistance, 0, ""); } private static int addLeash(CommandContext context, double maxDistance, double elasticDistance, int keepTicks) throws CommandSyntaxException { return addLeash(context, maxDistance, elasticDistance, keepTicks, ""); } private static int addLeash(CommandContext context, double maxDistance, double elasticDistance, int keepTicks, String reserved) throws CommandSyntaxException { Collection targets = EntityArgument.getEntities(context, "target"); Entity holder = EntityArgument.getEntity(context, "holder"); CommandSourceStack source = context.getSource(); List successful = new ArrayList<>(), failed = new ArrayList<>(); for (Entity target : targets) { if(LeashDataInnerAPI.LeashOperations.attach(target, holder, maxDistance, elasticDistance, keepTicks, reserved)) { successful.add(target); } else failed.add(target); } // todo: source.sendSuccess(() -> Component.translatable(/*成功{},失败{}*/), true); return successful.size(); } private static int addBlockLeash(CommandContext context) throws CommandSyntaxException { return addBlockLeash(context, CommonEventHandler.leashConfigManager.getMaxLeashLength(), CommonEventHandler.leashConfigManager.getElasticDistanceScale(), 0, ""); } private static int addBlockLeash(CommandContext context, double maxDistance) throws CommandSyntaxException { return addBlockLeash(context, maxDistance, CommonEventHandler.leashConfigManager.getElasticDistanceScale(), 0, ""); } private static int addBlockLeash(CommandContext context, double maxDistance, double elasticDistance, int keepTicks) throws CommandSyntaxException { return addBlockLeash(context, maxDistance, elasticDistance, keepTicks, ""); } private static int addBlockLeash(CommandContext context, double maxDistance, double elasticDistance, int keepTicks, String reserved) throws CommandSyntaxException { Collection targets = EntityArgument.getEntities(context, "target"); BlockPos pos = BlockPosArgument.getBlockPos(context, "pos"); CommandSourceStack source = context.getSource(); ServerLevel level = source.getLevel(); SuperLeashKnotEntity knotEntity = SuperLeashKnotEntity.get(level, pos) .or(() -> { if (SLPGameruleRegistry.getGameruleBoolValue(level, CreateSuperLeashKnotEntityIfAbsent.NAME_KEY)) return Optional.of(SuperLeashKnotEntity.createKnot(level, pos, true)); else return Optional.empty(); }).orElse(null); if (knotEntity == null) { // todo: source.sendFailure(Component.translatable(/*失败,目标上无拴绳结*/)); return -1; } List successful = new ArrayList<>(), failed = new ArrayList<>(); for (Entity target : targets) { if(LeashDataInnerAPI.LeashOperations.attach(target, knotEntity, maxDistance, elasticDistance, keepTicks, reserved)) { successful.add(target); } else failed.add(target); } // todo: source.sendSuccess(() -> Component.translatable(/*成功{},失败{}*/), true); return successful.size(); } private static int removeLeash(CommandContext context) throws CommandSyntaxException { Collection targets = EntityArgument.getEntities(context, "target"); Entity holder = EntityArgument.getEntity(context, "holder"); CommandSourceStack source = context.getSource(); int successCount = 0; for (Entity target : targets) { boolean success = LeashDataInnerAPI.LeashOperations.detach(target, holder); if (success) { successCount++; source.sendSuccess(() -> Component.literal("Removed leash from " + target.getName().getString() + " held by " + holder.getName().getString()), false); } else { source.sendFailure(Component.literal("No leash found for " + holder.getName().getString() + " on " + target.getName().getString())); } } return successCount; } private static int removeBlockLeash(CommandContext context) throws CommandSyntaxException { Collection targets = EntityArgument.getEntities(context, "target"); BlockPos pos = BlockPosArgument.getBlockPos(context, "pos"); CommandSourceStack source = context.getSource(); int successCount = 0; for (Entity target : targets) { boolean success = LeashDataInnerAPI.LeashOperations.detach(target, pos); if (success) { successCount++; source.sendSuccess(() -> Component.literal("Removed block leash from " + target.getName().getString() + " at " + pos.toShortString()), false); } else { source.sendFailure(Component.literal("No block leash found at " + pos.toShortString() + " on " + target.getName().getString())); } } return successCount; } private static int removeAllLeashes(CommandContext context) throws CommandSyntaxException { Collection targets = EntityArgument.getEntities(context, "target"); CommandSourceStack source = context.getSource(); for (Entity target : targets) { LeashDataInnerAPI.LeashOperations.detachAll(target); source.sendSuccess(() -> Component.literal("Removed all leashes from " + target.getName().getString()), false); } return targets.size(); } private static int transferLeash(CommandContext context) throws CommandSyntaxException { return transferLeash(context, ""); } private static int transferLeash(CommandContext context, String reserved) throws CommandSyntaxException { Collection targets = EntityArgument.getEntities(context, "target"); Entity from = EntityArgument.getEntity(context, "from"); Entity to = EntityArgument.getEntity(context, "to"); CommandSourceStack source = context.getSource(); int successCount = 0; for (Entity target : targets) { boolean success = reserved.isEmpty() ? LeashDataInnerAPI.TransferOperations.transfer(target, from, to) : LeashDataInnerAPI.TransferOperations.transfer(target, from, to, reserved); if (success) { successCount++; source.sendSuccess(() -> Component.literal("Transferred leash from " + from.getName().getString() + " to " + to.getName().getString() + " for " + target.getName().getString()), false); } else { source.sendFailure(Component.literal("Failed to transfer leash for " + target.getName().getString())); } } return successCount; } private static int applyForces(CommandContext context) throws CommandSyntaxException { Collection targets = EntityArgument.getEntities(context, "target"); CommandSourceStack source = context.getSource(); for (Entity target : targets) { LeashDataInnerAPI.PhysicsOperations.applyForces(target); source.sendSuccess(() -> Component.literal("Applied leash forces to " + target.getName().getString()), false); } return targets.size(); } }