Data Lifecycle

This describes the data flow for mods. In what order the different stages of data processing are done, what parts are available, and what's expected during each stage.

1. The settings stage

When Factorio first starts a single shared Lua state is created to process and collect mod setting prototype data from each mod into a global table data. Additionally a global table mods exists that contains a mapping of mod name to mod version for all enabled mods.

First the settings.lua file is called for each mod. Next, the settings-updates.lua file is called for each mod and finally the settings-final-fixes.lua file is called for each mod. During this time there's no game instance created and the standard Lua API is not available. The data table expects a specific format for each item in the table. Missing properties will either fall back to default values or give an error indicating what's missing. Extra properties that the game isn't looking for are simply ignored.

Changes made during each stage of the settings loading are automatically tracked and a history of which mod has changed which prototype is recorded by the game. After the settings stage of loading has finished the Lua state is discarded. Changes and functions defined during the data stage will not carry over to any other stages.

At the end of this stage all setting prototypes are constructed and the startup settings are read from the read-data directory "mod-settings.json" file.

2. The data stage

Next, a new shared Lua state is created to process and collect all other prototype data from each mod into a new global table data. The global table mods still exists and a global table 'settings' is populated with all startup type mod setting values during this stage.

First the data.lua file is called for each mod. Next, the data-updates.lua file is called for each mod and finally the data-final-fixes.lua file is called for each mod. During this time there's again no game instance created and the standard Lua API is not available. The data table expects a specific format for each item in the table. Missing properties will either fall back to default values or give an error indicating what's missing. Extra properties that the game isn't looking for are simply ignored.

Changes made during each stage of the data loading are automatically tracked and a history of which mod has changed which prototype is recorded by the game. After the data stage of loading has finished the Lua state is discarded. Changes and functions defined during the data stage will not carry over to any other stages.

At the end of this stage all prototypes are constructed and the game goes to the main menu.

3. Migrations

Migrations are a way to handle prototype changes between mod versions or game versions. Migrations do have access to the game state but are isolated from all other data stages. Migrations are each run in their own Lua state that's discarded after the migration haves been applied.

4. control.lua initialization

During this stage each mod's control.lua is loaded and executed in their own Lua instance that will be owned by that mod for the remainder of the play session. Each mod has its own Lua instance and own global table to store data. Because this is run every time a save file is created or loaded you don't need to restart the game to see changes made to the control.lua file. Simply restarting or reloading a save will re-run this stage.

During this stage access to the global table is not available. The script table and the remote table are however available. Note, although the global table has not been setup if a mod does populate the table with some data it will be overwritten by any loaded data.

At the end of this stage (if loading a save file) mod data saved in the map file is loaded and the global table for each mod is restored.

5. control.lua init/loaded

Using the mod order each mod is setup:

When creating a new game, script.on_init() will be called on each mod that has a control.lua file.

When loading a save game and the mod existed in that save game script.on_load() is called.

When loading a save game and the mod did not exist in that save game script.on_init() is called.

During the script.on_load() event handler access to the game table is not available. This handler is meant for only 3 things:

1. Re-setup meta-tables. Meta-tables are not persisted through save-load.

2. Re-setup conditional event handlers (subscribing to an event only when some condition is true to save processing time).

3. Create local references to data stored in the global table

Attempting to change the contents of the global table during the script.on_load() event handler is not allowed. Doing so can lead to desyncs if the mod is used in multiplayer and will generate an error if the game detects it has been changed in any way.

During the script.on_init() event handler access to the game table is available and any and all changes deemed necessary by the mod are free to be performed without risk of breaking anything.

6. control.lua script.on_configuration_changed()

When mods are changed (prototypes added or removed), the major game version changes, a mod version changes, a mod is removed, or a mod is added the script.on_configuration_changed() event is fired for each mod subscribed to that event.

This is the main place for handling mod internal data structure changes. Access to the global table, game table and game state are available and can be changed in any way seen fit by the mod.

Note: this is not the place to handle things such as recipe unlocks due to research changes - that is best done through migration scripts.

7. control.lua runtime:

At this stage the game is running fully and everything has been setup or loaded. Access to all tables is available in any event handler.