So the playtesting and balance-as-I-play is going well, I'm over 16 hours into this no-cheats playthrough and the end of the game is in sight now (if it were factorio, it'd be similar to having built the launchpad - and now needing to fuel it etc.)
Something I noticed with these large factories such as the one above was something was not quite right with the off-screen simulation. As a reminder: the on-screen factory small tick
updates 16 times a second, while the off-screen factory big tick
updates only twice a second (but the update is 8 times as large, to compensate).
The factory was always functioning, but running around it you could tell that items were not propagating around the conveyors as expected.
To debug I switched everything to the big tick
twice-a-second updates to be able to see what was nominally going on in the off-screen parts, the main issue looked to be with the heavy use of mid-game fast conveyor belts which move cargo at two tiles per second, so exactly one tile per big tick
.
Because each conveyor is updated in turn, depending on the direction the cargo was moving it could get moved many many tiles in just a single big tick
- i.e. if the conveyor belt pieces were iterated over in the same order as the cargo was moving. As seen here in this loop where the chalk seems to teleport from the top left of the cycle down to the bottom right in an instant.
So some belts (E to W, S to N) were moving things normally, others (W to E, N to S) were zipping them around at amazing speed. And it all depended on the direction the belts faced. Not great.
The solution was to flag the target conveyor with the frame number when moving a cargo between tiles. Then when iterating over the conveyors, we skip any conveyors we find which are flagged with the current frame number.
This was super simple and worked perfectly.
Unfortunately though... now I had another, nastier problem. How to move a packed belt? We want to move each cargo by one tile on each big tick
, but a cargo can only move if its destination is free. For two cardinal directions it works naturally. But for the other two we end up only half-filling the belt!
The current solution here is a bit more extreme, and I'll need to check its performance on device.
During the iteration phase over the conveyors, any conveyor which wants to move its Cargo to a currently occupied tile registers a Desire To Move in an array. Then, after all conveyors have been updated, the Desire To Move array is iterated over in reverse order and we attempt to move the Cargo once more.
The ones which still cannot move are saved into a second array, which gets iterated over next: either move or save back into the first array...
The procedure is iterated, alternating between the two arrays, until either a) we only moved 0, 1 or 2 Cargo in the previous iteration or b) we have made 8 iterations total. These numbers were chosen to try and balance the CPU load of the function. Here is an excerpt from the debug logs from this running on a single big tick for the above factory, 6 iterations were made on this frame.
A Iteration: moved 322 of 865
B Iteration: moved 33 of 543
A Iteration: moved 60 of 510
B Iteration: moved 7 of 450
A Iteration: moved 21 of 443
B Iteration: moved 2 of 422
With this in place, everything off-screen is back to moving as was intended! (At least, for the vast majority of the Cargo. Most of the 422 Cargo not managing to move here are likely genuinely stuck on a blocked belt, continuing to iterate to zero Cargo moved typically only requires a few more iterations)
Shown here with small tick
turned back on in the vicinity of the player. The belts feeding in from far off-screen are all delivering items at a constant two Cargo per second from all four directions.
I'm now of course wondering if there was anything more efficient I could have done to fix this particular issue...?