Files
claudetools/projects/factorio-quality-mod/factorio-mod-anatomy.md
Mike Swanson 30841fbfb1 sync: auto-sync from GURU-5070 at 2026-06-23 20:23:47
Author: Mike Swanson
Machine: GURU-5070
Timestamp: 2026-06-23 20:23:47
2026-06-23 20:25:46 -07:00

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.zip containing one top folder modname_1.2.3/. Dev: an unzipped folder (can be named just modname) in the mods dir.
  • Mods dir: Windows %APPDATA%\Factorio\mods, Linux ~/.factorio/mods, macOS ~/Library/Application Support/factorio/mods. mod-list.json tracks enabled mods.
  • Folder/zip name must agree with name + version in info.json.

2. info.json (mod root)

  • Required: name, version, title, author. Always set factorio_version ("2.0").
  • dependencies prefixes: (none) = required · ! incompatible · ? optional · (?) hidden optional · ~ required but does NOT affect load order. Version ops >= <= = < >. base is 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.luasettings-updates.luasettings-final-fixes.lua Define mod settings; 3 types: startup, runtime-global, runtime-per-user No
Data (prototypes) data.luadata-updates.luadata-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. (Was global in 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/*.lua run as control code on loading an older save; *.json for 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; portal mods.factorio.com. Debug: log(), serpent.block(), F4 menu, --instrument-mod, /c console.

8. 2.0 vs 1.1 must-knows

  • globalstorage.
  • Quality / Space Age / Elevated Rails are separate mods — depend on quality / space-age explicitly 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