Mapshot is a Factorio mod and a companion binary creating zoomable maps of a Factorio game. Click on the image for an example:
Source is fully available on Github. There are 2 parts:
I’m not really into game modding in general, so I did not really what to expect. I dabbled a bit in the past with the Warcraft addon API, also in Lua. I must say the Factorio API is nice - it is well documented and clear. It shows that it is (compared to Warcraft) a recent game, and that is it made by developers for developers.
One interesting aspect is that Factorio is using lock-step simulation for multiplayer. It means that anything causing randomness is avoided at all cost and the API reflects that. It makes it next to impossible to get a random number for example or get any reference to the save name from the game itself. Instead, I relied heavily on hashing data were I would have used random IDs.
In general, activating mods in Factorio is not something that can be done randomly on a specific save. For example, it might deactivate achievements. So, I wanted to be able to create renders without impacting the saved game.
So, I went the way of making copy of stuff. The CLI will make a copy of the savegame to render, a copy of the mod directory and add itself to the list of mod. A bit of config file parsing here and there, but nothing outlandish. Then it starts Factorio on that data.
Now, there is no real way to control Factorio from the outside - there is a game server control protocol, but it is very limited. But I needed to be able to:
When run from the CLI, the mod code is slightly modified - one of the Lua file is updated to indicate that it is run from the CLI and that a render is required. From there, the mod is able to get called on the first tick (through a “on-tick” event handler), see that a render is required and run it. Of course, one should not forget to unregister the handler to avoid generating multiple renders :)
Exiting is more dicey. There is no command to exit from a mod. So, the CLI is responsible for shutting down Factorio. On Linux, it is done through a signal; on Windows, it is a bit more brutal. Now the question becomes “when to issue the shutdown”. Here what happens is that the CLI just watches the mod output directory; and when it sees that the render is done, it shutdown Factorio.
But Factorio dev like performant code, so it turned out to not be that straightforward. The mod triggers “render-to-image” calls for all the tiles needed and then write a JSON file with some metadata. The CLI is waiting for this JSON file to shutdown the game. However, render-to-image is done in parallel to the game execution - in practice, it meant that tiles were often randomly missing.
A simple fix was to wait the next tick and write a “done” file. When the file is seen, the CLI shutdown the game. It turned out to work quite decently. And as things tuend out, while I was working on the mod, Factorio devs decided to add a function
set_wait_for_screenshots_to_finish - allowing the mod to block until tiles were properly written on disk.
The tile management is built using Leaflet. It is a library dedicated to create zoomable maps, and thus was a very good match. It was very straightforward to use, and allowed to add overlays on the map - Mapshot includes a few (player position, train stations for example). The only really annoying bit was a display artifact that has never been properly fixed in Leaflet. It is a non trivial issue on how browser render images; I added some hack (based on what was mentioned in the bug), but it is not really solving the underlying issue.