| name | working-on-factorio |
| description | Factorio plugin development for Savecraft. Use when working on files in plugins/factorio/, including the Lua mod (control.lua, info.json), WASM parser, reference modules (recipe_lookup, ratio_calculator), datagen pipeline, sprite sheet generator, or Factorio-specific views. Triggers on Factorio plugin code, Lua mod API, ratio calculator, recipe lookup, production chains, datagen, sprite sheets, or Factorio game data. |
Working on the Factorio Plugin
Factorio uses a hybrid mod + daemon architecture ā unique among Savecraft plugins. A Lua mod writes JSON to script-output/savecraft/, the daemon watches via fsnotify, and a thin WASM parser converts to ndjson. Design doc: plugins/factorio/README.md.
Architecture
plugins/factorio/
āāā mod/ # Factorio Lua mod (control.lua, info.json)
āāā parser/ # WASM pass-through parser (JSON ā ndjson)
āāā reference/ # WASM reference modules (recipe_lookup, ratio_calculator)
ā āāā data/ # Generated Go struct literals (*_gen.go)
ā āāā views/ # Svelte reference views (ratio-calculator.svelte)
āāā tools/
ā āāā datagen/ # Parses data-raw-dump.json ā generated Go files
ā āāā spritesheet/ # Packs icon PNGs into sprite sheet + manifest
āāā sprites/ # Generated sprite sheets (items.png, fluids.png + JSON manifests)
āāā plugin.toml # Plugin metadata, sources = ["wasm", "mod"]
āāā Justfile
Verification
cd plugins/factorio
just test
just build
just datagen
just spritesheet
Full suite: just test-go (all Go) + just test-worker (all Worker) + just build-views (view compilation).
View changes require just build-views + committing views.gen.ts. See working-on-views skill for details. CI does not rebuild views ā forgetting this ships stale HTML.
Key Conventions
Recipe Disambiguation
Never guess which recipe to use. When multiple non-recycling recipes produce the same item (e.g., solid-fuel from 3 oil sources), resolveRecipe() returns an error listing the options. The AI uses recipe_lookup (product query) to find options, picks contextually, and passes the explicit recipe name via the recipe or recipe_overrides parameters.
Anti-Hallucination Modules Have No Views
recipe_lookup and tech_tree_navigator return data for the LLM to reason over. No player-facing visualization ā the AI narrates results. Views exist only for modules whose output the player needs to SEE (ratio_calculator, oil_balancer, etc.).
Shared Chart Components
FlowChart lives in views/src/components/charts/ (shared, game-agnostic Sankey-style flow visualization). Factorio-specific components (ProductionChain, MachineNode, factorio-colors.ts) live in plugins/factorio/components/. FactorioIcon is in views/src/components/factorio/.
Data Pipeline
Source Data (from Steam Deck)
.reference/factorio-data-raw-dump.json # factorio --dump-data (27MB, all prototypes)
.reference/factorio-sprites/{item,fluid,...}/ # factorio --dump-icon-sprites (64x64 PNGs)
.reference/factorio-locale/*-locale.json # factorio --dump-prototype-locale (display names)
.reference/factorio-saves/ # Test save files
Extracting Fresh Data
nix-shell -p sshpass --run 'sshpass -p "..." ssh deck@172.31.0.39 \
"~/.steam/steam/steamapps/common/Factorio/bin/x64/factorio --dump-data"'
Datagen Flow
.reference/factorio-data-raw-dump.json
ā go run ./plugins/factorio/tools/datagen/
ā plugins/factorio/reference/data/*_gen.go
recipes_gen.go (659 recipes)
technologies_gen.go (275 techs)
machines_gen.go (17 crafting machines)
modules_gen.go (12 modules)
logistics_gen.go (belts, inserters, beacons)
fluids_gen.go (33 fluids)
ā compiled into reference.wasm (GOOS=wasip1 GOARCH=wasm)
Factorio data quirk: Empty collections are {} (object) not [] (array). The datagen tool handles this with parseStringArray().
Sprite Sheet Flow
.reference/factorio-sprites/item/*.png (340 icons, 64x64)
ā go run ./plugins/factorio/tools/spritesheet/
ā plugins/factorio/sprites/items.png (2048x704, 2.3MB)
ā plugins/factorio/sprites/items.json (manifest: name ā {x,y,w,h,label})
Labels come from .reference/factorio-locale/item-locale.json.
Lua Mod (Factorio 2.0 API)
Critical 2.0 Renames
| 1.x | 2.0 |
|---|
game.write_file | helpers.write_file |
game.table_to_json | helpers.table_to_json |
game.item_prototypes | prototypes.item |
global | storage |
force.evolution_factor | force.get_evolution_factor(surface) |
Mod Structure
mod/info.json ā mod metadata, factorio_version: "2.0" (two-part only)
mod/control.lua ā script.on_nth_tick() hooks: lightweight stats every 300 ticks (5s), heavy entity scans every 1800 ticks (30s)
- Output path:
script-output/savecraft/state.json
- Mod is untested in-game ā API calls are based on research, not runtime verification
Reference Module Architecture
Go WASM with baked-in data (RimWorld pattern, NOT native TypeScript like MTGA). Entry point: reference/main.go with query routing.
Ratio Calculator Formulas
effective_speed = machine.CraftingSpeed Ć (1 + module_speed + beacon_speed)
beacon_speed = Ī£(module_effect Ć dist_effectivity / ān) across all beacons
output_per_craft = result_amount Ć (1 + productivity_bonus)
items_per_sec = (effective_speed / craft_time) Ć output_per_craft
Productivity does NOT increase ingredient consumption ā only gives free bonus output. This is validated by TestValidation_ProductivityModules_AM3.
Adding New Reference Modules
- Add handler function in
plugins/factorio/reference/
- Wire into
main.go switch statement
- Add to
schema() in main.go
- Add to
[reference.modules.*] in plugin.toml
- If player-facing: add view in
plugins/factorio/reference/views/
Attribution
views/src/attributions.ts has a wube entry for Factorio content. Plugin.toml declares [attribution] sources = ["wube"].