| name | factorio-mod-dev |
| description | Develop and debug the creative-mod Factorio mod. Use when working on mod scripts, prototypes, GUI, events, or porting to a new Factorio version. Covers repo layout, mod structure, key internals, and the verify.py verification pipeline. |
Factorio Mod Dev
Repo layout
creative-mod/
├── info.json # mod metadata (name, version, factorio_version, deps)
├── settings.lua # startup/runtime settings (data stage)
├── data.lua # data stage entry — loads prototypes/
├── data-final-fixes.lua # data stage post-processing (runs after all mods)
├── control.lua # runtime entry — requires scripts/, registers events
├── defines.lua # all name/prefix constants → creative_mode_defines
├── scripts/ # runtime modules (one feature per file)
├── prototypes/ # entity, item, recipe, technology definitions
├── migrations/ # version migration scripts
└── locale/ # translations
Verification loop
verify.py is the canonical way to check the mod. It loads creative-mod in the
local Factorio install, runs assertions, and exits 0/non-zero with a stable,
greppable RESULT: line, so you can edit → verify → read result → iterate.
Run it via uv:
uv run verify.py doctor
uv run verify.py static
uv run verify.py load
uv run verify.py behavior
uv run verify.py all
uv run verify.py --help
The layered model is static → load → behavior (cheapest to deepest); all
runs the three in sequence. Read the result by grepping ^RESULT: and/or
checking $?:
RESULT: load=PASS # exit 0
RESULT: load=FAIL (control stage incomplete) # exit non-zero, reason names the failure
For investigation, use the bounded tooling modes (successors to the removed
standalone shell wrappers):
uv run verify.py shell '/c rcon.print(game.tick)'
uv run verify.py debug --command '/c ...'
uv run verify.py debug --gui
uv run verify.py load --clean
RCON gotcha: the /c console runs in its own environment — the mod's
runtime globals (super_boiler, global_util, storage.creative_mode, etc.)
are not reachable from it, so you can't call mod functions directly. (util
appears to work but resolves to core lualib's util, not the mod's.) To test mod
logic via RCON, inline a replica of the code against a live entity, or add a
behavior assertion that places an entity with raise_built=true (which registers
it into the real per-tick loops) and checks the effect after the server ticks.
Output channels for the values you inspect:
| Goal | Use | Where |
|---|
| Inspect a value | rcon.print(v) | echoed back to terminal |
| Trace code | log("msg") | factorio-current.log |
| Dump large table | helpers.write_file("f", d) | .debug/script-output/f |
→ See VERIFY.md (this skill folder) for the full subcommand reference, the
RESULT:/exit-code contract, and the replicable local install setup.
→ See DEBUG.md for the output-channel reference.
→ See DEBUGGING.md (this skill folder) for debugging methodology and porting guide.
→ See RELEASE.md (this skill folder) for release checklist and GitHub Actions workflow reference.
Changelog & versioning
Every PR that changes user-facing behavior (a feature, bugfix, or change) must
include a changelog.txt entry in the same PR — it is part of the feature, not
a separate step.
- Prepend the entry at the top of
changelog.txt using the documented format,
under the right section (Features: / Bugfixes: / Changes:). Set the
Version: header to the next release version (pick the bump from the version
table in RELEASE.md: new user-facing feature → minor; bugfix → patch).
- Do not bump
info.json in the feature PR. The info.json version bump and
the git tag happen together as a dedicated release step on master — see
RELEASE.md. This keeps feature branches free of version churn/conflicts.
Base game as a reference
The full Factorio base mod ships with the install and is readable on disk:
/mnt/quickstuff/git/factorio_linux/data/
├── changelog.txt # the full engine + API change log (every version) — VERY complete
└── base/
└── prototypes/ # all vanilla entity, item, recipe, technology definitions
When something breaks or behaves unexpectedly, read the base game source first:
- API errors (
doesn't contain key X, nil value, renamed/removed methods)?
grep data/changelog.txt for the symbol — the API change log is extremely
thorough and almost always names the exact rename/removal and its replacement
(this is how the LuaEntity::fluidbox → get_fluid/set_fluid/fluids_count
removal was diagnosed). Confirm the new signature by probing a live entity via
RCON before rewriting.
- Correct fields for a prototype type? Find a vanilla example in
data/base/prototypes/.
- How the Factorio devs actually use an API?
grep the base mod's scripts and
prototypes — prefer their real usage patterns over guessing or external docs.
- What a
defines.* value is? Defined in the engine, but usages are visible throughout the base mod.
This is the ground truth for the running version — always prefer it over external docs.
Key internals
creative_mode_defines — single source of truth for all names/prefixes (defines.lua)
storage.creative_mode — mod runtime state, initialised in scripts/global-util.lua
events — all event callbacks (scripts/events.lua), registered in control.lua
remote_interface — public API for other mods (scripts/remote-interface.lua)
- Prototype names: always use
creative_mode_defines.names.*, never hardcode strings
- Data stage only:
data.raw, data:extend(), prototype tables
- Runtime only:
game, storage, script, defines, helpers