Improved chunk deadlock detection system + patch Valhelsia Structures

when installed
This commit is contained in:
embeddedt 2023-04-12 19:06:40 -04:00
parent 10149e9f87
commit e977fcdfce
No known key found for this signature in database
GPG Key ID: A69433EC199B5613
8 changed files with 80 additions and 3 deletions

View File

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

View File

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

View File

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

View File

@ -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<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> 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<ChunkAccess> 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<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> future = CompletableFuture.supplyAsync(() -> this.getChunkFutureMainThread(chunkX, chunkZ, requiredStatus, false), this.mainThreadProcessor).join();
if(!future.isDone()) {
// Wait at least 500 milliseconds before printing anything
Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure> 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)));
}
}
}
}

View File

@ -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<Vec3> cir) {
if(this.getBlock() == ModBlocks.BONE_PILE.get())
cir.setReturnValue(new Vec3(0.0, -0.46875, 0.0));
}
}

View File

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

View File

@ -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
public net.minecraft.block.AbstractBlock$Properties field_200959_g # destroyTime
public net.minecraft.world.server.ServerChunkProvider$ChunkExecutor

View File

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