Skip to content

Commit

Permalink
Limit recursive block updates to prevent stack overflow crashes (#424)
Browse files Browse the repository at this point in the history
* Limit recursive block updates to prevent stack overflow crashes

* Review comments
  • Loading branch information
eigenraven authored Sep 21, 2024
1 parent 2b984e9 commit 0113edb
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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.")
Expand Down
6 changes: 6 additions & 0 deletions src/main/java/com/mitchej123/hodgepodge/mixins/Mixins.java
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
}

0 comments on commit 0113edb

Please sign in to comment.