diff --git a/src/main/java/top/r3944realms/superleadrope/CommonEventHandler.java b/src/main/java/top/r3944realms/superleadrope/CommonEventHandler.java index 526c4a9..43fad92 100644 --- a/src/main/java/top/r3944realms/superleadrope/CommonEventHandler.java +++ b/src/main/java/top/r3944realms/superleadrope/CommonEventHandler.java @@ -31,6 +31,7 @@ import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.Vec3; +import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.capabilities.RegisterCapabilitiesEvent; import net.minecraftforge.event.AttachCapabilitiesEvent; import net.minecraftforge.event.BuildCreativeModeTabContentsEvent; @@ -51,6 +52,7 @@ import net.minecraftforge.fml.event.config.ModConfigEvent; import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; import org.jetbrains.annotations.NotNull; import top.r3944realms.superleadrope.api.SuperLeadRopeApi; +import top.r3944realms.superleadrope.api.event.SuperLeadRopeEvent; import top.r3944realms.superleadrope.api.type.capabilty.LeashInfo; import top.r3944realms.superleadrope.config.LeashCommonConfig; import top.r3944realms.superleadrope.config.LeashConfigManager; @@ -269,8 +271,10 @@ public class CommonEventHandler { entities.forEach(entity -> LeashDataInnerAPI.LeashOperations.detach(entity, telEntity)); return; } + for (Entity beLeashedEntity : entities) { // --- 保存状态快照 --- + if (MinecraftForge.EVENT_BUS.post(new SuperLeadRopeEvent.teleportWithHolder(beLeashedEntity, telEntity, beLeashedEntity.level(), level, beLeashedEntity.position(), targetPos))) continue; Pose originalPose = beLeashedEntity.getPose(); boolean originalIsSprinting = beLeashedEntity.isSprinting(); float originalYaw = beLeashedEntity.getYRot(); @@ -290,7 +294,6 @@ public class CommonEventHandler { // --- 解除骑乘 --- List allPassengers = RidingFinder.getEntityFromRidingShip(originalRidingRelationship, serverLevel::getEntity); RidingDismounts.dismountEntities(allPassengers); - // --- 传送实体及乘客 --- for (Entity entity : allPassengers) { if (entity.level() != serverLevel) { diff --git a/src/main/java/top/r3944realms/superleadrope/api/event/SuperLeadRopeEvent.java b/src/main/java/top/r3944realms/superleadrope/api/event/SuperLeadRopeEvent.java index f289324..30ea34b 100644 --- a/src/main/java/top/r3944realms/superleadrope/api/event/SuperLeadRopeEvent.java +++ b/src/main/java/top/r3944realms/superleadrope/api/event/SuperLeadRopeEvent.java @@ -15,12 +15,23 @@ package top.r3944realms.superleadrope.api.event; +import net.minecraft.core.BlockPos; import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.Level; +import net.minecraft.world.phys.Vec3; import net.minecraftforge.eventbus.api.Cancelable; import net.minecraftforge.eventbus.api.Event; import net.minecraftforge.fml.event.IModBusEvent; import org.jetbrains.annotations.Nullable; +import top.r3944realms.superleadrope.api.type.capabilty.LeashHolder; +import top.r3944realms.superleadrope.api.type.capabilty.LeashInfo; +import top.r3944realms.superleadrope.util.capability.LeashDataInnerAPI; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +@SuppressWarnings("unused") public abstract class SuperLeadRopeEvent extends Event implements IModBusEvent { private final Entity LeashedEntity; @@ -31,7 +42,8 @@ public abstract class SuperLeadRopeEvent extends Event implements IModBusEvent { public Entity getLeashedEntity() { return LeashedEntity; } - + // ADD LEASH + @SuppressWarnings("unused") @Cancelable public static class AddLeash extends SuperLeadRopeEvent { private final Entity holderEntity; @@ -39,11 +51,14 @@ public abstract class SuperLeadRopeEvent extends Event implements IModBusEvent { private final Double maxLeashDistance; @Nullable private final Double elasticDistanceScale; - protected AddLeash(Entity leashedEntity, Entity holderEntity) { + public AddLeash(Entity leashedEntity, Entity holderEntity) { + this(leashedEntity, holderEntity, null, null); + } + public AddLeash(Entity leashedEntity, Entity holderEntity, @Nullable Double maxLeashDistance, @Nullable Double elasticDistanceScale) { super(leashedEntity); this.holderEntity = holderEntity; - this.maxLeashDistance = null; - this.elasticDistanceScale = null; + this.maxLeashDistance = maxLeashDistance; + this.elasticDistanceScale = elasticDistanceScale; } public Entity getHolderEntity() { return holderEntity; @@ -55,19 +70,218 @@ public abstract class SuperLeadRopeEvent extends Event implements IModBusEvent { return elasticDistanceScale; } } - // ADD LEASH + // REMOVE LEASH + @SuppressWarnings("unused") + @Cancelable + public static class RemoveLeash extends SuperLeadRopeEvent { + private final LeashHolder leashHolder; + public RemoveLeash(Entity leashedEntity, UUID holderEntity) { + this(leashedEntity, holderEntity, null, false); + } + public RemoveLeash(Entity leashedEntity, BlockPos holderKnot) { + this(leashedEntity, null, holderKnot, true); + } + private RemoveLeash(Entity leashedEntity, @Nullable UUID holderEntity, @Nullable BlockPos holderPos, boolean isSuperLeadRopeKnot) { + super(leashedEntity); + if (isSuperLeadRopeKnot) { + leashHolder = new LeashHolder(holderPos); + } else leashHolder = new LeashHolder(holderEntity); + } + public LeashHolder getLeashHolder() { + return leashHolder; + } + } // TRANSFORM LEASH + @SuppressWarnings("unused") + @Cancelable + public static class TransferLeash extends SuperLeadRopeEvent { + private final LeashHolder oldLeashHolder; + private final Entity newLeashHolder; + public TransferLeash(Entity leashedEntity, UUID holderEntity, Entity newLeashHolder) { + this(leashedEntity, holderEntity, null, false , newLeashHolder); + } + public TransferLeash(Entity leashedEntity, BlockPos holderKnot, Entity newLeashHolder) { + this(leashedEntity, null, holderKnot, true, newLeashHolder); + } + private TransferLeash(Entity leashedEntity, @Nullable UUID holderEntity, @Nullable BlockPos holderPos, boolean isSuperLeadRopeKnot, Entity newLeashHolder) { + super(leashedEntity); + if (isSuperLeadRopeKnot) { + oldLeashHolder = new LeashHolder(holderPos); + } else oldLeashHolder = new LeashHolder(holderEntity); + this.newLeashHolder = newLeashHolder; + } - // REMOVE LEASH + public Entity getNewLeashHolder() { + return newLeashHolder; + } + + public LeashHolder getOldLeashHolder() { + return oldLeashHolder; + } + } // MODIFY LEASH MAX_LEASH_LENGTH / ELASTIC_DISTANCE_SCALE + @SuppressWarnings("unused") + @Cancelable + public static class ModifyValue extends SuperLeadRopeEvent { + @Nullable + private final LeashHolder holder; + @Nullable + private final Double oldValue; + @Nullable + private final Double newValue; + private final Type type; + private final Scope scope; + public enum Type { + MAX_DISTANCE, + ELASTIC_DISTANCE_SCALE, + } + public enum Scope { + STATIC, + INSTANCE + } + public ModifyValue(Entity leashedEntity, UUID holderUUID, @Nullable Double oldValue, @Nullable Double newValue, Type type) { + super(leashedEntity); + this.holder = new LeashHolder(holderUUID); + this.oldValue = oldValue; + this.newValue = newValue; + this.type = type; + this.scope = Scope.INSTANCE; + } + public ModifyValue(Entity leashedEntity, BlockPos knotBlockpos, @Nullable Double oldValue, @Nullable Double newValue, Type type) { + super(leashedEntity); + this.holder = new LeashHolder(knotBlockpos); + this.oldValue = oldValue; + this.newValue = newValue; + this.type = type; + this.scope = Scope.INSTANCE; + } + public ModifyValue(Entity leashedEntity, @Nullable Double oldValue, @Nullable Double newValue, Type type) { + super(leashedEntity); + this.holder = null; + this.oldValue = oldValue; + this.newValue = newValue; + this.type = type; + this.scope = Scope.STATIC; + } + public @Nullable LeashHolder getHolder() { + return holder; + } + + public @Nullable Double getOldValue() { + return oldValue; + } + + public @Nullable Double getNewValue() { + return newValue; + } + + public Type getType() { + return type; + } + + public Scope getScope() { + return scope; + } + + } // HAS FOCUS + @SuppressWarnings("unused") + @Cancelable + public static class hasFocus extends SuperLeadRopeEvent { + private final Map vaildLeashHolders ; + private final Map vaildLeashKnots; + private final Entity finalForceTarget; + private Vec3 combinedForce; + public hasFocus(Entity leashedEntity, Entity finalForceTarget, Vec3 combinedForce, Map vaildLeashHolders, Map vaildLeashKnots) { + super(leashedEntity); + this.finalForceTarget = finalForceTarget; + this.combinedForce = combinedForce; + this.vaildLeashHolders = new HashMap<>(vaildLeashHolders); + this.vaildLeashKnots = new HashMap<>(vaildLeashKnots); + } + + public Entity getFinalForceTarget() { + return finalForceTarget; + } + public Vec3 getCombinedForce() { + return combinedForce; + } + + public void setCombinedForce(Vec3 combinedForce) { + this.combinedForce = combinedForce; + } + public Map getVaildLeashHolders() { + return vaildLeashHolders; + } + + public Map getVaildLeashKnots() { + return vaildLeashKnots; + } + } // KEEP NOT BREAK TICK + @SuppressWarnings("unused") + public static class keepNotBreakTick extends SuperLeadRopeEvent { + private final int remainedTicks; + private final Entity holderEntity; + private final Map.Entry entry; + public keepNotBreakTick(Entity leashedEntity, int remainedTicks, Entity holderEntity, Map.Entry entry) { + super(leashedEntity); + this.remainedTicks = remainedTicks; + this.holderEntity = holderEntity; + this.entry = entry; + } + public Entity getHolderEntity() { + return holderEntity; + } + public int getRemainedTicks() { + return remainedTicks; + } + public void resetRemainedTicks() { + entry.setValue(entry.getValue().resetKeepTicks()); + } + public int getMaxKeepTicks() { + return entry.getValue().maxKeepLeashTicks(); + } + } // TELEPORT + @Cancelable + @SuppressWarnings("unused") + public static class teleportWithHolder extends SuperLeadRopeEvent { + private final Entity holderEntity; + private final Level originalLevel, newLevel; + private final Vec3 originalPosition, newPosition; + public teleportWithHolder(Entity leashedEntity, Entity holderEntity, Level originalLevel, Level newLevel, Vec3 originalPosition, Vec3 newPosition) { + super(leashedEntity); + this.holderEntity = holderEntity; + this.originalLevel = originalLevel; + this.newLevel = newLevel; + this.originalPosition = originalPosition; + this.newPosition = newPosition; + } + public Entity getHolderEntity() { + return holderEntity; + } + public Vec3 getOriginalPosition() { + return originalPosition; + } + public Level getOriginalLevel() { + return originalLevel; + } + + public Level getNewLevel() { + return newLevel; + } + + public Vec3 getNewPosition() { + return newPosition; + } + + } } diff --git a/src/main/java/top/r3944realms/superleadrope/api/type/capabilty/LeashHolder.java b/src/main/java/top/r3944realms/superleadrope/api/type/capabilty/LeashHolder.java new file mode 100644 index 0000000..e97679a --- /dev/null +++ b/src/main/java/top/r3944realms/superleadrope/api/type/capabilty/LeashHolder.java @@ -0,0 +1,30 @@ +/* + * Super Lead rope mod + * Copyright (C) 2025 R3944Realms + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package top.r3944realms.superleadrope.api.type.capabilty; + +import net.minecraft.core.BlockPos; +import org.jetbrains.annotations.Nullable; + +import java.util.UUID; + +public record LeashHolder(@Nullable UUID holderUUID, @Nullable BlockPos knotPos, boolean isKnot) { + public LeashHolder(UUID holderUUID) { + this (holderUUID, null, false); + } + public LeashHolder(BlockPos knotPos) { + this(null, knotPos, true); + } +} diff --git a/src/main/java/top/r3944realms/superleadrope/config/LeashConfigManager.java b/src/main/java/top/r3944realms/superleadrope/config/LeashConfigManager.java index 17266a8..2157036 100644 --- a/src/main/java/top/r3944realms/superleadrope/config/LeashConfigManager.java +++ b/src/main/java/top/r3944realms/superleadrope/config/LeashConfigManager.java @@ -25,12 +25,15 @@ import top.r3944realms.superleadrope.SuperLeadRope; import java.util.*; import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Predicate; import java.util.regex.Matcher; import static top.r3944realms.superleadrope.config.LeashCommonConfig.Common.OFFSET_PATTERN; public class LeashConfigManager { - + // ========== 最值检测 ========== + public static final Predicate MAX_DISTANCE_CHECK = distance -> distance == null || (distance >= 6.0 && distance <= 256.0); + public static final Predicate ELASTIC_DISTANCE_CHECK = distance -> distance == null || (distance >= 0.2 && distance <= 4.0); // ========== 偏移映射 ========== private final Map entityHolderMap = new ConcurrentHashMap<>(); private final Map tagHolderMap = new ConcurrentHashMap<>(); 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 e3e7788..212f4f1 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 @@ -33,6 +33,7 @@ import net.minecraft.world.level.ClipContext; import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.HitResult; import net.minecraft.world.phys.Vec3; +import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.fml.loading.FMLEnvironment; import net.minecraftforge.network.PacketDistributor; import org.jetbrains.annotations.Contract; @@ -40,9 +41,11 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import top.r3944realms.superleadrope.CommonEventHandler; import top.r3944realms.superleadrope.SuperLeadRope; +import top.r3944realms.superleadrope.api.event.SuperLeadRopeEvent; import top.r3944realms.superleadrope.api.type.capabilty.LeashInfo; import top.r3944realms.superleadrope.compat.CurtainCompat; import top.r3944realms.superleadrope.api.type.capabilty.ILeashData; +import top.r3944realms.superleadrope.config.LeashConfigManager; import top.r3944realms.superleadrope.content.entity.SuperLeashKnotEntity; import top.r3944realms.superleadrope.core.register.SLPSoundEvents; import top.r3944realms.superleadrope.network.NetworkHandler; @@ -68,24 +71,25 @@ import java.util.stream.Stream; * * * - * 距离 ≤ maxDistance + * 距离 ≤ maxDistance * elasticDistanceScale * 正常弹性拉力,重置 keepLeashTicks 为最大值 * * - * maxDistance < distance ≤ 2*maxDistance + * maxDistance * elasticDistanceScale < distance ≤ 配置extremeSnapFactor * maxDistance * elasticDistanceScale * 增强拉力,并减少 keepLeashTicks(每Tick减1) * * - * distance > 2*maxDistance && keepLeashTicks > 0 + * distance > 配置extremeSnapFactor * maxDistance * elasticDistanceScale && keepLeashTicks > 0 * 施加更强拉力并减少Tick * * - * distance > 2*maxDistance && keepLeashTicks == 0 + * distance > 配置extremeSnapFactor * maxDistance * elasticDistanceScale && keepLeashTicks == 0 * 立即断裂 * * * */ +@SuppressWarnings("DuplicatedCode") public class LeashDataImpl implements ILeashData { private final Entity entity; private boolean needsSync = false; @@ -157,6 +161,8 @@ public class LeashDataImpl implements ILeashData { @Override public void setStaticMaxDistance(@Nullable Double distance) { + if (!LeashConfigManager.MAX_DISTANCE_CHECK.test(distance)) return; + if (MinecraftForge.EVENT_BUS.post(new SuperLeadRopeEvent.ModifyValue(this.entity, getStaticMaxDistance(), distance, SuperLeadRopeEvent.ModifyValue.Type.MAX_DISTANCE))) return; staticMaxDistance = distance; } @@ -178,14 +184,15 @@ public class LeashDataImpl implements ILeashData { public void updateAllMaxDistance() { leashHolders.forEach((uuid, leashInfo) -> { if (leashInfo.isNeedUpdateDistance()) { - setMaxDistance(uuid, getCurrentMaxDistance()); + setMaxDistanceInner(uuid, getCurrentMaxDistance()); } }); leashKnots.forEach((blockPos, leashInfo) -> { if (leashInfo.isNeedUpdateDistance()) { - setMaxDistance(blockPos, getCurrentMaxDistance()); + setMaxDistanceInner(blockPos, getCurrentMaxDistance()); } }); + markForSync(); } @Override @@ -194,8 +201,10 @@ public class LeashDataImpl implements ILeashData { } @Override - public void setStaticElasticDistanceScale(@Nullable Double distance) { - staticElasticDistanceScale = distance; + public void setStaticElasticDistanceScale(@Nullable Double scale) { + if (!LeashConfigManager.ELASTIC_DISTANCE_CHECK.test(scale)) return; + if (MinecraftForge.EVENT_BUS.post(new SuperLeadRopeEvent.ModifyValue(this.entity, getStaticElasticDistanceScale(), scale, SuperLeadRopeEvent.ModifyValue.Type.ELASTIC_DISTANCE_SCALE))) return; + staticElasticDistanceScale = scale; } @Override @@ -216,14 +225,15 @@ public class LeashDataImpl implements ILeashData { public void updateAllElasticDistanceScale() { leashHolders.forEach((uuid, leashInfo) -> { if (leashInfo.isNeedUpdateScale()) { - setElasticDistanceScale(uuid, getCurrentElasticDistanceScale()); + setElasticDistanceScaleInner(uuid, getCurrentElasticDistanceScale()); } }); leashKnots.forEach((blockPos, leashInfo) -> { if (leashInfo.isNeedUpdateScale()) { - setElasticDistanceScale(blockPos, getCurrentMaxDistance()); + setElasticDistanceScaleInner(blockPos, getCurrentMaxDistance()); } }); + markForSync(); } @Override @@ -273,7 +283,8 @@ public class LeashDataImpl implements ILeashData { || (isSuperKnot && leashKnots.containsKey(((SuperLeashKnotEntity) holder).getPos()))) { return false; } - + if (!LeashConfigManager.MAX_DISTANCE_CHECK.test(maxDistance) || !LeashConfigManager.ELASTIC_DISTANCE_CHECK.test(elasticDistanceScale)) return false; + if (MinecraftForge.EVENT_BUS.post(new SuperLeadRopeEvent.AddLeash(holder, this.entity, maxDistance, elasticDistanceScale))) return false; if (!canBeLeashed()) { Optional uuidOptional = occupyLeash(); if (uuidOptional.isEmpty()) { @@ -338,6 +349,20 @@ public class LeashDataImpl implements ILeashData { return true; } + private void updateLeashInfoInner( + @NotNull Map map, + K key, + Function updater + ) { + LeashInfo old = map.get(key); + if (old == null || old.holderIdOpt().isEmpty()) return; + + LeashInfo updated = updater.apply(old); + if (updated == null) return; + + map.put(key, updated); + } + @Override public boolean setMaxDistance(Entity holder, @Nullable Double newMaxDistance) { return holder instanceof SuperLeashKnotEntity superLeashKnotEntity ? @@ -353,15 +378,17 @@ public class LeashDataImpl implements ILeashData { } @Override - public boolean setMaxDistance(Entity holder, @Nullable Double distance, int maxKeepTicks, String reserved) { + public boolean setMaxDistance(Entity holder, @Nullable Double newMaxDistance, int maxKeepTicks, String reserved) { return holder instanceof SuperLeashKnotEntity superLeashKnotEntity ? - setMaxDistance(superLeashKnotEntity.getPos(), distance, maxKeepTicks, reserved) : - setMaxDistance(holder.getUUID(), distance, maxKeepTicks, reserved); + setMaxDistance(superLeashKnotEntity.getPos(), newMaxDistance, maxKeepTicks, reserved) : + setMaxDistance(holder.getUUID(), newMaxDistance, maxKeepTicks, reserved); } @SuppressWarnings("OptionalGetWithoutIsPresent") @Override public boolean setMaxDistance(UUID holderUUID, @Nullable Double newMaxDistance) { + if (!LeashConfigManager.MAX_DISTANCE_CHECK.test(newMaxDistance)) return false; + if (MinecraftForge.EVENT_BUS.post(new SuperLeadRopeEvent.ModifyValue(this.entity, holderUUID, leashHolders.get(holderUUID).maxDistance(), newMaxDistance, SuperLeadRopeEvent.ModifyValue.Type.MAX_DISTANCE))) return false; return updateLeashInfo(leashHolders, holderUUID, old -> new LeashInfo( old.holderUUIDOpt().get(), old.holderIdOpt().get(), @@ -373,10 +400,27 @@ public class LeashDataImpl implements ILeashData { old.maxKeepLeashTicks() )); } + @SuppressWarnings("OptionalGetWithoutIsPresent") + public void setMaxDistanceInner(UUID holderUUID, @Nullable Double newMaxDistance) { + if (!LeashConfigManager.MAX_DISTANCE_CHECK.test(newMaxDistance)) return; + if (MinecraftForge.EVENT_BUS.post(new SuperLeadRopeEvent.ModifyValue(this.entity, holderUUID, leashHolders.get(holderUUID).maxDistance(), newMaxDistance, SuperLeadRopeEvent.ModifyValue.Type.MAX_DISTANCE))) return; + updateLeashInfoInner(leashHolders, holderUUID, old -> new LeashInfo( + old.holderUUIDOpt().get(), + old.holderIdOpt().get(), + old.marks(), + old.reserved(), + newMaxDistance, + old.elasticDistanceScale(), + old.keepLeashTicks(), + old.maxKeepLeashTicks() + )); + } @SuppressWarnings("OptionalGetWithoutIsPresent") @Override public boolean setMaxDistance(UUID holderUUID, @Nullable Double newMaxDistance, int newMaxKeepLeashTicks) { + if (!LeashConfigManager.MAX_DISTANCE_CHECK.test(newMaxDistance)) return false; + if (MinecraftForge.EVENT_BUS.post(new SuperLeadRopeEvent.ModifyValue(this.entity, holderUUID, leashHolders.get(holderUUID).maxDistance(), newMaxDistance, SuperLeadRopeEvent.ModifyValue.Type.MAX_DISTANCE))) return false; return updateLeashInfo(leashHolders, holderUUID, old -> new LeashInfo( old.holderUUIDOpt().get(), old.holderIdOpt().get(), @@ -391,13 +435,15 @@ public class LeashDataImpl implements ILeashData { @SuppressWarnings("OptionalGetWithoutIsPresent") @Override - public boolean setMaxDistance(UUID holderUUID, @Nullable Double distance, int maxKeepTicks, String reserved) { + public boolean setMaxDistance(UUID holderUUID, @Nullable Double newMaxDistance, int maxKeepTicks, String reserved) { + if (!LeashConfigManager.MAX_DISTANCE_CHECK.test(newMaxDistance)) return false; + if (MinecraftForge.EVENT_BUS.post(new SuperLeadRopeEvent.ModifyValue(this.entity, holderUUID, leashHolders.get(holderUUID).maxDistance(), newMaxDistance, SuperLeadRopeEvent.ModifyValue.Type.MAX_DISTANCE))) return false; return updateLeashInfo(leashHolders, holderUUID, old -> new LeashInfo( old.holderUUIDOpt().get(), old.holderIdOpt().get(), old.marks(), reserved, - distance, + newMaxDistance, old.elasticDistanceScale(), Math.min(old.keepLeashTicks(), maxKeepTicks), maxKeepTicks @@ -407,6 +453,8 @@ public class LeashDataImpl implements ILeashData { @SuppressWarnings("OptionalGetWithoutIsPresent") @Override public boolean setMaxDistance(BlockPos knotPos, @Nullable Double newMaxDistance) { + if (!LeashConfigManager.MAX_DISTANCE_CHECK.test(newMaxDistance)) return false; + if (MinecraftForge.EVENT_BUS.post(new SuperLeadRopeEvent.ModifyValue(this.entity, knotPos, leashKnots.get(knotPos).maxDistance(), newMaxDistance, SuperLeadRopeEvent.ModifyValue.Type.MAX_DISTANCE))) return false; return updateLeashInfo(leashKnots, knotPos, old -> new LeashInfo( old.blockPosOpt().get(), old.holderIdOpt().get(), @@ -419,9 +467,27 @@ public class LeashDataImpl implements ILeashData { )); } + @SuppressWarnings("OptionalGetWithoutIsPresent") + public void setMaxDistanceInner(BlockPos knotPos, @Nullable Double newMaxDistance) { + if (!LeashConfigManager.MAX_DISTANCE_CHECK.test(newMaxDistance)) return; + if (MinecraftForge.EVENT_BUS.post(new SuperLeadRopeEvent.ModifyValue(this.entity, knotPos, leashKnots.get(knotPos).maxDistance(), newMaxDistance, SuperLeadRopeEvent.ModifyValue.Type.MAX_DISTANCE))) return; + updateLeashInfoInner(leashKnots, knotPos, old -> new LeashInfo( + old.blockPosOpt().get(), + old.holderIdOpt().get(), + old.marks(), + old.reserved(), + newMaxDistance, + old.elasticDistanceScale(), + old.keepLeashTicks(), + old.maxKeepLeashTicks() + )); + } + @SuppressWarnings("OptionalGetWithoutIsPresent") @Override public boolean setMaxDistance(BlockPos knotPos, @Nullable Double newMaxDistance, int newMaxKeepLeashTicks) { + if (!LeashConfigManager.MAX_DISTANCE_CHECK.test(newMaxDistance)) return false; + if (MinecraftForge.EVENT_BUS.post(new SuperLeadRopeEvent.ModifyValue(this.entity, knotPos, leashKnots.get(knotPos).maxDistance(), newMaxDistance, SuperLeadRopeEvent.ModifyValue.Type.MAX_DISTANCE))) return false; return updateLeashInfo(leashKnots, knotPos, old -> new LeashInfo( old.blockPosOpt().get(), old.holderIdOpt().get(), @@ -436,13 +502,15 @@ public class LeashDataImpl implements ILeashData { @SuppressWarnings("OptionalGetWithoutIsPresent") @Override - public boolean setMaxDistance(BlockPos knotPos, @Nullable Double distance, int maxKeepTicks, String reserved) { + public boolean setMaxDistance(BlockPos knotPos, @Nullable Double newMaxDistance, int maxKeepTicks, String reserved) { + if (!LeashConfigManager.MAX_DISTANCE_CHECK.test(newMaxDistance)) return false; + if (MinecraftForge.EVENT_BUS.post(new SuperLeadRopeEvent.ModifyValue(this.entity, knotPos, leashKnots.get(knotPos).maxDistance(), newMaxDistance, SuperLeadRopeEvent.ModifyValue.Type.MAX_DISTANCE))) return false; return updateLeashInfo(leashKnots, knotPos, old -> new LeashInfo( old.blockPosOpt().get(), old.holderIdOpt().get(), old.marks(), reserved, - distance, + newMaxDistance, old.elasticDistanceScale(), Math.min(old.keepLeashTicks(), maxKeepTicks), maxKeepTicks @@ -460,6 +528,8 @@ public class LeashDataImpl implements ILeashData { @SuppressWarnings("OptionalGetWithoutIsPresent") @Override public boolean setElasticDistanceScale(UUID holderUUID, @Nullable Double scale) { + if (!LeashConfigManager.ELASTIC_DISTANCE_CHECK.test(scale)) return false; + if (MinecraftForge.EVENT_BUS.post(new SuperLeadRopeEvent.ModifyValue(this.entity, holderUUID, leashHolders.get(holderUUID).elasticDistanceScale(), scale, SuperLeadRopeEvent.ModifyValue.Type.ELASTIC_DISTANCE_SCALE))) return false; return updateLeashInfo(leashHolders, holderUUID, old -> new LeashInfo( old.holderUUIDOpt().get(), old.holderIdOpt().get(), @@ -471,10 +541,27 @@ public class LeashDataImpl implements ILeashData { old.maxKeepLeashTicks() )); } + @SuppressWarnings("OptionalGetWithoutIsPresent") + public void setElasticDistanceScaleInner(UUID holderUUID, @Nullable Double scale) { + if (!LeashConfigManager.ELASTIC_DISTANCE_CHECK.test(scale)) return; + if (MinecraftForge.EVENT_BUS.post(new SuperLeadRopeEvent.ModifyValue(this.entity, holderUUID, leashHolders.get(holderUUID).elasticDistanceScale(), scale, SuperLeadRopeEvent.ModifyValue.Type.ELASTIC_DISTANCE_SCALE))) return; + updateLeashInfoInner(leashHolders, holderUUID, old -> new LeashInfo( + old.holderUUIDOpt().get(), + old.holderIdOpt().get(), + old.marks(), + old.reserved(), + old.maxDistance(), + scale, + old.keepLeashTicks(), + old.maxKeepLeashTicks() + )); + } @SuppressWarnings("OptionalGetWithoutIsPresent") @Override public boolean setElasticDistanceScale(BlockPos knotPos, @Nullable Double scale) { + if (!LeashConfigManager.ELASTIC_DISTANCE_CHECK.test(scale)) return false; + if (MinecraftForge.EVENT_BUS.post(new SuperLeadRopeEvent.ModifyValue(this.entity, knotPos, leashKnots.get(knotPos).elasticDistanceScale(), scale, SuperLeadRopeEvent.ModifyValue.Type.ELASTIC_DISTANCE_SCALE))) return false; return updateLeashInfo(leashKnots, knotPos, old -> new LeashInfo( old.blockPosOpt().get(), old.holderIdOpt().get(), @@ -486,6 +573,21 @@ public class LeashDataImpl implements ILeashData { old.maxKeepLeashTicks() )); } + @SuppressWarnings("OptionalGetWithoutIsPresent") + public void setElasticDistanceScaleInner(BlockPos knotPos, @Nullable Double scale) { + if (!LeashConfigManager.ELASTIC_DISTANCE_CHECK.test(scale)) return; + if (MinecraftForge.EVENT_BUS.post(new SuperLeadRopeEvent.ModifyValue(this.entity, knotPos, leashKnots.get(knotPos).elasticDistanceScale(), scale, SuperLeadRopeEvent.ModifyValue.Type.ELASTIC_DISTANCE_SCALE))) return; + updateLeashInfoInner(leashKnots, knotPos, old -> new LeashInfo( + old.blockPosOpt().get(), + old.holderIdOpt().get(), + old.marks(), + old.reserved(), + old.maxDistance(), + scale, + old.keepLeashTicks(), + old.maxKeepLeashTicks() + )); + } @Override public boolean setElasticDistanceScale(Entity holder, @Nullable Double scale, int newMaxKeepLeashTicks) { @@ -505,6 +607,8 @@ public class LeashDataImpl implements ILeashData { @SuppressWarnings("OptionalGetWithoutIsPresent") @Override public boolean setElasticDistanceScale(UUID holderUUID, @Nullable Double scale, int newMaxKeepLeashTicks) { + if (!LeashConfigManager.ELASTIC_DISTANCE_CHECK.test(scale)) return false; + if (MinecraftForge.EVENT_BUS.post(new SuperLeadRopeEvent.ModifyValue(this.entity, holderUUID, leashHolders.get(holderUUID).elasticDistanceScale(), scale, SuperLeadRopeEvent.ModifyValue.Type.ELASTIC_DISTANCE_SCALE))) return false; return updateLeashInfo(leashHolders, holderUUID, old -> new LeashInfo( old.holderUUIDOpt().get(), old.holderIdOpt().get(), @@ -520,6 +624,8 @@ public class LeashDataImpl implements ILeashData { @SuppressWarnings("OptionalGetWithoutIsPresent") @Override public boolean setElasticDistanceScale(UUID holderUUID, @Nullable Double scale, int maxKeepTicks, String reserved) { + if (!LeashConfigManager.ELASTIC_DISTANCE_CHECK.test(scale)) return false; + if (MinecraftForge.EVENT_BUS.post(new SuperLeadRopeEvent.ModifyValue(this.entity, holderUUID, leashHolders.get(holderUUID).elasticDistanceScale(), scale, SuperLeadRopeEvent.ModifyValue.Type.ELASTIC_DISTANCE_SCALE))) return false; return updateLeashInfo(leashHolders, holderUUID, old -> new LeashInfo( old.holderUUIDOpt().get(), old.holderIdOpt().get(), @@ -535,6 +641,8 @@ public class LeashDataImpl implements ILeashData { @SuppressWarnings("OptionalGetWithoutIsPresent") @Override public boolean setElasticDistanceScale(BlockPos knotPos, @Nullable Double scale, int newMaxKeepLeashTicks) { + if (!LeashConfigManager.ELASTIC_DISTANCE_CHECK.test(scale)) return false; + if (MinecraftForge.EVENT_BUS.post(new SuperLeadRopeEvent.ModifyValue(this.entity, knotPos, leashKnots.get(knotPos).elasticDistanceScale(), scale, SuperLeadRopeEvent.ModifyValue.Type.ELASTIC_DISTANCE_SCALE))) return false; return updateLeashInfo(leashKnots, knotPos, old -> new LeashInfo( old.blockPosOpt().get(), old.holderIdOpt().get(), @@ -550,6 +658,8 @@ public class LeashDataImpl implements ILeashData { @SuppressWarnings("OptionalGetWithoutIsPresent") @Override public boolean setElasticDistanceScale(BlockPos knotPos, @Nullable Double scale, int newMaxKeepLeashTicks, String reserved) { + if (!LeashConfigManager.ELASTIC_DISTANCE_CHECK.test(scale)) return false; + if (MinecraftForge.EVENT_BUS.post(new SuperLeadRopeEvent.ModifyValue(this.entity, knotPos, leashKnots.get(knotPos).elasticDistanceScale(), scale, SuperLeadRopeEvent.ModifyValue.Type.ELASTIC_DISTANCE_SCALE))) return false; return updateLeashInfo(leashKnots, knotPos, old -> new LeashInfo( old.blockPosOpt().get(), old.holderIdOpt().get(), @@ -570,14 +680,16 @@ public class LeashDataImpl implements ILeashData { Vec3 combinedForce = Vec3.ZERO; Vec3 combinedDirection = Vec3.ZERO; int validLeashes = 0; - - // 1. 计算所有拴绳的合力和平均方向 + Map vaildLeashHolders = new HashMap<>(); + Map vaildLeashKnots = new HashMap<>(); + // 计算所有拴绳的合力和平均方向 for (Map.Entry entry : leashHolders.entrySet()) { Vec3 force = calculateLeashForceForUUID(entry); if (force != null) { combinedForce = combinedForce.add(force); combinedDirection = combinedDirection.add(force.normalize()); validLeashes++; + vaildLeashHolders.put(entry.getKey(), entry.getValue()); } } @@ -587,13 +699,16 @@ public class LeashDataImpl implements ILeashData { combinedForce = combinedForce.add(force); combinedDirection = combinedDirection.add(force.normalize()); validLeashes++; + vaildLeashKnots.put(entry.getKey(), entry.getValue()); } } boolean hasForce = !combinedForce.equals(Vec3.ZERO); Entity targetEntity = RindingLeash.getFinalEntityForLeashIfForce(entity, hasForce); - if (targetEntity != null && hasForce) { + SuperLeadRopeEvent.hasFocus hasFocus = new SuperLeadRopeEvent.hasFocus(this.entity, targetEntity, combinedForce, vaildLeashHolders, vaildLeashKnots); + if (MinecraftForge.EVENT_BUS.post(hasFocus)) return; + combinedForce = hasFocus.getCombinedForce(); // 玩家与普通实体统一力应用 if (targetEntity instanceof ServerPlayer player && CurtainCompat.isNotFakePlayer(player)) { RindingLeash.applyForceToPlayer( @@ -733,7 +848,7 @@ public class LeashDataImpl implements ILeashData { } else { if (!delayedHolders.contains(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); + leashHolders.remove(uuid); //保持系统稳定的移除,不走事件 } return null; } @@ -764,6 +879,7 @@ public class LeashDataImpl implements ILeashData { // 计算临界拉力 Vec3 pullForce = calculateCriticalPullForce(holderPos, entityPos, distance, maxDistance, elasticLimitDistance); entry.setValue(info.decrementKeepTicks()); + MinecraftForge.EVENT_BUS.post(new SuperLeadRopeEvent.keepNotBreakTick(this.entity, entry.getValue().keepLeashTicks(), holder, entry)); return pullForce; } // 断裂 @@ -844,6 +960,7 @@ public class LeashDataImpl implements ILeashData { } @Override public boolean removeLeash(UUID holderUUID) { + if (MinecraftForge.EVENT_BUS.post(new SuperLeadRopeEvent.RemoveLeash(this.entity, holderUUID))) return false; boolean removed = leashHolders.remove(holderUUID) != null; if (removed) { LeashStateInnerAPI.Operations.detach(entity, holderUUID); @@ -852,35 +969,56 @@ public class LeashDataImpl implements ILeashData { return removed; } - @Override - public boolean removeLeash(BlockPos knotPos) { + private void removeLeashInner(BlockPos knotPos) { + if (MinecraftForge.EVENT_BUS.post(new SuperLeadRopeEvent.RemoveLeash(this.entity, knotPos))) return; + boolean removed = leashKnots.remove(knotPos) != null; + if (removed) { + LeashStateInnerAPI.Operations.detach(entity, knotPos); + } + } + + private void removeLeashInner(UUID holderUUID) { + if (MinecraftForge.EVENT_BUS.post(new SuperLeadRopeEvent.RemoveLeash(this.entity, holderUUID))) return; + boolean removed = leashHolders.remove(holderUUID) != null; + if (removed) { + LeashStateInnerAPI.Operations.detach(entity, holderUUID); + } + } + + @Override + public boolean removeLeash(BlockPos knotPos) { + if (MinecraftForge.EVENT_BUS.post(new SuperLeadRopeEvent.RemoveLeash(this.entity, knotPos))) return false; boolean removed = leashKnots.remove(knotPos) != null; if (removed) { LeashStateInnerAPI.Operations.detach(entity, knotPos); - markForSync(); } return removed; } @Override public void removeAllLeashes() { - leashHolders.clear(); - leashKnots.clear(); - LeashStateInnerAPI.Offset.removeHolderAll(entity); + removeAllHolderLeashesInner(); + removeAllKnotLeashes(); markForSync(); } + private void removeAllHolderLeashesInner() { + leashHolders.forEach((uuid, leashHolder) -> removeLeashInner(uuid)); + } + + private void removeAllKnotLeashesInner() { + leashKnots.forEach((blockPos, leashHolder) -> removeLeashInner(blockPos)); + } + @Override public void removeAllHolderLeashes() { - leashHolders.clear(); - LeashStateInnerAPI.Offset.removeAllHolderUUIDs(entity); + removeAllHolderLeashesInner(); markForSync(); } @Override public void removeAllKnotLeashes() { - leashKnots.clear(); - LeashStateInnerAPI.Offset.removeAllHolderBlockPoses(entity); + removeAllKnotLeashesInner(); markForSync(); } @@ -901,6 +1039,7 @@ public class LeashDataImpl implements ILeashData { // 将拴绳持有者转移到新实体(非拴绳结 -> 任意) @Override public boolean transferLeash(UUID oldHolderUUID, Entity newHolder) { + if(MinecraftForge.EVENT_BUS.post(new SuperLeadRopeEvent.TransferLeash(this.entity, oldHolderUUID, newHolder))) return false; LeashInfo info = leashHolders.remove(oldHolderUUID); if (info == null || newHolder == null) return false; if(newHolder instanceof SuperLeashKnotEntity superLeashKnotEntity) { @@ -916,6 +1055,7 @@ public class LeashDataImpl implements ILeashData { } @Override public boolean transferLeash(UUID oldHolderUUID, Entity newHolder, String reserved) { + if(MinecraftForge.EVENT_BUS.post(new SuperLeadRopeEvent.TransferLeash(this.entity, oldHolderUUID, newHolder))) return false; LeashInfo info = leashHolders.remove(oldHolderUUID); if (info == null || newHolder == null) return false; if(newHolder instanceof SuperLeashKnotEntity superLeashKnotEntity) { @@ -932,6 +1072,7 @@ public class LeashDataImpl implements ILeashData { @Override public boolean transferLeash(BlockPos knotPos, Entity newHolder) { + if(MinecraftForge.EVENT_BUS.post(new SuperLeadRopeEvent.TransferLeash(this.entity, knotPos, newHolder))) return false; LeashInfo info = leashKnots.remove(knotPos); if (info == null || newHolder == null) return false; if(newHolder instanceof SuperLeashKnotEntity superLeashKnotEntity) { @@ -948,6 +1089,7 @@ public class LeashDataImpl implements ILeashData { @Override public boolean transferLeash(BlockPos knotPos, Entity newHolder, String reserved) { + if(MinecraftForge.EVENT_BUS.post(new SuperLeadRopeEvent.TransferLeash(this.entity, knotPos, newHolder))) return false; LeashInfo info = leashKnots.remove(knotPos); if (info == null || newHolder == null) return false; if(newHolder instanceof SuperLeashKnotEntity superLeashKnotEntity) {