fix(parser): graceful fallback when v1.2 chain-actor ExtraData diverges#7
Merged
Merged
Conversation
The v1.2 gate at ObjectSerializer.cs:135 assumes FGConveyorChainActor's ExtraData wire format is unchanged from pre-1.2 (AnthorNet's Read.js has no version branching for it). Synthesized v1.2 tests confirm that assumption for the empty / one-conveyor / no-items shapes — but real Beta Game v1.2 saves throw EndOfStreamException at the first ObjectReference inside DeserializeConveyorChainActor. Rather than guess at the diverged format, fall back gracefully: capture the reader position before the chain-actor decode, catch EndOfStreamException, seek back, and let the existing missing-bytes absorber consume the bytes as raw hex. Consumers see ConveyorActors[] = empty for affected chains (matches the behaviour SaveFileReader.cs already documents as the fallback contract). Pre-1.2 saves and other gated actor types are unaffected — the catch filter requires v12 && IsConveyorActor. Tests - RealSaveFixtureTests.Deserialize_DoesNotThrow(Beta Game_autosave_2.sav) now passes (previously threw EndOfStreamException at StringSerializer.ReadCharArray → ObjectReferenceSerializer.Deserialize → DeserializeConveyorChainActor:389). - The three SatisfactorySaveNet - v1.2 - *.sav fixtures and the legacy synthesised v1.2 chain-actor tests continue to pass — no chain-actor format change is hit on the happy path. Downstream: unblocks ERP.Satisfactory #142.
ChrisonSimtian
added a commit
to erp-for-factory-games/ErpForFactoryGames
that referenced
this pull request
May 17, 2026
…loses #142) 4.1.1 crashed with EndOfStreamException on real Beta Game v1.2 saves inside DeserializeConveyorChainActor. The fork's PR ChrisonSimtian/SatisfactorySaveNet#7 lands a graceful fallback: when the chain-actor decode throws, rewind and let the existing missing-bytes absorber consume the bytes as raw hex. Consumers see ConveyorActors[] = empty for affected chains — matches the fallback contract that SaveFileReader.cs already documents. Verified locally: Beta Game (28h played, 19 miners, 64 buildings, 434 belts, 820 nodes) parses with 0 warnings, /factory/alerts surfaces 5 active alerts as expected.
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.
Problem
The v1.2 gate in
ObjectSerializer.cs:135assumesFGConveyorChainActor's ExtraData wire format is unchanged from pre-1.2 (AnthorNet'sRead.jshas no version branching for it). The existing synthesized v1.2 chain-actor tests confirm that assumption for the empty / one-conveyor / no-items shapes — but real Beta Game v1.2 saves throwEndOfStreamExceptionat the firstObjectReferenceinsideDeserializeConveyorChainActor(line 389).Stack trace (against
Beta Game_autosave_2.sav):Fix
Rather than guess at the diverged format, fall back gracefully: capture the reader position before the chain-actor decode, catch
EndOfStreamException, seek back, and let the existing missing-bytes absorber consume the bytes as raw hex. Consumers seeConveyorActors[] = emptyfor affected chains — matches the fallback contract that theERP.Satisfactorydownstream already documents.Pre-1.2 saves and other gated actor types are unaffected — the catch filter requires
v12 && IsConveyorActor.Tests
RealSaveFixtureTests.Deserialize_DoesNotThrow(Beta Game_autosave_2.sav)— was failing, now passes (regression marker).SatisfactorySaveNet - v1.2 - *.savfixtures continue to pass.ExtraDataSerializerLegacyTests,ExtraDataSerializerV12Tests) continue to pass — happy path is unchanged.Follow-up
Per
TODO.md §5, the proper fix is to port the actual v1.2 chain-actor wire format once a reference (etothepii's TS parser, AnthorNet's JS) shows what changed. This PR is the safety net so consumers stop crashing in the meantime.Downstream
Unblocks ERP.Satisfactory #142.