Skip SaveAndReload in HostServer when hosting from replay#4
Closed
Skip SaveAndReload in HostServer when hosting from replay#4
Conversation
HostUtil.CreateGameData unconditionally called SaveLoad.SaveAndReload to canonicalize the host's in-memory state via a serialize+deserialize roundtrip before sending the snapshot to clients. This is necessary for the SP-host path because SetupGameFromSingleplayer injects MP-specific state that needs to roundtrip through the load path to reach a canonical form. When hosting from a replay (fromReplay=true), the state was just deserialized from disk by Root_Play.Start -> LoadGameFromSaveFileNow and is already canonical. The roundtrip is wasted work, and worse, the in-process reload introduces Verse.Rand stream perturbations that don't occur on the client's fresh-process load. Mod-side spawn-time graphic randomization (e.g. AdaptiveStorage Framework via the ThingGrid.RegisterInCell transpiler hook) consumes Verse.Rand inside SeedMapFinalizeLoading's seeded scope at different stream positions on the host vs the client, shifting subsequent RegenerateEqualizeCells.Shuffle outputs and producing divergent equalizeCells per room. Within ~120 ticks the temperature- equalization drift propagates into a FreezeManager.DoIceMelting Rand.Chance short-circuit boundary and the main RNG stream desyncs. Switch CreateGameData to take fromReplay and use SaveGameData (save without reload) for the replay path. SP-host path is unchanged.
4 tasks
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.
Summary
HostUtil.CreateGameDataalways callsSaveLoad.SaveAndReload. The reload step is needed for the SP-host path (canonicalizes MP-injected state) but is wasted work for the replay-host path (state was just deserialized from disk).ThingGrid.RegisterInCelltranspiler hook) consumes Rand insideSeedMapFinalizeLoading's seeded scope at different stream positions on the host's in-process reload vs the client's fresh-process load. Result: divergentRoomTempTracker.equalizeCellsorderings → temperature drift → desync at the nextFreezeManager.DoIceMeltingRand.Chanceboundary (~120 ticks later).fromReplaytoCreateGameData. UseSaveLoad.SaveGameData(save without reload) on the replay path. SP-host path unchanged.Investigation
Probe instrumentation captured the divergence:
Map.FinalizeLoadingevent:Root_Play.Start): 42 regens, byte-identical to clientSaveAndReloadviaHostUtil.CreateGameData): 84 regens, divergent shuffle outputSaveAndReloadviaCreateJoinPointAndSendIfHost): also asymmetric, but fires after the desync is already manifestWallEqProbecaptured runtime divergence at tick 9954127 — host'sequalizeCellsfor room r=45 came from the SaveAndReload's shuffle output, client's from the fresh-process load.RandSeedProbecaptured the offending Rand consumer: AdaptiveStorage'sGraphic_Random.get_MatSinglechain via theThingGrid.RegisterInCelltranspiler hook on every item spawn duringMap.FinalizeLoading.Test plan
D:\Steam\steamapps\workshop\content\294100\2606448745\1.6\AssembliesCustom\)fromReplaywhich only matters when hosting MP).Notes
SaveAndReloadthere serves a different purpose (capture transient in-memory state for client snapshot). Left untouched.