package dev.modzuozhi.mixin; import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod; import com.llamalad7.mixinextras.injector.wrapoperation.Operation; import dev.modzuozhi.core.dimthread.EntityTickParallel; import dev.modzuozhi.core.dimthread.ISectionKeyAccessor; import java.util.stream.Collectors; import java.util.stream.Stream; import java.util.ArrayList; import java.util.List; import net.minecraft.util.AbortableIterationConsumer; import net.minecraft.world.level.entity.EntityAccess; import net.minecraft.world.level.entity.EntitySection; import net.minecraft.world.level.entity.EntityTypeTest; import net.minecraft.world.phys.AABB; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Unique; /** * 第4/5步:实体 tick 并行——{@code EntitySection} 内部存储的并发读写互斥(借鉴 MCMT 思路,写我们自己的实现)。 *

* {@code EntitySection.storage} 是 {@code ClassInstanceMultiMap}(内部 {@code HashMap}/{@code ArrayList}, * 非线程安全)。并行子任务中:村民大脑传感器({@code NearestLivingEntitySensor} → {@code getEntitiesOfClass}) * 和移动碰撞检测({@code getEntityCollisions})会遍历 {@code storage}({@code getEntities}), * 而实体移动({@code onMove})与区块加载({@code addEntityWithoutEvent})会在任意线程(子任务、维度 worker、 * 区块加载线程、服务器主线程)增删该 {@code storage},二者并发即抛 {@code ConcurrentModificationException}, * 中断实体 tick 导致实体卡进方块/遁地/窒息。 *

* 第5步细粒度化:将第4步的全局锁 {@link EntityTickParallel#SECTION_LOCK} 降级为分片锁。 * 本类注入 {@link #modzuozhi_sectionKey}(由 {@code EntitySectionStorageMixin} 在 createSection 时写入), * 对 add/remove/getEntities 用该 key 定位分片锁——不同区域的实体 section 互不阻塞,真正并行。 * 结构级操作(storage 遍历/创建/删除、manager、callback)仍持全局锁,锁序固定为 * 全局锁 → 分片锁,无反向路径,不会死锁。 * 用 {@link WrapMethod} 整方法包装,避免 {@code @WrapOperation at HEAD} 对含分支的方法注入失败。 */ @Mixin(EntitySection.class) public abstract class EntitySectionMixin implements ISectionKeyAccessor { /** 本 section 在 {@code EntitySectionStorage} 中的 key(SectionPos.asLong),由 storage 注入。 */ @Unique private long modzuozhi_sectionKey; @Override @Unique public void modzuozhi_setSectionKey(long key) { this.modzuozhi_sectionKey = key; } @Override @Unique public long modzuozhi_getSectionKey() { return this.modzuozhi_sectionKey; } @WrapMethod(method = "add") private void modzuozhi_lockedAdd(EntityAccess entity, Operation original) { // 内容写入:分片写锁(独占) EntityTickParallel.lockSection(this.modzuozhi_sectionKey); try { original.call(entity); } finally { EntityTickParallel.unlockSection(this.modzuozhi_sectionKey); } } @WrapMethod(method = "remove") private boolean modzuozhi_lockedRemove(EntityAccess entity, Operation original) { EntityTickParallel.lockSection(this.modzuozhi_sectionKey); try { return original.call(entity); } finally { EntityTickParallel.unlockSection(this.modzuozhi_sectionKey); } } @WrapMethod(method = "getEntities(Lnet/minecraft/world/phys/AABB;Lnet/minecraft/util/AbortableIterationConsumer;)Lnet/minecraft/util/AbortableIterationConsumer$Continuation;") private AbortableIterationConsumer.Continuation modzuozhi_lockedGetEntities( AABB aabb, AbortableIterationConsumer consumer, Operation original ) { // Never invoke arbitrary mod callbacks under a read lock: a callback can move/remove // an entity, attempting to upgrade this lock to a write lock and deadlocking itself. List matches = new ArrayList<>(); AbortableIterationConsumer.Continuation result; EntityTickParallel.lockSectionRead(this.modzuozhi_sectionKey); try { result = original.call(aabb, (AbortableIterationConsumer) entity -> { matches.add(entity); return AbortableIterationConsumer.Continuation.CONTINUE; }); } finally { EntityTickParallel.unlockSectionRead(this.modzuozhi_sectionKey); } for (EntityAccess entity : matches) { if (consumer.accept(entity).shouldAbort()) return AbortableIterationConsumer.Continuation.ABORT; } return result; } @WrapMethod(method = "getEntities(Lnet/minecraft/world/level/entity/EntityTypeTest;Lnet/minecraft/world/phys/AABB;Lnet/minecraft/util/AbortableIterationConsumer;)Lnet/minecraft/util/AbortableIterationConsumer$Continuation;") private AbortableIterationConsumer.Continuation modzuozhi_lockedGetEntitiesTyped( EntityTypeTest typeTest, AABB aabb, AbortableIterationConsumer consumer, Operation original ) { List matches = new ArrayList<>(); AbortableIterationConsumer.Continuation result; EntityTickParallel.lockSectionRead(this.modzuozhi_sectionKey); try { result = original.call(typeTest, aabb, (AbortableIterationConsumer) entity -> { matches.add(entity); return AbortableIterationConsumer.Continuation.CONTINUE; }); } finally { EntityTickParallel.unlockSectionRead(this.modzuozhi_sectionKey); } for (U entity : matches) { if (consumer.accept(entity).shouldAbort()) return AbortableIterationConsumer.Continuation.ABORT; } return result; } /** * {@code getEntities()} 返回的 {@code storage.stream()} 是惰性流,锁无法覆盖其消费阶段。 * 消费方({@code PersistentEntitySectionManager.updateChunkStatus/storeChunkSections})可能在子任务 * 并发增删时迭代底层 {@code ArrayList} 而抛 CME。这里在锁内收集成快照再返回,消费时必然安全。 */ @WrapMethod(method = "getEntities()Ljava/util/stream/Stream;") private Stream modzuozhi_lockedGetEntitiesStream(Operation> original) { EntityTickParallel.lockSectionRead(this.modzuozhi_sectionKey); try { return original.call().collect(Collectors.toList()).stream(); } finally { EntityTickParallel.unlockSectionRead(this.modzuozhi_sectionKey); } } }