Factorio sorts mods first by dependencies then by natural sort order accounting for case (https://en.wikipedia.org/wiki/Natural_sort_order).
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.
When Factorio first starts a single shared Lua state is created to process and collect prototype data from each mod into a global table
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 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.
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.
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.
Using the mod order each mod is setup:
script.on_init()will be called on each mod that has a control.lua file.
script.on_load() event handler access to the
game table is not available. This handler is meant for only 3 things:
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.
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.
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
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.
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.