3.9 KiB
3.9 KiB
Anatomy of a Factorio mod (2.0 / Space Age) — reference
Research (Grok 4.3 live web, 2026-06-23) cross-checked against Claude's knowledge. For the deterministic-quality mod project — see
DESIGN.md.
1. Package
- Mod = folder or zip. Distribution zip:
modname_1.2.3.zipcontaining one top foldermodname_1.2.3/. Dev: an unzipped folder (can be named justmodname) in the mods dir. - Mods dir: Windows
%APPDATA%\Factorio\mods, Linux~/.factorio/mods, macOS~/Library/Application Support/factorio/mods.mod-list.jsontracks enabled mods. - Folder/zip name must agree with
name+versionininfo.json.
2. info.json (mod root)
- Required:
name,version,title,author. Always setfactorio_version("2.0"). dependenciesprefixes: (none) = required ·!incompatible ·?optional ·(?)hidden optional ·~required but does NOT affect load order. Version ops>= <= = < >.baseis auto-depended unless overridden.
3. Load stages (strict phases, across all mods at once, dependency-ordered)
| Stage | Files (in order) | Purpose | Game access? |
|---|---|---|---|
| Settings | settings.lua → settings-updates.lua → settings-final-fixes.lua |
Define mod settings; 3 types: startup, runtime-global, runtime-per-user |
No |
| Data (prototypes) | data.lua → data-updates.lua → data-final-fixes.lua |
Build prototypes into global data.raw via data:extend{...} (items/entities/recipes/tech/…). Build-once, frozen. Can read other mods' prototypes + settings.startup. |
No (no game/state) |
| Control (runtime) | control.lua (+ required files) |
Only stage that runs in a loaded save. Event-driven scripting. | Yes |
Control essentials:
script.on_init(fn) -- once, new save / mod added
script.on_load(fn) -- every load; MUST NOT write storage
script.on_configuration_changed(fn)-- mod/version/prototype change → migrations
script.on_event(defines.events.on_tick, fn)
storage= persistent per-mod saved table. (Wasglobalin 1.1 — renamed in 2.0.)- Also
remote(inter-mod),commands(custom/cmds),game,defines. - Data stage CANNOT touch runtime; control stage CANNOT define prototypes.
4. Other standard files
locale/<lang>/*.cfg— INI with[category]sections ([item-name],[entity-name],[recipe-name],[mod-setting-name], …).graphics/,sounds/— assets.migrations/—*.luarun as control code on loading an older save;*.jsonfor prototype renames.changelog.txt— strict format: 99-hyphen separators,Version:/Date:headers, 2-space category headers (Bugfixes:), 4-space-entries.thumbnail.png— mod portal (~144px+).
5. Asset paths
"__modname__/graphics/x.png". Specials: __base__ (base game assets/prototypes), __core__ (engine).
6. Settings access from control.lua
settings.startup["k"].value · settings.global["k"].value ·
settings.get_player_settings(player)["k"].value.
7. Tooling
lua-api.factorio.com— versioned; prototype docs (data) vs runtime API docs are separate.- VS Code + FMTK (Factorio Modding Tool Kit) + sumneko Lua → autocomplete/diagnostics.
- Wiki
Tutorial:Modding; portalmods.factorio.com. Debug:log(),serpent.block(), F4 menu,--instrument-mod,/cconsole.
8. 2.0 vs 1.1 must-knows
global→storage.- Quality / Space Age / Elevated Rails are separate mods — depend on
quality/space-ageexplicitly when using their features. - Versioned API docs;
factorio_version: "2.0". - Quality fields touch nearly every item/entity prototype; rail/fluid/inserter changes; Space Age adds planets/space platforms.
Minimal skeleton
my-mod/
├─ info.json
├─ settings.lua (optional)
├─ data.lua
├─ control.lua
├─ changelog.txt
├─ thumbnail.png
└─ locale/en/locale.cfg