Deep-Dive für die Weiterentwicklung. Ergänzt die README.
- Main-Prozess (
electron/, Node): parst.sav-Dateien und hält den vollständigen geparsten Save im Speicher. Nur schlanke, IPC-freundliche Digests (SaveSummary,MapFeatureSet) gehen an den Renderer. - Preload (
electron/preload.ts): exponiert eine typsichere Bridge alswindow.satisfactory(contextIsolation an). - Renderer (
src/, Angular): UI, Karte (Leaflet) und Calculator. - IPC-Contract:
src/shared/ipc-types.ts(von beiden Seiten importiert).
Save-Parsing im Main; map-service.ts klassifiziert jedes positionierte
SaveEntity in eine FeatureCategory und reichert Ressourcen-Nodes/Extraktoren
mit Typ/Reinheit/Takt an.
ELECTRON_RUN_AS_NODE: Manche Shells setzen das; dann startet Electron als reines Node (appistundefined, Crash).scripts/launch-electron.jsentfernt die Variable vor dem Start.- Electron-TS kompiliert getrennt (
tsconfig.electron.json,Node16, CommonJS) nachdist-electron/electron/…. Deshalb nutztmain.ts../../für Pfade zum Projekt-Root (Renderer-Index,saves/). - Hash-Routing +
baseHref: "./"in Angular, damit es überfile://läuft. - Main-Prozess wird im Dev NICHT gewatcht: Änderungen in
electron/erst nachnpm run dev-Neustart aktiv (Renderer-Änderungen live via ng-serve-Watch).
Die Karte nutzt th.gl-Tiles unter Leaflet CRS.Simple. Konfiguration
(aus th.gls App extrahiert):
- bounds
[[-374999,-324999],[374999,424999]](=[gameY-Range, gameX-Range]) tileSize 512,minNativeZoom 0,maxNativeZoom 4, URL{z}/{y}/{x}.webp- Volles Kachelraster (kein weißer Rand),
EXTENT = 512.
Spielkoordinate → LatLng: gameToLatLng(x,y) bildet über die fraktionale
Position in den bounds ab. Wichtig: th.gls Node-Koordinaten sind gegenüber den
Save-Koordinaten transponiert (siehe unten) — die Tiles selbst sind aber über
die bounds direkt in Spielkoordinaten adressiert.
Problem: Der Save speichert für BP_ResourceNode_C nur mResourcesLeft —
kein Ressourcen-Typ, keine Reinheit (die sind statische Map-Daten). Ein Mod
(moritz-h/satisfactory-mapdata) bestätigt das.
Lösung: Referenz-Datensatz von th.gl geholt:
- Quelle:
https://cdn.th.gl/satisfactory/nodes/world.<hash>.raw(aktueller Hash steht alsnodesPathsim Seiten-HTML vonsatisfactory.th.gl/maps/World). - Format: CBOR (dekodieren mit
cbor-x). Struktur: Array von{ type, mapName, spawns: [{ p:[x,y,z] }] };typez.B.OreIron,OreIron_RP_Pure,OreIron_RP_Inpure(ohne Suffix = Normal), plus*_Depositund*_Well. - Koordinaten-Transform (verifiziert 459/459 exakt vs. Save):
gameX = thgl.y,gameY = thgl.x— reiner Achsen-Swap, kein Skalieren/Offset. - Offline dekodiert nach
electron/data/resource-nodes.ts(459 Nodes,[resource, purity, x, y, z]).
Wells: RESOURCE_WELLS (Öl/Stickstoff/Wasser, 17 Stück) im selben Datenmodul.
map-service.ts matcht BP_FrackingCore-Positionen (matchWell, 17/17 exakt) →
feature.resource. Karte: Well-Cores mit Ressourcen-Icon (türkiser Ring,
wellMarker), Satelliten bleiben generisch. Calculator: wellCount schließt die
„keine Node-Daten"-Lücke für Stickstoff/Wasser.
Matching: map-service.ts baut ein Spatial-Grid und matcht jeden Save-Node
per Position (matchNode, 4 m Toleranz) → Ressource + Reinheit. Occupied Nodes
(mit Extraktor darauf) werden unterdrückt (occupiedSources), damit kein
Doppel-Marker entsteht; ihr Extraktor bekommt die Reinheit über mExtractableResource.
Wichtig: Gilt nur für Standard-Maps (keine Node-Randomisierung). Prüfbar via
save.header.mapOptions(leer = Standard). Randomisierte Saves (Seed) bräuchten seed-basierte Daten.
- Ressource: aus der
OutputInventory-Komponente (mAllowedItemDescriptors[0], FallbackmInventoryStacks[].Item…itemReference) → z.B.Desc_Coal_C→Coal. Autoritativ & update-sicher. - Takt/Overclock:
mCurrentPotential(FallbackmPendingPotential, sonst 1.0). - Förderrate (
src/app/features/calculator/world-availability.ts):base(Gebäude) × purityFaktor × Takt. Basisraten @100%: MinerMk1/2/3 = 60/120/240 (normal), Öl-Extraktor 120, Wasserpumpe 120; Reinheit ×0.5/×1/×2.
- Spieldaten:
data/game-data.json(ausgreeny/SatisfactoryToolsdata/data1.0.json, reduziert auf 152 Items, 276 Maschinen-Rezepte, 11 Gebäude, 13 Rohstoffe; inkl. Icon-Slug). Lazy mit der Route gebündelt (resolveJsonModule). - Solver (
calculator.service.ts):solve(item, rate, choices)→ nach Rezept aggregierteProdStep[]+ Rohstoff-Summen + Strom.choices= Rezept-Override pro Item; Default = das Nicht-Alt-Rezept.solveTree(...)→ verschachtelterProdTreeNode(Baum-Ansicht).- Loop-Schutz: Rezept-Kreisläufe (z.B. Recycled Plastic ↔ Rubber) werden am Vorfahren-Set erkannt und als Rohstoff abgeschnitten (+ Warnung). Exakte Auflösung bräuchte einen linearen Solver (siehe Roadmap).
- Welt-Abgleich:
world-availability.tsmappt Item-Klasse → Node-Key (descToNodeKey:Desc_OreIron_C→OreIron) und aggregiert freie Nodes nach Reinheit + max. Förderpotenzial (MinerMk3 @100%) sowie tatsächliche Extraktor-Förderung (inkl. Overclock). - Icons:
iconUrl(cls)→icons/items/<slug>.png.
- Nodes:
nodeMarker(key, purity)=divIconmit<img>des Ressourcen-Icons, Ring-Farbe = Reinheit (grau/weiß/gold). Extraktoren:extractorMarker(key)mit orangem Ring. Leicht (kein CSS-drop-shadow-Filter → performant bei ~467). - Übrige Icon-Kategorien (Geysir/Fracking/Produktion/Lager/Fahrzeug/Spieler):
SVG-Glyph-Badges (
markerIcon). Massen-Layer (Flora/Logistik/Strom/Kreaturen/ Vorkommen/Sonstiges) sind Canvas-Kreise. Layer werden lazy pro Kategorie gebaut (ensureLayer). - Performance: Icon-(DOM-)Kategorien nutzen
L.markerClusterGroup(leaflet.markercluster): weit rausgezoomt Cluster-Bubbles, ab Zoom 6 einzeln,removeOutsideVisibleBounds(Viewport-Culling). Karte:markerZoomAnimation: false,fadeAnimation: false; Tile-LayerupdateWhenZooming: false. Cluster- Radius/Zoom inensureLayereinstellbar.
Alle gebündelten Daten sind eingecheckt (Repo läuft ohne Nachbau). Zum
Aktualisieren siehe scripts/data/:
npm run data:game, data:nodes, data:icons, data:tiles.
Offene, sinnvolle Ausbauten (Priorität grob absteigend):
- ✅ „Nächste freie Quelle" — Klick auf die Karte setzt einen Referenzpunkt; die Sidebar zeigt pro Ressource die nächstgelegene freie Node (Distanz + Reinheit, folgt den aktiven Ressourcen-/Reinheits-Filtern, Klick → hinfliegen).
- Deposits & Wells typisieren — begrenzte Vorkommen (
mResourceDepositTableIndex) und Öl-/Wasser-/Stickstoff-Wells (th.gl hat*_Deposit/*_Well-Gruppen). - Exakter Loop-Solver — lineares Gleichungssystem statt Kreislauf-Abschneiden.
- Geysir/Fracking eigene Marker, Gebäude-Icons.
- Randomisierte Saves — Seed-basierte Node-Daten (falls
mapOptionsgesetzt). - ✅ Packaging (electron-builder) —
npm run pack(entpackt) /npm run dist(NSIS-Installer). Config in package.jsonbuild; App-Icon fehlt noch (Default).