diff --git a/build.gradle b/build.gradle index 0a7e638..594d1c9 100644 --- a/build.gradle +++ b/build.gradle @@ -311,14 +311,7 @@ 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 { diff --git a/src/main/java/top/r3944realms/superleadrope/client/renderer/resolver/SuperLeashStateResolver.java b/src/main/java/top/r3944realms/superleadrope/client/renderer/resolver/SuperLeashStateResolver.java index 5ea8f46..1c7af4d 100644 --- a/src/main/java/top/r3944realms/superleadrope/client/renderer/resolver/SuperLeashStateResolver.java +++ b/src/main/java/top/r3944realms/superleadrope/client/renderer/resolver/SuperLeashStateResolver.java @@ -15,190 +15,296 @@ package top.r3944realms.superleadrope.client.renderer.resolver; +import net.minecraft.client.CameraType; +import net.minecraft.client.Minecraft; +import net.minecraft.util.Mth; import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.HumanoidArm; +import net.minecraft.world.entity.player.Player; 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.impi.LeashDataImpl; import top.r3944realms.superleadrope.content.capability.inter.ILeashData; +import top.r3944realms.superleadrope.content.capability.inter.ILeashState; import top.r3944realms.superleadrope.content.entity.SuperLeashKnotEntity; import top.r3944realms.superleadrope.util.capability.LeashStateAPI; import java.util.*; -import java.util.concurrent.atomic.AtomicReference; //TODO: 未来实现更高级的渲染 public class SuperLeashStateResolver { + private static final float MAX_TENSION = 1.5f; private static final float THICKNESS_BASE = 0.1f; private static final float THICKNESS_TENSION = 0.15f; - // 摆动计算缓存(存储上一帧数据) + /** 上一帧缓存 */ private static final Map frameCacheMap = new HashMap<>(); - private record FrameCache( - Vec3 lastStartPos, - Vec3 lastEndPos, - float lastSwingAngle, - float lastSwingSpeed - ) {} + private record FrameCache(Vec3 lastStartPos, Vec3 lastEndPos, float lastSwingAngle, float lastSwingSpeed) {} - public static Optional resolve( - Entity holder, - Entity leashedEntity, - ILeashData.LeashInfo leashInfo, - float partialTicks) { + private record SwingDynamics(float angle, float speed) {} - if (holder == null || leashedEntity == null) { - return Optional.empty(); + /* ------------------------ 主解析方法 ------------------------ */ + + public static Optional resolve(Entity holder, Entity leashedEntity, + ILeashData.LeashInfo leashInfo, float partialTicks) { + + if (holder == null || leashedEntity == null) return Optional.empty(); + + Optional leashedEntityStateOpt = LeashStateAPI.getLeashState(leashedEntity); + if (leashedEntityStateOpt.isEmpty()) return Optional.empty(); + + ILeashState leashedEntityState = leashedEntityStateOpt.get(); + Optional leashStateOpt = leashedEntityState.getLeashState(holder); + if (leashStateOpt.isEmpty()) return Optional.empty(); + + ILeashState.LeashState leashState = leashStateOpt.get(); + + // 计算实体端偏移 + Vec3 currentEntityOffset = getEntityLeashOffset(leashedEntity, leashState, partialTicks); + + // 计算持有者端偏移 + Vec3 holderOffset = getHolderOffset(holder, leashState); + + // 获取插值后的位置 + Vec3 currentHolderPos; + if (holder instanceof Player player) { + // 玩家手部偏移 + 旋转矩阵(第一人称/第三人称都适用) + if (Minecraft.getInstance().options.getCameraType() == CameraType.FIRST_PERSON) { + currentHolderPos = getFirstPersonLeashPos(player, partialTicks); + } else { + currentHolderPos = getEntityLeashHolderPos(player, holderOffset, partialTicks); + } + + } else if (holder instanceof SuperLeashKnotEntity) { + currentHolderPos = getInterpolatedPosition(holder, partialTicks); + } else { + // 普通实体,偏移 + 插值 + 旋转 + currentHolderPos = getEntityLeashHolderPos(holder, holderOffset, partialTicks); } - AtomicReference holderOffset = new AtomicReference<>(); - AtomicReference entityOffset = new AtomicReference<>(); - LeashStateAPI.getLeashState(leashedEntity).ifPresent(state -> - state - .getLeashState(holder) - .ifPresent(ls -> { - holderOffset.set( - Optional.ofNullable(ls.holderLocationOffset()) - .orElse(ls.defaultHolderLocationOffset()) - ); - entityOffset.set( - Optional.ofNullable(ls.applyEntityLocationOffset()) - .orElse( - state - .getLeashApplyEntityLocationOffset() - .orElse(state.getDefaultLeashApplyEntityLocationOffset()) - ) - ); - } - )); + Vec3 currentEntityPos = getInterpolatedPosition(leashedEntity, partialTicks) + .add(currentEntityOffset); - // 获取当前帧位置(带插值) - Vec3 currentHolderPos = getInterpolatedPosition(holder, partialTicks).add(holderOffset.get()); - Vec3 currentEntityPos = getInterpolatedPosition(leashedEntity, partialTicks).add(entityOffset.get()); - - - // 获取上一帧数据 + // 上一帧缓存 FrameCache cache = frameCacheMap.get(leashedEntity.getUUID()); Vec3 lastHolderPos = cache != null ? cache.lastStartPos() : currentHolderPos; Vec3 lastEntityPos = cache != null ? cache.lastEndPos() : currentEntityPos; float lastAngle = cache != null ? cache.lastSwingAngle() : 0f; float lastSpeed = cache != null ? cache.lastSwingSpeed() : 0f; - // 计算物理参数 + // 物理参数 double distance = currentHolderPos.distanceTo(currentEntityPos); double maxDistance = leashInfo.maxDistance(); double elasticDistance = leashInfo.elasticDistance(); - float tension = calculateTension(distance, maxDistance, elasticDistance);//这里暂时没用上 + float tension = calculateTension(distance, maxDistance, elasticDistance); float stretchRatio = (float) (distance / maxDistance); boolean isCritical = distance > maxDistance * 1.5; - // 计算摆动动态 + // 摆动动态 SwingDynamics swing = calculateSwingDynamics( currentHolderPos, currentEntityPos, lastHolderPos, lastEntityPos, lastAngle, lastSpeed, - tension); + tension + ); // 更新缓存 frameCacheMap.put(leashedEntity.getUUID(), - new FrameCache(currentHolderPos, currentEntityPos, - swing.angle(), swing.speed())); + new FrameCache(currentHolderPos, currentEntityPos, swing.angle(), swing.speed())); return Optional.of(new SuperLeashRenderState( - currentHolderPos, - currentEntityPos, - lastHolderPos, - lastEntityPos, - tension, - stretchRatio, - isCritical, + currentHolderPos, currentEntityPos, + lastHolderPos, lastEntityPos, + tension, stretchRatio, isCritical, leashInfo.keepLeashTicks(), selectColor(tension, isCritical), - THICKNESS_BASE + (tension * THICKNESS_TENSION), - swing.angle(), - swing.speed(), + THICKNESS_BASE + tension * THICKNESS_TENSION, + swing.angle(), swing.speed(), (float) leashInfo.maxDistance() )); } - private record SwingDynamics(float angle, float speed) {} + /* ------------------------ 实体偏移计算 ------------------------ */ + private static Vec3 getEntityLeashOffset(Entity entity, ILeashState.LeashState leashState, float partialTicks) { + // 获取实体自身的 LeashState 偏移 + Optional entityStateOpt = LeashStateAPI.getLeashState(entity); + Vec3 baseOffset = entityStateOpt + .map(eState -> eState.getLeashApplyEntityLocationOffset() + .orElse(eState.getDefaultLeashApplyEntityLocationOffset())) + .orElse(Vec3.ZERO); - private static SwingDynamics calculateSwingDynamics( - Vec3 currentStart, Vec3 currentEnd, - Vec3 lastStart, Vec3 lastEnd, - float lastAngle, float lastSpeed, - float tension) { + // 加上单条绳子的额外偏移 + baseOffset = baseOffset.add(leashState.applyEntityLocationOffset()); + + // 获取旋转角度 + float pitch = Mth.lerp(partialTicks, entity.getXRot(), entity.xRotO) * ((float)Math.PI / 180F); + float yaw = Mth.lerp(partialTicks, entity.getYRot(), entity.yRotO) * ((float)Math.PI / 180F); + float roll = 0f; + + // 玩家特殊处理:飞行/旋转攻击可能需要 roll + if (entity instanceof Player player && (player.isFallFlying() || player.isAutoSpinAttack())) { + Vec3 view = player.getViewVector(partialTicks); + Vec3 motion = player.getDeltaMovement(); + double motionLenSq = motion.horizontalDistanceSqr(); + double viewLenSq = view.horizontalDistanceSqr(); + if (motionLenSq > 0 && viewLenSq > 0) { + double dot = (motion.x * view.x + motion.z * view.z) / Math.sqrt(motionLenSq * viewLenSq); + double cross = motion.x * view.z - motion.z * view.x; + roll = (float)(Math.signum(cross) * Math.acos(dot)); + } + } + + // 将偏移从局部坐标转换到世界坐标 + return baseOffset.xRot(-pitch).yRot(-yaw).zRot(-roll); + } + private static Vec3 getHolderOffset(Entity holder, ILeashState.LeashState leashState) { + if (LeashStateAPI.Query.hasLeashState(holder)) { + Optional holderStateOpt = LeashStateAPI.getLeashState(holder); + if (holderStateOpt.isPresent()) { + ILeashState holderState = holderStateOpt.get(); + return holderState.getLeashApplyEntityLocationOffset() + .orElse(holderState.getDefaultLeashApplyEntityLocationOffset()); + } + } + return Optional.ofNullable(leashState.holderLocationOffset()) + .orElse(leashState.defaultHolderLocationOffset()); + } + private static boolean holderHasLeashState(Entity holder) { + if (LeashStateAPI.Query.hasLeashState(holder)) { + Optional holderStateOpt = LeashStateAPI.getLeashState(holder); + return holderStateOpt.isPresent(); + } + return false; + } + /** + * 获取玩家挂点位置,支持旋转偏移 + */ + private static Vec3 getFirstPersonLeashPos(Player player, float partialTicks) { + // 插值旋转角度 + float yaw = Mth.lerp(partialTicks, player.yRotO, player.getYRot()) * ((float)Math.PI / 180F); + float pitch = Mth.lerp(partialTicks, player.xRotO, player.getXRot()) * ((float)Math.PI / 180F); + float roll = 0f; + + // 计算 roll(飞行或旋转攻击时) + if (player.isFallFlying() || player.isAutoSpinAttack()) { + Vec3 view = player.getViewVector(partialTicks); + Vec3 motion = player.getDeltaMovement(); + double motionLenSq = motion.horizontalDistanceSqr(); + double viewLenSq = view.horizontalDistanceSqr(); + if (motionLenSq > 0 && viewLenSq > 0) { + double dot = (motion.x*view.x + motion.z*view.z) / Math.sqrt(motionLenSq*viewLenSq); + double cross = motion.x*view.z - motion.z*view.x; + roll = (float)(Math.signum(cross) * Math.acos(dot)); + } + } + + // 手部偏移 + double side = player.getMainArm() == HumanoidArm.RIGHT ? -1.0D : 1.0D; + Vec3 localPos = new Vec3(0.39D * side, -0.6D, 0.3D); + + // 局部坐标旋转到世界坐标 + Vec3 rotatedPos = localPos.xRot(-pitch).yRot(-yaw).zRot(-roll); + + // 返回世界坐标(玩家眼睛位置 + 手部旋转偏移) + return player.getEyePosition(partialTicks).add(rotatedPos); + } + /** + * 获取实体挂点位置,支持旋转偏移 + */ + public static Vec3 getEntityLeashHolderPos(Entity entity, Vec3 baseOffset, float partialTicks) { + + Vec3 pos = entity.getPosition(partialTicks); + double xOffset = baseOffset.x(); + double yOffset = baseOffset.y(); + double zOffset = baseOffset.z(); + + float pitch = Mth.lerp(partialTicks, entity.getXRot(), entity.xRotO) * ((float)Math.PI / 180F); + float yaw = Mth.lerp(partialTicks, entity.getYRot(), entity.yRotO) * ((float)Math.PI / 180F); + + if (entity instanceof Player player) { + xOffset *= player.getMainArm() == HumanoidArm.RIGHT ? -1.0D : 1.0D; + + if (!player.isFallFlying() && !player.isAutoSpinAttack()) { + if (player.isVisuallySwimming()) { + pos = pos.add(new Vec3(xOffset, 0.2D, -0.15D).xRot(-pitch).yRot(-yaw)); + } else { + double yBase = player.getBoundingBox().getYsize() - 1.0D; + double yAdjust = player.isCrouching() ? -0.2D : 0.07D; + pos = pos.add(new Vec3(xOffset, yBase, yAdjust).yRot(-yaw)); + } + } else { + Vec3 view = player.getViewVector(partialTicks); + Vec3 motion = player.getDeltaMovement(); + float roll = computeRoll(view, motion); + pos = pos.add(new Vec3(xOffset, -0.11D, 0.85D).zRot(-roll).xRot(-pitch).yRot(-yaw)); + } + } else { + double yBase = entity.getBbHeight() * 0.5 + yOffset; + pos = pos.add(new Vec3(xOffset, yBase, zOffset).yRot(-yaw).xRot(-pitch)); + } + + return pos; + } + + private static float computeRoll(Vec3 view, Vec3 motion) { + double d1 = motion.horizontalDistanceSqr(); + double d2 = view.horizontalDistanceSqr(); + if (d1 > 0.0 && d2 > 0.0) { + double dot = (motion.x * view.x + motion.z * view.z) / Math.sqrt(d1 * d2); + double cross = motion.x * view.z - motion.z * view.x; + return (float)(Math.signum(cross) * Math.acos(dot)); + } + return 0.0F; + } + + private static Vec3 getInterpolatedPosition(Entity entity, float partialTicks) { + return entity.getEyePosition(partialTicks); + } + + /* ------------------------ 摆动/张力 ------------------------ */ + + private static SwingDynamics calculateSwingDynamics(Vec3 currentStart, Vec3 currentEnd, + Vec3 lastStart, Vec3 lastEnd, + float lastAngle, float lastSpeed, + float tension) { - // 计算当前方向向量 Vec3 currentDir = currentEnd.subtract(currentStart).normalize(); Vec3 lastDir = lastEnd.subtract(lastStart).normalize(); - // 计算方向变化角度(使用叉积确定变化方向) Vec3 cross = lastDir.cross(currentDir); float angleChange = (float) Math.acos(Math.min(1.0, lastDir.dot(currentDir))); - angleChange *= (float) Math.signum(cross.y); // 使用y分量确定摆动方向 + angleChange *= Math.signum(cross.y); - // 模拟物理摆动(简谐运动) float newSpeed = lastSpeed * 0.9f + angleChange * 0.5f * tension; float newAngle = lastAngle + newSpeed; - // 添加随机扰动(模拟风等外力) if (tension > 0.3f) { - newSpeed += (float) ((Math.random() - 0.5) * 0.05 * tension); + newSpeed += (Math.random() - 0.5) * 0.05 * tension; } return new SwingDynamics(newAngle, newSpeed); } - private static Vec3 getInterpolatedPosition(Entity entity, float partialTicks) { - // 实体位置插值(使移动更平滑) - return new Vec3( - lerp(partialTicks, (float) entity.xo, (float) entity.getX()), - lerp(partialTicks, (float) entity.yo, (float) entity.getY()), - lerp(partialTicks, (float) entity.zo, (float) entity.getZ()) - ); - } - - private static float lerp(float delta, float start, float end) { - return start + delta * (end - start); - } - private static float calculateTension(double distance, double maxDistance, double elasticDistance) { - if (distance <= elasticDistance) { - return 0f; // 完全松弛 - } - - // 非线性张力计算 - double excess = distance - elasticDistance; - double range = maxDistance - elasticDistance; - float ratio = (float) (excess / range); - - // 应用缓动函数使变化更自然 - return easeOutQuad(Math.min(ratio, MAX_TENSION)); + if (distance <= elasticDistance) return 0f; + double ratio = (distance - elasticDistance) / (maxDistance - elasticDistance); + return easeOutQuad(Math.min((float)ratio, MAX_TENSION)); } - private static float easeOutQuad(float x) { - return 1 - (1 - x) * (1 - x); - } + private static float easeOutQuad(float x) { return 1 - (1 - x) * (1 - x); } private static int selectColor(float tension, boolean isCritical) { - if (isCritical) { - return SuperLeashRenderState.COLOR_CRITICAL; - } - return tension > 0.7f ? - SuperLeashRenderState.COLOR_TENSION : - SuperLeashRenderState.COLOR_NORMAL; + if (isCritical) return SuperLeashRenderState.COLOR_CRITICAL; + return tension > 0.7f ? SuperLeashRenderState.COLOR_TENSION : SuperLeashRenderState.COLOR_NORMAL; } - /** - * 获取实体所有拴绳的渲染状态 - */ - public static List resolveAll( - Entity leashedEntity, - LeashDataImpl leashData, - float partialTicks) { + /* ------------------------ 批量解析 ------------------------ */ + + public static List resolveAll(Entity leashedEntity, + LeashDataImpl leashData, float partialTicks) { List states = new ArrayList<>(); Level level = leashedEntity.level(); @@ -206,15 +312,14 @@ public class SuperLeashStateResolver { for (ILeashData.LeashInfo leashInfo : leashData.getAllLeashes()) { Entity holder = null; if (leashInfo.blockPosOpt().isEmpty() && leashInfo.holderIdOpt().isPresent()){ - holder = level.getEntity(leashInfo.holderIdOpt().get()); + holder = level.getEntity(leashInfo.holderIdOpt().get()); } else if (leashInfo.blockPosOpt().isPresent()) { holder = SuperLeashKnotEntity.getOrCreateKnot(level, leashInfo.blockPosOpt().get()); } if (holder != null) { resolve(holder, leashedEntity, leashInfo, partialTicks) .ifPresent(states::add); - } - else SuperLeadRope.logger.error("Holder is not found"); + } else SuperLeadRope.logger.error("Holder not found for leash of " + leashedEntity); } return states; diff --git a/src/main/java/top/r3944realms/superleadrope/config/LeashCommonConfig.java b/src/main/java/top/r3944realms/superleadrope/config/LeashCommonConfig.java index 94092e7..b9b9374 100644 --- a/src/main/java/top/r3944realms/superleadrope/config/LeashCommonConfig.java +++ b/src/main/java/top/r3944realms/superleadrope/config/LeashCommonConfig.java @@ -40,7 +40,7 @@ public class LeashCommonConfig { public final ForgeConfigSpec.DoubleValue springDampening; public final ForgeConfigSpec.ConfigValue> axisSpecificElasticity; public final ForgeConfigSpec.IntValue maxLeashesPerEntity; - public final ForgeConfigSpec.ConfigValue> defaultApplyEntityLocationOffset; + public final ForgeConfigSpec.ConfigValue> defaultApplyEntityLocationOffset, defaultHolderLocationOffset; // 正则表达式模式 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*" ); @@ -98,25 +98,45 @@ public class LeashCommonConfig { defaultApplyEntityLocationOffset = builder .comment( "Default leash attachment point offsets for entities.", + "Reference point: the entity's eyeHeight (eye / head position).", "Format: vec3(x,y,z) : [entity_list]", - "Optional Name : vector3, vec3d, offset", - "Entity list can contain:", + "Optional names: vector3, vec3d, offset", + "Entity list may 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", + " - #modid : all entities from a mod (e.g. #minecraft)", + " - * : all entities", + "Multiple entries can be separated by commas", "Example: vec3(0,0.2,0) : [minecraft:bee, minecraft:horse]", - "Priority: specific entity > tag > mod" + "Priority order: 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]" + "vec3(0,-0.5,0) : [*]", "vec3(0,-0.42,0) : [minecraft:player]" + ), + o -> o instanceof String s && isValidOffsetRefFormat(s) + ); + + defaultHolderLocationOffset = builder + .comment( + "Default leash holder attachment point offsets (where the leash attaches to the holder).", + "Reference point: the entity's eyeHeight (eye / head position).", + "Format: vec3(x,y,z) : [entity_list]", + "Optional names: vector3, vec3d, offset", + "Entity list may contain:", + " - modid:entity_id : specific entity (e.g. minecraft:player)", + " - #modid:tag_name : entity type tag (e.g. #minecraft:players)", + " - #modid : all entities from a mod (e.g. #minecraft)", + " - * : all entities", + "Multiple entries can be separated by commas", + "Example: vec3(0,1.5,0) : [minecraft:player]", + "Priority order: specific entity > tag > mod > *" + ) + .defineListAllowEmpty( + List.of("defaultHolderLocationOffset"), + List.of( + "vec3(0,-0.5,0) : [*]" ), o -> o instanceof String s && isValidOffsetRefFormat(s) ); @@ -124,6 +144,9 @@ public class LeashCommonConfig { } private static boolean isValidEntityRefFormat(String s) { + if ("*".equals(s)) { + return true; // 支持任意实体通配 + } if (s.startsWith("#")) { String body = s.substring(1); // 支持 #modid (整个模组)或 #modid:tag_name (标签) diff --git a/src/main/java/top/r3944realms/superleadrope/config/LeashConfigManager.java b/src/main/java/top/r3944realms/superleadrope/config/LeashConfigManager.java index 5ba7655..5626f7f 100644 --- a/src/main/java/top/r3944realms/superleadrope/config/LeashConfigManager.java +++ b/src/main/java/top/r3944realms/superleadrope/config/LeashConfigManager.java @@ -30,9 +30,9 @@ import java.util.regex.Matcher; import static top.r3944realms.superleadrope.config.LeashCommonConfig.Common.OFFSET_PATTERN; public class LeashConfigManager { - private final Map entityOffsetMap = new ConcurrentHashMap<>(); - private final Map tagOffsetMap = new ConcurrentHashMap<>(); - private final Map modOffsetMap = new ConcurrentHashMap<>(); + private final Map entityHolderOffsetMap = new ConcurrentHashMap<>(), entityLeashOffsetMap = new ConcurrentHashMap<>(); + private final Map tagHolderOffsetMap = new ConcurrentHashMap<>(), tagLeashOffsetMap = new ConcurrentHashMap<>(); + private final Map modHolderOffsetMap = new ConcurrentHashMap<>(), modLeashOffsetMap = new ConcurrentHashMap<>(); // 缓存常用配置值以提高性能 private volatile List teleportWhitelistCache; @@ -53,12 +53,13 @@ public class LeashConfigManager { * 解析偏移配置(线程安全) */ public void parseOffsetConfig() { - Map newEntityOffsetMap = new HashMap<>(); - Map newTagOffsetMap = new HashMap<>(); - Map newModOffsetMap = new HashMap<>(); + // --- Holder --- + Map holderEntityMap = new HashMap<>(); + Map holderTagMap = new HashMap<>(); + Map holderModMap = new HashMap<>(); - List offsets = LeashCommonConfig.COMMON.defaultApplyEntityLocationOffset.get(); - for (String offsetConfig : offsets) { + List holderOffsets = LeashCommonConfig.COMMON.defaultHolderLocationOffset.get(); + for (String offsetConfig : holderOffsets) { Matcher matcher = OFFSET_PATTERN.matcher(offsetConfig); if (!matcher.matches()) continue; @@ -69,37 +70,80 @@ public class LeashConfigManager { double[] offset = new double[]{x, y, z}; String entityList = matcher.group(4); - String[] entities = entityList.split(","); - - for (String entity : entities) { + for (String entity : entityList.split(",")) { String trimmed = entity.trim(); - if (trimmed.startsWith("#")) { + if (trimmed.equals("*")) { + // special case: apply to all entities + holderModMap.put("*", offset); + } else if (trimmed.startsWith("#")) { String body = trimmed.substring(1).trim(); if (body.contains(":")) { - newTagOffsetMap.put(body, offset); + holderTagMap.put(body, offset); } else { - newModOffsetMap.put(body, offset); + holderModMap.put(body, offset); } } else { - newEntityOffsetMap.put(trimmed, offset); + holderEntityMap.put(trimmed, offset); } } } catch (NumberFormatException e) { - System.err.println("Invalid offset config: " + offsetConfig); + SuperLeadRope.logger.error("Invalid holder offset config: {}", offsetConfig); } } - // 原子性更新映射 - entityOffsetMap.clear(); - entityOffsetMap.putAll(newEntityOffsetMap); + entityHolderOffsetMap.clear(); + entityHolderOffsetMap.putAll(holderEntityMap); + tagHolderOffsetMap.clear(); + tagHolderOffsetMap.putAll(holderTagMap); + modHolderOffsetMap.clear(); + modHolderOffsetMap.putAll(holderModMap); - tagOffsetMap.clear(); - tagOffsetMap.putAll(newTagOffsetMap); + // --- Leash --- + Map leashEntityMap = new HashMap<>(); + Map leashTagMap = new HashMap<>(); + Map leashModMap = new HashMap<>(); - modOffsetMap.clear(); - modOffsetMap.putAll(newModOffsetMap); + List leashOffsets = LeashCommonConfig.COMMON.defaultApplyEntityLocationOffset.get(); + for (String offsetConfig : leashOffsets) { + 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); + for (String entity : entityList.split(",")) { + String trimmed = entity.trim(); + if (trimmed.equals("*")) { + leashModMap.put("*", offset); + } else if (trimmed.startsWith("#")) { + String body = trimmed.substring(1).trim(); + if (body.contains(":")) { + leashTagMap.put(body, offset); + } else { + leashModMap.put(body, offset); + } + } else { + leashEntityMap.put(trimmed, offset); + } + } + } catch (NumberFormatException e) { + SuperLeadRope.logger.error("Invalid leash offset config: {}", offsetConfig); + } + } + + entityLeashOffsetMap.clear(); + entityLeashOffsetMap.putAll(leashEntityMap); + tagLeashOffsetMap.clear(); + tagLeashOffsetMap.putAll(leashTagMap); + modLeashOffsetMap.clear(); + modLeashOffsetMap.putAll(leashModMap); } + /** * 重新加载所有配置值到缓存 */ @@ -127,7 +171,7 @@ public class LeashConfigManager { * 获取实体类型的偏移量 */ @SuppressWarnings("deprecation") - public Vec3 getEntityOffset(EntityType entityType) { + public Vec3 getDefaultEntityOffset(EntityType entityType) { String entityId = entityType.builtInRegistryHolder().key().location().toString(); String modId = entityId.split(":")[0]; // 从实体ID提取modId @@ -137,37 +181,95 @@ public class LeashConfigManager { tagStrings.add(tag.location().toString()); } - double[] offset = getEntityOffset(entityId, modId, tagStrings); + double[] offset = getDefaultEntityOffset(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 Vec3 getDefaultEntityOffset(Entity entity) { + return getDefaultEntityOffset(entity.getType()); } /** * 获取实体偏移量(原始数据) */ - public double[] getEntityOffset(String entityId, String modId, List tags) { + public double[] getDefaultEntityOffset(String entityId, String modId, List tags) { // 1. 首先检查特定实体 - if (entityOffsetMap.containsKey(entityId)) { - return entityOffsetMap.get(entityId); + if (entityLeashOffsetMap.containsKey(entityId)) { + return entityLeashOffsetMap.get(entityId); } // 2. 检查标签 for (String tag : tags) { - if (tagOffsetMap.containsKey(tag)) { - return tagOffsetMap.get(tag); + if (tagLeashOffsetMap.containsKey(tag)) { + return tagHolderOffsetMap.get(tag); } } // 3. 检查模组 - if (modOffsetMap.containsKey(modId)) { - return modOffsetMap.get(modId); + if (modLeashOffsetMap.containsKey(modId)) { + return modLeashOffsetMap.get(modId); + } + + //4. 通配符 + if (modLeashOffsetMap.containsKey("*")) { + return modLeashOffsetMap.get("*"); + } + return null; + } + /** + * 获取实体类型的偏移量 + */ + @SuppressWarnings("deprecation") + public Vec3 getDefaultHolderOffset(EntityType entityType) { + String entityId = entityType.builtInRegistryHolder().key().location().toString(); + String modId = entityId.split(":")[0]; // 从实体ID提取modId + + // 获取实体的标签 + List tagStrings = new ArrayList<>(); + for (var tag : entityType.builtInRegistryHolder().tags().toList()) { + tagStrings.add(tag.location().toString()); + } + + double[] offset = getDefaultHolderOffset(entityId, modId, tagStrings); + return offset != null ? new Vec3(offset[0], offset[1], offset[2]) : Vec3.ZERO; + } + + /** + * 获取实体对象的偏移量(便捷方法) + */ + public Vec3 getDefaultHolderOffset(Entity entity) { + return getDefaultHolderOffset(entity.getType()); + } + + + /** + * 获取实体偏移量(原始数据) + */ + public double[] getDefaultHolderOffset(String entityId, String modId, List tags) { + // 1. 首先检查特定实体 + if (entityHolderOffsetMap.containsKey(entityId)) { + return entityHolderOffsetMap.get(entityId); + } + + // 2. 检查标签 + for (String tag : tags) { + if (tagHolderOffsetMap.containsKey(tag)) { + return tagHolderOffsetMap.get(tag); + } + } + + // 3. 检查模组 + if (modHolderOffsetMap.containsKey(modId)) { + return modHolderOffsetMap.get(modId); + } + + //4. 通配符 + if (modLeashOffsetMap.containsKey("*")) { + return modHolderOffsetMap.get("*"); } return null; @@ -335,15 +437,16 @@ public class LeashConfigManager { } public void clear() { - entityOffsetMap.clear(); - tagOffsetMap.clear(); - modOffsetMap.clear(); + entityHolderOffsetMap.clear();entityLeashOffsetMap.clear(); + tagHolderOffsetMap.clear();tagLeashOffsetMap.clear(); + modHolderOffsetMap.clear();modLeashOffsetMap.clear(); teleportWhitelistCache = Collections.emptyList(); } public String getStats() { - return String.format("Entities: %d, Tags: %d, Mods: %d, TeleportWhitelist: %d", - entityOffsetMap.size(), tagOffsetMap.size(), modOffsetMap.size(), + return String.format("Holder: Entities: %d, Tags: %d, Mods: %d \n Leash: Entities: %d, Tags: %d, Mods: %d, TeleportWhitelist: %d", + entityHolderOffsetMap.size(), tagHolderOffsetMap.size(), modHolderOffsetMap.size(), + entityLeashOffsetMap.size(), tagLeashOffsetMap.size(), modLeashOffsetMap.size(), teleportWhitelistCache.size()); } @@ -356,6 +459,7 @@ public class LeashConfigManager { } public static void unloading(LeashConfigManager manager) { - manager.clear(); + if(manager != null) + manager.clear(); } } diff --git a/src/main/java/top/r3944realms/superleadrope/content/capability/impi/LeashDataImpl.java b/src/main/java/top/r3944realms/superleadrope/content/capability/impi/LeashDataImpl.java index 9e2335d..90a85c2 100644 --- a/src/main/java/top/r3944realms/superleadrope/content/capability/impi/LeashDataImpl.java +++ b/src/main/java/top/r3944realms/superleadrope/content/capability/impi/LeashDataImpl.java @@ -202,7 +202,7 @@ public class LeashDataImpl implements ILeashData { } else { leashHolders.put(holder.getUUID(), info); } - LeashStateAPI.Operations.attach(entity, holder); + LeashStateAPI.Offset.setHolderFor(entity, holder); markForSync(); return true; } @@ -578,40 +578,6 @@ public class LeashDataImpl implements ILeashData { 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; - } /** * 根据生物类型和力的大小计算移动速度 */ @@ -636,7 +602,7 @@ public class LeashDataImpl implements ILeashData { return calculateLeashForce(uuidHolder, entry); } else { if (!delayedHolders.contains(uuid)) { - SuperLeadRope.logger.error("Could not apply leash forces for {}, because it is not found(it will be removed from list).", uuid); + SuperLeadRope.logger.warn("Could not apply leash forces for {}, because it is not found(it will be removed from list).", uuid); leashHolders.remove(uuid); } return null; @@ -760,21 +726,21 @@ public class LeashDataImpl implements ILeashData { public void removeAllLeashes() { leashHolders.clear(); leashKnots.clear(); - LeashStateAPI.Offset.removeAll(entity); + LeashStateAPI.Offset.removeHolderAll(entity); markForSync(); } @Override public void removeAllHolderLeashes() { leashHolders.clear(); - LeashStateAPI.Offset.removeAllUUIDs(entity); + LeashStateAPI.Offset.removeAllHolderUUIDs(entity); markForSync(); } @Override public void removeAllKnotLeashes() { leashKnots.clear(); - LeashStateAPI.Offset.removeAllBlockPoses(entity); + LeashStateAPI.Offset.removeAllHolderBlockPoses(entity); markForSync(); } diff --git a/src/main/java/top/r3944realms/superleadrope/content/capability/impi/LeashStateImpl.java b/src/main/java/top/r3944realms/superleadrope/content/capability/impi/LeashStateImpl.java index cf3490e..1681b47 100644 --- a/src/main/java/top/r3944realms/superleadrope/content/capability/impi/LeashStateImpl.java +++ b/src/main/java/top/r3944realms/superleadrope/content/capability/impi/LeashStateImpl.java @@ -20,11 +20,14 @@ import net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.ListTag; import net.minecraft.nbt.NbtUtils; +import net.minecraft.server.level.ServerLevel; import net.minecraft.world.entity.Entity; import net.minecraft.world.phys.Vec3; import net.minecraftforge.network.PacketDistributor; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import top.r3944realms.superleadrope.CommonEventHandler; import top.r3944realms.superleadrope.content.capability.inter.ILeashState; import top.r3944realms.superleadrope.content.entity.SuperLeashKnotEntity; import top.r3944realms.superleadrope.network.NetworkHandler; @@ -44,10 +47,12 @@ public class LeashStateImpl implements ILeashState { private long lastSyncTime; private final Map leashHolders = new ConcurrentHashMap<>(); private final Map leashKnots = new ConcurrentHashMap<>(); + @Nullable private volatile Vec3 staticApplyEntityLocationOffset; - private volatile Vec3 defaultApplyEntityLocationOffset = new Vec3(0 , 0.45, 0); - public LeashStateImpl(Entity entity) { + private volatile Vec3 defaultApplyEntityLocationOffset; + public LeashStateImpl(Entity entity, Vec3 defaultApplyEntityLocationOffset) { this.entity = entity; + this.defaultApplyEntityLocationOffset = defaultApplyEntityLocationOffset; } @Override @@ -86,6 +91,11 @@ public class LeashStateImpl implements ILeashState { needsSync = false; } + @Override + public boolean hasLeashState() { + return !leashKnots.isEmpty() || !leashHolders.isEmpty(); + } + @Override public Map getHolderLeashStates() { ConcurrentMap retMap = Maps.newConcurrentMap(); @@ -116,6 +126,22 @@ public class LeashStateImpl implements ILeashState { return leashKnots.containsKey(pos) ? Optional.of(leashKnots.get(pos)) : Optional.empty(); } + @Override + public Optional getHolderLocationOffset(Entity entity) { + return (entity instanceof SuperLeashKnotEntity leashKnot) ? + getHolderLocationOffset(leashKnot.getPos()) : getHolderLocationOffset(entity.getUUID()); + } + + @Override + public Optional getHolderLocationOffset(UUID uuid) { + return Optional.ofNullable(leashHolders.get(uuid)).map(LeashState::holderLocationOffset); + } + + @Override + public Optional getHolderLocationOffset(BlockPos pos) { + return Optional.ofNullable(leashKnots.get(pos)).map(LeashState::holderLocationOffset); + } + @Override public Vec3 getDefaultLeashApplyEntityLocationOffset() { return defaultApplyEntityLocationOffset; @@ -145,7 +171,7 @@ public class LeashStateImpl implements ILeashState { } @Override - public void setLeashHolderLocationOffset(Entity holder, Vec3 offset) { + public void setLeashHolderLocationOffset(Entity holder, @Nullable Vec3 offset) { if (holder instanceof SuperLeashKnotEntity leashKnot) { setLeashHolderLocationOffset(leashKnot.getPos(), offset); } else setLeashHolderLocationOffset(holder.getUUID(), offset); @@ -153,40 +179,51 @@ public class LeashStateImpl implements ILeashState { @Override public void setLeashHolderLocationOffset(UUID holderUUID, Vec3 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) - ); + if (entity.level() instanceof ServerLevel level) { + LeashState currentState = leashHolders.get(holderUUID); + if (currentState == null) { + Entity holder = level.getEntity(holderUUID); + Vec3 defaultHolderLocationOffset = Vec3.ZERO; + if(holder != null) { + defaultHolderLocationOffset = CommonEventHandler.leashConfigManager.getDefaultHolderOffset(holder); + } + // 创建新的状态,使用默认的应用实体偏移量 + leashHolders.put(holderUUID, new LeashState( + offset, + Vec3.ZERO, + defaultHolderLocationOffset + )); + } else { + // 更新现有状态 + leashHolders.put(holderUUID, + currentState.setHolderLocationOffset(offset) + ); + } + markForSync(); } - markForSync(); } @Override 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) - ); + if (entity.level() instanceof ServerLevel level) { + LeashState currentState = leashKnots.get(knotPos); + if (currentState == null) { + // 创建新的状态 + leashKnots.put(knotPos, new LeashState( + offset, + Vec3.ZERO, + SuperLeashKnotEntity.get(level, knotPos) + .map(CommonEventHandler.leashConfigManager::getDefaultHolderOffset) + .orElse(Vec3.ZERO) + )); + } else { + // 更新现有状态 + leashKnots.put(knotPos, + currentState.setHolderLocationOffset(offset) + ); + } + markForSync(); } - markForSync(); } @Override @@ -199,17 +236,13 @@ public class LeashStateImpl implements ILeashState { @Override public void addLeashHolderLocationOffset(UUID holderUUID, Vec3 offset) { LeashState currentState = leashHolders.get(holderUUID); - if (currentState == null) { - // 创建新的状态,使用默认的应用实体偏移量 - leashHolders.put(holderUUID, new LeashState( - offset, - getDefaultLeashApplyEntityLocationOffset(), - Vec3.ZERO // 或者合适的默认值 - )); - } else { - // 更新现有状态 + if (currentState != null) { + Vec3 newHolderLocationOffset; + if (currentState.holderLocationOffset() == null) { + newHolderLocationOffset = currentState.defaultHolderLocationOffset().add(offset); + } else newHolderLocationOffset = currentState.holderLocationOffset().add(offset); leashHolders.put(holderUUID, - currentState.setHolderLocationOffset(currentState.holderLocationOffset().add(offset)) + currentState.setHolderLocationOffset(newHolderLocationOffset) ); } markForSync(); @@ -218,17 +251,13 @@ public class LeashStateImpl implements ILeashState { @Override public void addLeashHolderLocationOffset(BlockPos knotPos, Vec3 offset) { LeashState currentState = leashKnots.get(knotPos); - if (currentState == null) { - // 创建新的状态 - leashKnots.put(knotPos, new LeashState( - offset, - getDefaultLeashApplyEntityLocationOffset(), - Vec3.ZERO - )); - } else { - // 更新现有状态 + if (currentState != null) { + Vec3 newHolderLocationOffset; + if (currentState.holderLocationOffset() == null) { + newHolderLocationOffset = currentState.defaultHolderLocationOffset().add(offset); + } else newHolderLocationOffset = currentState.holderLocationOffset().add(offset); leashKnots.put(knotPos, - currentState.setHolderLocationOffset(currentState.holderLocationOffset().add(offset)) + currentState.setHolderLocationOffset(newHolderLocationOffset) ); } markForSync(); @@ -344,7 +373,7 @@ public class LeashStateImpl implements ILeashState { private static @NotNull CompoundTag getCommonCompoundTag(@NotNull ILeashState.LeashState info, CompoundTag infoTag) { infoTag.put("ApplyEntityLocationOffset", NBTWriter.writeVec3(info.applyEntityLocationOffset())); - infoTag.put("HolderEntityLocationOffset", NBTWriter.writeVec3(info.holderLocationOffset())); + if(info.holderLocationOffset() != null) infoTag.put("HolderEntityLocationOffset", NBTWriter.writeVec3(info.holderLocationOffset())); infoTag.put("DefaultHolderLocationOffset", NBTWriter.writeVec3(info.defaultHolderLocationOffset())); return infoTag; } @@ -377,7 +406,7 @@ public class LeashStateImpl implements ILeashState { @Contract("_ -> new") private static @NotNull ILeashState.LeashState getUUIDLeashStateForm(@NotNull CompoundTag infoTag) { return new ILeashState.LeashState( - NBTReader.readVec3(infoTag.getCompound("HolderEntityLocationOffset")), + infoTag.contains("HolderEntityLocationOffset") ? NBTReader.readVec3(infoTag.getCompound("HolderEntityLocationOffset")) : null, NBTReader.readVec3(infoTag.getCompound("ApplyEntityLocationOffset")), NBTReader.readVec3(infoTag.getCompound("DefaultHolderLocationOffset")) ); diff --git a/src/main/java/top/r3944realms/superleadrope/content/capability/inter/ILeashData.java b/src/main/java/top/r3944realms/superleadrope/content/capability/inter/ILeashData.java index c225d06..4508b3b 100644 --- a/src/main/java/top/r3944realms/superleadrope/content/capability/inter/ILeashData.java +++ b/src/main/java/top/r3944realms/superleadrope/content/capability/inter/ILeashData.java @@ -32,7 +32,7 @@ import java.util.UUID; public interface ILeashData extends INBTSerializable { /* ---------------------- - * Add / remove leashes + * Add / removeApplyEntity leashes * ---------------------- */ boolean addLeash(Entity holder); boolean addLeash(Entity holder, String reserved); diff --git a/src/main/java/top/r3944realms/superleadrope/content/capability/inter/ILeashState.java b/src/main/java/top/r3944realms/superleadrope/content/capability/inter/ILeashState.java index ce2fa29..81418f8 100644 --- a/src/main/java/top/r3944realms/superleadrope/content/capability/inter/ILeashState.java +++ b/src/main/java/top/r3944realms/superleadrope/content/capability/inter/ILeashState.java @@ -20,6 +20,9 @@ import net.minecraft.nbt.CompoundTag; import net.minecraft.world.entity.Entity; import net.minecraft.world.phys.Vec3; import net.minecraftforge.common.util.INBTSerializable; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.Map; import java.util.Optional; @@ -32,6 +35,7 @@ public interface ILeashState extends INBTSerializable { /* ---------------------- * Query leash states * ---------------------- */ + boolean hasLeashState(); Map getHolderLeashStates(); Map getKnotLeashStates(); @@ -40,29 +44,42 @@ public interface ILeashState extends INBTSerializable { Optional getLeashState(BlockPos pos); /* ---------------------- - * Reset holder offsets + * Get offsets * ---------------------- */ + Optional getHolderLocationOffset(Entity entity); + Optional getHolderLocationOffset(UUID uuid); + Optional getHolderLocationOffset(BlockPos pos); + Optional getLeashApplyEntityLocationOffset(); + Vec3 getDefaultLeashApplyEntityLocationOffset(); + + + /* ---------------------- + * Reset offsets (setApplyEntity null) + * ---------------------- */ + void resetAllLeashApplyEntityLocationsOffset(); void resetAllLeashHolderLocationsOffset(); void resetLeashHolderLocationOffset(Entity holder); void resetLeashHolderLocationOffset(UUID holderUUID); void resetLeashHolderLocationOffset(BlockPos knotPos); /* ---------------------- - * Set holder offsets + * Set offsets (can setApplyEntity null) * ---------------------- */ - void setLeashHolderLocationOffset(Entity holder, Vec3 offset); - void setLeashHolderLocationOffset(UUID holderUUID, Vec3 offset); - void setLeashHolderLocationOffset(BlockPos knotPos, Vec3 offset); + void setLeashHolderLocationOffset(Entity holder,@Nullable Vec3 offset); + void setLeashHolderLocationOffset(UUID holderUUID,@Nullable Vec3 offset); + void setLeashHolderLocationOffset(BlockPos knotPos,@Nullable Vec3 offset); + void setLeashApplyEntityLocationOffset(Vec3 offset); /* ---------------------- - * Add holder offsets + * Add offsets * ---------------------- */ void addLeashHolderLocationOffset(Entity holder, Vec3 offset); void addLeashHolderLocationOffset(UUID holderUUID, Vec3 offset); void addLeashHolderLocationOffset(BlockPos knotPos, Vec3 offset); + void addLeashApplyEntityLocationOffset(Vec3 offset); /* ---------------------- - * Remove holder offsets + * Remove offsets (delete) * ---------------------- */ void removeLeashHolderLocationOffset(Entity holder); void removeLeashHolderLocationOffset(UUID holderUUID); @@ -70,17 +87,7 @@ public interface ILeashState extends INBTSerializable { void removeAllLeashHolderLocationOffset(); void removeAllLeashHolderUUIDLocationOffset(); void removeAllLeashHolderBlockPosLocationOffset(); - - /* ---------------------- - * Apply-entity offset - * ---------------------- */ - Optional getLeashApplyEntityLocationOffset(); - Vec3 getDefaultLeashApplyEntityLocationOffset(); - - void resetAllLeashApplyEntityLocationsOffset(); void removeLeashApplyEntityLocationOffset(); - void setLeashApplyEntityLocationOffset(Vec3 offset); - void addLeashApplyEntityLocationOffset(Vec3 offset); /* ---------------------- * Utility & sync @@ -95,19 +102,27 @@ public interface ILeashState extends INBTSerializable { * Data record * ---------------------- */ record LeashState( - Vec3 holderLocationOffset, + @Nullable Vec3 holderLocationOffset, Vec3 applyEntityLocationOffset, Vec3 defaultHolderLocationOffset ) { - public LeashState resetHolderLocationOffset() { - return new LeashState(defaultHolderLocationOffset, applyEntityLocationOffset, defaultHolderLocationOffset); + @Contract(" -> new") + public @NotNull LeashState resetHolderLocationOffset() { + return new LeashState(null, applyEntityLocationOffset, defaultHolderLocationOffset); } - public LeashState setHolderLocationOffset(Vec3 holderLocationOffset) { + @Contract("_ -> new") + public @NotNull LeashState setHolderLocationOffset(@Nullable Vec3 holderLocationOffset) { return new LeashState(holderLocationOffset, applyEntityLocationOffset, defaultHolderLocationOffset); } - public LeashState setApplyEntityLocationOffset(Vec3 applyEntityLocationOffset) { + @Contract("_ -> new") + public @NotNull LeashState setApplyEntityLocationOffset(@NotNull Vec3 applyEntityLocationOffset) { + return new LeashState(holderLocationOffset, applyEntityLocationOffset, defaultHolderLocationOffset); + } + + @Contract("_ -> new") + public @NotNull LeashState setDefaultHolderLocationOffset(@NotNull Vec3 defaultHolderLocationOffset) { return new LeashState(holderLocationOffset, applyEntityLocationOffset, defaultHolderLocationOffset); } } diff --git a/src/main/java/top/r3944realms/superleadrope/content/capability/provider/LeashStateProvider.java b/src/main/java/top/r3944realms/superleadrope/content/capability/provider/LeashStateProvider.java index 26e2919..6b6db94 100644 --- a/src/main/java/top/r3944realms/superleadrope/content/capability/provider/LeashStateProvider.java +++ b/src/main/java/top/r3944realms/superleadrope/content/capability/provider/LeashStateProvider.java @@ -24,6 +24,7 @@ import net.minecraftforge.common.capabilities.ICapabilitySerializable; import net.minecraftforge.common.util.LazyOptional; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import top.r3944realms.superleadrope.CommonEventHandler; import top.r3944realms.superleadrope.SuperLeadRope; import top.r3944realms.superleadrope.content.capability.CapabilityHandler; import top.r3944realms.superleadrope.content.capability.impi.LeashStateImpl; @@ -34,7 +35,7 @@ public class LeashStateProvider implements ICapabilitySerializable private final ILeashState instance; private final LazyOptional optional; public LeashStateProvider(Entity entity) { - this.instance = new LeashStateImpl(entity); + this.instance = new LeashStateImpl(entity, CommonEventHandler.leashConfigManager.getDefaultEntityOffset(entity)); this.optional = LazyOptional.of(() -> instance); } @Override diff --git a/src/main/java/top/r3944realms/superleadrope/content/command/LeashDataCommand.java b/src/main/java/top/r3944realms/superleadrope/content/command/LeashDataCommand.java index fb1837e..80aeac0 100644 --- a/src/main/java/top/r3944realms/superleadrope/content/command/LeashDataCommand.java +++ b/src/main/java/top/r3944realms/superleadrope/content/command/LeashDataCommand.java @@ -112,7 +112,7 @@ public class LeashDataCommand { ) ) ); - LiteralArgumentBuilder $$$add = Commands.literal("add") + LiteralArgumentBuilder $$$add = Commands.literal("addApplyEntity") .then(Commands.argument("target", EntityArgument.entities()) // 实体拴绳 .then($$$add$holder) @@ -120,7 +120,7 @@ public class LeashDataCommand { // 方块拴绳 .then($$$add$pos) ); - LiteralArgumentBuilder $$$remove = Commands.literal("remove") + LiteralArgumentBuilder $$$remove = Commands.literal("removeApplyEntity") .then(Commands.argument("target", EntityArgument.entities()) // 移除特定实体拴绳 .then(Commands.argument("holder", EntityArgument.entity()) @@ -237,7 +237,7 @@ public class LeashDataCommand { ) ) ); - LiteralArgumentBuilder $$$set = Commands.literal("set") + LiteralArgumentBuilder $$$set = Commands.literal("setApplyEntity") .then(Commands.argument("target", EntityArgument.entities()) // 实体拴绳设置 .then($$$set$holder) @@ -282,7 +282,7 @@ public class LeashDataCommand { } } } - public static final String SET_MAX_DISTANCE = SLP_LEASH_MESSAGE_ + "set.max_distance"; + public static final String SET_MAX_DISTANCE = SLP_LEASH_MESSAGE_ + "setApplyEntity.max_distance"; private static int setMaxDistance(CommandContext context) throws CommandSyntaxException { return setMaxDistance(context, CommonEventHandler.leashConfigManager.getMaxLeashLength(), ""); } @@ -297,11 +297,11 @@ public class LeashDataCommand { } return -1; } - public static final String REMOVE_ALL_BLOCK_LEASHES = SLP_LEASH_MESSAGE_ + "remove.all_block_leashes"; + public static final String REMOVE_ALL_BLOCK_LEASHES = SLP_LEASH_MESSAGE_ + "removeApplyEntity.all_block_leashes"; private static int removeAllBlockLeashes(CommandContext context) throws CommandSyntaxException { return -1; } - public static final String REMOVE_ALL_HOLDER_LEASHES = SLP_LEASH_MESSAGE_ + "remove.all_holder_leashes"; + public static final String REMOVE_ALL_HOLDER_LEASHES = SLP_LEASH_MESSAGE_ + "removeApplyEntity.all_holder_leashes"; private static int removeAllHolderLeashes(CommandContext context) throws CommandSyntaxException { return -1; } @@ -312,7 +312,7 @@ public class LeashDataCommand { private static int transferFromBlock(CommandContext context, String reserved) throws CommandSyntaxException { return -1; } - public static final String SET_ELASTIC_DISTANCE = SLP_LEASH_MESSAGE_ + "set.elastic_distance"; + public static final String SET_ELASTIC_DISTANCE = SLP_LEASH_MESSAGE_ + "setApplyEntity.elastic_distance"; private static int setElasticDistance(CommandContext context) throws CommandSyntaxException { return setElasticDistance(context, 0 ,""); } @@ -322,7 +322,7 @@ public class LeashDataCommand { private static int setElasticDistance(CommandContext context, int keepTicks, String reserved) throws CommandSyntaxException { return -1; } - public static final String SET_BLOCK_MAX_DISTANCE = SLP_LEASH_MESSAGE_ + "set.block_max_distance"; + public static final String SET_BLOCK_MAX_DISTANCE = SLP_LEASH_MESSAGE_ + "setApplyEntity.block_max_distance"; private static int setBlockMaxDistance(CommandContext context) throws CommandSyntaxException { return setBlockMaxDistance(context, 0 ,""); } @@ -332,7 +332,7 @@ public class LeashDataCommand { private static int setBlockMaxDistance(CommandContext context, int keepTicks, String reserved) throws CommandSyntaxException { return -1; } - public static final String SET_BLOCK_ELASTIC_DISTANCE = SLP_LEASH_MESSAGE_ + "set.block_elastic_distance"; + public static final String SET_BLOCK_ELASTIC_DISTANCE = SLP_LEASH_MESSAGE_ + "setApplyEntity.block_elastic_distance"; private static int setBlockElasticDistance(CommandContext context) throws CommandSyntaxException { return setBlockElasticDistance(context, 0 ,""); } diff --git a/src/main/java/top/r3944realms/superleadrope/content/command/LeashStateCommand.java b/src/main/java/top/r3944realms/superleadrope/content/command/LeashStateCommand.java index fd88029..5079bb0 100644 --- a/src/main/java/top/r3944realms/superleadrope/content/command/LeashStateCommand.java +++ b/src/main/java/top/r3944realms/superleadrope/content/command/LeashStateCommand.java @@ -21,7 +21,7 @@ import net.minecraft.commands.CommandSourceStack; public class LeashStateCommand { // 获取State // 设置State - // Holder> + // Holder> // 设置对应目标的 拴绳偏移 public static void register(CommandDispatcher dispatcher) { diff --git a/src/main/java/top/r3944realms/superleadrope/content/command/MotionCommand.java b/src/main/java/top/r3944realms/superleadrope/content/command/MotionCommand.java index ff18574..ad447ba 100644 --- a/src/main/java/top/r3944realms/superleadrope/content/command/MotionCommand.java +++ b/src/main/java/top/r3944realms/superleadrope/content/command/MotionCommand.java @@ -132,7 +132,7 @@ public class MotionCommand { LiteralArgumentBuilder Motion = $$motionRoot.requires(cs -> cs.hasPermission(2)) .then(Commands.argument("targets", EntityArgument.entities()) - .then(Commands.literal("add") + .then(Commands.literal("addApplyEntity") .then(Commands.argument("vecX", DoubleArgumentType.doubleArg()) .then(Commands.argument("vecY", DoubleArgumentType.doubleArg()) .then(Commands.argument("vecZ", DoubleArgumentType.doubleArg()) @@ -141,7 +141,7 @@ public class MotionCommand { ) ) ) - .then(Commands.literal("set") + .then(Commands.literal("setApplyEntity") .then(Commands.argument("vecX", DoubleArgumentType.doubleArg()) .then(Commands.argument("vecY", DoubleArgumentType.doubleArg()) .then(Commands.argument("vecZ", DoubleArgumentType.doubleArg()) diff --git a/src/main/java/top/r3944realms/superleadrope/content/entity/SuperLeashKnotEntity.java b/src/main/java/top/r3944realms/superleadrope/content/entity/SuperLeashKnotEntity.java index e62a298..765f91a 100644 --- a/src/main/java/top/r3944realms/superleadrope/content/entity/SuperLeashKnotEntity.java +++ b/src/main/java/top/r3944realms/superleadrope/content/entity/SuperLeashKnotEntity.java @@ -94,7 +94,13 @@ public class SuperLeashKnotEntity extends LeashFenceKnotEntity { @Override public boolean survives() { - return SuperLeashKnotEntity.isSupportBlock(this.level().getBlockState(this.pos)); + boolean supportBlock = SuperLeashKnotEntity.isSupportBlock(this.level().getBlockState(this.pos)); + if (!supportBlock) { + for (Entity entity : LeashDataImpl.leashableInArea(this)) { + LeashDataAPI.LeashOperations.detach(entity, this); + } + } + return supportBlock; } public static @NotNull SuperLeashKnotEntity getOrCreateKnot(@NotNull Level pLevel, @NotNull BlockPos pPos) { diff --git a/src/main/java/top/r3944realms/superleadrope/datagen/data/SLPLangKeyValue.java b/src/main/java/top/r3944realms/superleadrope/datagen/data/SLPLangKeyValue.java index 55460f7..1966b4e 100644 --- a/src/main/java/top/r3944realms/superleadrope/datagen/data/SLPLangKeyValue.java +++ b/src/main/java/top/r3944realms/superleadrope/datagen/data/SLPLangKeyValue.java @@ -310,14 +310,14 @@ public enum SLPLangKeyValue { "§7備註: §d%s" ), MESSAGE_LEASHDATA_ADD_SUCCESS( - "command.leashdata.add.success", ModPartEnum.COMMAND, + "command.leashdata.addApplyEntity.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, + "command.leashdata.removeApplyEntity.success", ModPartEnum.COMMAND, "§bRemoved leash successfully. §a%s §7- §e%s", "§b移除拴绳成功. §a%s §7- §e%s", "§b移除拴繩成功. §a%s §7- §e%s", @@ -331,7 +331,7 @@ public enum SLPLangKeyValue { "§b繫繩既移. §a%s §7→ §e%s §7→ §6%s" ), MESSAGE_LEASHDATA_SET_SUCCESS( - "command.leashdata.set.success", ModPartEnum.COMMAND, + "command.leashdata.setApplyEntity.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", diff --git a/src/main/java/top/r3944realms/superleadrope/util/capability/LeashStateAPI.java b/src/main/java/top/r3944realms/superleadrope/util/capability/LeashStateAPI.java index 88bae1a..d362395 100644 --- a/src/main/java/top/r3944realms/superleadrope/util/capability/LeashStateAPI.java +++ b/src/main/java/top/r3944realms/superleadrope/util/capability/LeashStateAPI.java @@ -19,7 +19,6 @@ 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; @@ -44,6 +43,9 @@ public final class LeashStateAPI { public static final class Query { private Query() { } + public static boolean hasLeashState(Entity entity) { + return getLeashState(entity).map(ILeashState::hasLeashState).orElse(false); + } public static Map getAllUUIDStates(Entity entity) { return getLeashState(entity) @@ -93,107 +95,111 @@ public final class LeashStateAPI { } // ---------------------- 重置操作 ---------------------- - public static void resetAll(Entity entity) { + public static void resetAllHolder(Entity entity) { getLeashState(entity).ifPresent(ILeashState::resetAllLeashHolderLocationsOffset); } - public static void resetFor(Entity entity, Entity holder) { + public static void resetHolderFor(Entity entity, Entity holder) { getLeashState(entity).ifPresent(state -> state.resetLeashHolderLocationOffset(holder)); } - public static void resetFor(Entity entity, UUID holderUUID) { + public static void resetHolderFor(Entity entity, UUID holderUUID) { getLeashState(entity).ifPresent(state -> state.resetLeashHolderLocationOffset(holderUUID)); } - public static void resetFor(Entity entity, BlockPos knotPos) { + public static void resetHolderFor(Entity entity, BlockPos knotPos) { getLeashState(entity).ifPresent(state -> state.resetLeashHolderLocationOffset(knotPos)); } // ---------------------- 设置操作 ---------------------- - public static void setFor(Entity entity, Entity holder, Vec3 offset) { + public static void setHolderFor(Entity entity, Entity holder, Vec3 offset) { getLeashState(entity).ifPresent(state -> state.setLeashHolderLocationOffset(holder, offset)); } - public static void setFor(Entity entity, UUID holderUUID, Vec3 offset) { + public static void setHolderFor(Entity entity, UUID holderUUID, Vec3 offset) { getLeashState(entity).ifPresent(state -> state.setLeashHolderLocationOffset(holderUUID, offset)); } - public static void setFor(Entity entity, BlockPos knotPos, Vec3 offset) { + public static void setHolderFor(Entity entity, BlockPos knotPos, Vec3 offset) { getLeashState(entity).ifPresent(state -> state.setLeashHolderLocationOffset(knotPos, offset)); } + public static void setHolderFor(Entity entity, BlockPos knotPos) { + getLeashState(entity).ifPresent(state -> state.setLeashHolderLocationOffset(knotPos, null)); + } + public static void setHolderFor(Entity entity, Entity holder) { + getLeashState(entity).ifPresent(state -> state.setLeashHolderLocationOffset(holder, null)); + } + + public static void setHolderFor(Entity entity, UUID holderUUID) { + getLeashState(entity).ifPresent(state -> state.setLeashHolderLocationOffset(holderUUID, null)); + } + // ---------------------- 添加操作 ---------------------- - public static void addTo(Entity entity, Entity holder, Vec3 offset) { + public static void addHolderTo(Entity entity, Entity holder, Vec3 offset) { getLeashState(entity).ifPresent(state -> state.addLeashHolderLocationOffset(holder, offset)); } - public static void addTo(Entity entity, UUID holderUUID, Vec3 offset) { + public static void addHolderTo(Entity entity, UUID holderUUID, Vec3 offset) { getLeashState(entity).ifPresent(state -> state.addLeashHolderLocationOffset(holderUUID, offset)); } - public static void addTo(Entity entity, BlockPos knotPos, Vec3 offset) { + public static void addHolderTo(Entity entity, BlockPos knotPos, Vec3 offset) { getLeashState(entity).ifPresent(state -> state.addLeashHolderLocationOffset(knotPos, offset)); } // ---------------------- 移除操作 ---------------------- - public static void removeFor(Entity entity, Entity holder) { + public static void removeHolderFor(Entity entity, Entity holder) { getLeashState(entity).ifPresent(state -> state.removeLeashHolderLocationOffset(holder)); } - public static void removeFor(Entity entity, UUID holderUUID) { + public static void removeHolderFor(Entity entity, UUID holderUUID) { getLeashState(entity).ifPresent(state -> state.removeLeashHolderLocationOffset(holderUUID)); } - public static void removeFor(Entity entity, BlockPos knotPos) { + public static void removeHolderFor(Entity entity, BlockPos knotPos) { getLeashState(entity).ifPresent(state -> state.removeLeashHolderLocationOffset(knotPos)); } - public static void removeAll(Entity entity) { + public static void removeHolderAll(Entity entity) { getLeashState(entity).ifPresent(ILeashState::removeAllLeashHolderLocationOffset); } - public static void removeAllUUIDs(Entity entity) { + public static void removeAllHolderUUIDs(Entity entity) { getLeashState(entity).ifPresent(ILeashState::removeAllLeashHolderUUIDLocationOffset); } - public static void removeAllBlockPoses(Entity entity) { + public static void removeAllHolderBlockPoses(Entity entity) { getLeashState(entity).ifPresent(ILeashState::removeAllLeashHolderBlockPosLocationOffset); } - } - - // ==================== 应用实体偏移量操作 ==================== - - public static final class ApplyEntity { - private ApplyEntity() { - } - - public static Optional getOffset(Entity entity) { + public static Optional getApplyEntityOffset(Entity entity) { return getLeashState(entity).flatMap(ILeashState::getLeashApplyEntityLocationOffset); } - public static Vec3 getDefaultOffset(Entity entity) { + public static Vec3 getDefaultApplyEntityOffset(Entity entity) { return getLeashState(entity) .map(ILeashState::getDefaultLeashApplyEntityLocationOffset) .orElse(Vec3.ZERO); } - public static void resetAll(Entity entity) { + public static void resetApplyEntityAll(Entity entity) { getLeashState(entity).ifPresent(ILeashState::resetAllLeashApplyEntityLocationsOffset); } - public static void remove(Entity entity) { + public static void removeApplyEntity(Entity entity) { getLeashState(entity).ifPresent(ILeashState::removeLeashApplyEntityLocationOffset); } - public static void set(Entity entity, Vec3 offset) { + public static void setApplyEntity(Entity entity, Vec3 offset) { getLeashState(entity).ifPresent(state -> state.setLeashApplyEntityLocationOffset(offset)); } - public static void add(Entity entity, Vec3 offset) { + public static void addApplyEntity(Entity entity, Vec3 offset) { getLeashState(entity).ifPresent(state -> state.addLeashApplyEntityLocationOffset(offset)); } } + // ==================== 同步操作 ==================== public static final class Sync { @@ -221,44 +227,44 @@ public final class LeashStateAPI { public static void attach(Entity leashed, Entity holder) { getLeashState(leashed).ifPresent(state -> - state.addLeashHolderLocationOffset(holder, - CommonEventHandler.leashConfigManager.getEntityOffset(holder)) + state.setLeashHolderLocationOffset(holder, + null) ); } public static void detach(Entity leashed, Entity holder) { - Offset.removeFor(leashed, holder); + Offset.removeHolderFor(leashed, holder); } public static void detach(Entity leashed, UUID holderUUID) { - Offset.removeFor(leashed, holderUUID); + Offset.removeHolderFor(leashed, holderUUID); } public static void detach(Entity leashed, BlockPos knotPos) { - Offset.removeFor(leashed, knotPos); + Offset.removeHolderFor(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)); + state.setLeashHolderLocationOffset(newHolder, + null); }); } public static void transfer(Entity leashed, UUID oldHolderUUID, Entity newHolder) { getLeashState(leashed).ifPresent(state -> { state.removeLeashHolderLocationOffset(oldHolderUUID); - state.addLeashHolderLocationOffset(newHolder, - CommonEventHandler.leashConfigManager.getEntityOffset(newHolder)); + state.setLeashHolderLocationOffset(newHolder, + null); }); } public static void transfer(Entity leashed, BlockPos oldKnotPos, Entity newHolder) { getLeashState(leashed).ifPresent(state -> { state.removeLeashHolderLocationOffset(oldKnotPos); - state.addLeashHolderLocationOffset(newHolder, - CommonEventHandler.leashConfigManager.getEntityOffset(newHolder)); + state.setLeashHolderLocationOffset(newHolder, + null); }); } diff --git a/src/main/resources/META-INF/mods.toml b/src/main/resources/META-INF/mods.toml index 349c1b5..52ea3c4 100644 --- a/src/main/resources/META-INF/mods.toml +++ b/src/main/resources/META-INF/mods.toml @@ -34,7 +34,7 @@ authors="${mod_authors}" #optional # MATCH_VERSION means that your mod will cause a red X if the versions on client and server differ. This is the default behaviour and should be what you choose if you have server and client elements to your mod. # IGNORE_SERVER_VERSION means that your mod will not cause a red X if it's present on the server but not on the client. This is what you should use if you're a server only mod. # IGNORE_ALL_VERSION means that your mod will not cause a red X if it's present on the client or the server. This is a special case and should only be used if your mod has no server component. -# NONE means that no display test is set on your mod. You need to do this yourself, see IExtensionPoint.DisplayTest for more information. You can define any scheme you wish with this value. +# NONE means that no display test is setApplyEntity on your mod. You need to do this yourself, see IExtensionPoint.DisplayTest for more information. You can define any scheme you wish with this value. # IMPORTANT NOTE: this is NOT an instruction as to which environments (CLIENT or DEDICATED SERVER) your mod loads on. Your mod should load (and maybe do nothing!) whereever it finds itself. #displayTest="MATCH_VERSION" # MATCH_VERSION is the default if nothing is specified (#optional) # The description text for the mod (multi line!) (#mandatory) diff --git a/src/main/resources/coremods/morsb_patch.js b/src/main/resources/coremods/morsb_patch.js index cc12b44..0b79d0e 100644 --- a/src/main/resources/coremods/morsb_patch.js +++ b/src/main/resources/coremods/morsb_patch.js @@ -41,10 +41,10 @@ function initializeCoreMod() { new VarInsnNode(Opcodes.ALOAD, 1), // Mob new VarInsnNode(Opcodes.ALOAD, 2), // Frustum ASMAPI.buildMethodCall( - 'your/package/LeashRenderHook', + 'top/r3944realms/superleadrope/core/hook/LeashRenderHook', null, ASMAPI.MethodType.STATIC, - 'shouldRenderExtraWithLog', + 'shouldRenderExtra', '(Lnet/minecraft/world/entity/Mob;Lnet/minecraft/client/renderer/culling/Frustum;)Z', ASMAPI.MethodCallMode.STATIC )