feat: API封装
fix: 部分内容
This commit is contained in:
parent
1a56faad9f
commit
e82f84f2a4
30
build.gradle
30
build.gradle
|
|
@ -135,7 +135,10 @@ legacyForge {
|
|||
}
|
||||
|
||||
// 源码资源目录
|
||||
sourceSets.main.resources { srcDir 'src/generated/resources' }
|
||||
sourceSets.main.resources {
|
||||
srcDir 'src/generated/resources'
|
||||
srcDir 'src/main/resources' // 记得加这一行,把 coremods 包含进去
|
||||
}
|
||||
|
||||
// ========== 依赖 ==========
|
||||
configurations {
|
||||
|
|
@ -150,8 +153,10 @@ dependencies {
|
|||
modRuntimeOnly("mezz.jei:jei-${minecraft_version}-forge:${jei_version}")
|
||||
modRuntimeOnly("curse.maven:spark-361579:4738952")
|
||||
compileOnly ('me.lucko:spark-api:0.1-SNAPSHOT')
|
||||
|
||||
|
||||
implementation 'org.ow2.asm:asm:9.6'
|
||||
implementation 'org.ow2.asm:asm-tree:9.6'
|
||||
implementation 'org.ow2.asm:asm-commons:9.6'
|
||||
implementation 'org.ow2.asm:asm-util:9.6'
|
||||
}
|
||||
|
||||
// ========== 编译配置 ==========
|
||||
|
|
@ -221,6 +226,14 @@ publishing {
|
|||
}
|
||||
}
|
||||
}
|
||||
// 新建一个可解析配置专门给 ProGuard
|
||||
configurations {
|
||||
proguardLibs {
|
||||
canBeResolved = true
|
||||
canBeConsumed = false
|
||||
extendsFrom modCompileOnly
|
||||
}
|
||||
}
|
||||
|
||||
// ========== ProGuard 配置 ==========
|
||||
tasks.register('proguard', ProGuardTask) {
|
||||
|
|
@ -231,8 +244,8 @@ tasks.register('proguard', ProGuardTask) {
|
|||
// JDK jmods 库
|
||||
libraryjars "${System.getProperty('java.home')}/jmods"
|
||||
|
||||
// 项目依赖作为库输入
|
||||
configurations.compileClasspath.files.each { file ->
|
||||
// 使用可解析配置
|
||||
configurations.proguardLibs.resolve().each { file ->
|
||||
libraryjars file.absolutePath
|
||||
}
|
||||
|
||||
|
|
@ -298,7 +311,14 @@ tasks.register("runWithRenderDoc", Exec) {
|
|||
println "Environment MOD_CLASSES: ${environment['MOD_CLASSES']}"
|
||||
}
|
||||
}
|
||||
tasks.register("copyCoreMods", Copy) {
|
||||
from("src/main/resources/coremods") // 源目录
|
||||
into("$buildDir/classes/java/main/coremods") // 目标目录
|
||||
}
|
||||
|
||||
tasks.named("classes") {
|
||||
dependsOn("copyCoreMods")
|
||||
}
|
||||
|
||||
// IDEA 支持
|
||||
idea {
|
||||
|
|
|
|||
4
proguard.pro
vendored
4
proguard.pro
vendored
|
|
@ -36,6 +36,10 @@
|
|||
-keepclassmembers class cpw.mods.** { *; }
|
||||
-dontwarn cpw.mods.**
|
||||
|
||||
-keep class mezz.jei.**
|
||||
-keepclassmembers class mezz.jei.**{ *; }
|
||||
-dontwarn mezz.jei.**
|
||||
|
||||
#---------------------------------------
|
||||
# 保留资源文件 (mods.toml / assets / data / pack.mcmeta)
|
||||
#---------------------------------------
|
||||
|
|
|
|||
|
|
@ -50,8 +50,11 @@ import net.minecraftforge.event.level.LevelEvent;
|
|||
import net.minecraftforge.event.server.ServerStartingEvent;
|
||||
import net.minecraftforge.event.server.ServerStoppingEvent;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import net.minecraftforge.fml.event.config.ModConfigEvent;
|
||||
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import top.r3944realms.superleadrope.config.LeashCommonConfig;
|
||||
import top.r3944realms.superleadrope.config.LeashConfigManager;
|
||||
import top.r3944realms.superleadrope.content.capability.CapabilityHandler;
|
||||
import top.r3944realms.superleadrope.content.capability.CapabilityRemainder;
|
||||
import top.r3944realms.superleadrope.content.capability.impi.LeashDataImpl;
|
||||
|
|
@ -71,6 +74,8 @@ import top.r3944realms.superleadrope.core.register.SLPItems;
|
|||
import top.r3944realms.superleadrope.core.util.PotatoMode;
|
||||
import top.r3944realms.superleadrope.core.util.PotatoModeHelper;
|
||||
import top.r3944realms.superleadrope.datagen.data.SLPLangKeyValue;
|
||||
import top.r3944realms.superleadrope.util.capability.LeashDataAPI;
|
||||
import top.r3944realms.superleadrope.util.capability.LeashStateAPI;
|
||||
import top.r3944realms.superleadrope.util.model.RidingRelationship;
|
||||
import top.r3944realms.superleadrope.util.riding.RidingApplier;
|
||||
import top.r3944realms.superleadrope.util.riding.RidingDismounts;
|
||||
|
|
@ -84,6 +89,7 @@ import java.util.UUID;
|
|||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
public class CommonEventHandler {
|
||||
public volatile static LeashConfigManager leashConfigManager;
|
||||
@net.minecraftforge.fml.common.Mod.EventBusSubscriber(modid = SuperLeadRope.MOD_ID, bus = net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus.FORGE)
|
||||
public static class Game {
|
||||
@SubscribeEvent
|
||||
|
|
@ -91,8 +97,8 @@ public class CommonEventHandler {
|
|||
Entity entity = event.getEntity();
|
||||
if (entity.level().isClientSide) return;
|
||||
if (entity instanceof LivingEntity || entity instanceof Boat || entity instanceof Minecart) {
|
||||
entity.getCapability(CapabilityHandler.LEASH_DATA_CAP).ifPresent(LeashSyncManager.Data::track);
|
||||
entity.getCapability(CapabilityHandler.LEASH_STATE_CAP).ifPresent(LeashSyncManager.State::track);
|
||||
LeashDataAPI.getLeashData(entity).ifPresent(LeashSyncManager.Data::track);
|
||||
LeashStateAPI.getLeashState(entity).ifPresent(LeashSyncManager.State::track);
|
||||
if (entity instanceof ServerPlayer serverPlayer) {
|
||||
LeashSyncManager.Data.forEach(i -> {
|
||||
if (i.isLeashedBy(serverPlayer) && i.isInDelayedLeash(serverPlayer.getUUID())) {
|
||||
|
|
@ -115,8 +121,8 @@ public class CommonEventHandler {
|
|||
}
|
||||
});
|
||||
}
|
||||
entity.getCapability(CapabilityHandler.LEASH_DATA_CAP).ifPresent(LeashSyncManager.Data::untrack);
|
||||
entity.getCapability(CapabilityHandler.LEASH_STATE_CAP).ifPresent(LeashSyncManager.State::untrack);
|
||||
LeashDataAPI.getLeashData(entity).ifPresent(LeashSyncManager.Data::untrack);
|
||||
LeashStateAPI.getLeashState(entity).ifPresent(LeashSyncManager.State::untrack);
|
||||
}
|
||||
}
|
||||
@SubscribeEvent
|
||||
|
|
@ -139,9 +145,6 @@ public class CommonEventHandler {
|
|||
@SubscribeEvent
|
||||
public static void onPlayerRightHitOnBlock(PlayerInteractEvent.RightClickBlock event) {
|
||||
Level level = event.getLevel();
|
||||
if (level.isClientSide) {
|
||||
return;
|
||||
}
|
||||
BlockPos blockPos = event.getHitVec().getBlockPos();
|
||||
BlockState blockState = level.getBlockState(blockPos);
|
||||
Player player = event.getEntity();
|
||||
|
|
@ -256,7 +259,7 @@ public class CommonEventHandler {
|
|||
List<Entity> entities = LeashDataImpl.leashableInArea(telEntity);
|
||||
//规则关闭则禁止
|
||||
if(!SLPGameruleRegistry.getGameruleBoolValue(event.getEntity().level(), TeleportWithLeashedEntities.ID)) {
|
||||
entities.forEach(i -> i.getCapability(CapabilityHandler.LEASH_DATA_CAP).ifPresent(j -> j.removeLeash(i)));
|
||||
entities.forEach(entity -> LeashDataAPI.LeashOperations.detach(entity, telEntity));
|
||||
return;
|
||||
}
|
||||
for (Entity beLeashedEntity : entities) {
|
||||
|
|
@ -268,9 +271,9 @@ public class CommonEventHandler {
|
|||
Vec3 originalDeltaMovement = beLeashedEntity.getDeltaMovement();
|
||||
|
||||
AtomicReference<ILeashData.LeashInfo> originalLeashInfo = new AtomicReference<>();
|
||||
beLeashedEntity.getCapability(CapabilityHandler.LEASH_DATA_CAP).ifPresent(cap -> {
|
||||
originalLeashInfo.set(cap.getLeashInfo(telEntity).orElse(null));
|
||||
cap.removeLeash(telEntity);
|
||||
LeashDataAPI.getLeashData(beLeashedEntity).ifPresent(data -> {
|
||||
originalLeashInfo.set(data.getLeashInfo(telEntity).orElse(null));
|
||||
data.removeLeash(telEntity);
|
||||
});
|
||||
|
||||
|
||||
|
|
@ -302,14 +305,11 @@ public class CommonEventHandler {
|
|||
entity.setPose(originalPose);
|
||||
}
|
||||
|
||||
// --- 恢复拴绳 ---
|
||||
// --- 将holder替换 ---
|
||||
ILeashData.LeashInfo leashInfo = Optional.ofNullable(originalLeashInfo.get())
|
||||
.map(info -> info.transferHolder(telEntity))
|
||||
.orElse(ILeashData.LeashInfo.EMPTY);
|
||||
|
||||
beLeashedEntity.getCapability(CapabilityHandler.LEASH_DATA_CAP).ifPresent(
|
||||
cap -> cap.addLeash(telEntity, leashInfo)
|
||||
);
|
||||
LeashDataAPI.LeashOperations.attachWithInfo(beLeashedEntity, telEntity, leashInfo);
|
||||
|
||||
// --- 重新应用骑乘关系,仅保留白名单根载具 ---
|
||||
RidingRelationship filteredRelationship = RidingSaver.filterByWhitelistRoot(originalRidingRelationship);
|
||||
|
|
@ -362,7 +362,8 @@ public class CommonEventHandler {
|
|||
@net.minecraftforge.fml.common.Mod.EventBusSubscriber(modid = SuperLeadRope.MOD_ID, bus= net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus.MOD)
|
||||
public static class Mod {
|
||||
@SubscribeEvent
|
||||
public static void onCommonInit (FMLCommonSetupEvent event) {
|
||||
public static void onFMLCommonInit(FMLCommonSetupEvent event) {
|
||||
event.enqueueWork(Mod::checkAndSet);
|
||||
event.enqueueWork(SLPGameruleRegistry::register);//规则注册
|
||||
}
|
||||
@SubscribeEvent
|
||||
|
|
@ -375,6 +376,43 @@ public class CommonEventHandler {
|
|||
event.accept(SLPItems.SUPER_LEAD_ROPE);
|
||||
}
|
||||
}
|
||||
@SubscribeEvent
|
||||
public void onConfigReloading(ModConfigEvent.Reloading event) {
|
||||
if (event.getConfig().getSpec() == LeashCommonConfig.SPEC) {
|
||||
SuperLeadRope.logger.debug("Config reloading detected...");
|
||||
}
|
||||
}
|
||||
private static void checkAndSet() {
|
||||
if (leashConfigManager == null) {
|
||||
synchronized (LeashConfigManager.class) {
|
||||
if (leashConfigManager == null) {
|
||||
leashConfigManager = new LeashConfigManager();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void onConfigLoaded(ModConfigEvent.Loading event) {
|
||||
if (event.getConfig().getSpec() == LeashCommonConfig.SPEC) {
|
||||
checkAndSet();
|
||||
LeashConfigManager.loading(leashConfigManager);
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void onConfigReloaded(ModConfigEvent.Reloading event) {
|
||||
if (event.getConfig().getSpec() == LeashCommonConfig.SPEC) {
|
||||
checkAndSet();
|
||||
LeashConfigManager.reloading(leashConfigManager);
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void onConfigUnloaded(ModConfigEvent.Unloading event) {
|
||||
if (event.getConfig().getSpec() == LeashCommonConfig.SPEC) {
|
||||
LeashConfigManager.unloading(leashConfigManager);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -33,6 +33,7 @@ import top.r3944realms.superleadrope.util.file.ConfigUtil;
|
|||
@Mod(value = SuperLeadRope.MOD_ID)
|
||||
public class SuperLeadRope {
|
||||
public static final Logger logger = LoggerFactory.getLogger(SuperLeadRope.class);
|
||||
|
||||
public static final String MOD_ID = "superleadrope";
|
||||
public SuperLeadRope() {
|
||||
IEventBus eventBus = FMLJavaModLoadingContext.get().getModEventBus();
|
||||
|
|
@ -41,6 +42,7 @@ public class SuperLeadRope {
|
|||
SLPSoundEvents.register(eventBus);
|
||||
NetworkHandler.register();
|
||||
initialize();
|
||||
|
||||
}
|
||||
public static void initialize() {
|
||||
logger.info("Initializing SuperLeadRope");
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ public class ClientEventHandler {
|
|||
(itemStack, clientLevel, livingEntity, i) -> {
|
||||
if (!itemStack.isDamageableItem()) return 0.0F;
|
||||
|
||||
return itemStack.getDamageValue() > 1024 - 50 ? 1.0F : 0.0F; // 损坏 → 返回 1.0(触发 override)
|
||||
return itemStack.getDamageValue() > 1024 - 33 ? 1.0F : 0.0F; // 损坏 → 返回 1.0(触发 override)
|
||||
}
|
||||
);
|
||||
PotatoMode mode = getCurrentMode();
|
||||
|
|
|
|||
|
|
@ -24,9 +24,9 @@ import net.minecraft.world.entity.Entity;
|
|||
import net.minecraft.world.level.Level;
|
||||
import top.r3944realms.superleadrope.SuperLeadRope;
|
||||
import top.r3944realms.superleadrope.client.renderer.resolver.SuperLeashStateResolver;
|
||||
import top.r3944realms.superleadrope.content.capability.CapabilityHandler;
|
||||
import top.r3944realms.superleadrope.content.capability.inter.ILeashData;
|
||||
import top.r3944realms.superleadrope.content.entity.SuperLeashKnotEntity;
|
||||
import top.r3944realms.superleadrope.util.capability.LeashDataAPI;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
|
@ -46,10 +46,9 @@ public class LeashRenderHandler {
|
|||
|
||||
// 遍历摄像机附近所有实体
|
||||
for (Entity entity : level.getEntitiesOfClass(Entity.class,
|
||||
cameraEntity.getBoundingBox().inflate(50))) {
|
||||
cameraEntity.getBoundingBox().inflate(100))) {
|
||||
|
||||
entity.getCapability(CapabilityHandler.LEASH_DATA_CAP).ifPresent(leashData -> {
|
||||
if(leashData instanceof ILeashData) {}
|
||||
LeashDataAPI.getLeashData(entity).ifPresent(leashData -> {
|
||||
for (ILeashData.LeashInfo leashInfo : leashData.getAllLeashes()) {
|
||||
renderLeashFromInfo(entity, leashInfo, poseStack, bufferSource, partialTick);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ public class SuperLeashRenderer {
|
|||
int skyLightEnd = getSkyLight(BlockPos.containing(endWorld));
|
||||
|
||||
// 差向量 + 偏移
|
||||
Offsets offsets = computeOffsets(state, startWorld, endWorld);
|
||||
Offsets offsets = computeOffsets(startWorld, endWorld);
|
||||
|
||||
// pass1: 0 → N
|
||||
for (int i = 0; i <= LEASH_STEPS; i++) {
|
||||
|
|
@ -76,7 +76,7 @@ public class SuperLeashRenderer {
|
|||
|
||||
|
||||
/** 计算差向量和偏移 */
|
||||
private static Offsets computeOffsets(SuperLeashRenderState state, Vec3 start, Vec3 end) {
|
||||
private static Offsets computeOffsets(Vec3 start, Vec3 end) {
|
||||
float dx = (float) (end.x - start.x);
|
||||
float dy = (float) (end.y - start.y);
|
||||
float dz = (float) (end.z - start.z);
|
||||
|
|
|
|||
|
|
@ -20,11 +20,10 @@ import net.minecraft.world.level.Level;
|
|||
import net.minecraft.world.phys.Vec3;
|
||||
import top.r3944realms.superleadrope.SuperLeadRope;
|
||||
import top.r3944realms.superleadrope.client.renderer.state.SuperLeashRenderState;
|
||||
import top.r3944realms.superleadrope.content.capability.CapabilityHandler;
|
||||
import top.r3944realms.superleadrope.content.capability.impi.LeashDataImpl;
|
||||
import top.r3944realms.superleadrope.content.capability.inter.ILeashData;
|
||||
import top.r3944realms.superleadrope.content.entity.SuperLeashKnotEntity;
|
||||
import top.r3944realms.superleadrope.util.capability.LeashUtil;
|
||||
import top.r3944realms.superleadrope.util.capability.LeashStateAPI;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
|
@ -56,7 +55,7 @@ public class SuperLeashStateResolver {
|
|||
}
|
||||
AtomicReference<Vec3> holderOffset = new AtomicReference<>();
|
||||
AtomicReference<Vec3> entityOffset = new AtomicReference<>();
|
||||
LeashUtil.getLeashState(leashedEntity).ifPresent(state ->
|
||||
LeashStateAPI.getLeashState(leashedEntity).ifPresent(state ->
|
||||
state
|
||||
.getLeashState(holder)
|
||||
.ifPresent(ls -> {
|
||||
|
|
@ -110,7 +109,6 @@ public class SuperLeashStateResolver {
|
|||
return Optional.of(new SuperLeashRenderState(
|
||||
currentHolderPos,
|
||||
currentEntityPos,
|
||||
leashInfo.attachOffset(),
|
||||
lastHolderPos,
|
||||
lastEntityPos,
|
||||
tension,
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ import net.minecraft.world.phys.Vec3;
|
|||
public record SuperLeashRenderState(
|
||||
Vec3 startPos, // 当前帧起点位置
|
||||
Vec3 endPos, // 当前帧终点位置
|
||||
Vec3 attachOffset, // 拴绳附着点偏移
|
||||
Vec3 lastStartPos, // 上一帧起点位置(用于摆动计算)
|
||||
Vec3 lastEndPos, // 上一帧终点位置(用于摆动计算)
|
||||
float tension, // 张力强度(0.0-1.0)
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ package top.r3944realms.superleadrope.config;
|
|||
import net.minecraftforge.common.ForgeConfigSpec;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class LeashCommonConfig {
|
||||
public static ForgeConfigSpec.Builder BUILDER = new ForgeConfigSpec.Builder();
|
||||
|
|
@ -38,13 +40,18 @@ public class LeashCommonConfig {
|
|||
public final ForgeConfigSpec.DoubleValue springDampening;
|
||||
public final ForgeConfigSpec.ConfigValue<List<? extends Double>> axisSpecificElasticity;
|
||||
public final ForgeConfigSpec.IntValue maxLeashesPerEntity;
|
||||
public final ForgeConfigSpec.ConfigValue<List<? extends String>> defaultApplyEntityLocationOffset;
|
||||
// 正则表达式模式
|
||||
static final Pattern OFFSET_PATTERN = Pattern.compile(
|
||||
"(?i)(?:vec3|vec3d|vector3|offset)\\s*\\(\\s*([-+]?[0-9]*\\.?[0-9]+)\\s*,\\s*([-+]?[0-9]*\\.?[0-9]+)\\s*,\\s*([-+]?[0-9]*\\.?[0-9]+)\\s*\\)\\s*:\\s*\\[\\s*([^]]+?)\\s*]\\s*" );
|
||||
|
||||
public Common(ForgeConfigSpec.Builder builder) {
|
||||
BUILDER.push("Command");
|
||||
EnableSLPModCommandPrefix = builder
|
||||
.comment("The prefix of this mod's commands")
|
||||
.define("SLPModCommandPrefix", true);
|
||||
SLPModCommandPrefix = builder
|
||||
.comment("The prefix of this mod's commands"," [ Default:'slp'] ")
|
||||
.comment("The prefix of this mod's commands", " [ Default:'slp'] ")
|
||||
.define("EnableSLPModCommandPrefix", "slp");
|
||||
BUILDER.pop();
|
||||
builder.push("Entity");
|
||||
|
|
@ -59,7 +66,7 @@ public class LeashCommonConfig {
|
|||
.defineListAllowEmpty(
|
||||
List.of("allowedTeleportEntities"),
|
||||
List.of("#minecraft", "modernlife:bicycle", "modernlife:motorboat"),
|
||||
o -> o instanceof String s && isValidFormat(s)
|
||||
o -> o instanceof String s && isValidEntityRefFormat(s)
|
||||
);
|
||||
builder.pop();
|
||||
builder.push("LeashSettings");
|
||||
|
|
@ -87,20 +94,70 @@ public class LeashCommonConfig {
|
|||
.defineInRange("maxLeashesPerEntity", 6, 1, 24);
|
||||
|
||||
builder.pop();
|
||||
builder.push("LeashStateSettings");
|
||||
defaultApplyEntityLocationOffset = builder
|
||||
.comment(
|
||||
"Default leash attachment point offsets for entities.",
|
||||
"Format: vec3(x,y,z) : [entity_list]",
|
||||
"Optional Name : vector3, vec3d, offset",
|
||||
"Entity list can contain:",
|
||||
" - modid:entity_id : specific entity (e.g. minecraft:bee)",
|
||||
" - #modid:tag_name : entity type tag (e.g. #minecraft:boats)",
|
||||
" - #modid : mod-wide (e.g. #minecraft)",
|
||||
"Multiple entities can be separated by commas",
|
||||
"Example: vec3(0,0.2,0) : [minecraft:bee, minecraft:horse]",
|
||||
"Priority: specific entity > tag > mod"
|
||||
)
|
||||
.defineListAllowEmpty(
|
||||
List.of("defaultApplyEntityLocationOffset"),
|
||||
List.of(
|
||||
"vec3(0,0.2,0) : [minecraft:bee]",
|
||||
"vec3(0,1.0,0) : [minecraft:horse, minecraft:donkey]",
|
||||
"vec3(0,0.5,0) : [#minecraft:boats]",
|
||||
"vec3(0,0.4,0) : [#minecraft:minecarts]",
|
||||
"vec3(0,0.3,0) : [#minecraft]",
|
||||
"vec3(0,0.6,0) : [#modernlife]"
|
||||
),
|
||||
o -> o instanceof String s && isValidOffsetRefFormat(s)
|
||||
);
|
||||
BUILDER.pop();
|
||||
}
|
||||
|
||||
private static boolean isValidFormat(String s) {
|
||||
private static boolean isValidEntityRefFormat(String s) {
|
||||
if (s.startsWith("#")) {
|
||||
String body = s.substring(1);
|
||||
// 支持 #modid (整个模组)
|
||||
if (body.matches("[a-z0-9_]+")) {
|
||||
return true;
|
||||
}
|
||||
// 支持 #modid:tag_name (标签)
|
||||
return body.matches("[a-z0-9_]+:[a-z0-9_/]+");
|
||||
// 支持 #modid (整个模组)或 #modid:tag_name (标签)
|
||||
return body.matches("[a-z0-9_]+(:[a-z0-9_/]+)?");
|
||||
}
|
||||
// 普通实体 ID
|
||||
// 普通实体 ID: modid:entity_id
|
||||
return s.matches("[a-z0-9_]+:[a-z0-9_/]+");
|
||||
}
|
||||
|
||||
private static boolean isValidOffsetRefFormat(String s) {
|
||||
// 匹配格式: vec3(x,y,z) : [entity_list]
|
||||
Matcher matcher = Common.OFFSET_PATTERN.matcher(s);
|
||||
if (!matcher.matches()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查坐标值是否有效
|
||||
try {
|
||||
Double.parseDouble(matcher.group(1));
|
||||
Double.parseDouble(matcher.group(2));
|
||||
Double.parseDouble(matcher.group(3));
|
||||
|
||||
// 检查实体列表格式
|
||||
String entityList = matcher.group(4);
|
||||
String[] entities = entityList.split(",");
|
||||
for (String entity : entities) {
|
||||
if (!isValidEntityRefFormat(entity.trim())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} catch (NumberFormatException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,361 @@
|
|||
/*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.r3944realms.superleadrope.config;
|
||||
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.tags.TagKey;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import top.r3944realms.superleadrope.SuperLeadRope;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.regex.Matcher;
|
||||
|
||||
import static top.r3944realms.superleadrope.config.LeashCommonConfig.Common.OFFSET_PATTERN;
|
||||
|
||||
public class LeashConfigManager {
|
||||
private final Map<String, double[]> entityOffsetMap = new ConcurrentHashMap<>();
|
||||
private final Map<String, double[]> tagOffsetMap = new ConcurrentHashMap<>();
|
||||
private final Map<String, double[]> modOffsetMap = new ConcurrentHashMap<>();
|
||||
|
||||
// 缓存常用配置值以提高性能
|
||||
private volatile List<String> teleportWhitelistCache;
|
||||
private volatile String commandPrefixCache;
|
||||
private volatile boolean enableCommandPrefixCache;
|
||||
private volatile double maxLeashLengthCache;
|
||||
private volatile double elasticDistanceCache;
|
||||
private volatile double extremeSnapFactorCache;
|
||||
private volatile double springDampeningCache;
|
||||
private volatile List<Double> axisSpecificElasticityCache;
|
||||
private volatile int maxLeashesPerEntityCache;
|
||||
|
||||
public LeashConfigManager() {
|
||||
this.reloadAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析偏移配置(线程安全)
|
||||
*/
|
||||
public void parseOffsetConfig() {
|
||||
Map<String, double[]> newEntityOffsetMap = new HashMap<>();
|
||||
Map<String, double[]> newTagOffsetMap = new HashMap<>();
|
||||
Map<String, double[]> newModOffsetMap = new HashMap<>();
|
||||
|
||||
List<? extends String> offsets = LeashCommonConfig.COMMON.defaultApplyEntityLocationOffset.get();
|
||||
for (String offsetConfig : offsets) {
|
||||
Matcher matcher = OFFSET_PATTERN.matcher(offsetConfig);
|
||||
if (!matcher.matches()) continue;
|
||||
|
||||
try {
|
||||
double x = Double.parseDouble(matcher.group(1).trim());
|
||||
double y = Double.parseDouble(matcher.group(2).trim());
|
||||
double z = Double.parseDouble(matcher.group(3).trim());
|
||||
double[] offset = new double[]{x, y, z};
|
||||
|
||||
String entityList = matcher.group(4);
|
||||
String[] entities = entityList.split(",");
|
||||
|
||||
for (String entity : entities) {
|
||||
String trimmed = entity.trim();
|
||||
if (trimmed.startsWith("#")) {
|
||||
String body = trimmed.substring(1).trim();
|
||||
if (body.contains(":")) {
|
||||
newTagOffsetMap.put(body, offset);
|
||||
} else {
|
||||
newModOffsetMap.put(body, offset);
|
||||
}
|
||||
} else {
|
||||
newEntityOffsetMap.put(trimmed, offset);
|
||||
}
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
System.err.println("Invalid offset config: " + offsetConfig);
|
||||
}
|
||||
}
|
||||
|
||||
// 原子性更新映射
|
||||
entityOffsetMap.clear();
|
||||
entityOffsetMap.putAll(newEntityOffsetMap);
|
||||
|
||||
tagOffsetMap.clear();
|
||||
tagOffsetMap.putAll(newTagOffsetMap);
|
||||
|
||||
modOffsetMap.clear();
|
||||
modOffsetMap.putAll(newModOffsetMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* 重新加载所有配置值到缓存
|
||||
*/
|
||||
public void reloadAll() {
|
||||
// 加载偏移配置
|
||||
parseOffsetConfig();
|
||||
|
||||
// 加载其他配置到缓存
|
||||
teleportWhitelistCache = new ArrayList<>(LeashCommonConfig.COMMON.teleportWhitelist.get());
|
||||
commandPrefixCache = LeashCommonConfig.COMMON.SLPModCommandPrefix.get();
|
||||
enableCommandPrefixCache = LeashCommonConfig.COMMON.EnableSLPModCommandPrefix.get();
|
||||
maxLeashLengthCache = LeashCommonConfig.COMMON.maxLeashLength.get();
|
||||
elasticDistanceCache = LeashCommonConfig.COMMON.elasticDistance.get();
|
||||
extremeSnapFactorCache = LeashCommonConfig.COMMON.extremeSnapFactor.get();
|
||||
springDampeningCache = LeashCommonConfig.COMMON.springDampening.get();
|
||||
axisSpecificElasticityCache = new ArrayList<>(LeashCommonConfig.COMMON.axisSpecificElasticity.get());
|
||||
maxLeashesPerEntityCache = LeashCommonConfig.COMMON.maxLeashesPerEntity.get();
|
||||
|
||||
SuperLeadRope.logger.debug("All configs reloaded: {}", getStats());
|
||||
}
|
||||
|
||||
// ========== 偏移配置相关方法 ==========
|
||||
|
||||
/**
|
||||
* 获取实体类型的偏移量
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public Vec3 getEntityOffset(EntityType<?> entityType) {
|
||||
String entityId = entityType.builtInRegistryHolder().key().location().toString();
|
||||
String modId = entityId.split(":")[0]; // 从实体ID提取modId
|
||||
|
||||
// 获取实体的标签
|
||||
List<String> tagStrings = new ArrayList<>();
|
||||
for (var tag : entityType.builtInRegistryHolder().tags().toList()) {
|
||||
tagStrings.add(tag.location().toString());
|
||||
}
|
||||
|
||||
double[] offset = getEntityOffset(entityId, modId, tagStrings);
|
||||
return offset != null ? new Vec3(offset[0], offset[1], offset[2]) : Vec3.ZERO;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取实体对象的偏移量(便捷方法)
|
||||
*/
|
||||
public Vec3 getEntityOffset(Entity entity) {
|
||||
return getEntityOffset(entity.getType());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取实体偏移量(原始数据)
|
||||
*/
|
||||
public double[] getEntityOffset(String entityId, String modId, List<String> tags) {
|
||||
// 1. 首先检查特定实体
|
||||
if (entityOffsetMap.containsKey(entityId)) {
|
||||
return entityOffsetMap.get(entityId);
|
||||
}
|
||||
|
||||
// 2. 检查标签
|
||||
for (String tag : tags) {
|
||||
if (tagOffsetMap.containsKey(tag)) {
|
||||
return tagOffsetMap.get(tag);
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 检查模组
|
||||
if (modOffsetMap.containsKey(modId)) {
|
||||
return modOffsetMap.get(modId);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// ========== 传送白名单相关方法 ==========
|
||||
|
||||
public List<String> getTeleportWhitelist() {
|
||||
return Collections.unmodifiableList(teleportWhitelistCache);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public boolean isEntityTeleportAllowed(EntityType<?> entityType) {
|
||||
String entityId = entityType.builtInRegistryHolder().key().location().toString();
|
||||
String modid = entityId.split(":")[0];
|
||||
|
||||
for (String entry : teleportWhitelistCache) {
|
||||
if (entry.startsWith("#")) {
|
||||
String body = entry.substring(1);
|
||||
|
||||
// Case 1: #modid → allow all entities from this mod
|
||||
if (!body.contains(":")) {
|
||||
if (modid.equals(body)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// Case 2: #modid:tag_name → allow all entities under this tag
|
||||
else {
|
||||
ResourceLocation tagId = new ResourceLocation(body);
|
||||
TagKey<EntityType<?>> tag = TagKey.create(Registries.ENTITY_TYPE, tagId);
|
||||
if (entityType.builtInRegistryHolder().is(tag)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Case 3: modid:entity_name → allow a specific entity
|
||||
if (entry.equals(entityId)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isEntityTeleportAllowed(String entityId) {
|
||||
// 对于字符串ID,我们无法检查标签,只能检查模组和特定实体
|
||||
String modid = entityId.contains(":") ? entityId.split(":")[0] : "minecraft";
|
||||
|
||||
for (String entry : teleportWhitelistCache) {
|
||||
if (entry.startsWith("#")) {
|
||||
String body = entry.substring(1);
|
||||
|
||||
// Case 1: #modid → allow all entities from this mod
|
||||
if (!body.contains(":")) {
|
||||
if (modid.equals(body)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// Case 2: #modid:tag_name → 字符串ID无法检查标签,跳过
|
||||
// 如果需要支持标签检查,需要传入EntityType而不是String
|
||||
} else {
|
||||
// Case 3: modid:entity_name → allow a specific entity
|
||||
if (entry.equals(entityId)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// 辅助方法:检查实体ID是否匹配模式(用于旧的匹配逻辑)
|
||||
private boolean matchesTeleportPattern(String pattern, String entityId) {
|
||||
if (pattern.startsWith("#")) {
|
||||
String body = pattern.substring(1);
|
||||
if (body.contains(":")) {
|
||||
// 标签格式: #modid:tag_name - 字符串ID无法准确匹配标签
|
||||
// 返回模组匹配作为近似
|
||||
String patternModId = body.split(":")[0];
|
||||
String entityModId = entityId.split(":")[0];
|
||||
return entityModId.equals(patternModId);
|
||||
} else {
|
||||
// 模组格式: #modid
|
||||
String entityModId = entityId.split(":")[0];
|
||||
return entityModId.equals(body);
|
||||
}
|
||||
} else {
|
||||
// 实体格式: modid:entity_id
|
||||
return entityId.equals(pattern);
|
||||
}
|
||||
}
|
||||
|
||||
// 添加一个重载方法,方便使用Entity对象
|
||||
public boolean isEntityTeleportAllowed(Entity entity) {
|
||||
return isEntityTeleportAllowed(entity.getType());
|
||||
}
|
||||
|
||||
// ========== 命令配置相关方法 ==========
|
||||
|
||||
public String getCommandPrefix() {
|
||||
return commandPrefixCache;
|
||||
}
|
||||
|
||||
public boolean isCommandPrefixEnabled() {
|
||||
return enableCommandPrefixCache;
|
||||
}
|
||||
|
||||
public String getFullCommand(String subCommand) {
|
||||
return isCommandPrefixEnabled() ?
|
||||
getCommandPrefix() + " " + subCommand :
|
||||
subCommand;
|
||||
}
|
||||
|
||||
// ========== 拴绳物理配置相关方法 ==========
|
||||
|
||||
public double getMaxLeashLength() {
|
||||
return maxLeashLengthCache;
|
||||
}
|
||||
|
||||
public double getElasticDistance() {
|
||||
return elasticDistanceCache;
|
||||
}
|
||||
|
||||
public double getExtremeSnapFactor() {
|
||||
return extremeSnapFactorCache;
|
||||
}
|
||||
|
||||
public double getBreakDistance() {
|
||||
return getMaxLeashLength() * getExtremeSnapFactor();
|
||||
}
|
||||
|
||||
public double getSpringDampening() {
|
||||
return springDampeningCache;
|
||||
}
|
||||
|
||||
public List<Double> getAxisSpecificElasticity() {
|
||||
return Collections.unmodifiableList(axisSpecificElasticityCache);
|
||||
}
|
||||
|
||||
public double getXElasticity() {
|
||||
return !axisSpecificElasticityCache.isEmpty() ? axisSpecificElasticityCache.get(0) : 0.8;
|
||||
}
|
||||
|
||||
public double getYElasticity() {
|
||||
return axisSpecificElasticityCache.size() > 1 ? axisSpecificElasticityCache.get(1) : 0.2;
|
||||
}
|
||||
|
||||
public double getZElasticity() {
|
||||
return axisSpecificElasticityCache.size() > 2 ? axisSpecificElasticityCache.get(2) : 0.8;
|
||||
}
|
||||
|
||||
// ========== 实体限制配置相关方法 ==========
|
||||
|
||||
public int getMaxLeashesPerEntity() {
|
||||
return maxLeashesPerEntityCache;
|
||||
}
|
||||
|
||||
public boolean canEntityAcceptMoreLeashes(Entity entity, int currentLeashCount) {
|
||||
return currentLeashCount < getMaxLeashesPerEntity();
|
||||
}
|
||||
|
||||
// ========== 管理方法 ==========
|
||||
|
||||
public void reload() {
|
||||
reloadAll();
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
entityOffsetMap.clear();
|
||||
tagOffsetMap.clear();
|
||||
modOffsetMap.clear();
|
||||
teleportWhitelistCache = Collections.emptyList();
|
||||
}
|
||||
|
||||
public String getStats() {
|
||||
return String.format("Entities: %d, Tags: %d, Mods: %d, TeleportWhitelist: %d",
|
||||
entityOffsetMap.size(), tagOffsetMap.size(), modOffsetMap.size(),
|
||||
teleportWhitelistCache.size());
|
||||
}
|
||||
|
||||
public static void loading(LeashConfigManager manager) {
|
||||
manager.reloadAll();
|
||||
}
|
||||
|
||||
public static void reloading(LeashConfigManager manager) {
|
||||
manager.reloadAll();
|
||||
}
|
||||
|
||||
public static void unloading(LeashConfigManager manager) {
|
||||
manager.clear();
|
||||
}
|
||||
}
|
||||
|
|
@ -18,7 +18,7 @@ package top.r3944realms.superleadrope.content.capability;
|
|||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraftforge.event.entity.player.PlayerEvent;
|
||||
import top.r3944realms.superleadrope.util.capability.LeashUtil;
|
||||
import top.r3944realms.superleadrope.util.capability.LeashStateAPI;
|
||||
|
||||
public class CapabilityRemainder {
|
||||
public static void onPlayerClone(PlayerEvent.Clone event) {
|
||||
|
|
@ -26,9 +26,9 @@ public class CapabilityRemainder {
|
|||
if(newEntity instanceof ServerPlayer newPlayer) {
|
||||
Player original = event.getOriginal();
|
||||
original.reviveCaps();
|
||||
LeashUtil.getLeashState(original)
|
||||
LeashStateAPI.getLeashState(original)
|
||||
.ifPresent(oldCap ->
|
||||
LeashUtil.getLeashState(newPlayer)
|
||||
LeashStateAPI.getLeashState(newPlayer)
|
||||
.ifPresent(newData ->
|
||||
newData.copy(oldCap, newEntity)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -21,8 +21,8 @@ import net.minecraft.nbt.ListTag;
|
|||
import net.minecraft.nbt.NbtUtils;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.sounds.SoundSource;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.entity.Mob;
|
||||
import net.minecraft.world.entity.animal.horse.Llama;
|
||||
|
|
@ -30,26 +30,26 @@ import net.minecraft.world.entity.player.Player;
|
|||
import net.minecraft.world.entity.vehicle.Boat;
|
||||
import net.minecraft.world.entity.vehicle.Minecart;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.pathfinder.Path;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import net.minecraftforge.network.PacketDistributor;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import top.r3944realms.superleadrope.CommonEventHandler;
|
||||
import top.r3944realms.superleadrope.SuperLeadRope;
|
||||
import top.r3944realms.superleadrope.config.LeashCommonConfig;
|
||||
import top.r3944realms.superleadrope.content.capability.inter.ILeashData;
|
||||
import top.r3944realms.superleadrope.content.entity.SuperLeashKnotEntity;
|
||||
import top.r3944realms.superleadrope.core.register.SLPSoundEvents;
|
||||
import top.r3944realms.superleadrope.network.NetworkHandler;
|
||||
import top.r3944realms.superleadrope.network.toClient.LeashDataSyncPacket;
|
||||
import top.r3944realms.superleadrope.util.capability.LeashUtil;
|
||||
import top.r3944realms.superleadrope.util.nbt.NBTReader;
|
||||
import top.r3944realms.superleadrope.util.nbt.NBTWriter;
|
||||
import top.r3944realms.superleadrope.util.capability.LeashDataAPI;
|
||||
import top.r3944realms.superleadrope.util.capability.LeashStateAPI;
|
||||
import top.r3944realms.superleadrope.util.riding.RindingLeash;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
|
@ -85,34 +85,6 @@ import java.util.stream.Stream;
|
|||
* </table>
|
||||
*/
|
||||
public class LeashDataImpl implements ILeashData {
|
||||
private static final class Config {
|
||||
private Config() {} // 私有构造防止实例化
|
||||
|
||||
static double maxLeashDistance() {
|
||||
return LeashCommonConfig.COMMON.maxLeashLength.get();
|
||||
}
|
||||
|
||||
static double leashElasticDist() {
|
||||
return LeashCommonConfig.COMMON.elasticDistance.get();
|
||||
}
|
||||
|
||||
static double leashExtremeSnapDistFactor() {
|
||||
return LeashCommonConfig.COMMON.extremeSnapFactor.get();
|
||||
}
|
||||
|
||||
static double springDampening() {
|
||||
return LeashCommonConfig.COMMON.springDampening.get();
|
||||
}
|
||||
|
||||
static Vec3 axisSpecificElasticity() {
|
||||
List<? extends Double> list = LeashCommonConfig.COMMON.axisSpecificElasticity.get();
|
||||
return new Vec3(list.get(0), list.get(1), list.get(2));
|
||||
}
|
||||
|
||||
static int maxLeashesPerEntity() {
|
||||
return LeashCommonConfig.COMMON.maxLeashesPerEntity.get();
|
||||
}
|
||||
}
|
||||
private final Entity entity;
|
||||
private boolean needsSync = false;
|
||||
private long lastSyncTime;
|
||||
|
|
@ -172,18 +144,18 @@ public class LeashDataImpl implements ILeashData {
|
|||
|
||||
@Override
|
||||
public boolean addLeash(Entity holder) {
|
||||
return addLeash(holder, Config.maxLeashDistance());
|
||||
return addLeash(holder, CommonEventHandler.leashConfigManager.getMaxLeashLength());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addLeash(Entity holder, String reserved) {
|
||||
return addLeash(holder, Config.maxLeashDistance(), reserved);
|
||||
return addLeash(holder, CommonEventHandler.leashConfigManager.getMaxLeashLength(), reserved);
|
||||
}
|
||||
|
||||
// 添加拴绳(支持自定义最大长度)
|
||||
@Override
|
||||
public boolean addLeash(Entity holder, double maxDistance) {
|
||||
return addLeash(holder, maxDistance, Config.leashElasticDist(), 0, "");
|
||||
return addLeash(holder, maxDistance, CommonEventHandler.leashConfigManager.getElasticDistance(), 0, "");
|
||||
}
|
||||
|
||||
// 添加拴绳(支持自定义最大长度和弹性距离)
|
||||
|
|
@ -195,7 +167,7 @@ public class LeashDataImpl implements ILeashData {
|
|||
// 添加拴绳(支持自定义最大长度 + reserved 字段)
|
||||
@Override
|
||||
public boolean addLeash(Entity holder, double maxDistance, String reserved) {
|
||||
return addLeash(holder, maxDistance, Config.leashElasticDist(), 0, reserved);
|
||||
return addLeash(holder, maxDistance, CommonEventHandler.leashConfigManager.getElasticDistance(), 0, reserved);
|
||||
}
|
||||
|
||||
// 添加拴绳(最终实现:支持最大长度、弹性距离、保持 Tick、reserved)
|
||||
|
|
@ -219,7 +191,6 @@ public class LeashDataImpl implements ILeashData {
|
|||
LeashInfo info = LeashInfo.create(
|
||||
holder,
|
||||
reserved,
|
||||
calculateAttachOffset(entity),
|
||||
maxDistance,
|
||||
elasticDistance,
|
||||
maxKeepLeashTicks,
|
||||
|
|
@ -231,7 +202,7 @@ public class LeashDataImpl implements ILeashData {
|
|||
} else {
|
||||
leashHolders.put(holder.getUUID(), info);
|
||||
}
|
||||
|
||||
LeashStateAPI.Operations.attach(entity, holder);
|
||||
markForSync();
|
||||
return true;
|
||||
}
|
||||
|
|
@ -301,7 +272,6 @@ public class LeashDataImpl implements ILeashData {
|
|||
old.holderUUIDOpt().get(),
|
||||
old.holderIdOpt().get(),
|
||||
old.reserved(),
|
||||
old.attachOffset(),
|
||||
newMaxDistance,
|
||||
old.elasticDistance(),
|
||||
old.keepLeashTicks(),
|
||||
|
|
@ -316,7 +286,6 @@ public class LeashDataImpl implements ILeashData {
|
|||
old.holderUUIDOpt().get(),
|
||||
old.holderIdOpt().get(),
|
||||
old.reserved(),
|
||||
old.attachOffset(),
|
||||
newMaxDistance,
|
||||
old.elasticDistance(),
|
||||
Math.min(old.keepLeashTicks(), newMaxKeepLeashTicks),
|
||||
|
|
@ -331,7 +300,6 @@ public class LeashDataImpl implements ILeashData {
|
|||
old.holderUUIDOpt().get(),
|
||||
old.holderIdOpt().get(),
|
||||
reserved,
|
||||
old.attachOffset(),
|
||||
distance,
|
||||
old.elasticDistance(),
|
||||
Math.min(old.keepLeashTicks(), maxKeepTicks),
|
||||
|
|
@ -346,7 +314,6 @@ public class LeashDataImpl implements ILeashData {
|
|||
old.blockPosOpt().get(),
|
||||
old.holderIdOpt().get(),
|
||||
old.reserved(),
|
||||
old.attachOffset(),
|
||||
newMaxDistance,
|
||||
old.elasticDistance(),
|
||||
old.keepLeashTicks(),
|
||||
|
|
@ -361,7 +328,6 @@ public class LeashDataImpl implements ILeashData {
|
|||
old.blockPosOpt().get(),
|
||||
old.holderIdOpt().get(),
|
||||
old.reserved(),
|
||||
old.attachOffset(),
|
||||
newMaxDistance,
|
||||
old.elasticDistance(),
|
||||
Math.min(old.keepLeashTicks(), newMaxKeepLeashTicks),
|
||||
|
|
@ -376,7 +342,6 @@ public class LeashDataImpl implements ILeashData {
|
|||
old.blockPosOpt().get(),
|
||||
old.holderIdOpt().get(),
|
||||
reserved,
|
||||
old.attachOffset(),
|
||||
distance,
|
||||
old.elasticDistance(),
|
||||
Math.min(old.keepLeashTicks(), maxKeepTicks),
|
||||
|
|
@ -399,7 +364,6 @@ public class LeashDataImpl implements ILeashData {
|
|||
old.holderUUIDOpt().get(),
|
||||
old.holderIdOpt().get(),
|
||||
old.reserved(),
|
||||
old.attachOffset(),
|
||||
old.maxDistance(),
|
||||
newElasticDistance,
|
||||
old.keepLeashTicks(),
|
||||
|
|
@ -414,7 +378,6 @@ public class LeashDataImpl implements ILeashData {
|
|||
old.blockPosOpt().get(),
|
||||
old.holderIdOpt().get(),
|
||||
old.reserved(),
|
||||
old.attachOffset(),
|
||||
old.maxDistance(),
|
||||
newElasticDistance,
|
||||
old.keepLeashTicks(),
|
||||
|
|
@ -444,7 +407,6 @@ public class LeashDataImpl implements ILeashData {
|
|||
old.holderUUIDOpt().get(),
|
||||
old.holderIdOpt().get(),
|
||||
old.reserved(),
|
||||
old.attachOffset(),
|
||||
old.maxDistance(),
|
||||
newElasticDistance,
|
||||
Math.min(old.keepLeashTicks(), newMaxKeepLeashTicks),
|
||||
|
|
@ -459,7 +421,6 @@ public class LeashDataImpl implements ILeashData {
|
|||
old.holderUUIDOpt().get(),
|
||||
old.holderIdOpt().get(),
|
||||
reserved,
|
||||
old.attachOffset(),
|
||||
old.maxDistance(),
|
||||
distance,
|
||||
Math.min(old.keepLeashTicks(), maxKeepTicks),
|
||||
|
|
@ -474,7 +435,6 @@ public class LeashDataImpl implements ILeashData {
|
|||
old.blockPosOpt().get(),
|
||||
old.holderIdOpt().get(),
|
||||
old.reserved(),
|
||||
old.attachOffset(),
|
||||
old.maxDistance(),
|
||||
newElasticDistance,
|
||||
old.keepLeashTicks(),
|
||||
|
|
@ -489,7 +449,6 @@ public class LeashDataImpl implements ILeashData {
|
|||
old.blockPosOpt().get(),
|
||||
old.holderIdOpt().get(),
|
||||
reserved,
|
||||
old.attachOffset(),
|
||||
old.maxDistance(),
|
||||
newElasticDistance,
|
||||
Math.min(old.keepLeashTicks(), newMaxKeepLeashTicks),
|
||||
|
|
@ -502,13 +461,17 @@ public class LeashDataImpl implements ILeashData {
|
|||
*/
|
||||
@Override
|
||||
public void applyLeashForces() {
|
||||
Vec3 combinedForce = Vec3.ZERO; // 初始化合力向量
|
||||
Vec3 combinedForce = Vec3.ZERO;
|
||||
Vec3 combinedDirection = Vec3.ZERO;
|
||||
int validLeashes = 0;
|
||||
|
||||
// 计算所有拴绳的合力
|
||||
// 计算所有拴绳的合力和平均方向
|
||||
for (Map.Entry<UUID, LeashInfo> entry : leashHolders.entrySet()) {
|
||||
Vec3 force = calculateLeashForceForUUID(entry);
|
||||
if (force != null) {
|
||||
combinedForce = combinedForce.add(force);
|
||||
combinedDirection = combinedDirection.add(force.normalize());
|
||||
validLeashes++;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -516,27 +479,152 @@ public class LeashDataImpl implements ILeashData {
|
|||
Vec3 force = calculateLeashForceForBlockPos(entry);
|
||||
if (force != null) {
|
||||
combinedForce = combinedForce.add(force);
|
||||
combinedDirection = combinedDirection.add(force.normalize());
|
||||
validLeashes++;
|
||||
}
|
||||
}
|
||||
|
||||
boolean hasForce = !combinedForce.equals(Vec3.ZERO);
|
||||
Entity finalApplyEntity = RindingLeash.getFinalEntityForLeashIfForce(entity, hasForce);
|
||||
if (hasForce) {
|
||||
|
||||
if (hasForce) {
|
||||
// 处理玩家和其他实体
|
||||
if (finalApplyEntity instanceof ServerPlayer player) {
|
||||
RindingLeash.applyForceToPlayer(player, combinedForce);
|
||||
return;
|
||||
} else {
|
||||
finalApplyEntity.setDeltaMovement(finalApplyEntity.getDeltaMovement().add(combinedForce));
|
||||
finalApplyEntity.hurtMarked = true;
|
||||
|
||||
// 对生物使用合力方向进行移动(只有在能够移动时才执行)
|
||||
if (finalApplyEntity instanceof Mob mob && validLeashes > 0 && canMobMove(mob)) {
|
||||
moveMobTowardsCombinedDirection(mob, combinedDirection, validLeashes, combinedForce.length());
|
||||
} else if (finalApplyEntity instanceof Mob mob) {
|
||||
// 无法移动时停止导航
|
||||
mob.getNavigation().stop();
|
||||
}
|
||||
}
|
||||
|
||||
RindingLeash.protectAnimalMovement(finalApplyEntity, true);
|
||||
} else {
|
||||
RindingLeash.protectAnimalMovement(finalApplyEntity, false);
|
||||
|
||||
// 没有力时也停止导航
|
||||
if (finalApplyEntity instanceof Mob mob) {
|
||||
mob.getNavigation().stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查生物是否能够移动
|
||||
*/
|
||||
private boolean canMobMove(Mob mob) {
|
||||
// 检查各种无法移动的情况
|
||||
return !mob.isNoGravity() && // 有重力才能移动
|
||||
!mob.isSleeping() && // 没有在睡觉
|
||||
!mob.isDeadOrDying() && // 没有死亡或濒死
|
||||
!mob.isFreezing() && // 没有被冻结
|
||||
mob.canUpdate() && // 可以更新
|
||||
mob.isEffectiveAi() && // AI有效
|
||||
mob.getDeltaMovement().lengthSqr() < 100.0; // 移动速度不是特别快(防止异常情况)
|
||||
}
|
||||
|
||||
/**
|
||||
* 让生物朝着合力方向移动
|
||||
*/
|
||||
private void moveMobTowardsCombinedDirection(Mob mob, Vec3 combinedDirection, int leashCount, double forceMagnitude) {
|
||||
if (combinedDirection.equals(Vec3.ZERO)) return;
|
||||
|
||||
// 再次检查是否能够移动
|
||||
if (!canMobMove(mob)) {
|
||||
mob.getNavigation().stop();
|
||||
return;
|
||||
}
|
||||
|
||||
// 计算平均方向
|
||||
Vec3 averageDirection = combinedDirection.scale(1.0 / leashCount).normalize();
|
||||
|
||||
// 根据力的大小调整移动速度
|
||||
double speed = calculateMobSpeed(mob, forceMagnitude);
|
||||
|
||||
// 计算目标位置(在合力方向上稍微超前一点)
|
||||
Vec3 targetPos = mob.position().add(averageDirection.scale(3.0)); // 3格距离
|
||||
|
||||
// 检查目标位置是否可达
|
||||
if (isPositionReachable(mob, targetPos)) {
|
||||
// 设置移动目标
|
||||
mob.getNavigation().moveTo(targetPos.x, targetPos.y, targetPos.z, speed);
|
||||
|
||||
// 设置生物朝向合力方向
|
||||
mob.getLookControl().setLookAt(targetPos);
|
||||
} else {
|
||||
// 位置不可达时停止导航
|
||||
mob.getNavigation().stop();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查位置是否可达
|
||||
*/
|
||||
private boolean isPositionReachable(Mob mob, Vec3 targetPos) {
|
||||
// 简单的距离检查
|
||||
double distance = mob.position().distanceTo(targetPos);
|
||||
if (distance > 20.0) { // 距离太远
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查是否有导航路径
|
||||
Path path = mob.getNavigation().createPath(targetPos.x, targetPos.y, targetPos.z, 0);
|
||||
return path != null && !path.isDone();
|
||||
}
|
||||
|
||||
/**
|
||||
* 增强的移动能力检查
|
||||
*/
|
||||
private boolean canMobMoveEnhanced(Mob mob) {
|
||||
// 基础移动检查
|
||||
if (!canMobMove(mob)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查导航系统是否可用
|
||||
if (mob.getNavigation().isDone() || mob.getNavigation().isStuck()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查生物是否被拴绳过度拉扯(防止无限尝试移动)
|
||||
Vec3 motion = mob.getDeltaMovement();
|
||||
if (motion.lengthSqr() > 4.0 && mob.tickCount % 20 == 0) {
|
||||
// 如果移动速度过快,偶尔跳过移动尝试
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查生物是否在尝试移动但实际没有移动(卡住检测)
|
||||
if (mob.getNavigation().getPath() != null &&
|
||||
!mob.getNavigation().getPath().isDone()) {
|
||||
|
||||
// 获取上一tick的位置进行比较
|
||||
double distanceMoved = mob.position().distanceTo(new Vec3(mob.xOld, mob.yOld, mob.zOld));
|
||||
|
||||
// 生物卡住了(有路径但几乎没有移动)
|
||||
return !(distanceMoved < 0.1);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* 根据生物类型和力的大小计算移动速度
|
||||
*/
|
||||
private double calculateMobSpeed(Mob mob, double forceMagnitude) {
|
||||
double baseSpeed = mob instanceof Llama ? 2.0 : 1.0;
|
||||
|
||||
// 力越大,移动速度越快(但有上限)
|
||||
double forceFactor = Math.min(forceMagnitude * 0.5, 2.0); // 限制最大加速2倍
|
||||
|
||||
return baseSpeed * (1.0 + forceFactor);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 为UUID拴绳计算力
|
||||
|
|
@ -569,9 +657,9 @@ public class LeashDataImpl implements ILeashData {
|
|||
private Vec3 calculateLeashForce(Entity holder, Map.Entry<?, LeashInfo> entry) {
|
||||
Vec3 holderPos = holder.position().add(0, holder.getBbHeight() * 0.7, 0);
|
||||
LeashInfo info = entry.getValue();
|
||||
Vec3 entityPos = entity.position().add(info.attachOffset());
|
||||
Vec3 entityPos = entity.position();
|
||||
double distance = holderPos.distanceTo(entityPos);
|
||||
double extremeSnapDist = info.maxDistance() * Config.leashExtremeSnapDistFactor();
|
||||
double extremeSnapDist = info.maxDistance() * CommonEventHandler.leashConfigManager.getExtremeSnapFactor();
|
||||
|
||||
// 1. 检查是否超出断裂距离
|
||||
if (distance > extremeSnapDist) {
|
||||
|
|
@ -583,6 +671,8 @@ public class LeashDataImpl implements ILeashData {
|
|||
}
|
||||
// 断裂
|
||||
removeLeash(holder);
|
||||
//TODO: 是不是应该考虑让断裂统一发出声音,还是就这样由断裂发出
|
||||
entity.level().playSound(null, holder.getOnPos(), SLPSoundEvents.LEAD_BREAK.get(), SoundSource.PLAYERS);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
@ -590,15 +680,6 @@ public class LeashDataImpl implements ILeashData {
|
|||
Vec3 pullForce = Vec3.ZERO;
|
||||
if (distance > info.elasticDistance()) {
|
||||
pullForce = calculatePullForce(holderPos, entityPos, distance, info);
|
||||
|
||||
// 生物添加跟随逻辑(保持不变)
|
||||
if(entity instanceof Mob mob) {
|
||||
Vec3 vec3 = (new Vec3(holder.getX() - entity.getX(), holder.getY() - entity.getY(), holder.getZ() - entity.getZ()))
|
||||
.normalize()
|
||||
.scale(Math.max(distance - 2.0F, 0.0F));
|
||||
double speed = mob instanceof Llama ? 2.0 : 1.0;
|
||||
mob.getNavigation().moveTo(entity.getX() + vec3.x, entity.getY() + vec3.y, entity.getZ() + vec3.z, speed);
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 重置缓冲Tick
|
||||
|
|
@ -621,13 +702,13 @@ public class LeashDataImpl implements ILeashData {
|
|||
}
|
||||
|
||||
Vec3 pullForce = pullDirection.scale(
|
||||
(distance - info.elasticDistance()) * pullStrength * Config.springDampening()
|
||||
(distance - info.elasticDistance()) * pullStrength * CommonEventHandler.leashConfigManager.getSpringDampening()
|
||||
);
|
||||
|
||||
return new Vec3(
|
||||
pullForce.x * Config.axisSpecificElasticity().x,
|
||||
pullForce.y * Config.axisSpecificElasticity().y,
|
||||
pullForce.z * Config.axisSpecificElasticity().z
|
||||
pullForce.x * CommonEventHandler.leashConfigManager.getXElasticity(),
|
||||
pullForce.y * CommonEventHandler.leashConfigManager.getXElasticity(),
|
||||
pullForce.z * CommonEventHandler.leashConfigManager.getZElasticity()
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -638,13 +719,13 @@ public class LeashDataImpl implements ILeashData {
|
|||
double pullStrength = 1.0 + excessRatio * 2.0;
|
||||
|
||||
Vec3 pullForce = pullDirection.scale(
|
||||
(distance - info.elasticDistance()) * pullStrength * Config.springDampening()
|
||||
(distance - info.elasticDistance()) * pullStrength * CommonEventHandler.leashConfigManager.getSpringDampening()
|
||||
);
|
||||
|
||||
return new Vec3(
|
||||
pullForce.x * Config.axisSpecificElasticity().x,
|
||||
pullForce.y * Config.axisSpecificElasticity().y,
|
||||
pullForce.z * Config.axisSpecificElasticity().z
|
||||
pullForce.x * CommonEventHandler.leashConfigManager.getXElasticity(),
|
||||
pullForce.y * CommonEventHandler.leashConfigManager.getYElasticity(),
|
||||
pullForce.z * CommonEventHandler.leashConfigManager.getZElasticity()
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -658,16 +739,20 @@ public class LeashDataImpl implements ILeashData {
|
|||
@Override
|
||||
public boolean removeLeash(UUID holderUUID) {
|
||||
boolean removed = leashHolders.remove(holderUUID) != null;
|
||||
if (removed)
|
||||
if (removed) {
|
||||
LeashStateAPI.Operations.detach(entity, holderUUID);
|
||||
markForSync();
|
||||
}
|
||||
return removed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeLeash(BlockPos knotPos) {
|
||||
boolean removed = leashKnots.remove(knotPos) != null;
|
||||
if (removed)
|
||||
if (removed) {
|
||||
LeashStateAPI.Operations.detach(entity, knotPos);
|
||||
markForSync();
|
||||
}
|
||||
return removed;
|
||||
}
|
||||
|
||||
|
|
@ -675,18 +760,21 @@ public class LeashDataImpl implements ILeashData {
|
|||
public void removeAllLeashes() {
|
||||
leashHolders.clear();
|
||||
leashKnots.clear();
|
||||
LeashStateAPI.Offset.removeAll(entity);
|
||||
markForSync();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAllHolderLeashes() {
|
||||
leashHolders.clear();
|
||||
LeashStateAPI.Offset.removeAllUUIDs(entity);
|
||||
markForSync();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAllKnotLeashes() {
|
||||
leashKnots.clear();
|
||||
LeashStateAPI.Offset.removeAllBlockPoses(entity);
|
||||
markForSync();
|
||||
}
|
||||
|
||||
|
|
@ -716,6 +804,7 @@ public class LeashDataImpl implements ILeashData {
|
|||
LeashInfo leashInfo = info.transferHolder(newHolder);
|
||||
leashHolders.put(newHolder.getUUID(), leashInfo);
|
||||
}
|
||||
LeashStateAPI.Operations.transfer(entity, oldHolderUUID, newHolder);
|
||||
markForSync();
|
||||
return true;
|
||||
}
|
||||
|
|
@ -730,6 +819,7 @@ public class LeashDataImpl implements ILeashData {
|
|||
LeashInfo leashInfo = info.transferHolder(newHolder, reserved);
|
||||
leashHolders.put(newHolder.getUUID(), leashInfo);
|
||||
}
|
||||
LeashStateAPI.Operations.transfer(entity, oldHolderUUID, newHolder);
|
||||
markForSync();
|
||||
return true;
|
||||
}
|
||||
|
|
@ -745,6 +835,7 @@ public class LeashDataImpl implements ILeashData {
|
|||
LeashInfo leashInfo = info.transferHolder(newHolder);
|
||||
leashHolders.put(newHolder.getUUID(), leashInfo);
|
||||
}
|
||||
LeashStateAPI.Operations.transfer(entity, knotPos, newHolder);
|
||||
markForSync();
|
||||
return true;
|
||||
}
|
||||
|
|
@ -760,6 +851,7 @@ public class LeashDataImpl implements ILeashData {
|
|||
LeashInfo leashInfo = info.transferHolder(newHolder, reserved);
|
||||
leashHolders.put(newHolder.getUUID(), leashInfo);
|
||||
}
|
||||
LeashStateAPI.Operations.transfer(entity, knotPos, newHolder);
|
||||
markForSync();
|
||||
return true;
|
||||
}
|
||||
|
|
@ -885,7 +977,6 @@ public class LeashDataImpl implements ILeashData {
|
|||
}
|
||||
infoTag.putInt("HolderID", info.holderIdOpt().get());
|
||||
infoTag.putString("LeashItem", info.reserved());
|
||||
infoTag.put("Offset", NBTWriter.writeVec3(info.attachOffset()));
|
||||
infoTag.putDouble("MaxDistance", info.maxDistance());
|
||||
infoTag.putDouble("ElasticDistance", info.elasticDistance());
|
||||
infoTag.putInt("KeepLeashTicks", info.keepLeashTicks());
|
||||
|
|
@ -933,7 +1024,6 @@ public class LeashDataImpl implements ILeashData {
|
|||
infoTag.getUUID("HolderUUID"),
|
||||
infoTag.getInt("HolderID"),
|
||||
infoTag.getString("LeashItem"),
|
||||
NBTReader.readVec3(infoTag.getCompound("Offset")),
|
||||
infoTag.getDouble("MaxDistance"),
|
||||
infoTag.contains("ElasticDistance") ? infoTag.getDouble("ElasticDistance") : 6.0,
|
||||
infoTag.getInt("KeepLeashTicks"),
|
||||
|
|
@ -949,7 +1039,6 @@ public class LeashDataImpl implements ILeashData {
|
|||
NbtUtils.readBlockPos(infoTag.getCompound("KnotBlockPos")),
|
||||
infoTag.getInt("HolderID"),
|
||||
infoTag.getString("LeashItem"),
|
||||
NBTReader.readVec3(infoTag.getCompound("Offset")),
|
||||
infoTag.getDouble("MaxDistance"),
|
||||
infoTag.contains("ElasticDistance") ? infoTag.getDouble("ElasticDistance") : 6.0,
|
||||
infoTag.getInt("KeepLeashTicks"),
|
||||
|
|
@ -961,7 +1050,7 @@ public class LeashDataImpl implements ILeashData {
|
|||
|
||||
@Override
|
||||
public boolean canBeLeashed() {
|
||||
return (leashHolders.size() + leashKnots.size()) <= Config.maxLeashesPerEntity();
|
||||
return (leashHolders.size() + leashKnots.size()) <= CommonEventHandler.leashConfigManager.getMaxLeashesPerEntity();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -1007,48 +1096,28 @@ public class LeashDataImpl implements ILeashData {
|
|||
return leashableInArea(holder, i -> isLeashHolder(i, holder), 1024D);
|
||||
}
|
||||
public boolean canBeAttachedTo(Entity pEntity) {
|
||||
if(pEntity == entity) {
|
||||
if (pEntity == entity) {
|
||||
return false;
|
||||
} else {
|
||||
Optional<LeashInfo> leashInfo = getLeashInfo(pEntity);
|
||||
return leashInfo.isEmpty() && (entity.distanceTo(pEntity) <= Config.leashElasticDist() * Config.leashExtremeSnapDistFactor()) && canBeLeashed();//距离最大,则不可以被固定或转移
|
||||
return leashInfo.isEmpty() && (entity.distanceTo(pEntity) <= CommonEventHandler.leashConfigManager.getElasticDistance() * CommonEventHandler.leashConfigManager.getExtremeSnapFactor()) && canBeLeashed();//距离最大,则不可以被固定或转移
|
||||
}
|
||||
}
|
||||
public static boolean isLeashHolder(@NotNull Entity pEntity, UUID pHolderUUID) {
|
||||
AtomicBoolean isTarget = new AtomicBoolean(false);
|
||||
LeashUtil.getLeashData(pEntity)
|
||||
.ifPresent(i ->
|
||||
isTarget.set(i.isLeashedBy(pHolderUUID))
|
||||
);
|
||||
return isTarget.get();
|
||||
}
|
||||
public static boolean isLeashHolder(@NotNull Entity pEntity, BlockPos pKnotPos) {
|
||||
AtomicBoolean isTarget = new AtomicBoolean(false);
|
||||
LeashUtil.getLeashData(pEntity)
|
||||
.ifPresent(i ->
|
||||
isTarget.set(i.isLeashedBy(pKnotPos))
|
||||
);
|
||||
return isTarget.get();
|
||||
}
|
||||
public static boolean isLeashHolder(@NotNull Entity pEntity, Entity pTestHolder) {
|
||||
return pTestHolder instanceof SuperLeashKnotEntity superLeashKnotEntity ?
|
||||
isLeashHolder(pEntity, superLeashKnotEntity.getPos()) :
|
||||
isLeashHolder(pEntity, pTestHolder.getUUID());
|
||||
}
|
||||
|
||||
|
||||
// 计算拴绳附着点
|
||||
@Contract("_ -> new")
|
||||
private @NotNull Vec3 calculateAttachOffset(@NotNull Entity entity) {
|
||||
EntityType<?> type = entity.getType();
|
||||
if (type == EntityType.HORSE || type == EntityType.DONKEY) {
|
||||
return new Vec3(0, 1.4, 0.3);
|
||||
} else if (type == EntityType.IRON_GOLEM) {
|
||||
return new Vec3(0, 1.8, 0);
|
||||
}
|
||||
//TODO: 未来自定义配置
|
||||
return new Vec3(0, entity.getBbHeight() * 0.8, 0);
|
||||
public static boolean isLeashHolder(@NotNull Entity pEntity, UUID pHolderUUID) {
|
||||
return LeashDataAPI.getLeashData(pEntity)
|
||||
.map(leashData -> leashData.isLeashedBy(pHolderUUID))
|
||||
.orElse(false);
|
||||
}
|
||||
|
||||
|
||||
public static boolean isLeashHolder(@NotNull Entity pEntity, BlockPos pKnotPos) {
|
||||
return LeashDataAPI.getLeashData(pEntity)
|
||||
.map(leashData -> leashData.isLeashedBy(pKnotPos))
|
||||
.orElse(false);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -127,7 +127,7 @@ public class LeashStateImpl implements ILeashState {
|
|||
|
||||
@Override
|
||||
public void resetLeashHolderLocationOffset(Entity holder) {
|
||||
if (entity instanceof SuperLeashKnotEntity leashKnot) {
|
||||
if (holder instanceof SuperLeashKnotEntity leashKnot) {
|
||||
resetLeashHolderLocationOffset(leashKnot.getPos());
|
||||
} else resetLeashHolderLocationOffset(holder.getUUID());
|
||||
}
|
||||
|
|
@ -146,45 +146,97 @@ public class LeashStateImpl implements ILeashState {
|
|||
|
||||
@Override
|
||||
public void setLeashHolderLocationOffset(Entity holder, Vec3 offset) {
|
||||
if (entity instanceof SuperLeashKnotEntity leashKnot) {
|
||||
if (holder instanceof SuperLeashKnotEntity leashKnot) {
|
||||
setLeashHolderLocationOffset(leashKnot.getPos(), offset);
|
||||
} else setLeashHolderLocationOffset(holder.getUUID(), offset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLeashHolderLocationOffset(UUID holderUUID, Vec3 offset) {
|
||||
leashHolders.computeIfPresent(holderUUID, (uuid, state) -> state.setHolderLocationOffset(offset));
|
||||
LeashState currentState = leashHolders.get(holderUUID);
|
||||
if (currentState == null) {
|
||||
// 创建新的状态,使用默认的应用实体偏移量
|
||||
leashHolders.put(holderUUID, new LeashState(
|
||||
offset,
|
||||
getDefaultLeashApplyEntityLocationOffset(),
|
||||
Vec3.ZERO // 或者合适的默认值
|
||||
));
|
||||
} else {
|
||||
// 更新现有状态
|
||||
leashHolders.put(holderUUID,
|
||||
currentState.setHolderLocationOffset(offset)
|
||||
);
|
||||
}
|
||||
markForSync();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLeashHolderLocationOffset(BlockPos knotPos, Vec3 leashHolderLocationOffset) {
|
||||
leashKnots.computeIfPresent(knotPos, (blockPos, state) -> state.setHolderLocationOffset(leashHolderLocationOffset));
|
||||
public void setLeashHolderLocationOffset(BlockPos knotPos, Vec3 offset) {
|
||||
LeashState currentState = leashKnots.get(knotPos);
|
||||
if (currentState == null) {
|
||||
// 创建新的状态
|
||||
leashKnots.put(knotPos, new LeashState(
|
||||
offset,
|
||||
getDefaultLeashApplyEntityLocationOffset(),
|
||||
Vec3.ZERO
|
||||
));
|
||||
} else {
|
||||
// 更新现有状态
|
||||
leashKnots.put(knotPos,
|
||||
currentState.setHolderLocationOffset(offset)
|
||||
);
|
||||
}
|
||||
markForSync();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addLeashHolderLocationOffset(Entity holder, Vec3 offset) {
|
||||
if (entity instanceof SuperLeashKnotEntity leashKnot) {
|
||||
if (holder instanceof SuperLeashKnotEntity leashKnot) {
|
||||
addLeashHolderLocationOffset(leashKnot.getPos(), offset);
|
||||
} else addLeashHolderLocationOffset(holder.getUUID(), offset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addLeashHolderLocationOffset(UUID holderUUID, Vec3 offset) {
|
||||
leashHolders.computeIfPresent(holderUUID, (uuid, state) -> state.setHolderLocationOffset(state.holderLocationOffset().add(offset)));
|
||||
LeashState currentState = leashHolders.get(holderUUID);
|
||||
if (currentState == null) {
|
||||
// 创建新的状态,使用默认的应用实体偏移量
|
||||
leashHolders.put(holderUUID, new LeashState(
|
||||
offset,
|
||||
getDefaultLeashApplyEntityLocationOffset(),
|
||||
Vec3.ZERO // 或者合适的默认值
|
||||
));
|
||||
} else {
|
||||
// 更新现有状态
|
||||
leashHolders.put(holderUUID,
|
||||
currentState.setHolderLocationOffset(currentState.holderLocationOffset().add(offset))
|
||||
);
|
||||
}
|
||||
markForSync();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addLeashHolderLocationOffset(BlockPos knotPos, Vec3 offset) {
|
||||
leashKnots.computeIfPresent(knotPos, (blockPos, state) -> state.setHolderLocationOffset(state.holderLocationOffset().add(offset)));
|
||||
LeashState currentState = leashKnots.get(knotPos);
|
||||
if (currentState == null) {
|
||||
// 创建新的状态
|
||||
leashKnots.put(knotPos, new LeashState(
|
||||
offset,
|
||||
getDefaultLeashApplyEntityLocationOffset(),
|
||||
Vec3.ZERO
|
||||
));
|
||||
} else {
|
||||
// 更新现有状态
|
||||
leashKnots.put(knotPos,
|
||||
currentState.setHolderLocationOffset(currentState.holderLocationOffset().add(offset))
|
||||
);
|
||||
}
|
||||
markForSync();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeLeashHolderLocationOffset(Entity holder) {
|
||||
if (entity instanceof SuperLeashKnotEntity leashKnot) {
|
||||
if (holder instanceof SuperLeashKnotEntity leashKnot) {
|
||||
removeLeashHolderLocationOffset(leashKnot.getPos());
|
||||
} else removeLeashHolderLocationOffset(holder.getUUID());
|
||||
}
|
||||
|
|
@ -201,6 +253,25 @@ public class LeashStateImpl implements ILeashState {
|
|||
markForSync();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAllLeashHolderLocationOffset() {
|
||||
leashKnots.clear();
|
||||
leashHolders.clear();
|
||||
markForSync();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAllLeashHolderUUIDLocationOffset() {
|
||||
leashHolders.clear();
|
||||
markForSync();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAllLeashHolderBlockPosLocationOffset() {
|
||||
leashKnots.clear();
|
||||
markForSync();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetAllLeashHolderLocationsOffset() {
|
||||
leashKnots.replaceAll((pos, leashState) -> leashState.resetHolderLocationOffset());
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ import net.minecraft.core.BlockPos;
|
|||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import net.minecraftforge.common.util.INBTSerializable;
|
||||
import top.r3944realms.superleadrope.content.entity.SuperLeashKnotEntity;
|
||||
|
||||
|
|
@ -142,7 +141,6 @@ public interface ILeashData extends INBTSerializable<CompoundTag> {
|
|||
Optional<UUID> holderUUIDOpt,
|
||||
Optional<Integer> holderIdOpt, // Only for client side use
|
||||
String reserved, // 保留字段
|
||||
Vec3 attachOffset,
|
||||
double maxDistance,
|
||||
double elasticDistance,
|
||||
int keepLeashTicks, // 剩余 Tick 数
|
||||
|
|
@ -150,14 +148,13 @@ public interface ILeashData extends INBTSerializable<CompoundTag> {
|
|||
) {
|
||||
public static final LeashInfo EMPTY = new LeashInfo(
|
||||
Optional.empty(), Optional.empty(), Optional.empty(),
|
||||
"", Vec3.ZERO, 12.0D, 6.0D, 0, 0
|
||||
"", 12.0D, 6.0D, 0, 0
|
||||
);
|
||||
|
||||
/* ---------- Factory ---------- */
|
||||
public static LeashInfo create(
|
||||
Entity entity,
|
||||
String reserved,
|
||||
Vec3 offset,
|
||||
double maxDistance,
|
||||
double elasticDistance,
|
||||
int keepTicks,
|
||||
|
|
@ -165,32 +162,31 @@ public interface ILeashData extends INBTSerializable<CompoundTag> {
|
|||
) {
|
||||
return entity instanceof SuperLeashKnotEntity knot
|
||||
? new LeashInfo(knot.getPos(), entity.getId(), reserved,
|
||||
offset, maxDistance, elasticDistance, keepTicks, maxKeepTicks)
|
||||
: new LeashInfo(entity.getUUID(), entity.getId(), reserved,
|
||||
offset, maxDistance, elasticDistance, keepTicks, maxKeepTicks);
|
||||
maxDistance, elasticDistance, keepTicks, maxKeepTicks)
|
||||
: new LeashInfo(entity.getUUID(), entity.getId(), reserved, maxDistance, elasticDistance, keepTicks, maxKeepTicks);
|
||||
}
|
||||
|
||||
public LeashInfo(UUID holderUUID, int holderId, String reserved, Vec3 offset,
|
||||
public LeashInfo(UUID holderUUID, int holderId, String reserved,
|
||||
double maxDistance, double elasticDistance, int keepTicks, int maxKeepTicks) {
|
||||
this(Optional.empty(), Optional.of(holderUUID), Optional.of(holderId),
|
||||
reserved, offset, maxDistance, elasticDistance, keepTicks, maxKeepTicks);
|
||||
reserved, maxDistance, elasticDistance, keepTicks, maxKeepTicks);
|
||||
}
|
||||
|
||||
public LeashInfo(BlockPos knotPos, int holderId, String reserved, Vec3 offset,
|
||||
public LeashInfo(BlockPos knotPos, int holderId, String reserved,
|
||||
double maxDistance, double elasticDistance, int keepTicks, int maxKeepTicks) {
|
||||
this(Optional.of(knotPos), Optional.empty(), Optional.of(holderId),
|
||||
reserved, offset, maxDistance, elasticDistance, keepTicks, maxKeepTicks);
|
||||
reserved, maxDistance, elasticDistance, keepTicks, maxKeepTicks);
|
||||
}
|
||||
|
||||
/* ---------- State updates ---------- */
|
||||
public LeashInfo decrementKeepTicks() {
|
||||
return new LeashInfo(blockPosOpt, holderUUIDOpt, holderIdOpt, reserved, attachOffset,
|
||||
return new LeashInfo(blockPosOpt, holderUUIDOpt, holderIdOpt, reserved,
|
||||
maxDistance, elasticDistance,
|
||||
Math.max(0, keepLeashTicks - 1), maxKeepLeashTicks);
|
||||
}
|
||||
|
||||
public LeashInfo resetKeepTicks() {
|
||||
return new LeashInfo(blockPosOpt, holderUUIDOpt, holderIdOpt, reserved, attachOffset,
|
||||
return new LeashInfo(blockPosOpt, holderUUIDOpt, holderIdOpt, reserved,
|
||||
maxDistance, elasticDistance,
|
||||
maxKeepLeashTicks, maxKeepLeashTicks);
|
||||
}
|
||||
|
|
@ -205,7 +201,7 @@ public interface ILeashData extends INBTSerializable<CompoundTag> {
|
|||
isKnot ? Optional.of(((SuperLeashKnotEntity) entity).getPos()) : Optional.empty(),
|
||||
!isKnot ? Optional.of(entity.getUUID()) : Optional.empty(),
|
||||
Optional.of(entity.getId()),
|
||||
newReserved, attachOffset, maxDistance, elasticDistance,
|
||||
newReserved, maxDistance, elasticDistance,
|
||||
keepLeashTicks, maxKeepLeashTicks
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,6 +67,9 @@ public interface ILeashState extends INBTSerializable<CompoundTag> {
|
|||
void removeLeashHolderLocationOffset(Entity holder);
|
||||
void removeLeashHolderLocationOffset(UUID holderUUID);
|
||||
void removeLeashHolderLocationOffset(BlockPos knotPos);
|
||||
void removeAllLeashHolderLocationOffset();
|
||||
void removeAllLeashHolderUUIDLocationOffset();
|
||||
void removeAllLeashHolderBlockPosLocationOffset();
|
||||
|
||||
/* ----------------------
|
||||
* Apply-entity offset
|
||||
|
|
|
|||
|
|
@ -19,13 +19,13 @@ import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
|||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.commands.Commands;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import top.r3944realms.superleadrope.config.LeashCommonConfig;
|
||||
import top.r3944realms.superleadrope.CommonEventHandler;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class Command {
|
||||
public static final String PREFIX = LeashCommonConfig.COMMON.SLPModCommandPrefix.get();
|
||||
public static boolean SHOULD_USE_PREFIX = LeashCommonConfig.COMMON.EnableSLPModCommandPrefix.get();
|
||||
public static final String PREFIX = CommonEventHandler.leashConfigManager.getCommandPrefix();
|
||||
public static boolean SHOULD_USE_PREFIX = CommonEventHandler.leashConfigManager.isCommandPrefixEnabled();
|
||||
static LiteralArgumentBuilder<CommandSourceStack> getLiterArgumentBuilderOfCSS(String name, boolean shouldAddToList, @Nullable List<LiteralArgumentBuilder<CommandSourceStack>> list) {
|
||||
LiteralArgumentBuilder<CommandSourceStack> literal = Commands.literal(name);
|
||||
if (shouldAddToList) {
|
||||
|
|
|
|||
|
|
@ -16,13 +16,525 @@
|
|||
package top.r3944realms.superleadrope.content.command;
|
||||
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import com.mojang.brigadier.arguments.*;
|
||||
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.*;
|
||||
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.content.capability.inter.ILeashData;
|
||||
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.LeashDataAPI;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import static top.r3944realms.superleadrope.content.command.Command.*;
|
||||
import static top.r3944realms.superleadrope.content.command.Command.SHOULD_USE_PREFIX;
|
||||
public class LeashDataCommand {
|
||||
// 获取Data
|
||||
// 设置Data
|
||||
// <add/transfer/remove> Holder<BlockPos/Entity<需判断实体类型>>
|
||||
// 设置对应目标的 最大长度 最长断裂距离 保持不断裂时间刻
|
||||
public static final String SLP_LEASH_MESSAGE_ = SuperLeadRope.MOD_ID + ".command.leash.message.";
|
||||
public static final String LEASH_DATA_GET_ = SLP_LEASH_MESSAGE_ + ".get.",
|
||||
TITLE = LEASH_DATA_GET_ + "title",
|
||||
TOTAL = LEASH_DATA_GET_ + "total",
|
||||
BLOCK = LEASH_DATA_GET_ + "block",
|
||||
UUID = LEASH_DATA_GET_ + "uuid",
|
||||
MAX = LEASH_DATA_GET_ + "max",
|
||||
ELASTIC = LEASH_DATA_GET_ + "elastic",
|
||||
KEEP = LEASH_DATA_GET_ + "keep",
|
||||
RESERVED = LEASH_DATA_GET_ + "reserved"
|
||||
;
|
||||
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> $$leashDataRoot = getLiterArgumentBuilderOfCSS("leashdata", !SHOULD_USE_PREFIX, nodeList);
|
||||
RequiredArgumentBuilder<CommandSourceStack, EntitySelector> $$$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("elasticDistance", DoubleArgumentType.doubleArg(1.0, 128.0))
|
||||
.executes(context -> addLeash(context,
|
||||
DoubleArgumentType.getDouble(context, "maxDistance"),
|
||||
DoubleArgumentType.getDouble(context, "elasticDistance")))
|
||||
.then(Commands.argument("keepTicks", IntegerArgumentType.integer(0))
|
||||
.executes(context -> addLeash(context,
|
||||
DoubleArgumentType.getDouble(context, "maxDistance"),
|
||||
DoubleArgumentType.getDouble(context, "elasticDistance"),
|
||||
IntegerArgumentType.getInteger(context, "keepTicks")))
|
||||
.then(Commands.argument("reserved", StringArgumentType.string())
|
||||
.executes(context -> addLeash(context,
|
||||
DoubleArgumentType.getDouble(context, "maxDistance"),
|
||||
DoubleArgumentType.getDouble(context, "elasticDistance"),
|
||||
IntegerArgumentType.getInteger(context, "keepTicks"),
|
||||
StringArgumentType.getString(context, "reserved")))
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
LiteralArgumentBuilder<CommandSourceStack> $$$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("elasticDistance", DoubleArgumentType.doubleArg(1.0, 128.0))
|
||||
.executes(context -> addBlockLeash(context,
|
||||
DoubleArgumentType.getDouble(context, "maxDistance"),
|
||||
DoubleArgumentType.getDouble(context, "elasticDistance"), 0, ""))
|
||||
.then(Commands.argument("keepTicks", IntegerArgumentType.integer(0))
|
||||
.executes(context -> addBlockLeash(context,
|
||||
DoubleArgumentType.getDouble(context, "maxDistance"),
|
||||
DoubleArgumentType.getDouble(context, "elasticDistance"),
|
||||
IntegerArgumentType.getInteger(context, "keepTicks")))
|
||||
.then(Commands.argument("reserved", StringArgumentType.string())
|
||||
.executes(context -> addBlockLeash(context,
|
||||
DoubleArgumentType.getDouble(context, "maxDistance"),
|
||||
DoubleArgumentType.getDouble(context, "elasticDistance"),
|
||||
IntegerArgumentType.getInteger(context, "keepTicks"),
|
||||
StringArgumentType.getString(context, "reserved")))
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
LiteralArgumentBuilder<CommandSourceStack> $$$add = Commands.literal("add")
|
||||
.then(Commands.argument("target", EntityArgument.entities())
|
||||
// 实体拴绳
|
||||
.then($$$add$holder)
|
||||
|
||||
// 方块拴绳
|
||||
.then($$$add$pos)
|
||||
);
|
||||
LiteralArgumentBuilder<CommandSourceStack> $$$remove = Commands.literal("remove")
|
||||
.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<CommandSourceStack> $$$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<CommandSourceStack, EntitySelector> $$$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("elasticDistance")
|
||||
.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<CommandSourceStack> $$$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("elasticDistance")
|
||||
.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<CommandSourceStack> $$$set = Commands.literal("set")
|
||||
.then(Commands.argument("target", EntityArgument.entities())
|
||||
// 实体拴绳设置
|
||||
.then($$$set$holder)
|
||||
|
||||
// 方块拴绳设置
|
||||
.then($$$set$pos)
|
||||
);
|
||||
LiteralArgumentBuilder<CommandSourceStack> $$$applayForces = Commands.literal("applyForces")
|
||||
.then(Commands.argument("target", EntityArgument.entities())
|
||||
.executes(LeashDataCommand::applyForces)
|
||||
);
|
||||
LiteralArgumentBuilder<CommandSourceStack> $$$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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
public static final String SET_MAX_DISTANCE = SLP_LEASH_MESSAGE_ + "set.max_distance";
|
||||
private static int setMaxDistance(CommandContext<CommandSourceStack> context) throws CommandSyntaxException {
|
||||
return setMaxDistance(context, CommonEventHandler.leashConfigManager.getMaxLeashLength(), "");
|
||||
}
|
||||
private static int setMaxDistance(CommandContext<CommandSourceStack> context, double maxDistance) throws CommandSyntaxException {
|
||||
return setMaxDistance(context, maxDistance, "");
|
||||
}
|
||||
private static int setMaxDistance(CommandContext<CommandSourceStack> context, double maxDistance, String reserved) throws CommandSyntaxException {
|
||||
Collection<? extends Entity> targets = EntityArgument.getEntities(context, "target");
|
||||
Entity holder = EntityArgument.getEntity(context, "holder");
|
||||
for (Entity target : targets) {
|
||||
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
public static final String REMOVE_ALL_BLOCK_LEASHES = SLP_LEASH_MESSAGE_ + "remove.all_block_leashes";
|
||||
private static int removeAllBlockLeashes(CommandContext<CommandSourceStack> context) throws CommandSyntaxException {
|
||||
return -1;
|
||||
}
|
||||
public static final String REMOVE_ALL_HOLDER_LEASHES = SLP_LEASH_MESSAGE_ + "remove.all_holder_leashes";
|
||||
private static int removeAllHolderLeashes(CommandContext<CommandSourceStack> context) throws CommandSyntaxException {
|
||||
return -1;
|
||||
}
|
||||
public static final String TRANSFER_FROM_BLOCK = SLP_LEASH_MESSAGE_ + "transfer.from_block";
|
||||
private static int transferFromBlock(CommandContext<CommandSourceStack> context) throws CommandSyntaxException {
|
||||
return transferFromBlock(context, "");
|
||||
}
|
||||
private static int transferFromBlock(CommandContext<CommandSourceStack> context, String reserved) throws CommandSyntaxException {
|
||||
return -1;
|
||||
}
|
||||
public static final String SET_ELASTIC_DISTANCE = SLP_LEASH_MESSAGE_ + "set.elastic_distance";
|
||||
private static int setElasticDistance(CommandContext<CommandSourceStack> context) throws CommandSyntaxException {
|
||||
return setElasticDistance(context, 0 ,"");
|
||||
}
|
||||
private static int setElasticDistance(CommandContext<CommandSourceStack> context, int keepTicks) throws CommandSyntaxException {
|
||||
return setElasticDistance(context, keepTicks ,"");
|
||||
}
|
||||
private static int setElasticDistance(CommandContext<CommandSourceStack> context, int keepTicks, String reserved) throws CommandSyntaxException {
|
||||
return -1;
|
||||
}
|
||||
public static final String SET_BLOCK_MAX_DISTANCE = SLP_LEASH_MESSAGE_ + "set.block_max_distance";
|
||||
private static int setBlockMaxDistance(CommandContext<CommandSourceStack> context) throws CommandSyntaxException {
|
||||
return setBlockMaxDistance(context, 0 ,"");
|
||||
}
|
||||
private static int setBlockMaxDistance(CommandContext<CommandSourceStack> context, int keepTicks) throws CommandSyntaxException {
|
||||
return setBlockMaxDistance(context, keepTicks ,"");
|
||||
}
|
||||
private static int setBlockMaxDistance(CommandContext<CommandSourceStack> context, int keepTicks, String reserved) throws CommandSyntaxException {
|
||||
return -1;
|
||||
}
|
||||
public static final String SET_BLOCK_ELASTIC_DISTANCE = SLP_LEASH_MESSAGE_ + "set.block_elastic_distance";
|
||||
private static int setBlockElasticDistance(CommandContext<CommandSourceStack> context) throws CommandSyntaxException {
|
||||
return setBlockElasticDistance(context, 0 ,"");
|
||||
}
|
||||
private static int setBlockElasticDistance(CommandContext<CommandSourceStack> context, int keepTicks) throws CommandSyntaxException {
|
||||
return setBlockElasticDistance(context, keepTicks ,"");
|
||||
}
|
||||
private static int setBlockElasticDistance(CommandContext<CommandSourceStack> context, int keepTicks, String reserved) throws CommandSyntaxException {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// ==================== 命令执行方法 ====================
|
||||
|
||||
private static int getLeashData(CommandContext<CommandSourceStack> context) throws CommandSyntaxException {
|
||||
Collection<? extends Entity> targets = EntityArgument.getEntities(context, "target");
|
||||
CommandSourceStack source = context.getSource();
|
||||
|
||||
for (Entity target : targets) {
|
||||
Collection<ILeashData.LeashInfo> leashes = LeashDataAPI.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 (ILeashData.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.elasticDistance()).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<CommandSourceStack> context) throws CommandSyntaxException {
|
||||
return addLeash(context, CommonEventHandler.leashConfigManager.getMaxLeashLength(), CommonEventHandler.leashConfigManager.getElasticDistance(), 0, "");
|
||||
}
|
||||
|
||||
private static int addLeash(CommandContext<CommandSourceStack> context,
|
||||
double maxDistance) throws CommandSyntaxException {
|
||||
return addLeash(context, maxDistance, CommonEventHandler.leashConfigManager.getElasticDistance(), 0, "");
|
||||
}
|
||||
|
||||
private static int addLeash(CommandContext<CommandSourceStack> context,
|
||||
double maxDistance, double elasticDistance) throws CommandSyntaxException {
|
||||
return addLeash(context, maxDistance, elasticDistance, 0, "");
|
||||
}
|
||||
private static int addLeash(CommandContext<CommandSourceStack> context,
|
||||
double maxDistance, double elasticDistance, int keepTicks) throws CommandSyntaxException {
|
||||
return addLeash(context, maxDistance, elasticDistance, keepTicks, "");
|
||||
}
|
||||
private static int addLeash(CommandContext<CommandSourceStack> context,
|
||||
double maxDistance, double elasticDistance, int keepTicks, String reserved)
|
||||
throws CommandSyntaxException {
|
||||
Collection<? extends Entity> targets = EntityArgument.getEntities(context, "target");
|
||||
Entity holder = EntityArgument.getEntity(context, "holder");
|
||||
CommandSourceStack source = context.getSource();
|
||||
List<Entity> successful = new ArrayList<>(), failed = new ArrayList<>();
|
||||
for (Entity target : targets) {
|
||||
if(LeashDataAPI.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<CommandSourceStack> context) throws CommandSyntaxException {
|
||||
return addBlockLeash(context, CommonEventHandler.leashConfigManager.getMaxLeashLength(), CommonEventHandler.leashConfigManager.getElasticDistance(), 0, "");
|
||||
}
|
||||
private static int addBlockLeash(CommandContext<CommandSourceStack> context,
|
||||
double maxDistance) throws CommandSyntaxException {
|
||||
return addBlockLeash(context, maxDistance, CommonEventHandler.leashConfigManager.getElasticDistance(), 0, "");
|
||||
}
|
||||
private static int addBlockLeash(CommandContext<CommandSourceStack> context,
|
||||
double maxDistance, double elasticDistance, int keepTicks) throws CommandSyntaxException {
|
||||
return addBlockLeash(context, maxDistance, elasticDistance, keepTicks, "");
|
||||
}
|
||||
private static int addBlockLeash(CommandContext<CommandSourceStack> context,
|
||||
double maxDistance, double elasticDistance, int keepTicks, String reserved)
|
||||
throws CommandSyntaxException {
|
||||
Collection<? extends Entity> 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<Entity> successful = new ArrayList<>(), failed = new ArrayList<>();
|
||||
for (Entity target : targets) {
|
||||
if(LeashDataAPI.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<CommandSourceStack> context) throws CommandSyntaxException {
|
||||
Collection<? extends Entity> targets = EntityArgument.getEntities(context, "target");
|
||||
Entity holder = EntityArgument.getEntity(context, "holder");
|
||||
CommandSourceStack source = context.getSource();
|
||||
int successCount = 0;
|
||||
|
||||
for (Entity target : targets) {
|
||||
boolean success = LeashDataAPI.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<CommandSourceStack> context) throws CommandSyntaxException {
|
||||
Collection<? extends Entity> targets = EntityArgument.getEntities(context, "target");
|
||||
BlockPos pos = BlockPosArgument.getBlockPos(context, "pos");
|
||||
CommandSourceStack source = context.getSource();
|
||||
int successCount = 0;
|
||||
|
||||
for (Entity target : targets) {
|
||||
boolean success = LeashDataAPI.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<CommandSourceStack> context) throws CommandSyntaxException {
|
||||
Collection<? extends Entity> targets = EntityArgument.getEntities(context, "target");
|
||||
CommandSourceStack source = context.getSource();
|
||||
|
||||
for (Entity target : targets) {
|
||||
LeashDataAPI.LeashOperations.detachAll(target);
|
||||
source.sendSuccess(() -> Component.literal("Removed all leashes from " + target.getName().getString()), false);
|
||||
}
|
||||
|
||||
return targets.size();
|
||||
}
|
||||
|
||||
private static int transferLeash(CommandContext<CommandSourceStack> context) throws CommandSyntaxException {
|
||||
return transferLeash(context, "");
|
||||
}
|
||||
private static int transferLeash(CommandContext<CommandSourceStack> context, String reserved)
|
||||
throws CommandSyntaxException {
|
||||
Collection<? extends Entity> 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() ?
|
||||
LeashDataAPI.TransferOperations.transfer(target, from, to) :
|
||||
LeashDataAPI.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<CommandSourceStack> context) throws CommandSyntaxException {
|
||||
Collection<? extends Entity> targets = EntityArgument.getEntities(context, "target");
|
||||
CommandSourceStack source = context.getSource();
|
||||
|
||||
for (Entity target : targets) {
|
||||
LeashDataAPI.PhysicsOperations.applyForces(target);
|
||||
source.sendSuccess(() -> Component.literal("Applied leash forces to " + target.getName().getString()), false);
|
||||
}
|
||||
|
||||
return targets.size();
|
||||
}
|
||||
}
|
||||
|
|
@ -34,13 +34,13 @@ import net.minecraft.world.level.block.state.BlockState;
|
|||
import net.minecraft.world.level.gameevent.GameEvent;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import top.r3944realms.superleadrope.content.capability.CapabilityHandler;
|
||||
import top.r3944realms.superleadrope.content.capability.impi.LeashDataImpl;
|
||||
import top.r3944realms.superleadrope.core.register.SLPEntityTypes;
|
||||
import top.r3944realms.superleadrope.util.capability.LeashUtil;
|
||||
import top.r3944realms.superleadrope.util.capability.LeashDataAPI;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public class SuperLeashKnotEntity extends LeashFenceKnotEntity {
|
||||
|
|
@ -83,7 +83,7 @@ public class SuperLeashKnotEntity extends LeashFenceKnotEntity {
|
|||
this.playSound(SoundEvents.LEASH_KNOT_BREAK);
|
||||
List<Entity> entities = LeashDataImpl.leashableInArea(this.level(), pos.getCenter(), entity -> LeashDataImpl.isLeashHolder(entity, this));
|
||||
entities.forEach(entity ->
|
||||
LeashUtil.getLeashData(entity)
|
||||
LeashDataAPI.getLeashData(entity)
|
||||
.map(iLeashDataCapability -> iLeashDataCapability.removeLeash(this))
|
||||
);
|
||||
}
|
||||
|
|
@ -120,6 +120,27 @@ public class SuperLeashKnotEntity extends LeashFenceKnotEntity {
|
|||
pLevel.addFreshEntity(superLeashKnotEntity1);
|
||||
return superLeashKnotEntity1;
|
||||
}
|
||||
public static @NotNull Optional<SuperLeashKnotEntity> get(@NotNull Level level, @NotNull BlockPos pos) {
|
||||
AABB searchArea = new AABB(pos).inflate(1.0D);
|
||||
|
||||
return level.getEntitiesOfClass(SuperLeashKnotEntity.class, searchArea)
|
||||
.stream()
|
||||
.filter(knot -> knot.getPos().equals(pos))
|
||||
.findFirst();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建拴绳结,请不用直接调用这个,除非你知道自己在干上面
|
||||
* @return 拴绳结
|
||||
*/
|
||||
public static @NotNull SuperLeashKnotEntity createKnot(@NotNull Level pLevel, @NotNull BlockPos pPos, boolean isEmpty) {
|
||||
if(isEmpty) {
|
||||
SuperLeashKnotEntity superLeashKnotEntity1 = new SuperLeashKnotEntity(pLevel, pPos);
|
||||
pLevel.addFreshEntity(superLeashKnotEntity1);
|
||||
return superLeashKnotEntity1;
|
||||
}
|
||||
throw new IllegalArgumentException("Cannot create Knot Entity of type " + SuperLeashKnotEntity.class.getSimpleName());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void recalculateBoundingBox() {
|
||||
|
|
@ -169,7 +190,7 @@ public class SuperLeashKnotEntity extends LeashFenceKnotEntity {
|
|||
List<Entity> entities = LeashDataImpl.leashableInArea(player);
|
||||
for(Entity entity : entities) {
|
||||
if (LeashDataImpl.isLeashHolder(entity, player.getUUID()))
|
||||
LeashUtil.getLeashData(entity)
|
||||
LeashDataAPI.getLeashData(entity)
|
||||
.ifPresent(i -> {
|
||||
i.transferLeash(player.getUUID(), this);
|
||||
isTransferLeash.set(true);
|
||||
|
|
@ -182,7 +203,7 @@ public class SuperLeashKnotEntity extends LeashFenceKnotEntity {
|
|||
this.discard();
|
||||
List<Entity> entities1 = LeashDataImpl.leashableInArea(this);
|
||||
entities1.forEach(entity ->
|
||||
LeashUtil.getLeashData(entity)
|
||||
LeashDataAPI.getLeashData(entity)
|
||||
.ifPresent(iLeashDataCapability -> {
|
||||
iLeashDataCapability.removeLeash(this);
|
||||
isRemoveLeashKnot.set(true);
|
||||
|
|
|
|||
|
|
@ -24,12 +24,12 @@ public class SLPGamerules {
|
|||
public static final SLPGameruleRegistry GAMERULE_REGISTRY = SLPGameruleRegistry.INSTANCE;
|
||||
public static final HashMap<String, Boolean> gamerulesBooleanValuesClient = new HashMap<>();
|
||||
public static final HashMap<String, Integer> gameruleIntegerValuesClient = new HashMap<>();
|
||||
public static final String RULE_KEY_PERFix_ = "gamerule." + GAMERULE_PREFIX.toLowerCase();
|
||||
public static final String RULE_KEY_PERFiX_ = "gamerule." + GAMERULE_PREFIX.toLowerCase();
|
||||
public static String getDescriptionKey(Class<?> gameRuleClass) {
|
||||
return RULE_KEY_PERFix_ + gameRuleClass.getSimpleName() + ".description";
|
||||
return RULE_KEY_PERFiX_ + gameRuleClass.getSimpleName() + ".description";
|
||||
}
|
||||
public static String getDescriptionKey(String gameRuleName) {
|
||||
return RULE_KEY_PERFix_ + gameRuleName + ".description";
|
||||
return RULE_KEY_PERFiX_ + gameRuleName + ".description";
|
||||
}
|
||||
public static String getGameruleName(Class<?> clazz) {
|
||||
return SLPGamerules.GAMERULE_PREFIX + clazz.getSimpleName();
|
||||
|
|
@ -39,7 +39,7 @@ public class SLPGamerules {
|
|||
}
|
||||
|
||||
public static String getNameKey(Class<?> gameRuleClass) {
|
||||
return RULE_KEY_PERFix_ + gameRuleClass.getSimpleName();
|
||||
return RULE_KEY_PERFiX_ + gameRuleClass.getSimpleName();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.r3944realms.superleadrope.content.gamerule.server;
|
||||
|
||||
import net.minecraft.world.level.GameRules;
|
||||
import top.r3944realms.superleadrope.content.gamerule.SLPGamerules;
|
||||
|
||||
import static top.r3944realms.superleadrope.content.gamerule.SLPGamerules.GAMERULE_REGISTRY;
|
||||
|
||||
public class CreateSuperLeashKnotEntityIfAbsent {
|
||||
public static final boolean DEFAULT_VALUE = true;
|
||||
public static final String ID = SLPGamerules.getGameruleName(CreateSuperLeashKnotEntityIfAbsent.class);
|
||||
public static final String DESCRIPTION_KEY = SLPGamerules.getDescriptionKey(CreateSuperLeashKnotEntityIfAbsent.class);
|
||||
public static final String NAME_KEY = SLPGamerules.getNameKey(CreateSuperLeashKnotEntityIfAbsent.class);
|
||||
public static final GameRules.Category CATEGORY = GameRules.Category.PLAYER;
|
||||
|
||||
public static void register() {
|
||||
GAMERULE_REGISTRY.registerGamerule(ID, CATEGORY, DEFAULT_VALUE);
|
||||
}
|
||||
}
|
||||
|
|
@ -29,15 +29,13 @@ import net.minecraft.world.level.Level;
|
|||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.gameevent.GameEvent;
|
||||
import net.minecraftforge.common.extensions.IForgeItem;
|
||||
import net.minecraftforge.common.util.LazyOptional;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import top.r3944realms.superleadrope.content.SLPToolTier;
|
||||
import top.r3944realms.superleadrope.content.capability.CapabilityHandler;
|
||||
import top.r3944realms.superleadrope.content.capability.impi.LeashDataImpl;
|
||||
import top.r3944realms.superleadrope.content.capability.inter.ILeashData;
|
||||
import top.r3944realms.superleadrope.content.entity.SuperLeashKnotEntity;
|
||||
import top.r3944realms.superleadrope.core.register.SLPSoundEvents;
|
||||
import top.r3944realms.superleadrope.util.capability.LeashUtil;
|
||||
import top.r3944realms.superleadrope.util.capability.LeashDataAPI;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
|
@ -83,7 +81,7 @@ public class SuperLeadRopeItem extends TieredItem implements IForgeItem {
|
|||
}
|
||||
|
||||
public static boolean canUse(ItemStack itemStack) {
|
||||
return itemStack.getDamageValue() < 974;
|
||||
return itemStack.getDamageValue() < 1200;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -106,11 +104,10 @@ public class SuperLeadRopeItem extends TieredItem implements IForgeItem {
|
|||
* @param newHolder 新实体
|
||||
* @param player 明确持有玩家
|
||||
* @param level 维度世界
|
||||
* @param leashStack 拴绳物品实例
|
||||
* @return 是否成功
|
||||
*/
|
||||
public static boolean bindToEntity(Entity newHolder, Player player, Level level, ItemStack leashStack) {
|
||||
return bindToEntity(newHolder, player, level, player.getOnPos(), leashStack);
|
||||
public static boolean bindToEntity(Entity newHolder, Player player, Level level) {
|
||||
return bindToEntity(newHolder, player, level, player.getOnPos());
|
||||
}
|
||||
/**
|
||||
* 右键蹲下绑定到另一实体上
|
||||
|
|
@ -118,10 +115,9 @@ public class SuperLeadRopeItem extends TieredItem implements IForgeItem {
|
|||
* @param player 明确持有玩家
|
||||
* @param level 维度世界
|
||||
* @param pos 坐标(一般是明确持有玩家的位置)
|
||||
* @param leashStack 拴绳物品实例
|
||||
* @return 是否成功
|
||||
*/
|
||||
public static boolean bindToEntity(Entity newHolder, Player player, Level level, BlockPos pos, ItemStack leashStack) {
|
||||
public static boolean bindToEntity(Entity newHolder, Player player, Level level, BlockPos pos) {
|
||||
boolean isSuccess = false;
|
||||
|
||||
// 查找当前玩家持有的可拴生物
|
||||
|
|
@ -131,7 +127,7 @@ public class SuperLeadRopeItem extends TieredItem implements IForgeItem {
|
|||
);
|
||||
|
||||
for (Entity e : list) {
|
||||
Optional<ILeashData> leashDataOpt = LeashUtil.getLeashData(e);
|
||||
Optional<ILeashData> leashDataOpt = LeashDataAPI.getLeashData(e);
|
||||
|
||||
if (leashDataOpt.map(i -> i.canBeAttachedTo(newHolder)).orElse(false)) {
|
||||
leashDataOpt.ifPresent(i -> i.transferLeash(player.getUUID(), newHolder));
|
||||
|
|
@ -182,16 +178,16 @@ public class SuperLeadRopeItem extends TieredItem implements IForgeItem {
|
|||
if (leashStack.isEmpty() || !canUse(leashStack)) {
|
||||
return false;
|
||||
}
|
||||
knot = SuperLeashKnotEntity.getOrCreateKnot(level, pos);
|
||||
knot.playPlacementSound();
|
||||
knot = SuperLeashKnotEntity.getOrCreateKnot(level, pos);
|
||||
knot.playPlacementSound();
|
||||
|
||||
SuperLeashKnotEntity finalKnot = knot;
|
||||
LeashUtil.getLeashData(player).ifPresent(i -> {
|
||||
if (i.canBeAttachedTo(finalKnot)) {
|
||||
i.addLeash(finalKnot);
|
||||
isSuccess.set(true);
|
||||
}
|
||||
});
|
||||
SuperLeashKnotEntity finalKnot = knot;
|
||||
LeashDataAPI.getLeashData(player).ifPresent(i -> {
|
||||
if (i.canBeAttachedTo(finalKnot)) {
|
||||
if (!level.isClientSide) i.addLeash(finalKnot);
|
||||
isSuccess.set(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
// 情况二:把已有生物拴到 knot
|
||||
else if (!list.isEmpty()) {
|
||||
|
|
@ -202,9 +198,9 @@ public class SuperLeadRopeItem extends TieredItem implements IForgeItem {
|
|||
}
|
||||
SuperLeashKnotEntity finalKnot = knot;
|
||||
|
||||
LeashUtil.getLeashData(e).ifPresent(i -> {
|
||||
LeashDataAPI.getLeashData(e).ifPresent(i -> {
|
||||
if (i.canBeAttachedTo(finalKnot)) {
|
||||
i.transferLeash(uuid, finalKnot);
|
||||
if (!level.isClientSide) i.transferLeash(uuid, finalKnot);
|
||||
isSuccess.set(true);
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.r3944realms.superleadrope.core.hook;
|
||||
|
||||
import net.minecraft.client.renderer.culling.Frustum;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.Mob;
|
||||
import top.r3944realms.superleadrope.SuperLeadRope;
|
||||
import top.r3944realms.superleadrope.util.capability.LeashDataAPI;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public class LeashRenderHook {
|
||||
public static boolean shouldRenderExtra(Mob mob, Frustum camera) {
|
||||
SuperLeadRope.logger.debug("[SuperLeash] Checking entity: {} at position: {}, {}, {}", mob.getName().getString(), mob.getX(), mob.getY(), mob.getZ());
|
||||
AtomicBoolean flag = new AtomicBoolean(false);
|
||||
LeashDataAPI.getLeashData(mob).ifPresent(i -> {
|
||||
i.getAllLeashes().forEach(j -> {
|
||||
Optional<Integer> i1 = j.holderIdOpt();
|
||||
if (i1.isPresent()) {
|
||||
Entity entity = mob.level().getEntity(i1.get());
|
||||
if (entity != null) {
|
||||
flag.set(camera.isVisible(entity.getBoundingBoxForCulling()));
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
return flag.get();
|
||||
}
|
||||
}
|
||||
|
|
@ -16,6 +16,8 @@
|
|||
package top.r3944realms.superleadrope.core.leash;
|
||||
|
||||
|
||||
import net.minecraft.sounds.SoundEvent;
|
||||
import net.minecraft.sounds.SoundEvents;
|
||||
import net.minecraft.sounds.SoundSource;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.InteractionResult;
|
||||
|
|
@ -24,15 +26,16 @@ import net.minecraft.world.entity.player.Player;
|
|||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.gameevent.GameEvent;
|
||||
import net.minecraftforge.common.util.LazyOptional;
|
||||
import net.minecraftforge.event.entity.player.AttackEntityEvent;
|
||||
import net.minecraftforge.event.entity.player.PlayerInteractEvent;
|
||||
import top.r3944realms.superleadrope.content.capability.CapabilityHandler;
|
||||
import top.r3944realms.superleadrope.content.capability.impi.LeashDataImpl;
|
||||
import top.r3944realms.superleadrope.content.capability.inter.ILeashData;
|
||||
import top.r3944realms.superleadrope.content.item.SuperLeadRopeItem;
|
||||
import top.r3944realms.superleadrope.core.register.SLPItems;
|
||||
import top.r3944realms.superleadrope.core.register.SLPSoundEvents;
|
||||
import top.r3944realms.superleadrope.util.capability.LeashDataAPI;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public class LeashInteractHandler {
|
||||
//只有玩家可以互动触发(其它的暂不支持(考虑到0 Mixin)
|
||||
|
|
@ -48,6 +51,7 @@ public class LeashInteractHandler {
|
|||
event.setCanceled(true);
|
||||
event.setCancellationResult(InteractionResult.SUCCESS);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
if (hand == InteractionHand.OFF_HAND) {
|
||||
|
|
@ -57,8 +61,8 @@ public class LeashInteractHandler {
|
|||
if (!LeashDataImpl.isLeashable(target)) {
|
||||
return;
|
||||
}
|
||||
LazyOptional<ILeashData> LeashCap = target.getCapability(CapabilityHandler.LEASH_DATA_CAP);
|
||||
if (!LeashCap.isPresent()) {
|
||||
Optional<ILeashData> LeashCap = LeashDataAPI.getLeashData(target);
|
||||
if (LeashCap.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -72,7 +76,7 @@ public class LeashInteractHandler {
|
|||
|
||||
) {
|
||||
|
||||
boolean isSuccess = SuperLeadRopeItem.bindToEntity(target, player, player.level(), player.getOnPos(), ItemStack.EMPTY);
|
||||
boolean isSuccess = SuperLeadRopeItem.bindToEntity(target, player, player.level(), player.getOnPos());
|
||||
if (isSuccess) {
|
||||
event.setCanceled(true);
|
||||
event.setCancellationResult(InteractionResult.SUCCESS);
|
||||
|
|
@ -103,7 +107,7 @@ public class LeashInteractHandler {
|
|||
boolean success = iLeashDataCapability.addLeash(player);
|
||||
if (success) {
|
||||
if(!player.isCreative())
|
||||
itemStack.hurtAndBreak(24, player, e->{});
|
||||
itemStack.hurtAndBreak(24, player, e-> e.level().playSound(null, player.getOnPos(), SoundEvents.ITEM_BREAK, SoundSource.PLAYERS));
|
||||
level.playSound(null, target.getOnPos(), SLPSoundEvents.LEAD_TIED.get(), SoundSource.PLAYERS);
|
||||
event.setCanceled(true);
|
||||
event.setCancellationResult(InteractionResult.SUCCESS);
|
||||
|
|
@ -123,7 +127,7 @@ public class LeashInteractHandler {
|
|||
}
|
||||
} else {
|
||||
if (flag) {
|
||||
target.getCapability(CapabilityHandler.LEASH_DATA_CAP).ifPresent(leashDataCapability -> {
|
||||
LeashDataAPI.getLeashData(target).ifPresent(leashDataCapability -> {
|
||||
if (leashDataCapability.hasLeash()){
|
||||
int size = leashDataCapability.getAllLeashes().size();
|
||||
if (player.isSecondaryUseActive())
|
||||
|
|
|
|||
|
|
@ -18,7 +18,9 @@ package top.r3944realms.superleadrope.datagen.data;
|
|||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import top.r3944realms.superleadrope.content.command.LeashDataCommand;
|
||||
import top.r3944realms.superleadrope.content.command.MotionCommand;
|
||||
import top.r3944realms.superleadrope.content.gamerule.server.CreateSuperLeashKnotEntityIfAbsent;
|
||||
import top.r3944realms.superleadrope.content.gamerule.server.TeleportWithLeashedEntities;
|
||||
import top.r3944realms.superleadrope.content.item.EternalPotatoItem;
|
||||
import top.r3944realms.superleadrope.core.register.SLPEntityTypes;
|
||||
|
|
@ -42,21 +44,24 @@ public enum SLPLangKeyValue {
|
|||
"Eternal Potato", "永恒土豆", "永恆馬鈴薯", "不滅薯", true
|
||||
),
|
||||
|
||||
EP_TOOLTIP_TITLE(EternalPotatoItem.getDescKey("title"), ModPartEnum.DESCRIPTION,
|
||||
EP_TOOLTIP_TITLE(
|
||||
EternalPotatoItem.getDescKey("title"), ModPartEnum.DESCRIPTION,
|
||||
"§6Mythical Item §7- §6Eternal Potato",
|
||||
"§6神话物品 §7- §6永恒土豆",
|
||||
"§6神話物品 §7- §6永恒土豆",
|
||||
"§6永恒土豆 §7- §6传奇之物"
|
||||
),
|
||||
|
||||
EP_DESC_TOOLTIP(EternalPotatoItem.getDescKey("desc"), ModPartEnum.DESCRIPTION,
|
||||
EP_DESC_TOOLTIP(
|
||||
EternalPotatoItem.getDescKey("desc"), ModPartEnum.DESCRIPTION,
|
||||
"§7Symbol of server-wide contract, cannot be discarded",
|
||||
"§7象征全服契约,不可丢弃",
|
||||
"§7象徵全服契約,不可丟棄",
|
||||
"§7象征全服契约,绝不可弃"
|
||||
),
|
||||
|
||||
EP_BIND_OWNER(EternalPotatoItem.getDescKey("bind_owner"), ModPartEnum.DESCRIPTION,
|
||||
EP_BIND_OWNER(
|
||||
EternalPotatoItem.getDescKey("bind_owner"), ModPartEnum.DESCRIPTION,
|
||||
"§bBound Owner: §f%s",
|
||||
"§b绑定主人: §f%s",
|
||||
"§b綁定主人: §f%s",
|
||||
|
|
@ -70,42 +75,48 @@ public enum SLPLangKeyValue {
|
|||
"§c尚未绑定主人"
|
||||
),
|
||||
|
||||
EP_OBLIGATION_TOOLTIP(EternalPotatoItem.getDescKey("obligation"), ModPartEnum.DESCRIPTION,
|
||||
EP_OBLIGATION_TOOLTIP(
|
||||
EternalPotatoItem.getDescKey("obligation"), ModPartEnum.DESCRIPTION,
|
||||
"§7Daily obligations remaining: §a%d §c(+%d§c overdue)",
|
||||
"§7今日剩余义务: §a%d §c(+%d §c逾期未完成)",
|
||||
"§7今日剩餘義務: §a%d §c(+%d §c逾期未完成)",
|
||||
"§7今日责务尚余: §a%d §c(+%d §c逾期未尽)"
|
||||
),
|
||||
|
||||
EP_PUNISH_TOOLTIP(EternalPotatoItem.getDescKey("punish"), ModPartEnum.DESCRIPTION,
|
||||
EP_PUNISH_TOOLTIP(
|
||||
EternalPotatoItem.getDescKey("punish"), ModPartEnum.DESCRIPTION,
|
||||
"§cOverdue punishments: §4%d §7(will be applied), grace exceeded: §4%d",
|
||||
"§c逾期未完成责务: §4%d §7(将会受罚),超出宽限数: §4%d",
|
||||
"§c逾期未完成责務: §4%d §7(將會受罰),超出寬限數: §4%d",
|
||||
"§c逾期责务尚未完成: §4%d §7(將受懲罰),超出寬限數: §4%d"
|
||||
),
|
||||
|
||||
EP_OBLIGATION_INFO(EternalPotatoItem.getMsgKey("obligation_info"), ModPartEnum.MESSAGE,
|
||||
EP_OBLIGATION_INFO(
|
||||
EternalPotatoItem.getMsgKey("obligation_info"), ModPartEnum.MESSAGE,
|
||||
"§e[Eternal Potato] §fThis is the server-wide shared person, remaining obligations today: §a%d§f.",
|
||||
"§e[永恒土豆] §f这是全服共有之人,今日义务剩余:§a%d§f次。",
|
||||
"§e[永恒土豆] §f這是全服共有之人,今日義務剩餘:§a%d§f次。",
|
||||
"§e[永恒土豆] §f此为全服共享之人,今日责务尚余:§a%d§f次。"
|
||||
),
|
||||
|
||||
EP_POTATO_HEAL(EternalPotatoItem.getMsgKey("potato_heal"), ModPartEnum.MESSAGE,
|
||||
EP_POTATO_HEAL(
|
||||
EternalPotatoItem.getMsgKey("potato_heal"), ModPartEnum.MESSAGE,
|
||||
"§aThe power of the Eternal Potato comforts you, it won't disappear.",
|
||||
"§a永恒土豆的力量抚慰了你,但它不会消失。",
|
||||
"§a永恆土豆的力量撫慰了你,但它不會消失。",
|
||||
"§a永恒土豆之力慰心,永不消逝。"
|
||||
),
|
||||
|
||||
EP_CANNOT_DROP(EternalPotatoItem.getMsgKey("cannot_drop"), ModPartEnum.MESSAGE,
|
||||
EP_CANNOT_DROP(
|
||||
EternalPotatoItem.getMsgKey("cannot_drop"), ModPartEnum.MESSAGE,
|
||||
"§cThe Eternal Potato cannot be dropped! +%d punishments.",
|
||||
"§c永恒土豆是不可丢弃的,惩罚数加%d!",
|
||||
"§c永恆土豆不可丟棄,懲罰數加%d!",
|
||||
"§c永恒土豆不可丟棄,懲罰數增加%d!"
|
||||
),
|
||||
|
||||
EP_BIND_MSG(EternalPotatoItem.getMsgKey("bind_msg"), ModPartEnum.MESSAGE,
|
||||
EP_BIND_MSG(
|
||||
EternalPotatoItem.getMsgKey("bind_msg"), ModPartEnum.MESSAGE,
|
||||
"§6Bound to you as the server-wide shared person.",
|
||||
"§6已与你绑定,成为全服共有之人。",
|
||||
"§6已與你綁定,成為全服共有之人。",
|
||||
|
|
@ -114,21 +125,24 @@ public enum SLPLangKeyValue {
|
|||
|
||||
|
||||
|
||||
EP_OBLIGATION_DONE(EternalPotatoItem.getMsgKey("obligation_done"), ModPartEnum.MESSAGE,
|
||||
EP_OBLIGATION_DONE(
|
||||
EternalPotatoItem.getMsgKey("obligation_done"), ModPartEnum.MESSAGE,
|
||||
"§eObligation completed, remaining: §a%d§e",
|
||||
"§e义务完成一次,剩余 §a%d §e次。",
|
||||
"§e義務完成一次,剩餘 §a%d §e次。",
|
||||
"§e责务完成,尚余 §a%d §e次。"
|
||||
),
|
||||
|
||||
EP_OBLIGATION_FULL(EternalPotatoItem.getMsgKey("obligation_full"), ModPartEnum.MESSAGE,
|
||||
EP_OBLIGATION_FULL(
|
||||
EternalPotatoItem.getMsgKey("obligation_full"), ModPartEnum.MESSAGE,
|
||||
"§aAll obligations completed today!",
|
||||
"§a今日义务已全部完成!",
|
||||
"§a今日義務已全部完成!",
|
||||
"§a今日责务尽矣!"
|
||||
),
|
||||
|
||||
EP_PUNISH_MSG(EternalPotatoItem.getMsgKey("punish_msg"), ModPartEnum.MESSAGE,
|
||||
EP_PUNISH_MSG(
|
||||
EternalPotatoItem.getMsgKey("punish_msg"), ModPartEnum.MESSAGE,
|
||||
"§cYesterday obligations incomplete, punished!",
|
||||
"§c未完成昨日义务,受到惩罚!",
|
||||
"§c未完成昨日義務,受到懲罰!",
|
||||
|
|
@ -142,14 +156,16 @@ public enum SLPLangKeyValue {
|
|||
"受罚倒数:§a%d §f瞬"
|
||||
),
|
||||
|
||||
EP_PICKUP_NOT_OWNER(EternalPotatoItem.getMsgKey("pickup_not_owner"), ModPartEnum.MESSAGE,
|
||||
EP_PICKUP_NOT_OWNER(
|
||||
EternalPotatoItem.getMsgKey("pickup_not_owner"), ModPartEnum.MESSAGE,
|
||||
"§cYou are not the rightful owner and cannot pick this up!",
|
||||
"§c非绑定主人无法拾取此物品!",
|
||||
"§c非綁定主人無法拾取此物品!",
|
||||
"§c非汝所主,勿取!"
|
||||
),
|
||||
|
||||
EP_PUNISH_NOT_OWNER(EternalPotatoItem.getMsgKey("punish_not_owner"), ModPartEnum.MESSAGE,
|
||||
EP_PUNISH_NOT_OWNER(
|
||||
EternalPotatoItem.getMsgKey("punish_not_owner"), ModPartEnum.MESSAGE,
|
||||
"§cYou are not the rightful owner, punished by lightning!",
|
||||
"§c非绑定主人使用,受到闪电惩罚!",
|
||||
"§c非綁定主人使用,受到閃電懲罰!",
|
||||
|
|
@ -188,13 +204,29 @@ public enum SLPLangKeyValue {
|
|||
SLPEntityTypes.getEntityNameKey("super_lead_knot"), ModPartEnum.ENTITY,
|
||||
"Super Lead Knot", "超级拴绳结", "超級拴繩結", "神駒羈縻索結"
|
||||
),
|
||||
TELEPORT_WITH_LEASHED_ENTITIES_NAME(TeleportWithLeashedEntities.NAME_KEY, ModPartEnum.GAME_RULE,
|
||||
TELEPORT_WITH_LEASHED_ENTITIES_NAME(
|
||||
TeleportWithLeashedEntities.NAME_KEY, ModPartEnum.GAME_RULE,
|
||||
"Teleport leashed player with player holder",
|
||||
"被拴实体随玩家持有者传送",
|
||||
"被拴实体随玩家持有者傳送",
|
||||
"繫畜隨持者傳送"
|
||||
),
|
||||
TELEPORT_WITH_LEASHED_DESCRIPTION(TeleportWithLeashedEntities.DESCRIPTION_KEY, ModPartEnum.DESCRIPTION,
|
||||
CREATE_SUPER_LEASH_KNOT_ENTITY_IF_ABSENT_NAME(
|
||||
CreateSuperLeashKnotEntityIfAbsent.NAME_KEY, ModPartEnum.NAME,
|
||||
"Create Leash Fence Knot Entity if absent",
|
||||
"如果缺失则创建超级拴绳结",
|
||||
"如果缺失則創建超級拴繩結",
|
||||
"若阙则创超级繫绳结"
|
||||
),
|
||||
CREATE_SUPER_LEASH_KNOT_ENTITY_IF_ABSENT_DESCRIPTION(
|
||||
CreateSuperLeashKnotEntityIfAbsent.DESCRIPTION_KEY, ModPartEnum.DESCRIPTION,
|
||||
"Create LeashKnot Entity if it's absent on fence or other supported positions",
|
||||
"如果在栅栏等支持处缺失超级拴绳结,则创建它",
|
||||
"如果在柵欄等支持處缺失超級拴繩結,則創建它",
|
||||
"若栅等支处阙超级繫绳结,则创之"
|
||||
),
|
||||
TELEPORT_WITH_LEASHED_DESCRIPTION(
|
||||
TeleportWithLeashedEntities.DESCRIPTION_KEY, ModPartEnum.DESCRIPTION,
|
||||
"Holder will teleport with their leashed players ",
|
||||
"传送时将被拴实体与持有者一起传送",
|
||||
"將被拴实体將隨持有者一起傳送",
|
||||
|
|
@ -221,6 +253,90 @@ public enum SLPLangKeyValue {
|
|||
"§b倍乘成功.§a%s§7:§f[§e加速§7:(§a%.2f§7,§a%.2f§7,§a%.2f§7)§f]§r",
|
||||
"§b倍乘既成.§a%s§7:§f[§e速勢§7:(§a%.2f§7,§a%.2f§7,§a%.2f§7)§f]§r"
|
||||
),
|
||||
MESSAGE_LEASHDATA_GET_TITLE(
|
||||
LeashDataCommand.TITLE, ModPartEnum.COMMAND,
|
||||
"=== Leash Data for %s ===",
|
||||
"=== %s 的拴绳数据 ===",
|
||||
"=== %s 的拴繩數據 ===",
|
||||
"=== %s 之繫繩數據 ==="
|
||||
),
|
||||
MESSAGE_LEASHDATA_GET_TOTAL(
|
||||
LeashDataCommand.TOTAL, ModPartEnum.COMMAND,
|
||||
"Total leashes: %d",
|
||||
"总拴绳数: %d",
|
||||
"總拴繩數: %d",
|
||||
"繫繩總數: %d"
|
||||
),
|
||||
MESSAGE_LEASHDATA_GET_BLOCK(
|
||||
LeashDataCommand.BLOCK, ModPartEnum.COMMAND,
|
||||
"§7Block: §e%s",
|
||||
"§7方块: §e%s",
|
||||
"§7方塊: §e%s",
|
||||
"§7磚石: §e%s"
|
||||
),
|
||||
MESSAGE_LEASHDATA_GET_UUID(
|
||||
LeashDataCommand.UUID, ModPartEnum.COMMAND,
|
||||
"§7UUID: §b%s",
|
||||
"§7UUID: §b%s",
|
||||
"§7UUID: §b%s",
|
||||
"§7UUID: §b%s"
|
||||
),
|
||||
MESSAGE_LEASHDATA_GET_MAX(
|
||||
LeashDataCommand.MAX, ModPartEnum.COMMAND,
|
||||
"§7Max: §a%.1f",
|
||||
"§7最大距离: §a%.1f",
|
||||
"§7最大距離: §a%.1f",
|
||||
"§7極距: §a%.1f"
|
||||
),
|
||||
MESSAGE_LEASHDATA_GET_ELASTIC(
|
||||
LeashDataCommand.ELASTIC, ModPartEnum.COMMAND,
|
||||
"§7Elastic: §6%.1f",
|
||||
"§7弹性距离: §6%.1f",
|
||||
"§7彈性距離: §6%.1f",
|
||||
"§7彈距: §6%.1f"
|
||||
),
|
||||
MESSAGE_LEASHDATA_GET_KEEP(
|
||||
LeashDataCommand.KEEP, ModPartEnum.COMMAND,
|
||||
"§7Keep: §c%d§7/§c%d",
|
||||
"§7保持: §c%d§7/§c%d",
|
||||
"§7保持: §c%d§7/§c%d",
|
||||
"§7持時: §c%d§7/§c%d"
|
||||
),
|
||||
MESSAGE_LEASHDATA_GET_RESERVED(
|
||||
LeashDataCommand.RESERVED, ModPartEnum.COMMAND,
|
||||
"§7Reserved: §d%s",
|
||||
"§7保留字段: §d%s",
|
||||
"§7保留字段: §d%s",
|
||||
"§7備註: §d%s"
|
||||
),
|
||||
MESSAGE_LEASHDATA_ADD_SUCCESS(
|
||||
"command.leashdata.add.success", ModPartEnum.COMMAND,
|
||||
"§bAdded leash successfully. §a%s §7→ §e%s",
|
||||
"§b添加拴绳成功. §a%s §7→ §e%s",
|
||||
"§b添加拴繩成功. §a%s §7→ §e%s",
|
||||
"§b繫繩既添. §a%s §7→ §e%s"
|
||||
),
|
||||
MESSAGE_LEASHDATA_REMOVE_SUCCESS(
|
||||
"command.leashdata.remove.success", ModPartEnum.COMMAND,
|
||||
"§bRemoved leash successfully. §a%s §7- §e%s",
|
||||
"§b移除拴绳成功. §a%s §7- §e%s",
|
||||
"§b移除拴繩成功. §a%s §7- §e%s",
|
||||
"§b繫繩既除. §a%s §7- §e%s"
|
||||
),
|
||||
MESSAGE_LEASHDATA_TRANSFER_SUCCESS(
|
||||
"command.leashdata.transfer.success", ModPartEnum.COMMAND,
|
||||
"§bTransferred leash successfully. §a%s §7→ §e%s §7→ §6%s",
|
||||
"§b转移拴绳成功. §a%s §7→ §e%s §7→ §6%s",
|
||||
"§b轉移拴繩成功. §a%s §7→ §e%s §7→ §6%s",
|
||||
"§b繫繩既移. §a%s §7→ §e%s §7→ §6%s"
|
||||
),
|
||||
MESSAGE_LEASHDATA_SET_SUCCESS(
|
||||
"command.leashdata.set.success", ModPartEnum.COMMAND,
|
||||
"§bSet leash property successfully. §a%s §7: §e%s §7= §6%.1f",
|
||||
"§b设置拴绳属性成功. §a%s §7: §e%s §7= §6%.1f",
|
||||
"§b設置拴繩屬性成功. §a%s §7: §e%s §7= §6%.1f",
|
||||
"§b繫繩性既定. §a%s §7: §e%s §7= §6%.1f"
|
||||
);
|
||||
|
||||
;
|
||||
private final Supplier<?> supplier;
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ import net.minecraft.nbt.CompoundTag;
|
|||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraftforge.network.NetworkEvent;
|
||||
import top.r3944realms.superleadrope.content.capability.CapabilityHandler;
|
||||
import top.r3944realms.superleadrope.util.capability.LeashDataAPI;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
|
|
@ -42,7 +42,7 @@ public record LeashDataSyncPacket(int entityId, CompoundTag leashData) {
|
|||
if (level != null) {
|
||||
Entity entity = level.getEntity(msg.entityId);
|
||||
if (entity != null) {
|
||||
entity.getCapability(CapabilityHandler.LEASH_DATA_CAP).ifPresent(cap -> {
|
||||
LeashDataAPI.getLeashData(entity).ifPresent(cap -> {
|
||||
// 只在数据确实变化时更新
|
||||
CompoundTag current = cap.serializeNBT();
|
||||
if (!current.equals(msg.leashData)) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,315 @@
|
|||
/*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.r3944realms.superleadrope.util.capability;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import top.r3944realms.superleadrope.content.capability.CapabilityHandler;
|
||||
import top.r3944realms.superleadrope.content.capability.inter.ILeashData;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 拴绳数据API - 提供统一的API接口操作拴绳数据能力
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public final class LeashDataAPI {
|
||||
// ==================== 基础能力获取 ====================
|
||||
public static Optional<ILeashData> getLeashData(@NotNull Entity entity) {
|
||||
Objects.requireNonNull(entity, "Entity cannot be null");
|
||||
return entity.getCapability(CapabilityHandler.LEASH_DATA_CAP).resolve();
|
||||
}
|
||||
// ==================== 拴绳数据管理 API ====================
|
||||
|
||||
public static final class LeashOperations {
|
||||
private LeashOperations() {}
|
||||
|
||||
// ---------------------- 添加拴绳 ----------------------
|
||||
public static boolean attach(Entity entity, Entity holder) {
|
||||
return getLeashData(entity).map(data -> data.addLeash(holder)).orElse(false);
|
||||
}
|
||||
|
||||
public static boolean attach(Entity entity, Entity holder, String reserved) {
|
||||
return getLeashData(entity).map(data -> data.addLeash(holder, reserved)).orElse(false);
|
||||
}
|
||||
|
||||
public static boolean attach(Entity entity, Entity holder, double maxDistance) {
|
||||
return getLeashData(entity).map(data -> data.addLeash(holder, maxDistance)).orElse(false);
|
||||
}
|
||||
|
||||
public static boolean attach(Entity entity, Entity holder, double maxDistance, double elasticDistance, int maxKeepTicks) {
|
||||
return getLeashData(entity).map(data -> data.addLeash(holder, maxDistance, elasticDistance, maxKeepTicks)).orElse(false);
|
||||
}
|
||||
|
||||
public static boolean attach(Entity entity, Entity holder, double maxDistance, String reserved) {
|
||||
return getLeashData(entity).map(data -> data.addLeash(holder, maxDistance, reserved)).orElse(false);
|
||||
}
|
||||
|
||||
public static boolean attach(Entity entity, Entity holder, double maxDistance, double elasticDistance, int maxKeepTicks, String reserved) {
|
||||
return getLeashData(entity).map(data -> data.addLeash(holder, maxDistance, elasticDistance, maxKeepTicks, reserved)).orElse(false);
|
||||
}
|
||||
|
||||
public static void attachWithInfo(Entity entity, Entity holder, ILeashData.LeashInfo info) {
|
||||
getLeashData(entity).ifPresent(data -> data.addLeash(holder, info));
|
||||
}
|
||||
|
||||
// ---------------------- 延迟拴绳 ----------------------
|
||||
public static void attachDelayed(Entity entity, Player holderPlayer) {
|
||||
getLeashData(entity).ifPresent(data -> data.addDelayedLeash(holderPlayer));
|
||||
}
|
||||
|
||||
public static void removeDelayed(Entity entity, UUID onceHolderPlayerUUID) {
|
||||
getLeashData(entity).ifPresent(data -> data.removeDelayedLeash(onceHolderPlayerUUID));
|
||||
}
|
||||
|
||||
// ---------------------- 移除拴绳 ----------------------
|
||||
public static boolean detach(Entity entity, Entity holder) {
|
||||
return getLeashData(entity).map(data -> data.removeLeash(holder)).orElse(false);
|
||||
}
|
||||
|
||||
public static boolean detach(Entity entity, UUID holderUUID) {
|
||||
return getLeashData(entity).map(data -> data.removeLeash(holderUUID)).orElse(false);
|
||||
}
|
||||
|
||||
public static boolean detach(Entity entity, BlockPos knotPos) {
|
||||
return getLeashData(entity).map(data -> data.removeLeash(knotPos)).orElse(false);
|
||||
}
|
||||
|
||||
public static void detachAll(Entity entity) {
|
||||
getLeashData(entity).ifPresent(ILeashData::removeAllLeashes);
|
||||
}
|
||||
|
||||
public static void detachAllHolders(Entity entity) {
|
||||
getLeashData(entity).ifPresent(ILeashData::removeAllHolderLeashes);
|
||||
}
|
||||
|
||||
public static void detachAllKnots(Entity entity) {
|
||||
getLeashData(entity).ifPresent(ILeashData::removeAllKnotLeashes);
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 拴绳属性修改 API ====================
|
||||
|
||||
public static final class PropertyOperations {
|
||||
private PropertyOperations() {}
|
||||
|
||||
// ---------------------- 设置最大距离 ----------------------
|
||||
public static boolean setMaxDistance(Entity entity, Entity holder, double distance) {
|
||||
return getLeashData(entity).map(data -> data.setMaxDistance(holder, distance)).orElse(false);
|
||||
}
|
||||
|
||||
public static boolean setMaxDistance(Entity entity, Entity holder, double distance, int maxKeepTicks) {
|
||||
return getLeashData(entity).map(data -> data.setMaxDistance(holder, distance, maxKeepTicks)).orElse(false);
|
||||
}
|
||||
|
||||
public static boolean setMaxDistance(Entity entity, Entity holder, double distance, int maxKeepTicks, String reserved) {
|
||||
return getLeashData(entity).map(data -> data.setMaxDistance(holder, distance, maxKeepTicks, reserved)).orElse(false);
|
||||
}
|
||||
|
||||
public static boolean setMaxDistance(Entity entity, UUID holderUUID, double distance) {
|
||||
return getLeashData(entity).map(data -> data.setMaxDistance(holderUUID, distance)).orElse(false);
|
||||
}
|
||||
|
||||
public static boolean setMaxDistance(Entity entity, UUID holderUUID, double distance, int maxKeepTicks) {
|
||||
return getLeashData(entity).map(data -> data.setMaxDistance(holderUUID, distance, maxKeepTicks)).orElse(false);
|
||||
}
|
||||
|
||||
public static boolean setMaxDistance(Entity entity, UUID holderUUID, double distance, int maxKeepTicks, String reserved) {
|
||||
return getLeashData(entity).map(data -> data.setMaxDistance(holderUUID, distance, maxKeepTicks, reserved)).orElse(false);
|
||||
}
|
||||
|
||||
public static boolean setMaxDistance(Entity entity, BlockPos knotPos, double distance) {
|
||||
return getLeashData(entity).map(data -> data.setMaxDistance(knotPos, distance)).orElse(false);
|
||||
}
|
||||
|
||||
public static boolean setMaxDistance(Entity entity, BlockPos knotPos, double distance, int maxKeepTicks) {
|
||||
return getLeashData(entity).map(data -> data.setMaxDistance(knotPos, distance, maxKeepTicks)).orElse(false);
|
||||
}
|
||||
|
||||
public static boolean setMaxDistance(Entity entity, BlockPos knotPos, double distance, int maxKeepTicks, String reserved) {
|
||||
return getLeashData(entity).map(data -> data.setMaxDistance(knotPos, distance, maxKeepTicks, reserved)).orElse(false);
|
||||
}
|
||||
|
||||
// ---------------------- 设置弹性距离 ----------------------
|
||||
public static boolean setElasticDistance(Entity entity, Entity holder, double distance) {
|
||||
return getLeashData(entity).map(data -> data.setElasticDistance(holder, distance)).orElse(false);
|
||||
}
|
||||
|
||||
public static boolean setElasticDistance(Entity entity, Entity holder, double distance, int maxKeepTicks) {
|
||||
return getLeashData(entity).map(data -> data.setElasticDistance(holder, distance, maxKeepTicks)).orElse(false);
|
||||
}
|
||||
|
||||
public static boolean setElasticDistance(Entity entity, Entity holder, double distance, int maxKeepTicks, String reserved) {
|
||||
return getLeashData(entity).map(data -> data.setElasticDistance(holder, distance, maxKeepTicks, reserved)).orElse(false);
|
||||
}
|
||||
|
||||
public static boolean setElasticDistance(Entity entity, UUID holderUUID, double distance) {
|
||||
return getLeashData(entity).map(data -> data.setElasticDistance(holderUUID, distance)).orElse(false);
|
||||
}
|
||||
|
||||
public static boolean setElasticDistance(Entity entity, UUID holderUUID, double distance, int maxKeepTicks) {
|
||||
return getLeashData(entity).map(data -> data.setElasticDistance(holderUUID, distance, maxKeepTicks)).orElse(false);
|
||||
}
|
||||
|
||||
public static boolean setElasticDistance(Entity entity, UUID holderUUID, double distance, int maxKeepTicks, String reserved) {
|
||||
return getLeashData(entity).map(data -> data.setElasticDistance(holderUUID, distance, maxKeepTicks, reserved)).orElse(false);
|
||||
}
|
||||
|
||||
public static boolean setElasticDistance(Entity entity, BlockPos knotPos, double distance) {
|
||||
return getLeashData(entity).map(data -> data.setElasticDistance(knotPos, distance)).orElse(false);
|
||||
}
|
||||
|
||||
public static boolean setElasticDistance(Entity entity, BlockPos knotPos, double distance, int maxKeepTicks) {
|
||||
return getLeashData(entity).map(data -> data.setElasticDistance(knotPos, distance, maxKeepTicks)).orElse(false);
|
||||
}
|
||||
|
||||
public static boolean setElasticDistance(Entity entity, BlockPos knotPos, double distance, int maxKeepTicks, String reserved) {
|
||||
return getLeashData(entity).map(data -> data.setElasticDistance(knotPos, distance, maxKeepTicks, reserved)).orElse(false);
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 物理应用 API ====================
|
||||
|
||||
public static final class PhysicsOperations {
|
||||
private PhysicsOperations() {}
|
||||
|
||||
public static void applyForces(Entity entity) {
|
||||
getLeashData(entity).ifPresent(ILeashData::applyLeashForces);
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 拴绳转移 API ====================
|
||||
|
||||
public static final class TransferOperations {
|
||||
private TransferOperations() {}
|
||||
|
||||
public static boolean transfer(Entity entity, Entity holder, Entity newHolder) {
|
||||
return getLeashData(entity).map(data -> data.transferLeash(holder, newHolder)).orElse(false);
|
||||
}
|
||||
|
||||
public static boolean transfer(Entity entity, Entity holder, Entity newHolder, String reserved) {
|
||||
return getLeashData(entity).map(data -> data.transferLeash(holder, newHolder, reserved)).orElse(false);
|
||||
}
|
||||
|
||||
public static boolean transfer(Entity entity, UUID holderUUID, Entity newHolder) {
|
||||
return getLeashData(entity).map(data -> data.transferLeash(holderUUID, newHolder)).orElse(false);
|
||||
}
|
||||
|
||||
public static boolean transfer(Entity entity, UUID holderUUID, Entity newHolder, String reserved) {
|
||||
return getLeashData(entity).map(data -> data.transferLeash(holderUUID, newHolder, reserved)).orElse(false);
|
||||
}
|
||||
|
||||
public static boolean transfer(Entity entity, BlockPos knotPos, Entity newHolder) {
|
||||
return getLeashData(entity).map(data -> data.transferLeash(knotPos, newHolder)).orElse(false);
|
||||
}
|
||||
|
||||
public static boolean transfer(Entity entity, BlockPos knotPos, Entity newHolder, String reserved) {
|
||||
return getLeashData(entity).map(data -> data.transferLeash(knotPos, newHolder, reserved)).orElse(false);
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 查询操作 API ====================
|
||||
|
||||
public static final class QueryOperations {
|
||||
private QueryOperations() {}
|
||||
|
||||
public static boolean hasLeash(Entity entity) {
|
||||
return getLeashData(entity).map(ILeashData::hasLeash).orElse(false);
|
||||
}
|
||||
|
||||
public static boolean hasKnotLeash(Entity entity) {
|
||||
return getLeashData(entity).map(ILeashData::hasKnotLeash).orElse(false);
|
||||
}
|
||||
|
||||
public static boolean hasHolderLeash(Entity entity) {
|
||||
return getLeashData(entity).map(ILeashData::hasHolderLeash).orElse(false);
|
||||
}
|
||||
|
||||
public static Collection<ILeashData.LeashInfo> getAllLeashes(Entity entity) {
|
||||
return getLeashData(entity).map(ILeashData::getAllLeashes).orElse(Collections.emptyList());
|
||||
}
|
||||
|
||||
public static boolean isLeashedBy(Entity entity, Entity holder) {
|
||||
return getLeashData(entity).map(data -> data.isLeashedBy(holder)).orElse(false);
|
||||
}
|
||||
|
||||
public static boolean isLeashedBy(Entity entity, UUID holderUUID) {
|
||||
return getLeashData(entity).map(data -> data.isLeashedBy(holderUUID)).orElse(false);
|
||||
}
|
||||
|
||||
public static boolean isLeashedBy(Entity entity, BlockPos knotPos) {
|
||||
return getLeashData(entity).map(data -> data.isLeashedBy(knotPos)).orElse(false);
|
||||
}
|
||||
|
||||
public static boolean isInDelayedLeash(Entity entity, UUID holderUUID) {
|
||||
return getLeashData(entity).map(data -> data.isInDelayedLeash(holderUUID)).orElse(false);
|
||||
}
|
||||
|
||||
public static Optional<ILeashData.LeashInfo> getLeashInfo(Entity entity, Entity holder) {
|
||||
return getLeashData(entity).flatMap(data -> data.getLeashInfo(holder));
|
||||
}
|
||||
|
||||
public static Optional<ILeashData.LeashInfo> getLeashInfo(Entity entity, UUID holderUUID) {
|
||||
return getLeashData(entity).flatMap(data -> data.getLeashInfo(holderUUID));
|
||||
}
|
||||
|
||||
public static Optional<ILeashData.LeashInfo> getLeashInfo(Entity entity, BlockPos knotPos) {
|
||||
return getLeashData(entity).flatMap(data -> data.getLeashInfo(knotPos));
|
||||
}
|
||||
|
||||
public static boolean canBeLeashed(Entity entity) {
|
||||
return getLeashData(entity).map(ILeashData::canBeLeashed).orElse(false);
|
||||
}
|
||||
|
||||
public static boolean canBeAttachedTo(Entity entity, Entity target) {
|
||||
return getLeashData(entity).map(data -> data.canBeAttachedTo(target)).orElse(false);
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 占用和同步 API ====================
|
||||
|
||||
public static final class ManagementOperations {
|
||||
private ManagementOperations() {}
|
||||
|
||||
public static Optional<UUID> occupyLeash(Entity entity) {
|
||||
return getLeashData(entity).flatMap(ILeashData::occupyLeash);
|
||||
}
|
||||
|
||||
public static void markForSync(Entity entity) {
|
||||
getLeashData(entity).ifPresent(ILeashData::markForSync);
|
||||
}
|
||||
|
||||
public static void immediateSync(Entity entity) {
|
||||
getLeashData(entity).ifPresent(ILeashData::immediateSync);
|
||||
}
|
||||
|
||||
public static void checkSync(Entity entity) {
|
||||
getLeashData(entity).ifPresent(ILeashData::checkSync);
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 工具方法 ====================
|
||||
|
||||
public static final class Utils {
|
||||
private Utils() {}
|
||||
|
||||
public static boolean hasLeashData(Entity entity) {
|
||||
return getLeashData(entity).isPresent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,273 @@
|
|||
/*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.r3944realms.superleadrope.util.capability;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import top.r3944realms.superleadrope.CommonEventHandler;
|
||||
import top.r3944realms.superleadrope.content.capability.CapabilityHandler;
|
||||
import top.r3944realms.superleadrope.content.capability.inter.ILeashState;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public final class LeashStateAPI {
|
||||
|
||||
private LeashStateAPI() {
|
||||
} // 防止实例化
|
||||
|
||||
public static Optional<ILeashState> getLeashState(@NotNull Entity entity) {
|
||||
Objects.requireNonNull(entity, "Entity cannot be null");
|
||||
return entity.getCapability(CapabilityHandler.LEASH_STATE_CAP).resolve();
|
||||
}
|
||||
|
||||
// ==================== 查询操作 ====================
|
||||
|
||||
public static final class Query {
|
||||
private Query() {
|
||||
}
|
||||
|
||||
public static Map<UUID, ILeashState.LeashState> getAllUUIDStates(Entity entity) {
|
||||
return getLeashState(entity)
|
||||
.map(ILeashState::getHolderLeashStates)
|
||||
.orElse(Map.of());
|
||||
}
|
||||
|
||||
public static Map<BlockPos, ILeashState.LeashState> getAllBlockPosStates(Entity entity) {
|
||||
return getLeashState(entity)
|
||||
.map(ILeashState::getKnotLeashStates)
|
||||
.orElse(Map.of());
|
||||
}
|
||||
|
||||
public static Optional<ILeashState.LeashState> getState(Entity entity, Entity holder) {
|
||||
return getLeashState(entity).flatMap(state -> state.getLeashState(holder));
|
||||
}
|
||||
|
||||
public static Optional<ILeashState.LeashState> getState(Entity entity, UUID holderUUID) {
|
||||
return getLeashState(entity).flatMap(state -> state.getLeashState(holderUUID));
|
||||
}
|
||||
|
||||
public static Optional<ILeashState.LeashState> getState(Entity entity, BlockPos knotPos) {
|
||||
return getLeashState(entity).flatMap(state -> state.getLeashState(knotPos));
|
||||
}
|
||||
|
||||
public static boolean hasState(Entity entity) {
|
||||
return getLeashState(entity).isPresent();
|
||||
}
|
||||
|
||||
public static boolean hasStateFor(Entity entity, Entity holder) {
|
||||
return getState(entity, holder).isPresent();
|
||||
}
|
||||
|
||||
public static boolean hasStateFor(Entity entity, UUID holderUUID) {
|
||||
return getState(entity, holderUUID).isPresent();
|
||||
}
|
||||
|
||||
public static boolean hasStateFor(Entity entity, BlockPos knotPos) {
|
||||
return getState(entity, knotPos).isPresent();
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 偏移量操作 ====================
|
||||
|
||||
public static final class Offset {
|
||||
private Offset() {
|
||||
}
|
||||
|
||||
// ---------------------- 重置操作 ----------------------
|
||||
public static void resetAll(Entity entity) {
|
||||
getLeashState(entity).ifPresent(ILeashState::resetAllLeashHolderLocationsOffset);
|
||||
}
|
||||
|
||||
public static void resetFor(Entity entity, Entity holder) {
|
||||
getLeashState(entity).ifPresent(state -> state.resetLeashHolderLocationOffset(holder));
|
||||
}
|
||||
|
||||
public static void resetFor(Entity entity, UUID holderUUID) {
|
||||
getLeashState(entity).ifPresent(state -> state.resetLeashHolderLocationOffset(holderUUID));
|
||||
}
|
||||
|
||||
public static void resetFor(Entity entity, BlockPos knotPos) {
|
||||
getLeashState(entity).ifPresent(state -> state.resetLeashHolderLocationOffset(knotPos));
|
||||
}
|
||||
|
||||
// ---------------------- 设置操作 ----------------------
|
||||
public static void setFor(Entity entity, Entity holder, Vec3 offset) {
|
||||
getLeashState(entity).ifPresent(state -> state.setLeashHolderLocationOffset(holder, offset));
|
||||
}
|
||||
|
||||
public static void setFor(Entity entity, UUID holderUUID, Vec3 offset) {
|
||||
getLeashState(entity).ifPresent(state -> state.setLeashHolderLocationOffset(holderUUID, offset));
|
||||
}
|
||||
|
||||
public static void setFor(Entity entity, BlockPos knotPos, Vec3 offset) {
|
||||
getLeashState(entity).ifPresent(state -> state.setLeashHolderLocationOffset(knotPos, offset));
|
||||
}
|
||||
|
||||
// ---------------------- 添加操作 ----------------------
|
||||
public static void addTo(Entity entity, Entity holder, Vec3 offset) {
|
||||
getLeashState(entity).ifPresent(state -> state.addLeashHolderLocationOffset(holder, offset));
|
||||
}
|
||||
|
||||
public static void addTo(Entity entity, UUID holderUUID, Vec3 offset) {
|
||||
getLeashState(entity).ifPresent(state -> state.addLeashHolderLocationOffset(holderUUID, offset));
|
||||
}
|
||||
|
||||
public static void addTo(Entity entity, BlockPos knotPos, Vec3 offset) {
|
||||
getLeashState(entity).ifPresent(state -> state.addLeashHolderLocationOffset(knotPos, offset));
|
||||
}
|
||||
|
||||
// ---------------------- 移除操作 ----------------------
|
||||
public static void removeFor(Entity entity, Entity holder) {
|
||||
getLeashState(entity).ifPresent(state -> state.removeLeashHolderLocationOffset(holder));
|
||||
}
|
||||
|
||||
public static void removeFor(Entity entity, UUID holderUUID) {
|
||||
getLeashState(entity).ifPresent(state -> state.removeLeashHolderLocationOffset(holderUUID));
|
||||
}
|
||||
|
||||
public static void removeFor(Entity entity, BlockPos knotPos) {
|
||||
getLeashState(entity).ifPresent(state -> state.removeLeashHolderLocationOffset(knotPos));
|
||||
}
|
||||
|
||||
public static void removeAll(Entity entity) {
|
||||
getLeashState(entity).ifPresent(ILeashState::removeAllLeashHolderLocationOffset);
|
||||
}
|
||||
|
||||
public static void removeAllUUIDs(Entity entity) {
|
||||
getLeashState(entity).ifPresent(ILeashState::removeAllLeashHolderUUIDLocationOffset);
|
||||
}
|
||||
|
||||
public static void removeAllBlockPoses(Entity entity) {
|
||||
getLeashState(entity).ifPresent(ILeashState::removeAllLeashHolderBlockPosLocationOffset);
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 应用实体偏移量操作 ====================
|
||||
|
||||
public static final class ApplyEntity {
|
||||
private ApplyEntity() {
|
||||
}
|
||||
|
||||
public static Optional<Vec3> getOffset(Entity entity) {
|
||||
return getLeashState(entity).flatMap(ILeashState::getLeashApplyEntityLocationOffset);
|
||||
}
|
||||
|
||||
public static Vec3 getDefaultOffset(Entity entity) {
|
||||
return getLeashState(entity)
|
||||
.map(ILeashState::getDefaultLeashApplyEntityLocationOffset)
|
||||
.orElse(Vec3.ZERO);
|
||||
}
|
||||
|
||||
public static void resetAll(Entity entity) {
|
||||
getLeashState(entity).ifPresent(ILeashState::resetAllLeashApplyEntityLocationsOffset);
|
||||
}
|
||||
|
||||
public static void remove(Entity entity) {
|
||||
getLeashState(entity).ifPresent(ILeashState::removeLeashApplyEntityLocationOffset);
|
||||
}
|
||||
|
||||
public static void set(Entity entity, Vec3 offset) {
|
||||
getLeashState(entity).ifPresent(state -> state.setLeashApplyEntityLocationOffset(offset));
|
||||
}
|
||||
|
||||
public static void add(Entity entity, Vec3 offset) {
|
||||
getLeashState(entity).ifPresent(state -> state.addLeashApplyEntityLocationOffset(offset));
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 同步操作 ====================
|
||||
|
||||
public static final class Sync {
|
||||
private Sync() {
|
||||
}
|
||||
|
||||
public static void mark(Entity entity) {
|
||||
getLeashState(entity).ifPresent(ILeashState::markForSync);
|
||||
}
|
||||
|
||||
public static void immediate(Entity entity) {
|
||||
getLeashState(entity).ifPresent(ILeashState::immediateSync);
|
||||
}
|
||||
|
||||
public static void check(Entity entity) {
|
||||
getLeashState(entity).ifPresent(ILeashState::checkSync);
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 高级操作 ====================
|
||||
|
||||
public static final class Operations {
|
||||
private Operations() {
|
||||
}
|
||||
|
||||
public static void attach(Entity leashed, Entity holder) {
|
||||
getLeashState(leashed).ifPresent(state ->
|
||||
state.addLeashHolderLocationOffset(holder,
|
||||
CommonEventHandler.leashConfigManager.getEntityOffset(holder))
|
||||
);
|
||||
}
|
||||
|
||||
public static void detach(Entity leashed, Entity holder) {
|
||||
Offset.removeFor(leashed, holder);
|
||||
}
|
||||
|
||||
public static void detach(Entity leashed, UUID holderUUID) {
|
||||
Offset.removeFor(leashed, holderUUID);
|
||||
}
|
||||
|
||||
public static void detach(Entity leashed, BlockPos knotPos) {
|
||||
Offset.removeFor(leashed, knotPos);
|
||||
}
|
||||
|
||||
public static void transfer(Entity leashed, Entity oldHolder, Entity newHolder) {
|
||||
getLeashState(leashed).ifPresent(state -> {
|
||||
state.removeLeashHolderLocationOffset(oldHolder);
|
||||
state.addLeashHolderLocationOffset(newHolder,
|
||||
CommonEventHandler.leashConfigManager.getEntityOffset(newHolder));
|
||||
});
|
||||
}
|
||||
|
||||
public static void transfer(Entity leashed, UUID oldHolderUUID, Entity newHolder) {
|
||||
getLeashState(leashed).ifPresent(state -> {
|
||||
state.removeLeashHolderLocationOffset(oldHolderUUID);
|
||||
state.addLeashHolderLocationOffset(newHolder,
|
||||
CommonEventHandler.leashConfigManager.getEntityOffset(newHolder));
|
||||
});
|
||||
}
|
||||
|
||||
public static void transfer(Entity leashed, BlockPos oldKnotPos, Entity newHolder) {
|
||||
getLeashState(leashed).ifPresent(state -> {
|
||||
state.removeLeashHolderLocationOffset(oldKnotPos);
|
||||
state.addLeashHolderLocationOffset(newHolder,
|
||||
CommonEventHandler.leashConfigManager.getEntityOffset(newHolder));
|
||||
});
|
||||
}
|
||||
|
||||
public static void copy(Entity source, Entity target) {
|
||||
getLeashState(source).ifPresent(sourceState ->
|
||||
getLeashState(target).ifPresent(targetState ->
|
||||
targetState.copy(sourceState, target)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
/*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.r3944realms.superleadrope.util.capability;
|
||||
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import top.r3944realms.superleadrope.content.capability.CapabilityHandler;
|
||||
import top.r3944realms.superleadrope.content.capability.inter.ILeashData;
|
||||
import top.r3944realms.superleadrope.content.capability.inter.ILeashState;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
public class LeashUtil {
|
||||
public static Optional<ILeashData> getLeashData(@NotNull Entity entity) {
|
||||
Objects.requireNonNull(entity, "Entity cannot be null");
|
||||
return entity.getCapability(CapabilityHandler.LEASH_DATA_CAP).resolve();
|
||||
}
|
||||
public static Optional<ILeashState> getLeashState(@NotNull Entity entity) {
|
||||
Objects.requireNonNull(entity, "Entity cannot be null");
|
||||
return entity.getCapability(CapabilityHandler.LEASH_STATE_CAP).resolve();
|
||||
}
|
||||
}
|
||||
|
|
@ -16,6 +16,7 @@
|
|||
package top.r3944realms.superleadrope.util.riding;
|
||||
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import top.r3944realms.superleadrope.CommonEventHandler;
|
||||
import top.r3944realms.superleadrope.SuperLeadRope;
|
||||
import top.r3944realms.superleadrope.core.exception.RidingCycleException;
|
||||
import top.r3944realms.superleadrope.util.model.RidingRelationship;
|
||||
|
|
@ -55,7 +56,7 @@ public class RidingApplier {
|
|||
if (entity == null) continue;
|
||||
|
||||
// ---------- 白名单保护 ----------
|
||||
if (!RidingValidator.isInWhitelist(entity.getType())) {
|
||||
if (!CommonEventHandler.leashConfigManager.isEntityTeleportAllowed(entity)) {
|
||||
// 不在白名单,跳过本节点,但保留其乘客挂回上层
|
||||
if (vehicle != null) {
|
||||
// 将当前节点的乘客挂回上层载具
|
||||
|
|
@ -75,7 +76,7 @@ public class RidingApplier {
|
|||
}
|
||||
|
||||
// 如果有指定的载具,尝试上车
|
||||
if (vehicle != null && RidingValidator.isInWhitelist(vehicle.getType())) {
|
||||
if (vehicle != null && CommonEventHandler.leashConfigManager.isEntityTeleportAllowed(vehicle)) {
|
||||
if (RidingValidator.wouldCreateCycle(entity, vehicle)) {
|
||||
throw new RidingCycleException(entityId, vehicleId);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ package top.r3944realms.superleadrope.util.riding;
|
|||
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import top.r3944realms.superleadrope.CommonEventHandler;
|
||||
import top.r3944realms.superleadrope.core.exception.RidingCycleException;
|
||||
import top.r3944realms.superleadrope.core.util.ImmutablePair;
|
||||
import top.r3944realms.superleadrope.util.model.RidingRelationship;
|
||||
|
|
@ -72,7 +73,7 @@ public class RidingSaver {
|
|||
processedEntities.add(passengerId);
|
||||
|
||||
// ✅ 校验白名单
|
||||
if (!RidingValidator.isInWhitelist(passenger.getType())) {
|
||||
if (!CommonEventHandler.leashConfigManager.isEntityTeleportAllowed(passenger.getType())) {
|
||||
// ❌ 不在白名单,直接截断
|
||||
continue;
|
||||
}
|
||||
|
|
@ -105,7 +106,7 @@ public class RidingSaver {
|
|||
if (relationship == null) return null;
|
||||
|
||||
// 如果当前根节点在白名单,则直接处理子节点
|
||||
if (RidingValidator.isInWhitelist(Objects.requireNonNull(getEntityType(relationship.getEntityId())))) {
|
||||
if (CommonEventHandler.leashConfigManager.isEntityTeleportAllowed(Objects.requireNonNull(getEntityType(relationship.getEntityId())))) {
|
||||
RidingRelationship filtered = new RidingRelationship();
|
||||
filtered.setEntityId(relationship.getEntityId());
|
||||
filtered.setVehicleId(relationship.getVehicleId());
|
||||
|
|
@ -114,7 +115,7 @@ public class RidingSaver {
|
|||
} else {
|
||||
// 根节点不在白名单,尝试找到合法的子节点作为新的根
|
||||
for (RidingRelationship child : relationship.getPassengers()) {
|
||||
if (RidingValidator.isInWhitelist(Objects.requireNonNull(getEntityType(child.getEntityId())))) {
|
||||
if (CommonEventHandler.leashConfigManager.isEntityTeleportAllowed(Objects.requireNonNull(getEntityType(child.getEntityId())))) {
|
||||
// 设置父节点为当前节点的父(倒二叉逻辑)
|
||||
RidingRelationship newRoot = new RidingRelationship();
|
||||
newRoot.setEntityId(child.getEntityId());
|
||||
|
|
|
|||
|
|
@ -15,52 +15,12 @@
|
|||
|
||||
package top.r3944realms.superleadrope.util.riding;
|
||||
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.tags.TagKey;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import top.r3944realms.superleadrope.config.LeashCommonConfig;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.Queue;
|
||||
|
||||
public class RidingValidator {
|
||||
/**
|
||||
* 是否在配置白名单里
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public static boolean isInWhitelist(EntityType<?> type) {
|
||||
String key = type.builtInRegistryHolder().key().location().toString();
|
||||
String modid = key.split(":")[0];
|
||||
|
||||
for (String entry : LeashCommonConfig.COMMON.teleportWhitelist.get()) {
|
||||
if (entry.startsWith("#")) {
|
||||
String body = entry.substring(1);
|
||||
|
||||
// Case 1: #modid → allow all entities from this mod
|
||||
if (!body.contains(":")) {
|
||||
if (modid.equals(body)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// Case 2: #modid:tag_name → allow all entities under this tag
|
||||
else {
|
||||
ResourceLocation tagId = new ResourceLocation(body);
|
||||
TagKey<EntityType<?>> tag = TagKey.create(Registries.ENTITY_TYPE, tagId);
|
||||
if (type.builtInRegistryHolder().is(tag)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Case 3: modid:entity_name → allow a specific entity
|
||||
if (entry.equals(key)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* 检查骑乘是否会产生循环引用
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import net.minecraft.world.entity.ai.goal.Goal;
|
|||
import net.minecraft.world.entity.animal.Animal;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import top.r3944realms.superleadrope.CommonEventHandler;
|
||||
import top.r3944realms.superleadrope.network.NetworkHandler;
|
||||
import top.r3944realms.superleadrope.network.toClient.UpdatePlayerMovementPacket;
|
||||
|
||||
|
|
@ -37,7 +38,7 @@ public class RindingLeash {
|
|||
|
||||
Entity current = root;
|
||||
while (current != null) {
|
||||
if (RidingValidator.isInWhitelist(current.getType())) {
|
||||
if (CommonEventHandler.leashConfigManager.isEntityTeleportAllowed(current.getType())) {
|
||||
return current; // 找到白名单载具
|
||||
}
|
||||
current = current.getVehicle();
|
||||
|
|
@ -60,7 +61,7 @@ public class RindingLeash {
|
|||
|
||||
Entity current = root;
|
||||
while (current != null) {
|
||||
if (RidingValidator.isInWhitelist(current.getType())) {
|
||||
if (CommonEventHandler.leashConfigManager.isEntityTeleportAllowed(current.getType())) {
|
||||
return current; // 找到白名单载具
|
||||
}
|
||||
current = current.getVehicle();
|
||||
|
|
|
|||
|
|
@ -1 +1,3 @@
|
|||
{}
|
||||
{
|
||||
"morsb_patch":"coremods/morsb_patch.js"
|
||||
}
|
||||
62
src/main/resources/coremods/morsb_patch.js
Normal file
62
src/main/resources/coremods/morsb_patch.js
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
var Opcodes = Java.type("org.objectweb.asm.Opcodes");
|
||||
var ASMAPI = Java.type("net.minecraftforge.coremod.api.ASMAPI");
|
||||
var VarInsnNode = Java.type("org.objectweb.asm.tree.VarInsnNode");
|
||||
var MethodInsnNode = Java.type("org.objectweb.asm.tree.MethodInsnNode");
|
||||
var InsnNode = Java.type("org.objectweb.asm.tree.InsnNode");
|
||||
|
||||
function initializeCoreMod() {
|
||||
return {
|
||||
"leash_render_patch": {
|
||||
"target": {
|
||||
"type": "METHOD",
|
||||
"class": "net.minecraft.client.renderer.entity.MobRenderer",
|
||||
"methodName": "m_5523_",
|
||||
"methodDesc": "(Lnet/minecraft/world/entity/Mob;Lnet/minecraft/client/renderer/culling/Frustum;DDD)Z"
|
||||
},
|
||||
"transformer": function(method) {
|
||||
var insns = method.instructions;
|
||||
|
||||
for (var i = 0; i < insns.size(); i++) {
|
||||
var insn = insns.get(i);
|
||||
if (insn.getOpcode && insn.getOpcode() === Opcodes.ICONST_0) {
|
||||
var next = insns.get(i + 1);
|
||||
if (next && next.getOpcode() === Opcodes.IRETURN) {
|
||||
// 插入调试日志和方法调用
|
||||
insns.insertBefore(insn, ASMAPI.listOf(
|
||||
new VarInsnNode(Opcodes.ALOAD, 1), // Mob
|
||||
new VarInsnNode(Opcodes.ALOAD, 2), // Frustum
|
||||
ASMAPI.buildMethodCall(
|
||||
'your/package/LeashRenderHook',
|
||||
null,
|
||||
ASMAPI.MethodType.STATIC,
|
||||
'shouldRenderExtraWithLog',
|
||||
'(Lnet/minecraft/world/entity/Mob;Lnet/minecraft/client/renderer/culling/Frustum;)Z',
|
||||
ASMAPI.MethodCallMode.STATIC
|
||||
)
|
||||
));
|
||||
// 移除原来的 ICONST_0
|
||||
insns.remove(insn);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return method;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -13,12 +13,10 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.r3944realms.superleadrope.util.coremods;
|
||||
package top.r3944realms.superleadropetest;
|
||||
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
import top.r3944realms.superleadrope.util.capability.LeashDataAPI;
|
||||
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
public class InvokerMethod {
|
||||
public class Placeholder {
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.r3944realms.superleadropetest.asm;
|
||||
|
||||
import org.objectweb.asm.*;
|
||||
import org.objectweb.asm.tree.*;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
|
||||
public class ASMTest {
|
||||
public static void main(String[] args) throws Exception {
|
||||
Path jarPath = Paths.get("G:\\WhimsyMod\\SuperLeadRope\\build\\moddev\\artifacts\\forge-1.20.1-47.3.4-merged.jar");
|
||||
try (JarFile jar = new JarFile(jarPath.toFile())) {
|
||||
JarEntry entry = jar.getJarEntry("net/minecraft/client/renderer/entity/MobRenderer.class");
|
||||
try (InputStream in = jar.getInputStream(entry)) {
|
||||
byte[] classBytes = in.readAllBytes();
|
||||
|
||||
ClassReader reader = new ClassReader(classBytes);
|
||||
ClassNode classNode = new ClassNode();
|
||||
reader.accept(classNode, 0);
|
||||
|
||||
System.out.println("Methods in MobRenderer:");
|
||||
classNode.methods.forEach(m -> System.out.println(" - " + m.name + m.desc));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.r3944realms.superleadropetest.asm;
|
||||
|
||||
import org.objectweb.asm.*;
|
||||
import org.objectweb.asm.tree.*;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class CoreModTransformerMemoryTest {
|
||||
|
||||
// 1️⃣ 静态类替代 Minecraft 类
|
||||
public static class TestMob {}
|
||||
public static class TestFrustum {}
|
||||
|
||||
// Hook 模拟
|
||||
public static class LeashRenderHook {
|
||||
public static boolean shouldRenderExtra(TestMob mob, TestFrustum frustum) {
|
||||
System.out.println("[Hook] shouldRenderExtra called with Mob=" + mob + ", Frustum=" + frustum);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
// 2️⃣ 构造假的 shouldRender 方法
|
||||
MethodNode methodNode = new MethodNode(Opcodes.ASM9, Opcodes.ACC_PUBLIC, "shouldRender",
|
||||
"(Ltop/r3944realms/superleadrope/core/test/CoreModTransformerMemoryTest$TestMob;" +
|
||||
"Ltop/r3944realms/superleadrope/core/test/CoreModTransformerMemoryTest$TestFrustum;)Z",
|
||||
null, null);
|
||||
|
||||
InsnList insns = methodNode.instructions;
|
||||
insns.clear(); // 清空原有指令
|
||||
// 插入 Hook 调用
|
||||
InsnList patch = new InsnList();
|
||||
patch.add(new VarInsnNode(Opcodes.ALOAD, 1));
|
||||
patch.add(new VarInsnNode(Opcodes.ALOAD, 2));
|
||||
patch.add(new MethodInsnNode(
|
||||
Opcodes.INVOKESTATIC,
|
||||
"top/r3944realms/superleadrope/core/test/CoreModTransformerMemoryTest$LeashRenderHook",
|
||||
"shouldRenderExtra",
|
||||
"(Ltop/r3944realms/superleadrope/core/test/CoreModTransformerMemoryTest$TestMob;" +
|
||||
"Ltop/r3944realms/superleadrope/core/test/CoreModTransformerMemoryTest$TestFrustum;)Z",
|
||||
false
|
||||
));
|
||||
patch.add(new InsnNode(Opcodes.IRETURN));
|
||||
insns.add(patch);
|
||||
|
||||
// 4️⃣ 创建假的 ClassNode
|
||||
ClassNode classNode = new ClassNode();
|
||||
classNode.version = Opcodes.V1_8;
|
||||
classNode.access = Opcodes.ACC_PUBLIC;
|
||||
classNode.name = "FakeMobRenderer";
|
||||
classNode.superName = "java/lang/Object";
|
||||
classNode.methods.add(methodNode);
|
||||
|
||||
// 添加默认构造器 <init>
|
||||
MethodNode constructor = new MethodNode(Opcodes.ASM9, Opcodes.ACC_PUBLIC, "<init>", "()V", null, null);
|
||||
InsnList initInsns = constructor.instructions;
|
||||
initInsns.add(new VarInsnNode(Opcodes.ALOAD, 0)); // this
|
||||
initInsns.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false));
|
||||
initInsns.add(new InsnNode(Opcodes.RETURN));
|
||||
constructor.maxStack = 1;
|
||||
constructor.maxLocals = 1;
|
||||
classNode.methods.add(constructor);
|
||||
|
||||
// 5️⃣ 写入字节码
|
||||
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
|
||||
classNode.accept(cw);
|
||||
byte[] bytes = cw.toByteArray();
|
||||
|
||||
// 6️⃣ 自定义 ClassLoader,直接返回 Class 对象
|
||||
ClassLoader loader = new ClassLoader(CoreModTransformerMemoryTest.class.getClassLoader()) {
|
||||
public Class<?> defineClassFromBytes(byte[] b) {
|
||||
return defineClass("FakeMobRenderer", b, 0, b.length);
|
||||
}
|
||||
};
|
||||
Class<?> clazz = ((Class<?>) loader.getClass().getMethod("defineClassFromBytes", byte[].class).invoke(loader, bytes));
|
||||
|
||||
// 7️⃣ 实例化
|
||||
Object rendererInstance = clazz.getDeclaredConstructor().newInstance();
|
||||
Method shouldRender = clazz.getMethod("shouldRender", TestMob.class, TestFrustum.class);
|
||||
|
||||
boolean result = (boolean) shouldRender.invoke(rendererInstance, new TestMob(), new TestFrustum());
|
||||
System.out.println("[Test] Result of shouldRender (after patch): " + result);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,132 @@
|
|||
/*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.r3944realms.superleadropetest.config;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class OffsetReadTest {
|
||||
private static final Pattern OFFSET_PATTERN = Pattern.compile(
|
||||
"(?i)(?:vec3|vec3d|vector3|offset)\\s*\\(\\s*([-+]?[0-9]*\\.?[0-9]+)\\s*,\\s*([-+]?[0-9]*\\.?[0-9]+)\\s*,\\s*([-+]?[0-9]*\\.?[0-9]+)\\s*\\)\\s*:\\s*\\[\\s*([^]]+?)\\s*]\\s*"
|
||||
);
|
||||
private static boolean isValidEntityRefFormat(String s) {
|
||||
if (s.startsWith("#")) {
|
||||
String body = s.substring(1);
|
||||
// 支持 #modid (整个模组)或 #modid:tag_name (标签)
|
||||
return body.matches("[a-z0-9_]+(:[a-z0-9_/]+)?");
|
||||
}
|
||||
// 普通实体 ID: modid:entity_id
|
||||
return s.matches("[a-z0-9_]+:[a-z0-9_/]+");
|
||||
}
|
||||
private static boolean isValidOffsetRefFormat(String s) {
|
||||
// 匹配格式: function_name(x,y,z) : [entity_list]
|
||||
Matcher matcher = OFFSET_PATTERN.matcher(s);
|
||||
if (!matcher.matches()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查坐标值是否有效
|
||||
try {
|
||||
// 组索引 现在坐标在组1、2、3,实体列表在组4
|
||||
Double.parseDouble(matcher.group(1));
|
||||
Double.parseDouble(matcher.group(2));
|
||||
Double.parseDouble(matcher.group(3));
|
||||
|
||||
// 检查实体列表格式
|
||||
String entityList = matcher.group(4);
|
||||
String[] entities = entityList.split(",");
|
||||
for (String entity : entities) {
|
||||
if (!isValidEntityRefFormat(entity.trim())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} catch (NumberFormatException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
private static void testCase(String testString, boolean expected) {
|
||||
boolean result = isValidOffsetRefFormat(testString);
|
||||
String status = result == expected ? "✓ PASS" : "✗ FAIL";
|
||||
System.out.printf("%s: %s -> %s (expected: %s)%n",
|
||||
status, testString, result, expected);
|
||||
|
||||
if (result) {
|
||||
Matcher matcher = OFFSET_PATTERN.matcher(testString);
|
||||
if (matcher.matches()) {
|
||||
System.out.printf(" 解析结果: X=%s, Y=%s, Z=%s, Entities=%s%n%n",
|
||||
matcher.group(1), matcher.group(2), matcher.group(3), matcher.group(4));
|
||||
}
|
||||
} else {
|
||||
System.out.println();
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println("=== 有效测试用例 ===");
|
||||
|
||||
// 基本格式测试
|
||||
testCase("vec3(0,0.2,0) : [minecraft:bee]", true);
|
||||
testCase("Vec3(0, 0.2, 0) : [minecraft:bee]", true);
|
||||
testCase("VEC3(0,0.2,0) : [minecraft:bee]", true);
|
||||
|
||||
// 不同函数名测试
|
||||
testCase("vec3d(0,0.2,0) : [minecraft:bee]", true);
|
||||
testCase("Vector3(0,0.2,0) : [minecraft:bee]", true);
|
||||
testCase("offset(0,0.2,0) : [minecraft:bee]", true);
|
||||
|
||||
// 空格兼容测试
|
||||
testCase("vec3( 0 , 0.2 , 0 ) : [ minecraft:bee ]", true);
|
||||
testCase("vec3(0,0.2,0) : [minecraft:bee] ", true);
|
||||
testCase("vec3(0, 0.2, 0) : [ minecraft:bee ] ", true);
|
||||
|
||||
// 多实体测试
|
||||
testCase("vec3(0,1.0,0) : [minecraft:horse, minecraft:donkey]", true);
|
||||
testCase("vec3(0,0.5,0) : [#minecraft:boats, #minecraft:minecarts]", true);
|
||||
|
||||
// 标签和模组测试
|
||||
testCase("vec3(0,0.5,0) : [#minecraft:boats]", true);
|
||||
testCase("vec3(0,0.3,0) : [#minecraft]", true);
|
||||
|
||||
// 负数和小数测试
|
||||
testCase("vec3(-1, 1.5, 2.8) : [minecraft:horse]", true);
|
||||
testCase("vec3(0.0, -0.5, 1.23) : [minecraft:bee]", true);
|
||||
|
||||
System.out.println("=== 无效测试用例 ===");
|
||||
|
||||
// 错误函数名
|
||||
testCase("vector(0,0.2,0) : [minecraft:bee]", false);
|
||||
testCase("pos(0,0.2,0) : [minecraft:bee]", false);
|
||||
|
||||
// 格式错误
|
||||
testCase("vec3(0,0.2,0) : minecraft:bee]", false); // 缺少左括号
|
||||
testCase("vec3(0,0.2,0) : [minecraft:bee", false); // 缺少右括号
|
||||
testCase("vec3(0,0.2) : [minecraft:bee]", false); // 缺少Z坐标
|
||||
|
||||
// 无效坐标
|
||||
testCase("vec3(a,b,c) : [minecraft:bee]", false);
|
||||
testCase("vec3(0,0.2,invalid) : [minecraft:bee]", false);
|
||||
|
||||
// 无效实体引用
|
||||
testCase("vec3(0,0.2,0) : [invalid_entity]", false);
|
||||
testCase("vec3(0,0.2,0) : [minecraft:bee, invalid]", false);
|
||||
|
||||
// 缺少冒号
|
||||
testCase("vec3(0,0.2,0) [minecraft:bee]", false);
|
||||
|
||||
System.out.println("=== 测试完成 ===");
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user