SuperLeadRope/src/main/java/top/r3944realms/superleadrope/content/command/LeashStateCommand.java
3944Realms 2c7a56de94 版本更新到0.0.0.8
将玩家拴绳应力逻辑放入到客户端
2025-12-04 19:14:32 +08:00

500 lines
26 KiB
Java

/*
* 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 阿 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 <https://www.gnu.org/licenses/>.
*/
package top.r3944realms.superleadrope.content.command;
import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.context.CommandContext;
import net.minecraft.ChatFormatting;
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.coordinates.Vec3Argument;
import net.minecraft.core.BlockPos;
import net.minecraft.network.chat.ClickEvent;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.HoverEvent;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import top.r3944realms.superleadrope.util.capability.LeashStateInnerAPI;
import java.util.ArrayList;
import java.util.List;
import static top.r3944realms.superleadrope.content.command.Command.*;
import static top.r3944realms.superleadrope.content.command.LeashDataCommand.*;
public class LeashStateCommand {
/**
* The constant SLP_LEASH_STATE_MESSAGE_.
*/
public static final String SLP_LEASH_STATE_MESSAGE_ = Command.BASE_ + "leash_state.message.";
public static final String DEFAULT_OFFSET = SLP_LEASH_STATE_MESSAGE_ + "default_offset";
public static final String APPLY_OFFSET = SLP_LEASH_STATE_MESSAGE_ + "apply_offset";
// ==================== 重置操作消息键 ====================
public static final String RESET_ALL_HOLDER_ = SLP_LEASH_STATE_MESSAGE_ + "reset_all_holder.";
public static final String RESET_ALL_HOLDER_SUC = RESET_ALL_HOLDER_ + SUC;
public static final String RESET_HOLDER_FOR_ = SLP_LEASH_STATE_MESSAGE_ + "reset_holder_for.";
public static final String RESET_HOLDER_FOR_SUC = RESET_HOLDER_FOR_ + SUC;
public static final String RESET_HOLDER_FOR_BLOCK_POS_ = SLP_LEASH_STATE_MESSAGE_ + "reset_holder_for_block_pos.";
public static final String RESET_HOLDER_FOR_BLOCK_POS_SUC = RESET_HOLDER_FOR_BLOCK_POS_ + SUC;
public static final String RESET_APPLY_ENTITY_ALL_ = SLP_LEASH_STATE_MESSAGE_ + "reset_apply_entity_all.";
public static final String RESET_APPLY_ENTITY_ALL_SUC = RESET_APPLY_ENTITY_ALL_ + SUC;
// ==================== 设置操作消息键 ====================
public static final String SET_HOLDER_FOR_ = SLP_LEASH_STATE_MESSAGE_ + "set_holder_for.";
public static final String SET_HOLDER_FOR_SUC = SET_HOLDER_FOR_ + SUC;
public static final String SET_HOLDER_FOR_BLOCK_POS_ = SLP_LEASH_STATE_MESSAGE_ + "set_holder_for_block_pos.";
public static final String SET_HOLDER_FOR_BLOCK_POS_SUC = SET_HOLDER_FOR_BLOCK_POS_ + SUC;
public static final String SET_APPLY_ENTITY_ = SLP_LEASH_STATE_MESSAGE_ + "set_apply_entity.";
public static final String SET_APPLY_ENTITY_SUC = SET_APPLY_ENTITY_ + SUC;
// ==================== 查询操作消息键 ====================
public static final String QUERY_HAS_STATE_ = SLP_LEASH_STATE_MESSAGE_ + "query.has_state.";
public static final String QUERY_HAS_STATE_SUC = QUERY_HAS_STATE_ + SUC;
public static final String GET_ALL_UUID_STATES_ = SLP_LEASH_STATE_MESSAGE_ + "get_all_uuid_states.";
public static final String GET_ALL_UUID_STATES_SUC = GET_ALL_UUID_STATES_ + SUC;
public static final String GET_ALL_BLOCK_POS_STATES_ = SLP_LEASH_STATE_MESSAGE_ + "get_all_block_pos_states.";
public static final String GET_ALL_BLOCK_POS_STATES_SUC = GET_ALL_BLOCK_POS_STATES_ + SUC;
public static final String GET_APPLY_ENTITY_OFFSET_ = SLP_LEASH_STATE_MESSAGE_ + "get_apply_entity_offset.";
public static final String GET_APPLY_ENTITY_OFFSET_SUC = GET_APPLY_ENTITY_OFFSET_ + SUC;
public static final String GET_APPLY_ENTITY_OFFSET_NONE = GET_APPLY_ENTITY_OFFSET_ + "none";
public static final String GET_DEFAULT_APPLY_ENTITY_OFFSET_ = SLP_LEASH_STATE_MESSAGE_ + "get_default_apply_entity_offset.";
public static final String GET_DEFAULT_APPLY_ENTITY_OFFSET_SUC = GET_DEFAULT_APPLY_ENTITY_OFFSET_ + SUC;
/**
* Register.
*
* @param dispatcher the dispatcher
*/
public static void register(CommandDispatcher<CommandSourceStack> dispatcher) {
@Nullable List<LiteralArgumentBuilder<CommandSourceStack>> nodeList = SHOULD_USE_PREFIX ? null : new ArrayList<>();
LiteralArgumentBuilder<CommandSourceStack> literalArgumentBuilder = Commands.literal(PREFIX);
LiteralArgumentBuilder<CommandSourceStack> $$leashStateRoot = getLiterArgumentBuilderOfCSS("leashstate", !SHOULD_USE_PREFIX, nodeList);
$$leashStateRoot.then(
// ==================== 重置操作 ====================
Commands.literal("resetAllHolder")
.then(Commands.argument("entity", EntityArgument.entity())
.executes(context -> resetAllHolder(context, EntityArgument.getEntity(context, "entity")))
)
)
.then(Commands.literal("resetHolderFor")
.then(Commands.argument("entity", EntityArgument.entity())
.then(Commands.argument("holder", EntityArgument.entity())
.executes(context -> resetHolderFor(context,
EntityArgument.getEntity(context, "entity"),
EntityArgument.getEntity(context, "holder")))
)
)
)
.then(Commands.literal("resetHolderForBlockPos")
.then(Commands.argument("entity", EntityArgument.entity())
.then(Commands.argument("pos", BlockPosArgument.blockPos())
.executes(context -> resetHolderForBlockPos(context,
EntityArgument.getEntity(context, "entity"),
BlockPosArgument.getLoadedBlockPos(context, "pos")))
)
)
)
.then(Commands.literal("resetApplyEntityAll")
.then(Commands.argument("entity", EntityArgument.entity())
.executes(context -> resetApplyEntityAll(context, EntityArgument.getEntity(context, "entity")))
)
)
// ==================== 设置操作 ====================
.then(Commands.literal("setHolderFor")
.then(Commands.argument("entity", EntityArgument.entity())
.then(Commands.argument("holder", EntityArgument.entity())
.executes(context -> setHolderFor(context,
EntityArgument.getEntity(context, "entity"),
EntityArgument.getEntity(context, "holder")))
.then(Commands.argument("offset", Vec3Argument.vec3())
.executes(context -> setHolderForWithOffset(context,
EntityArgument.getEntity(context, "entity"),
EntityArgument.getEntity(context, "holder"),
Vec3Argument.getVec3(context, "offset")))
)
)
)
)
.then(Commands.literal("setHolderForBlockPos")
.then(Commands.argument("entity", EntityArgument.entity())
.then(Commands.argument("pos", BlockPosArgument.blockPos())
.executes(context -> setHolderForBlockPos(context,
EntityArgument.getEntity(context, "entity"),
BlockPosArgument.getLoadedBlockPos(context, "pos")))
.then(Commands.argument("offset", Vec3Argument.vec3())
.executes(context -> setHolderForBlockPosWithOffset(context,
EntityArgument.getEntity(context, "entity"),
BlockPosArgument.getLoadedBlockPos(context, "pos"),
Vec3Argument.getVec3(context, "offset")))
)
)
)
)
.then(Commands.literal("setApplyEntity")
.then(Commands.argument("entity", EntityArgument.entity())
.then(Commands.argument("offset", Vec3Argument.vec3())
.executes(context -> setApplyEntity(context,
EntityArgument.getEntity(context, "entity"),
Vec3Argument.getVec3(context, "offset")))
)
)
)
// ==================== 查询操作 ====================
.then(Commands.literal("query")
.then(Commands.literal("hasState")
.then(Commands.argument("entity", EntityArgument.entity())
.executes(context -> queryHasState(context, EntityArgument.getEntity(context, "entity")))
)
)
.then(Commands.literal("getAllUUIDStates")
.then(Commands.argument("entity", EntityArgument.entity())
.executes(context -> getAllUUIDStates(context, EntityArgument.getEntity(context, "entity")))
)
)
.then(Commands.literal("getAllBlockPosStates")
.then(Commands.argument("entity", EntityArgument.entity())
.executes(context -> getAllBlockPosStates(context, EntityArgument.getEntity(context, "entity")))
)
)
.then(Commands.literal("getApplyEntityOffset")
.then(Commands.argument("entity", EntityArgument.entity())
.executes(context -> getApplyEntityOffset(context, EntityArgument.getEntity(context, "entity")))
)
)
.then(Commands.literal("getDefaultApplyEntityOffset")
.then(Commands.argument("entity", EntityArgument.entity())
.executes(context -> getDefaultApplyEntityOffset(context, EntityArgument.getEntity(context, "entity")))
)
)
.then(Commands.literal("getAllStates")
.then(Commands.argument("entity", EntityArgument.entity())
.executes(context -> getAllStates(context, EntityArgument.getEntity(context, "entity")))
)
)
).requires(source -> source.hasPermission(2));
if(SHOULD_USE_PREFIX){
literalArgumentBuilder.then($$leashStateRoot);
dispatcher.register(literalArgumentBuilder);
} else {
if (nodeList != null) {
nodeList.forEach(dispatcher::register);
}
}
}
// ==================== 重置操作实现 ====================
private static int resetAllHolder(@NotNull CommandContext<CommandSourceStack> context, Entity entity) {
LeashStateInnerAPI.Offset.resetAllHolder(entity);
context.getSource().sendSuccess(() ->
Component.translatable(RESET_ALL_HOLDER_SUC, getSLPName(entity)), false);
return 1;
}
private static int resetHolderFor(@NotNull CommandContext<CommandSourceStack> context, Entity entity, Entity holder) {
LeashStateInnerAPI.Offset.resetHolderFor(entity, holder);
context.getSource().sendSuccess(() ->
Component.translatable(RESET_HOLDER_FOR_SUC, getSLPName(entity), getSLPName(holder)), false);
return 1;
}
private static int resetHolderForBlockPos(@NotNull CommandContext<CommandSourceStack> context, Entity entity, BlockPos pos) {
LeashStateInnerAPI.Offset.resetHolderFor(entity, pos);
context.getSource().sendSuccess(() ->
Component.translatable(RESET_HOLDER_FOR_BLOCK_POS_SUC, getSLPName(entity), getSLPName(pos)), false);
return 1;
}
private static int resetApplyEntityAll(@NotNull CommandContext<CommandSourceStack> context, Entity entity) {
LeashStateInnerAPI.Offset.resetApplyEntityAll(entity);
context.getSource().sendSuccess(() ->
Component.translatable(RESET_APPLY_ENTITY_ALL_SUC, getSLPName(entity)), false);
return 1;
}
// ==================== 设置操作实现 ====================
private static int setHolderFor(@NotNull CommandContext<CommandSourceStack> context, Entity entity, Entity holder) {
LeashStateInnerAPI.Offset.setHolderFor(entity, holder);
context.getSource().sendSuccess(() ->
Component.translatable(SET_HOLDER_FOR_SUC, getSLPName(entity), getSLPName(holder)), false);
return 1;
}
private static int setHolderForWithOffset(@NotNull CommandContext<CommandSourceStack> context, Entity entity, Entity holder, Vec3 offset) {
LeashStateInnerAPI.Offset.setHolderFor(entity, holder, offset);
context.getSource().sendSuccess(() ->
Component.translatable(SET_HOLDER_FOR_SUC, getSLPName(entity), getSLPName(holder), offset), false);
return 1;
}
private static int setHolderForBlockPos(@NotNull CommandContext<CommandSourceStack> context, Entity entity, BlockPos pos) {
LeashStateInnerAPI.Offset.setHolderFor(entity, pos);
context.getSource().sendSuccess(() ->
Component.translatable(SET_HOLDER_FOR_BLOCK_POS_SUC, getSLPName(entity), getSLPName(pos)), false);
return 1;
}
private static int setHolderForBlockPosWithOffset(@NotNull CommandContext<CommandSourceStack> context, Entity entity, BlockPos pos, Vec3 offset) {
LeashStateInnerAPI.Offset.setHolderFor(entity, pos, offset);
context.getSource().sendSuccess(() ->
Component.translatable(SET_HOLDER_FOR_BLOCK_POS_SUC, getSLPName(entity), getSLPName(pos), offset), false);
return 1;
}
private static int setApplyEntity(@NotNull CommandContext<CommandSourceStack> context, Entity entity, Vec3 offset) {
LeashStateInnerAPI.Offset.setApplyEntity(entity, offset);
context.getSource().sendSuccess(() ->
Component.translatable(SET_APPLY_ENTITY_SUC, getSLPName(entity), offset), false);
return 1;
}
// ==================== 查询操作实现 ====================
private static int queryHasState(@NotNull CommandContext<CommandSourceStack> context, Entity entity) {
boolean hasState = LeashStateInnerAPI.Query.hasState(entity);
MutableComponent send = Component.empty();
send.append(Component.translatable(QUERY_HAS_STATE_SUC, getSLPName(entity), hasState));
context.getSource().sendSuccess(() -> send, false);
return 1;
}
private static int getAllUUIDStates(CommandContext<CommandSourceStack> context, Entity entity) {
var states = LeashStateInnerAPI.Query.getAllUUIDStates(entity);
MutableComponent head = Component.translatable(GET_ALL_UUID_STATES_SUC, getSLPName(entity), states.size()).append("\n");
MutableComponent content = Component.empty();
if (states.isEmpty()) {
content.append(Component.translatable(NONE));
} else {
int count = 0;
for (var entry : states.entrySet()) {
if (count >= MAX_SHOW_NUMBER) {
content.append(Component.translatable(ABBREVIATION));
break;
}
// UUID: xxx, 状态: xxx
MutableComponent stateInfo = Component.literal(" UUID: " + entry.getKey() + ", 状态: " + entry.getValue());
// 添加悬停信息
MutableComponent hover = Component.empty();
hover.append(Component.translatable(UUID).withStyle(ChatFormatting.DARK_AQUA))
.append(Component.translatable(COLON).withStyle(ChatFormatting.GRAY))
.append(Component.literal(entry.getKey().toString())).append("\n");
hover.append(Component.translatable(STATE).withStyle(ChatFormatting.DARK_AQUA))
.append(Component.translatable(COLON).withStyle(ChatFormatting.GRAY))
.append(Component.literal(entry.getValue().toString()));
stateInfo.withStyle(s -> s.withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, hover)));
content.append(stateInfo);
if (count < Math.min(states.size(), MAX_SHOW_NUMBER) - 1) {
content.append("\n");
}
count++;
}
}
context.getSource().sendSuccess(() -> head.append(content), false);
return 1;
}
private static int getAllBlockPosStates(CommandContext<CommandSourceStack> context, Entity entity) {
var states = LeashStateInnerAPI.Query.getAllBlockPosStates(entity);
MutableComponent head = Component.translatable(GET_ALL_BLOCK_POS_STATES_SUC, getSLPName(entity), states.size()).append("\n");
MutableComponent content = Component.empty();
if (states.isEmpty()) {
content.append(Component.translatable(NONE));
} else {
int count = 0;
for (var entry : states.entrySet()) {
if (count >= MAX_SHOW_NUMBER) {
content.append(Component.translatable(ABBREVIATION));
break;
}
BlockPos pos = entry.getKey();
// 位置: (x,y,z), 状态: xxx
MutableComponent stateInfo = Component.literal(Component.translatable(BLOCK_POS, pos.getX(), pos.getY(), pos.getZ()) +"," + Component.translatable(STATE) + Component.translatable(COLON) + entry.getValue());
// 添加悬停信息
MutableComponent hover = Component.empty();
hover.append(Component.translatable(BLOCK).withStyle(ChatFormatting.DARK_AQUA))
.append(Component.translatable(COLON).withStyle(ChatFormatting.GRAY))
.append(Component.literal(pos.toShortString())).append("\n");
hover.append(Component.translatable(STATE).withStyle(ChatFormatting.DARK_AQUA))
.append(Component.translatable(COLON).withStyle(ChatFormatting.GRAY))
.append(Component.literal(entry.getValue().toString()));
// 添加点击事件(建议传送到该位置)
ClickEvent clickEvent = new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND,
String.format("/tp @s %d %d %d", pos.getX(), pos.getY(), pos.getZ()));
stateInfo.withStyle(s -> s.withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, hover))
.withClickEvent(clickEvent));
content.append(stateInfo);
if (count < Math.min(states.size(), MAX_SHOW_NUMBER) - 1) {
content.append("\n");
}
count++;
}
}
context.getSource().sendSuccess(() -> head.append(content), false);
return 1;
}
private static int getApplyEntityOffset(CommandContext<CommandSourceStack> context, Entity entity) {
LeashStateInnerAPI.Offset.getApplyEntityOffset(entity).ifPresentOrElse(
offset -> {
MutableComponent send = Component.empty();
send.append(Component.translatable(GET_APPLY_ENTITY_OFFSET_SUC, getSLPName(entity), offset));
context.getSource().sendSuccess(() -> send, false);
},
() -> {
MutableComponent send = Component.empty();
send.append(Component.translatable(GET_APPLY_ENTITY_OFFSET_NONE, getSLPName(entity)));
context.getSource().sendSuccess(() -> send, false);
}
);
return 1;
}
private static int getDefaultApplyEntityOffset(@NotNull CommandContext<CommandSourceStack> context, Entity entity) {
Vec3 offset = LeashStateInnerAPI.Offset.getDefaultApplyEntityOffset(entity);
MutableComponent send = Component.empty();
send.append(Component.translatable(GET_DEFAULT_APPLY_ENTITY_OFFSET_SUC, getSLPName(entity), offset));
context.getSource().sendSuccess(() -> send, false);
return 1;
}
public static final String GET_ALL_STATES_HEAD = SLP_LEASH_STATE_MESSAGE_ + "get_all_states.head";
private static int getAllStates(CommandContext<CommandSourceStack> context, @NotNull Entity entity) {
MutableComponent head = Component.translatable(GET_ALL_STATES_HEAD, entity.getDisplayName()).append("\n");
// 获取所有状态信息
var uuidStates = LeashStateInnerAPI.Query.getAllUUIDStates(entity);
var blockPosStates = LeashStateInnerAPI.Query.getAllBlockPosStates(entity);
var applyOffset = LeashStateInnerAPI.Offset.getApplyEntityOffset(entity);
var defaultOffset = LeashStateInnerAPI.Offset.getDefaultApplyEntityOffset(entity);
MutableComponent content = Component.empty();
// UUID 状态
content.append(Component.translatable(UUID).withStyle(ChatFormatting.YELLOW)).append(": ");
if (uuidStates.isEmpty()) {
content.append(Component.translatable(NONE));
} else {
int count = 0;
for (var entry : uuidStates.entrySet()) {
if (count >= 4) { // 限制显示数量
content.append(Component.translatable(ABBREVIATION));
break;
}
MutableComponent stateComp = Component.literal("[U]").withStyle(ChatFormatting.GREEN);
MutableComponent hover = Component.empty();
hover.append(Component.translatable(UUID).withStyle(ChatFormatting.DARK_AQUA))
.append(Component.translatable(COLON).withStyle(ChatFormatting.GRAY))
.append(Component.literal(entry.getKey().toString())).append("\n");
hover.append(Component.translatable(STATE).withStyle(ChatFormatting.DARK_AQUA))
.append(Component.translatable(COLON).withStyle(ChatFormatting.GRAY))
.append(Component.literal(entry.getValue().toString()));
stateComp.withStyle(s -> s.withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, hover)));
content.append(stateComp);
if (count < Math.min(uuidStates.size(), 4) - 1) {
content.append(", ");
}
count++;
}
}
content.append("\n");
// 方块位置状态
content.append(Component.translatable(BLOCK).withStyle(ChatFormatting.YELLOW)).append(": ");
if (blockPosStates.isEmpty()) {
content.append(Component.translatable(NONE));
} else {
int count = 0;
for (var entry : blockPosStates.entrySet()) {
if (count >= 4) { // 限制显示数量
content.append(Component.translatable(ABBREVIATION));
break;
}
MutableComponent stateComp = Component.literal("[B]").withStyle(ChatFormatting.BLUE);
MutableComponent hover = Component.empty();
hover.append(Component.translatable(BLOCK).withStyle(ChatFormatting.DARK_AQUA))
.append(Component.translatable(COLON).withStyle(ChatFormatting.GRAY))
.append(Component.literal(entry.getKey().toShortString())).append("\n");
hover.append(Component.translatable(STATE).withStyle(ChatFormatting.DARK_AQUA))
.append(Component.translatable(COLON).withStyle(ChatFormatting.GRAY))
.append(Component.literal(entry.getValue().toString()));
stateComp.withStyle(s -> s.withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, hover)));
content.append(stateComp);
if (count < Math.min(blockPosStates.size(), 4) - 1) {
content.append(", ");
}
count++;
}
}
content.append("\n");
// 应用偏移量
content.append(Component.translatable(APPLY_OFFSET).withStyle(ChatFormatting.YELLOW)).append(": ");
applyOffset.ifPresentOrElse(
offset -> content.append(Component.literal(String.format("(%.2f, %.2f, %.2f)", offset.x, offset.y, offset.z))),
() -> content.append(Component.translatable(NONE))
);
content.append("\n");
// 默认偏移量
content.append(Component.translatable(DEFAULT_OFFSET).withStyle(ChatFormatting.YELLOW)).append(": ");
content.append(Component.literal(String.format("(%.2f, %.2f, %.2f)", defaultOffset.x, defaultOffset.y, defaultOffset.z)));
context.getSource().sendSuccess(() -> head.append(content), false);
return 1;
}
}