From e977fcdfcebcbd12c87bafdd7ec2c39f0ab294cb Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Wed, 12 Apr 2023 19:06:40 -0400 Subject: [PATCH] Improved chunk deadlock detection system + patch Valhelsia Structures when installed --- build.gradle | 1 + .../modernfix/core/ModernFixMixinPlugin.java | 8 +++++ .../core/config/ModernFixEarlyConfig.java | 1 + .../chunk_deadlock/ServerChunkCacheMixin.java | 36 +++++++++++++++++++ .../valhesia/BlockStateBaseMixin.java | 28 +++++++++++++++ .../modernfix/world/IntegratedWatchdog.java | 5 +-- .../resources/META-INF/accesstransformer.cfg | 3 +- src/main/resources/modernfix.mixins.json | 1 + 8 files changed, 80 insertions(+), 3 deletions(-) create mode 100644 src/main/java/org/embeddedt/modernfix/mixin/bugfix/chunk_deadlock/valhesia/BlockStateBaseMixin.java diff --git a/build.gradle b/build.gradle index cc83e53a..80727ea5 100644 --- a/build.gradle +++ b/build.gradle @@ -98,6 +98,7 @@ dependencies { modRuntimeOnly("curse.maven:ferritecore-429235:4074330") modCompileOnly("team.chisel.ctm:CTM:${ctm_version}") modCompileOnly("curse.maven:supermartijncore-454372:4455378") + modCompileOnly("curse.maven:valhesiastructures-347488:3476252") } tasks.withType(JavaCompile) { diff --git a/src/main/java/org/embeddedt/modernfix/core/ModernFixMixinPlugin.java b/src/main/java/org/embeddedt/modernfix/core/ModernFixMixinPlugin.java index be713fa6..3ce635a5 100644 --- a/src/main/java/org/embeddedt/modernfix/core/ModernFixMixinPlugin.java +++ b/src/main/java/org/embeddedt/modernfix/core/ModernFixMixinPlugin.java @@ -251,6 +251,14 @@ public class ModernFixMixinPlugin implements IMixinConfigPlugin { } } } + } else if(mixinClassName.equals("org.embeddedt.modernfix.mixin.bugfix.chunk_deadlock.valhesia.BlockStateBaseMixin")) { + // We need to destroy Valhelsia's callback so it can never run getBlockState + for(MethodNode m : targetClass.methods) { + if(m.name.contains("valhelsia_placeDousedTorch")) { + m.instructions.clear(); + m.instructions.add(new InsnNode(Opcodes.RETURN)); + } + } } } } \ No newline at end of file diff --git a/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java b/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java index d4f7a59c..d3cbb02d 100644 --- a/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java +++ b/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java @@ -50,6 +50,7 @@ public class ModernFixEarlyConfig { this.addMixinRule("bugfix.structure_manager_crash", true); this.addMixinRule("bugfix.mc218112", true); this.addMixinRule("bugfix.chunk_deadlock", true); + this.addMixinRule("bugfix.chunk_deadlock.valhesia", modPresent("valhelsia_structures")); this.addMixinRule("bugfix.tf_cme_on_load", modPresent("twilightforest")); this.addMixinRule("bugfix.refinedstorage", modPresent("refinedstorage")); this.addMixinRule("perf.async_jei", modPresent("jei")); diff --git a/src/main/java/org/embeddedt/modernfix/mixin/bugfix/chunk_deadlock/ServerChunkCacheMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/bugfix/chunk_deadlock/ServerChunkCacheMixin.java index 9ebcac80..4b675a62 100644 --- a/src/main/java/org/embeddedt/modernfix/mixin/bugfix/chunk_deadlock/ServerChunkCacheMixin.java +++ b/src/main/java/org/embeddedt/modernfix/mixin/bugfix/chunk_deadlock/ServerChunkCacheMixin.java @@ -1,5 +1,7 @@ package org.embeddedt.modernfix.mixin.bugfix.chunk_deadlock; +import com.mojang.datafixers.util.Either; +import net.minecraft.server.level.ChunkHolder; import net.minecraft.server.level.ServerChunkCache; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.level.ChunkPos; @@ -13,12 +15,18 @@ import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import java.util.concurrent.*; @Mixin(value = ServerChunkCache.class, priority = 1100) public abstract class ServerChunkCacheMixin { @Shadow @Final private Thread mainThread; @Shadow @Final public ServerLevel level; + + @Shadow protected abstract CompletableFuture> getChunkFutureMainThread(int k, int l, ChunkStatus arg, boolean bl); + + @Shadow @Final private ServerChunkCache.MainThreadExecutor mainThreadProcessor; private final boolean debugDeadServerAccess = Boolean.getBoolean("modernfix.debugBadChunkloading"); + @Inject(method = "getChunk", at = @At("HEAD"), cancellable = true) private void bailIfServerDead(int chunkX, int chunkZ, ChunkStatus requiredStatus, boolean load, CallbackInfoReturnable cir) { if(!this.mainThread.isAlive()) { @@ -27,6 +35,34 @@ public abstract class ServerChunkCacheMixin { new Exception().printStackTrace(); } cir.setReturnValue(new EmptyLevelChunk(this.level, new ChunkPos(chunkX, chunkZ))); + } else if(Thread.currentThread() != this.mainThread) { + CompletableFuture> future = CompletableFuture.supplyAsync(() -> this.getChunkFutureMainThread(chunkX, chunkZ, requiredStatus, false), this.mainThreadProcessor).join(); + if(!future.isDone()) { + // Wait at least 500 milliseconds before printing anything + Either resultingChunk = null; + try { + resultingChunk = future.get(500, TimeUnit.MILLISECONDS); + } catch(InterruptedException | ExecutionException | TimeoutException ignored) { + } + if(resultingChunk != null && resultingChunk.left().isPresent()) { + cir.setReturnValue(resultingChunk.left().get()); + return; + } + if(debugDeadServerAccess) + ModernFix.LOGGER.warn("Async loading of a chunk was requested, this might not be desirable", new Exception()); + else + ModernFix.LOGGER.warn("Suspicious async chunkload, pass -Dmodernfix.debugBadChunkloading=true for more details"); + try { + resultingChunk = future.get(10, TimeUnit.SECONDS); + if(resultingChunk.left().isPresent()) { + cir.setReturnValue(resultingChunk.left().get()); + return; + } + } catch(InterruptedException | ExecutionException | TimeoutException e) { + ModernFix.LOGGER.error("Async chunk load took way too long, this needs to be reported to the appropriate mod.", e); + } + //cir.setReturnValue(new EmptyLevelChunk(this.level, new ChunkPos(chunkX, chunkZ))); + } } } } diff --git a/src/main/java/org/embeddedt/modernfix/mixin/bugfix/chunk_deadlock/valhesia/BlockStateBaseMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/bugfix/chunk_deadlock/valhesia/BlockStateBaseMixin.java new file mode 100644 index 00000000..b66d9ed4 --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/mixin/bugfix/chunk_deadlock/valhesia/BlockStateBaseMixin.java @@ -0,0 +1,28 @@ +package org.embeddedt.modernfix.mixin.bugfix.chunk_deadlock.valhesia; + +import com.stal111.valhelsia_structures.init.ModBlocks; +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockBehaviour; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.Vec3; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(value = BlockBehaviour.BlockStateBase.class, priority = 900) +public abstract class BlockStateBaseMixin { + @Shadow public abstract Block getBlock(); + + /** + * Do not call getBlockState here; this can cause deadlocks during world gen/lighting. + */ + @Inject(method = "getOffset", at = @At("HEAD"), cancellable = true) + private void useThisBlock(BlockGetter getter, BlockPos pos, CallbackInfoReturnable cir) { + if(this.getBlock() == ModBlocks.BONE_PILE.get()) + cir.setReturnValue(new Vec3(0.0, -0.46875, 0.0)); + } +} diff --git a/src/main/java/org/embeddedt/modernfix/world/IntegratedWatchdog.java b/src/main/java/org/embeddedt/modernfix/world/IntegratedWatchdog.java index d2d8b4df..643712ed 100644 --- a/src/main/java/org/embeddedt/modernfix/world/IntegratedWatchdog.java +++ b/src/main/java/org/embeddedt/modernfix/world/IntegratedWatchdog.java @@ -29,8 +29,9 @@ public class IntegratedWatchdog extends Thread { while(server.isRunning()) { long nextTick = this.server.getNextTickTime(); long curTime = Util.getMillis(); - if((curTime - nextTick) > MAX_TICK_DELTA) { - LOGGER.error("A single server tick has taken {}, more than {} milliseconds", (nextTick - curTime), MAX_TICK_DELTA); + long delta = curTime - nextTick; + if(delta > MAX_TICK_DELTA) { + LOGGER.error("A single server tick has taken {}, more than {} milliseconds", delta, MAX_TICK_DELTA); ThreadMXBean threadmxbean = ManagementFactory.getThreadMXBean(); ThreadInfo[] athreadinfo = threadmxbean.dumpAllThreads(true, true); StringBuilder sb = new StringBuilder(); diff --git a/src/main/resources/META-INF/accesstransformer.cfg b/src/main/resources/META-INF/accesstransformer.cfg index 6c8b87f3..a12ea313 100644 --- a/src/main/resources/META-INF/accesstransformer.cfg +++ b/src/main/resources/META-INF/accesstransformer.cfg @@ -22,4 +22,5 @@ public net.minecraft.block.AbstractBlock$Properties field_235817_s_ # isViewBloc public net.minecraft.block.AbstractBlock$Properties field_235818_t_ # hasPostProcess public net.minecraft.block.AbstractBlock$Properties field_235819_u_ # emissiveRendering public net.minecraft.block.AbstractBlock$Properties field_235806_h_ # requiresCorrectToolForDrops -public net.minecraft.block.AbstractBlock$Properties field_200959_g # destroyTime \ No newline at end of file +public net.minecraft.block.AbstractBlock$Properties field_200959_g # destroyTime +public net.minecraft.world.server.ServerChunkProvider$ChunkExecutor \ No newline at end of file diff --git a/src/main/resources/modernfix.mixins.json b/src/main/resources/modernfix.mixins.json index 78067ad7..a0e39552 100644 --- a/src/main/resources/modernfix.mixins.json +++ b/src/main/resources/modernfix.mixins.json @@ -16,6 +16,7 @@ "bugfix.refinedstorage.te_bug.ItemExternalStorageCacheMixin", "bugfix.refinedstorage.te_bug.ItemExternalStorageMixin", "bugfix.chunk_deadlock.ServerChunkCacheMixin", + "bugfix.chunk_deadlock.valhesia.BlockStateBaseMixin", "perf.remove_biome_temperature_cache.BiomeMixin", "perf.resourcepacks.ModFileResourcePackMixin", "perf.resourcepacks.VanillaPackMixin",