You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Game save support: read a world from a .sav and apply it to the plan #584
Upload a Satisfactory save, read the facts about that world out of it, hold them on the plan, and let the planner check the plan against them.
Today the planner assumes one world: the default vanilla map, every recipe unlocked, every building available. Both halves of that assumption are wrong for most people, and silently so.
Raw Resources (feat: measure a plan against what the map actually holds #602) measures a plan against a hand-typed table of the vanilla map. A 1.2 world generated with node randomisation or one of the resource-rich presets has different nodes, so every figure in that table is wrong for it, and nothing on screen can tell.
Every "at 250%" ceiling assumes a Miner Mk.3. On a save without Mk.3 unlocked the real ceiling is half what we show.
A plan can use the Converter, the Resource Well Pressurizer or an alternate recipe the player has never unlocked, and the planner will happily balance it. It is not buildable and we never say so.
This replaces the Import world [WIP] button and WorldImport.vue, which currently only counts Somersloops.
What a save actually contains
Verified by parsing four 1.2 saves (a vanilla new game, a node-randomised one, a fossil-fuel-rich one, and a real 8-tier world).
Per-node overrides — every BP_ResourceNode and BP_FrackingSatellite carries mResourceClassOverride (an ObjectProperty naming Desc_OreCopper_C and so on) and mPurityOverride (RP_Inpure / RP_Normal / RP_Pure). A full per-node resource and purity table, no seed reproduction needed.
Progression — SchematicManager.mPurchasedSchematics gives milestones by tier (Schematic_4-2), MAM and hard-drive schematics; RecipeManager.mAvailableRecipes gives every recipe currently buildable including buildings and alternates; GamePhaseManager.mCurrentGamePhase gives the phase.
A real save read this way: phase 3, 38 milestones across 8 tiers, 411 recipes, 52 alternates, and no Resource Well Pressurizer, Miner Mk.3, Converter, Particle Accelerator or Quantum Encoder.
A save is a diff, not a snapshot. A vanilla save contains none of the above — no overrides, not even the settings properties, because nothing differs from the level defaults. So the parser cannot replace world-resources.ts; it patches it. The hand-typed table stays as the baseline.
Scope
Part 1 — ingest and state (the bulk of the work)
Parse a .sav in the browser, locally. Nothing is uploaded anywhere.
Extract: world generation settings, per-node resource + purity, purchased schematics, available recipes, game phase, and extractors already built on nodes.
Store the result on the plan, persisted with it, versioned so an old snapshot survives a format change.
Feed it into the calculation engine behind a toggle: plan-only (what we do today) or plan-against-this-world.
Part 2 — UI
Drop zone replacing the current dialog, re-uploadable as the world progresses.
The toggle, and a clear indication of which mode is on.
Raw Resources reads the world's real node counts and its best available extractor.
Products and buildings flag what the save cannot build yet.
Probably one PR — the state layer is untestable without something consuming it — but Part 1 should land reviewable on its own inside it.
Test fixtures
Commit real saves so the parser is tested against actual files rather than fabricated bytes. Recommend:
One small vanilla new game (~140 KB) for the parse path and the "no overrides" case.
One node-randomised and one resource-rich save for the override paths.
For progression, a JSON fixture extracted from a progressed save rather than the save itself — a real 8-tier world is ~10 MB and does not belong in the repo.
Open questions
Does sav2json@1.0.1 still work? It predates 1.0. A hand-rolled parser reads these 1.2 saves correctly in ~120 lines (header list, then length-prefixed data blocks in the same order); worth measuring against the dependency before committing to either. Whatever wins must handle the ~10 MB real-world case without locking the tab — likely a Web Worker.
Where does the snapshot live — on the plan (syncs, shareable, bigger payload) or in localStorage beside it?
What does the toggle do to a plan that becomes invalid when it goes on? Warn, or refuse to apply?
Does a shared plan carry its world with it?
Out of scope
#437 (recipe cost and power multipliers). Those settings are not written to a save at default, and the work here is a natural place to hang them later.
Upload a Satisfactory save, read the facts about that world out of it, hold them on the plan, and let the planner check the plan against them.
Today the planner assumes one world: the default vanilla map, every recipe unlocked, every building available. Both halves of that assumption are wrong for most people, and silently so.
This replaces the Import world [WIP] button and
WorldImport.vue, which currently only counts Somersloops.What a save actually contains
Verified by parsing four 1.2 saves (a vanilla new game, a node-randomised one, a fossil-fuel-rich one, and a real 8-tier world).
World generation — on
BP_GameState_C:mNodeRandomizationENodeRandomizationMode::NRM_Strict,NRM_FossilFuelRichmNodePuritySettingsENodePuritySettings::NPS_AllRandommNodeRandomizationSeed162272096Per-node overrides — every
BP_ResourceNodeandBP_FrackingSatellitecarriesmResourceClassOverride(an ObjectProperty namingDesc_OreCopper_Cand so on) andmPurityOverride(RP_Inpure/RP_Normal/RP_Pure). A full per-node resource and purity table, no seed reproduction needed.Progression —
SchematicManager.mPurchasedSchematicsgives milestones by tier (Schematic_4-2), MAM and hard-drive schematics;RecipeManager.mAvailableRecipesgives every recipe currently buildable including buildings and alternates;GamePhaseManager.mCurrentGamePhasegives the phase.A real save read this way: phase 3, 38 milestones across 8 tiers, 411 recipes, 52 alternates, and no Resource Well Pressurizer, Miner Mk.3, Converter, Particle Accelerator or Quantum Encoder.
A save is a diff, not a snapshot. A vanilla save contains none of the above — no overrides, not even the settings properties, because nothing differs from the level defaults. So the parser cannot replace
world-resources.ts; it patches it. The hand-typed table stays as the baseline.Scope
Part 1 — ingest and state (the bulk of the work)
.savin the browser, locally. Nothing is uploaded anywhere.Part 2 — UI
Probably one PR — the state layer is untestable without something consuming it — but Part 1 should land reviewable on its own inside it.
Test fixtures
Commit real saves so the parser is tested against actual files rather than fabricated bytes. Recommend:
Open questions
sav2json@1.0.1still work? It predates 1.0. A hand-rolled parser reads these 1.2 saves correctly in ~120 lines (header list, then length-prefixed data blocks in the same order); worth measuring against the dependency before committing to either. Whatever wins must handle the ~10 MB real-world case without locking the tab — likely a Web Worker.localStoragebeside it?Out of scope
#437 (recipe cost and power multipliers). Those settings are not written to a save at default, and the work here is a natural place to hang them later.