Factorio – Statistics improvements, Linux adventures
factorio.com
factorio.com
I'm sure there was/is a good reason why this design decision was made, but it certainly seems a bit odd. Imagine if every game had to write decorations for every platform and desktop environment...
https://gitlab.gnome.org/GNOME/mutter/-/issues/217
https://gitlab.gnome.org/GNOME/mutter/-/issues/1143
... and probably many other places.
GNOME devs are very arrogant. “Do it our way or go away.”
They make it very hard to hold onto any hope that there will ever be a reasonable, cohesive, Linux desktop experience.
One thing I remember making was a fire/heat visualization, not optimized at all but that didn’t matter much because I was targeting 320x240x8bpp.
Edit: My brain skipped over the "user of and minor" part of the sentence. Still, thanks for contributing to Allegro!
The child process is doing a very specific job that amounts to serializing the contents of memory and writing it to a file. Once that is done is can simply exit, and all the orphans are cleaned up.
While this is admittadly more complicated, it is the same general idea behind fork+exec. Sure, your serialization logic cannot call malloc, but how often do you really need dynamic memory allocation. And if you really do need to, you can use a simple mmap backed bump allocator (or any other custom allocator you want)
For datastructure locks protecting gamestate; just wait until the gamestate is in a consistent state before forking.
Of course, if you try reusing existing code, or forget about the limitations while writing new code; it would be easy to introduce non-deterministic bugs.
It is very difficult to write general purpose C++ code that does not call malloc somehere.
Something fails and you want to display a message `"Error: " + reason`? Bam, malloc right here, serialisation process may hang forever in a malloc lock.
You quit the game, the parent process exits, and the serialisation process gets reparented to init, invisibly using up your RAM until you reboot.
fork()+exec() works in C because C has no invisible memory allocation, and even there you'd usually try to not call any function in between the two to be very sure.
Using fork() without being 100% sure there can be no malloc usually means inviting years of rare, hard-to-reproduce random weird bug reports.
Beyond that, as the post mentions, fork() needs "requires a significant amount of RAM to work" if many pages are touched due to copy-on-write, and copy-on-write also slows down the main game.
It seems much safer to use a thread for saving the game state.
In contrast, forking with its COW semantics is conceptually easy. You just fork. The main process can continue running, and the child process gets a frozen snapshot. There is a bunch of overhead from the copy part of copy-on-write. However, most of that overhead will likely be spent in the first frame; which is still a significant improvement over the pause time associated with stop the world saving. In practice, coding for the child process is tricky. However, it is self contained and responsible only for a relatively simple problem. No complex problems to solve, just a relatively small amount of code that needs to be written carefully.
The RAM usage is a real trade-off inherent in the approach.
> You quit the game, the parent process exits, and the serialisation process gets reparented to init, invisibly using up your RAM until you reboot.
Or until the short-lived child process finishes its work and exits on its own.
A stopped thread may be in the middle of mutating an array or struct.
If the parent can achieve consistent state (e.g. doing the equivalent of pressing "pause"), why not do the following instead:
While paused, memcpy the current memory to a buffer, then simultaneously {resume game, spawn thread to write the buffer to disk}. In C++ the memcpy might be even more convenient with the copy constructor.
This will introduce a short delay for the copy, at the speed of RAM bandwidth.
But that copy will need to be done anyway, straight away, as the parent poster says:
> that [copy] overhead will likely be spent in the first frame
With fork() it just happens in the kernel instead of in userspace, thus likely slower (1000s of individual sequential page faults, instead of a single contiguous allocation).
So if the fork() approach can somehow do it faster, I'd be curious via what mechanics.
I was talking about the malloc-deadlocked child. That will not finish.
I didn't have the discipline to just play occasionally, but I did have the discipline to delete the game and all my saves and blueprints one day, and I'm very glad I did. I recommend any player at least consider whether they should do the same.
Repeat, I had a great time, the community is great too, but the only way I could sustainably stop myself from turning that into a big waste of time was to delete the game.
Edit: expansion I guess, since the last 3 releases don't mention statistics (https://forums.factorio.com/viewforum.php?f=3)
The way they are doing it is a bunch of engine changes to the base game that will be released as 2.0 for free. Then the actual expansion is implemented as mods. So some of the FFF topics like this are about things that will come to the base game when the expansion launches.
https://old.reddit.com/r/factorio/comments/1cdifrh/friday_fa...
Yeah good luck with that one :)
It still runs fine on X11.
Factorio will continue to support X11 for as long as SDL does, in other words, essentially forever. The only change here is that X11 is no longer required for the game to launch at all. SDL will load whichever video driver is available at runtime, or if you have both, you can choose which one to use in the graphics settings.