diff --git a/src/main/java/com/mitchej123/hodgepodge/config/FixesConfig.java b/src/main/java/com/mitchej123/hodgepodge/config/FixesConfig.java index 7dd410b1..db7e2023 100644 --- a/src/main/java/com/mitchej123/hodgepodge/config/FixesConfig.java +++ b/src/main/java/com/mitchej123/hodgepodge/config/FixesConfig.java @@ -352,6 +352,12 @@ public class FixesConfig { @Config.RequiresMcRestart public static boolean fixCaseCommands; + @Config.Comment("Limit the number of recursive cascading block updates during world generation to prevent stack overflow crashes, set to -1 to disable the limit.") + @Config.RangeInt(min = -1) + @Config.DefaultInt(256) // A stack overflow with water updates happens somewhere above 300 updates with default Java + // settings + public static int limitRecursiveBlockUpdateDepth; + // affecting multiple mods @Config.Comment("Remove old/stale/outdated update checks.") diff --git a/src/main/java/com/mitchej123/hodgepodge/mixins/Mixins.java b/src/main/java/com/mitchej123/hodgepodge/mixins/Mixins.java index c01aed89..7089dfab 100644 --- a/src/main/java/com/mitchej123/hodgepodge/mixins/Mixins.java +++ b/src/main/java/com/mitchej123/hodgepodge/mixins/Mixins.java @@ -421,6 +421,12 @@ public enum Mixins { .setPhase(Phase.EARLY).setSide(Side.BOTH).addTargetedMod(TargetedMod.VANILLA) .addMixinClasses("minecraft.MixinCommandHandler_CommandFix").setApplyIf(() -> FixesConfig.fixCaseCommands)), + LIMIT_RECURSIVE_BLOCK_UPDATE_DEPTH(new Builder( + "Limit the number of recursive cascading block updates during world generation to prevent stack overflow crashes") + .setPhase(Phase.EARLY).setSide(Side.BOTH).addTargetedMod(TargetedMod.VANILLA) + .addMixinClasses("minecraft.MixinWorldServer_LimitUpdateRecursion") + .setApplyIf(() -> FixesConfig.limitRecursiveBlockUpdateDepth >= 0)), + // Ic2 adjustments IC2_UNPROTECTED_GET_BLOCK_FIX(new Builder("IC2 Kinetic Fix").setPhase(Phase.EARLY).setSide(Side.BOTH) .addMixinClasses("ic2.MixinIc2WaterKinetic").setApplyIf(() -> FixesConfig.fixIc2UnprotectedGetBlock) diff --git a/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/MixinWorldServer_LimitUpdateRecursion.java b/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/MixinWorldServer_LimitUpdateRecursion.java new file mode 100644 index 00000000..7087ed30 --- /dev/null +++ b/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/MixinWorldServer_LimitUpdateRecursion.java @@ -0,0 +1,46 @@ +package com.mitchej123.hodgepodge.mixins.early.minecraft; + +import net.minecraft.block.Block; +import net.minecraft.world.WorldServer; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import com.mitchej123.hodgepodge.Common; +import com.mitchej123.hodgepodge.config.FixesConfig; + +@Mixin(WorldServer.class) +public class MixinWorldServer_LimitUpdateRecursion { + + @Unique + private int hodgepodge$currentBlockUpdateRecursiveCalls = 0; + + @Inject(method = "scheduleBlockUpdateWithPriority", at = @At("HEAD"), cancellable = true) + private void hodgepodge$incrementBlockUpdateRecursionCounter(int x, int y, int z, Block block, int tickDelay, + int priority, CallbackInfo ci) { + if (hodgepodge$currentBlockUpdateRecursiveCalls >= FixesConfig.limitRecursiveBlockUpdateDepth) { + final StackOverflowError error = new StackOverflowError( + String.format( + "Too many recursive block updates (%d) at world %d, block %s (%d, %d, %d) - aborting further block updates", + hodgepodge$currentBlockUpdateRecursiveCalls, + ((WorldServer) (Object) this).provider.dimensionId, + block, + x, + y, + z)); + Common.log.error(error.getMessage(), error); + ci.cancel(); + return; + } + hodgepodge$currentBlockUpdateRecursiveCalls++; + } + + @Inject(method = "scheduleBlockUpdateWithPriority", at = @At("RETURN")) + private void hodgepodge$decrementBlockUpdateRecursionCounter(int x, int y, int z, Block block, int tickDelay, + int priority, CallbackInfo ci) { + hodgepodge$currentBlockUpdateRecursiveCalls = Math.max(0, hodgepodge$currentBlockUpdateRecursiveCalls - 1); + } +}