fix: 1.渲染优化调整 2.API规整

This commit is contained in:
叁玖领域 2025-09-15 22:08:10 +08:00
parent e82f84f2a4
commit 77d39a1aba
17 changed files with 596 additions and 348 deletions

View File

@ -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 {

View File

@ -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<UUID, FrameCache> 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<SuperLeashRenderState> 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<SuperLeashRenderState> resolve(Entity holder, Entity leashedEntity,
ILeashData.LeashInfo leashInfo, float partialTicks) {
if (holder == null || leashedEntity == null) return Optional.empty();
Optional<ILeashState> leashedEntityStateOpt = LeashStateAPI.getLeashState(leashedEntity);
if (leashedEntityStateOpt.isEmpty()) return Optional.empty();
ILeashState leashedEntityState = leashedEntityStateOpt.get();
Optional<ILeashState.LeashState> 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<Vec3> holderOffset = new AtomicReference<>();
AtomicReference<Vec3> 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<ILeashState> 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<ILeashState> 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<ILeashState> 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<SuperLeashRenderState> resolveAll(
Entity leashedEntity,
LeashDataImpl leashData,
float partialTicks) {
/* ------------------------ 批量解析 ------------------------ */
public static List<SuperLeashRenderState> resolveAll(Entity leashedEntity,
LeashDataImpl leashData, float partialTicks) {
List<SuperLeashRenderState> 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;

View File

@ -40,7 +40,7 @@ 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;
public final ForgeConfigSpec.ConfigValue<List<? extends String>> 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 标签

View File

@ -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<String, double[]> entityOffsetMap = new ConcurrentHashMap<>();
private final Map<String, double[]> tagOffsetMap = new ConcurrentHashMap<>();
private final Map<String, double[]> modOffsetMap = new ConcurrentHashMap<>();
private final Map<String, double[]> entityHolderOffsetMap = new ConcurrentHashMap<>(), entityLeashOffsetMap = new ConcurrentHashMap<>();
private final Map<String, double[]> tagHolderOffsetMap = new ConcurrentHashMap<>(), tagLeashOffsetMap = new ConcurrentHashMap<>();
private final Map<String, double[]> modHolderOffsetMap = new ConcurrentHashMap<>(), modLeashOffsetMap = new ConcurrentHashMap<>();
// 缓存常用配置值以提高性能
private volatile List<String> teleportWhitelistCache;
@ -53,12 +53,13 @@ public class LeashConfigManager {
* 解析偏移配置线程安全
*/
public void parseOffsetConfig() {
Map<String, double[]> newEntityOffsetMap = new HashMap<>();
Map<String, double[]> newTagOffsetMap = new HashMap<>();
Map<String, double[]> newModOffsetMap = new HashMap<>();
// --- Holder ---
Map<String, double[]> holderEntityMap = new HashMap<>();
Map<String, double[]> holderTagMap = new HashMap<>();
Map<String, double[]> holderModMap = new HashMap<>();
List<? extends String> offsets = LeashCommonConfig.COMMON.defaultApplyEntityLocationOffset.get();
for (String offsetConfig : offsets) {
List<? extends String> 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<String, double[]> leashEntityMap = new HashMap<>();
Map<String, double[]> leashTagMap = new HashMap<>();
Map<String, double[]> leashModMap = new HashMap<>();
modOffsetMap.clear();
modOffsetMap.putAll(newModOffsetMap);
List<? extends String> 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<String> tags) {
public double[] getDefaultEntityOffset(String entityId, String modId, List<String> 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<String> 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<String> 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();
}
}

View File

@ -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();
}

View File

@ -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<UUID, ILeashState.LeashState> leashHolders = new ConcurrentHashMap<>();
private final Map<BlockPos, ILeashState.LeashState> 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<UUID, LeashState> getHolderLeashStates() {
ConcurrentMap<UUID, LeashState> 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<Vec3> getHolderLocationOffset(Entity entity) {
return (entity instanceof SuperLeashKnotEntity leashKnot) ?
getHolderLocationOffset(leashKnot.getPos()) : getHolderLocationOffset(entity.getUUID());
}
@Override
public Optional<Vec3> getHolderLocationOffset(UUID uuid) {
return Optional.ofNullable(leashHolders.get(uuid)).map(LeashState::holderLocationOffset);
}
@Override
public Optional<Vec3> 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"))
);

View File

@ -32,7 +32,7 @@ import java.util.UUID;
public interface ILeashData extends INBTSerializable<CompoundTag> {
/* ----------------------
* Add / remove leashes
* Add / removeApplyEntity leashes
* ---------------------- */
boolean addLeash(Entity holder);
boolean addLeash(Entity holder, String reserved);

View File

@ -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<CompoundTag> {
/* ----------------------
* Query leash states
* ---------------------- */
boolean hasLeashState();
Map<UUID, LeashState> getHolderLeashStates();
Map<BlockPos, LeashState> getKnotLeashStates();
@ -40,29 +44,42 @@ public interface ILeashState extends INBTSerializable<CompoundTag> {
Optional<LeashState> getLeashState(BlockPos pos);
/* ----------------------
* Reset holder offsets
* Get offsets
* ---------------------- */
Optional<Vec3> getHolderLocationOffset(Entity entity);
Optional<Vec3> getHolderLocationOffset(UUID uuid);
Optional<Vec3> getHolderLocationOffset(BlockPos pos);
Optional<Vec3> 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<CompoundTag> {
void removeAllLeashHolderLocationOffset();
void removeAllLeashHolderUUIDLocationOffset();
void removeAllLeashHolderBlockPosLocationOffset();
/* ----------------------
* Apply-entity offset
* ---------------------- */
Optional<Vec3> 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<CompoundTag> {
* 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);
}
}

View File

@ -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<CompoundTag>
private final ILeashState instance;
private final LazyOptional<ILeashState> 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

View File

@ -112,7 +112,7 @@ public class LeashDataCommand {
)
)
);
LiteralArgumentBuilder<CommandSourceStack> $$$add = Commands.literal("add")
LiteralArgumentBuilder<CommandSourceStack> $$$add = Commands.literal("addApplyEntity")
.then(Commands.argument("target", EntityArgument.entities())
// 实体拴绳
.then($$$add$holder)
@ -120,7 +120,7 @@ public class LeashDataCommand {
// 方块拴绳
.then($$$add$pos)
);
LiteralArgumentBuilder<CommandSourceStack> $$$remove = Commands.literal("remove")
LiteralArgumentBuilder<CommandSourceStack> $$$remove = Commands.literal("removeApplyEntity")
.then(Commands.argument("target", EntityArgument.entities())
// 移除特定实体拴绳
.then(Commands.argument("holder", EntityArgument.entity())
@ -237,7 +237,7 @@ public class LeashDataCommand {
)
)
);
LiteralArgumentBuilder<CommandSourceStack> $$$set = Commands.literal("set")
LiteralArgumentBuilder<CommandSourceStack> $$$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<CommandSourceStack> 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<CommandSourceStack> 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<CommandSourceStack> context) throws CommandSyntaxException {
return -1;
}
@ -312,7 +312,7 @@ public class LeashDataCommand {
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";
public static final String SET_ELASTIC_DISTANCE = SLP_LEASH_MESSAGE_ + "setApplyEntity.elastic_distance";
private static int setElasticDistance(CommandContext<CommandSourceStack> context) throws CommandSyntaxException {
return setElasticDistance(context, 0 ,"");
}
@ -322,7 +322,7 @@ public class LeashDataCommand {
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";
public static final String SET_BLOCK_MAX_DISTANCE = SLP_LEASH_MESSAGE_ + "setApplyEntity.block_max_distance";
private static int setBlockMaxDistance(CommandContext<CommandSourceStack> context) throws CommandSyntaxException {
return setBlockMaxDistance(context, 0 ,"");
}
@ -332,7 +332,7 @@ public class LeashDataCommand {
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";
public static final String SET_BLOCK_ELASTIC_DISTANCE = SLP_LEASH_MESSAGE_ + "setApplyEntity.block_elastic_distance";
private static int setBlockElasticDistance(CommandContext<CommandSourceStack> context) throws CommandSyntaxException {
return setBlockElasticDistance(context, 0 ,"");
}

View File

@ -21,7 +21,7 @@ import net.minecraft.commands.CommandSourceStack;
public class LeashStateCommand {
// 获取State
// 设置State
// <add/set/reset> Holder<BlockPos/Entity<需判断实体类型>> <Holder/Entity> <x> <y> <z>
// <addApplyEntity/setApplyEntity/reset> Holder<BlockPos/Entity<需判断实体类型>> <Holder/Entity> <x> <y> <z>
// 设置对应目标的 拴绳偏移
public static void register(CommandDispatcher<CommandSourceStack> dispatcher) {

View File

@ -132,7 +132,7 @@ public class MotionCommand {
LiteralArgumentBuilder<CommandSourceStack> 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())

View File

@ -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) {

View File

@ -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",

View File

@ -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<UUID, ILeashState.LeashState> 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<Vec3> getOffset(Entity entity) {
public static Optional<Vec3> 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);
});
}

View File

@ -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)

View File

@ -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
)