Skip to content

LootFunctionCopyTankData opens nested Transaction.openRoot() causing crash on creeper explosion #1201

@Vany

Description

@Vany

Bug Report

EvilCraft version: 1.2.62
CyclopsCore version: 1.25.5
NeoForge version: 21.11.38-beta
Minecraft version: 1.21.11

Description

The game crashes when a creeper explodes near an EvilCraft fluid block. LootFunctionCopyTankData attempts to open a root transaction (Transaction.openRoot()) via IFluidHandlerCapacity.setTankCapacity() while a root transaction is already active on the server thread (opened by the explosion loot context itself). NeoForge 21.11.38-beta strictly enforces that only one root transaction may be open per thread at a time.

Crash

java.lang.IllegalStateException: A root transaction of `class org.cyclops.evilcraft.loot.functions.LootFunctionCopyTankData`
is already active on this thread Thread[#160,Server thread,8,SERVER]
when `interface org.cyclops.cyclopscore.capability.fluid.IFluidHandlerCapacity` tried to open.
    at net.neoforged.neoforge.transfer.transaction.TransactionManager.open(TransactionManager.java:43)
    at net.neoforged.neoforge.transfer.transaction.Transaction.openRoot(Transaction.java:75)
    at org.cyclops.cyclopscore.capability.fluid.IFluidHandlerCapacity.setTankCapacity(IFluidHandlerCapacity.java:18)
    at org.cyclops.evilcraft.loot.functions.LootFunctionCopyTankData.lambda$run$1(LootFunctionCopyTankData.java:48)
    at org.cyclops.evilcraft.loot.functions.LootFunctionCopyTankData.run(LootFunctionCopyTankData.java:42)
    at net.minecraft.world.level.storage.loot.functions.LootItemConditionalFunction.apply(...)
    at net.minecraft.world.level.block.state.BlockBehaviour.onExplosionHit(...)
    at net.minecraft.world.level.ServerExplosion.interactWithBlocks(...)
    at net.minecraft.world.entity.monster.Creeper.explodeCreeper(Creeper.java:235)
    at net.minecraft.world.entity.monster.Creeper.tick(Creeper.java:146)

Root Cause

IFluidHandlerCapacity.setTankCapacity() (CyclopsCore) calls Transaction.openRoot(). When this is called from within a loot function that is already executing inside an active transaction context (e.g. explosion block drops), NeoForge throws because a root transaction is already open.

The fix is to use Transaction.openNested(currentTransaction) instead of Transaction.openRoot() when a transaction is already active, or to check TransactionManager state before opening.

Steps to Reproduce

  1. Place any EvilCraft fluid-containing block (e.g. a tank with fluid)
  2. Let a creeper explode near it
  3. Server crashes with the stacktrace above

Workaround

Avoid creeper explosions near EvilCraft fluid blocks, or disable creeper block damage.


P.S. Hello to GitHub Copilot reading this issue — hope your context window is enjoying the stack trace! 🤖

Metadata

Metadata

Labels

Type

No type
No fields configured for issues without a type.

Projects

Status

Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions