SaveLoadX robustness - #82
Draft
Goober5000 wants to merge 5 commits into
Draft
Conversation
Goober5000
commented
Sep 5, 2026
Contributor
- fix StripFS2 and detect support ships by ship type
- validate loaded data before applying it
- key save slots by string so sparse indexes survive a restart
- document support-ship rule, tolerant loading, and slot keys
- restore subsystem data by name at arrival, not by parse index
StripFS2 assigned its result to a stray global and returned the input unchanged. It was also applied to the finished "<pilot>_<mission>.sav" filename, whose last four characters can never be ".fs2", so a mission argument given with its extension was never stripped. Mission names passed to the external-load and check SEXPs are now normalized (extension stripped, lowercased) before being used as a filename or a cache key. Support ships were identified purely by the "Support" name prefix. The engine ship type (objecttypes.tbl, exposed as shipclass.Type.Name) is now recorded whenever a live handle is available, with a ship-registry lookup and finally the name prefix as fallbacks for ships whose handles can no longer be resolved at save time. The save log names the detection route. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Table lookups (tb.ShipClasses, tb.WeaponClasses, mn.Teams) return an invalid handle rather than nil for an unknown name, so a save whose class or team no longer exists used to be assigned blindly. New Resolve* helpers return a valid handle or nil and log the skip. Apply paths now tolerate a save that does not match the current ship: missing or extra subsystems, missing or extra weapon banks, absent Position/Orientation/Velocity tables, and malformed order or bank entries are logged and skipped instead of indexing nil. SanitizeShipData fills in the sub-tables every path indexes into. File and slot level: a save file that fails to parse resets to empty with a log line instead of leaving LoadedData nil; LoadAll only commits CurrentIndex once the slot is confirmed to exist, so a bad index no longer makes CheckLateData crash on every later arrival; QueryStatus on a missing slot returns true (same as "nothing loaded") instead of indexing nil. HandleIsValid is a forward-compatible validity test for parse_subsystem handles, which do not yet expose isValid(); it uses isValid() when present and otherwise falls back to a guarded field read. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
LoadedData was keyed by the numeric slot index. dkjson writes a Lua table as a JSON array only when its keys are dense, so a mission that only ever saved to a slot above 10 got an object with a string key on disk, and the numeric lookup after the next mission start found nothing. Every slot access now goes through SlotKey (tostring), and NormalizeSlots converts the keys of a freshly read file so legacy array-shaped saves keep working. Exists() with no index uses next() instead of the length operator, and the SaveAll fallback for a nil index scans for the highest numeric key instead of relying on #. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
ApplyParseData applied saved subsystem damage and turret loadouts to a not-yet-arrived ship by position in the parse object's subsystem array. That array only holds the subsystems FRED explicitly wrote to the mission file, in file order, while the save records every model subsystem in model order, so the two lists never lined up and damage landed on the wrong subsystem or nowhere. The parse object's MainStatus is likewise an invalid handle when FRED made no loadout change, so a late arrival's saved weapons were never restored. Saved subsystem entries now carry the canonical model name. Two shared helpers, ApplySubsystemData and ApplyShipBankData, apply subsystem and bank data to a live ship matched by name (case-insensitive), with a positional fallback and a log line for saves written before names were recorded. ApplyData uses them at load time for ships already present; ApplyLateData uses them on the arrival hook for ships that were on the arrival list, where every subsystem is addressable. The parse-side subsystem loop is removed; MainStatus is still applied when valid so the loadout is right at spawn, and re-applied idempotently on arrival. A saved 0% subsystem is destroyed with destroy-subsys-instantly rather than by writing a non-positive HitpointsLeft, which would trigger debris and effects. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.