Crash multiblock structure
Magiks21 opened this issue ยท 14 comments
Found the crash issue before that was squashed in 0.3.0
(#98)
Seems to have returned in 0.4.1A2
Sorry if this is already fixed.
Used in FTB Infinity 1.7x v1.2.0
Yeah, I'll give it a shot. Is there a place to grab deobf/dev builds of TiCon?
Yes, we have a public jenkins. This is the release version for 1.8.3b: https://dvs1.progwml6.com/jenkins/job/TConstruct_1.7.x/919/
Looks like this is my bug. The problem seems to be that the invalidate calls are evaluated on the server after tickEnd() but before tickBegin(). This leaves a one-tick race condition whereby a controller which shouldn't be ticked is ticked, which violates a bunch of runtime assumptions.
(a) You remove the entire top of the reactor, causing BR to look at the topmost block in the reactor's bounding box, grab a TE and try to cast it to a ControlRod.
(b) You break all the fuel rods.
It's going to take some digging to figure out how to solve this correctly. It might be as simple as doing all the recalculation logic in TickBegin instead of trying to be clever with TickBegin/TickEnd.
Those are different callstacks, so they're different crashes.
This looks like the sort of thing which would occur if, somehow, you created a reactor with no fuel rods. Can you supply any information as to how this occurred?
Interrogated users to get the info you needed.
Turned out someone used a hammer from TiC with a silk touch upgrade on it. harvested the area with the fuel rod. Maybe server timing sync fell out and tried to reassemble the multiblock without the fuel rod?
Ah yes. Using a TIC hammer on a reactor is on the "don't do that" list. The TIC hammer's multi-block breaking seems to happen outside of the normal tick loop, so all sorts of stuff breaks, and there's pretty much nothing I can do to guard against it.
To be exact tic breaks the additional blocks during the blockbreak of the original block. It's behaving exactly like vanilla, except it all happens within 1 call.
Is a callback missing that big reactors relies on for its multiblock logic? All appropriate callbacks and events should get called/fired.
So, the call-chain works like this:
- In BeefCore, I rely on TileEntity::Invalidate to know when a block has been broken, or otherwise removed by the user: https://github.com/erogenousbeef/BeefCore/blob/master/src/main/java/erogenousbeef/core/multiblock/MultiblockTileEntityBase.java#L123
- That calls MultiblockTileEntityBase::detachSelf, which informs the multiblock's manager object that the block is detaching, via MultiblockControllerBase::detachBlock (https://github.com/erogenousbeef/BeefCore/blob/master/src/main/java/erogenousbeef/core/multiblock/MultiblockControllerBase.java#L215)
- That hits the onDetachBlock method, which sets the multiblock as Paused which forwards the method to the abstract onBlockDetached method.
- This is implemented in MultiblockReactor::onBlockDetached, which will remove the block from any internal tracking lists (such as the list of fuel rods). (https://github.com/erogenousbeef/BigReactors/blob/master/src/main/java/erogenousbeef/bigreactors/common/multiblock/MultiblockReactor.java#L214)
This will also mark the controller dirty, which will then (on the subsequent Server TickEnd call) revalidate the multiblock. Since the multiblock is inevitably now invalid, it will split apart and not run the MultiblockReactor::updateServer
method, which is where the crash is emerging.
So what I'm wondering is:
(a) Is Invalidate
getting called for all the blocks the TIC hammer is breaking?
(b) If so, then is the BlockBreak occurring after the server's TickEnd and before the server's next TickBegin calls? That would mean the controller is clean on TickEnd, has some blocks removed, and then is ticked in the subsequent TickBegin.
I thought all world modifications happened after TickBegin but before TickEnd, at least based on my observations when initially building BeefCore. (Maybe something moved around?)
I am, for the most part, following this. However, I have a large server full of players wondering why they cannot use hammers (And possibly other items that cause large scale block breaking?) to disassemble their reactors (and maybe other multiblocks).
Would it be selfish of me to ask for help explaining it to them? The explanation I have currently I'm finding shaky at best. And I know pointing the general pop from my server to this ticket will just confuse them more.
The easiest way to explain it is basically, "there's a bug in BR or a bug in TIC, not sure which at the moment, just don't do it."
The hammer calls the same vanilla functions that a regular block break would also call, during the blockbreaking of the original block:
- https://github.com/SlimeKnights/TinkersConstruct/blob/master/src/main/java/tconstruct/library/tools/AOEHarvestTool.java#L28
- https://github.com/SlimeKnights/TinkersConstruct/blob/master/src/main/java/tconstruct/library/tools/HarvestTool.java#L300
- removedByPlayer in the vanilla Block class calls world.setBlockToAir, which calls setBlock
- This calls 'func_150807_a' in the chunk
- Which then calls world.removeTileEntity
- Which calls TileEntity.invalidate
From that it should behave exactly as if the blocks were broken by a player one after another in the same tick.
Maybe you can put a breakpoint into the invalidate function and see if it's called for each block if you break it with a tinkers hammer?