Repository navigation
sync: a game being played is held back from syncing until the session ends - #117
Conversation
… ends A game autosaves every few minutes, and each autosave was synced as it came: one afternoon a device took eleven versions of a game in 26 minutes from the device it was being played on, each with its own snapshot, and the playing device snapshotted every autosave too. And a device took other devices' versions of a game running on it, writing a save under the game. While a game is being played on a device (its play sessions): - it is not synced either way there (ErrPlayingHere, quiet as ErrHeld); - another device asking for it is answered "playing", for its manifest and for deletions, and says so once per session instead of failing; the answer contains SettlingMessage, so an older build takes it as busy; - the watcher snapshots a change to it at most once per 30 minutes. When the session ends, the existing "After playing" snapshot is taken and the save goes to the other devices in one sync. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
While a session is open the game's save is now held back from syncing, so a session that outlives the game holds the save with it. A process named like the game stayed behind for fifteen minutes after one closed, with under 1 MB and no processor time; a killed one left two that kept their memory and used no processor time at all. Processes are measured on Windows (memory, processor time; sessions.Proc). One holding under 32 MB that used no processor time since the last poll (sessions.Idle), or one that used none for a minute whatever it holds, is not the game (daemon.activeProcesses). A game being played uses some all the time; a paused one still draws its menu. One that cannot be measured still counts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@ChakraFusion is attempting to deploy a commit to the sivadaboi's projects Team on Vercel. A member of the Team first needs to authorize it. |
|
Thanks, this reads well, and it fits with the Deck plugin's play sessions (#109). One question on |
Asked in review: a game minimised in the background can throttle to almost nothing, or be suspended, and Windows trims its memory as well. Either rule - under 32 MB and no processor time since the last poll, or no processor time for a minute - could end its session while it was still running, and the save would be synced under it. There is one rule now: a process that has used no processor time for five minutes is not the game, whatever memory it holds. That still lets go of what a closed game left behind well before the quarter of an hour one lingered. Memory is no longer read. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A process left behind that still ticks over now and then - a crash reporter, a launcher's helper - counts as running however long it lingers, and while a session is open the game's save is held back from syncing. So a session whose save has not changed for an hour is ended: kept and synced as when the game closes. The process still there does not start it again; the save changing again does, and so does the game being started anew. TestQuietSession_EndsAfterAnHourWithoutASave fails without it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The hour without a save ended any session, and so two it should not: - a game saved only by hand, at save points, can go hours without a save while it is played, and its save would then be synced under it; - a session begun from outside - the Deck plugin from Steam's launch, `opensave wrap` - ends when that says so, and knows. Now a session begun from outside is never ended this way, and one seen from running processes is ended only if, besides not saving for an hour, the game's processes used under 1% of one core in it (some 36 seconds). A game being played keeps drawing and uses far more; a crash reporter or launcher helper left behind uses less. Measured in hour-long windows from the session's start or the save's last change. A process that cannot be measured is never taken as quiet. sessions.CPUOf sums a game's processes, matched as Running matches them; Tracker.Marked says a session was begun from outside. Tests: a leftover that ticks over is let go, a game played for three hours without saving keeps its session, and a session begun from outside is not ended; the last two fail under the hour alone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Good point, and it went further than This makes it the intended behaviour: a game that has used no processor time for five minutes is taken as not being played, its session ends and the save syncs, so nothing a game leaves behind can hold the save back for longer. A game in the background that still ticks over keeps its session. One gap was left: a leftover process that keeps using a little processor time (a crash reporter, a launcher helper) would keep the session, and the hold on the save, open indefinitely. Closed in af64c5b and 9d4ecb1: a session found from running processes is taken as over when, for an hour, its save hasn't changed and the game's processes used under 1% of one core (about 36 seconds). It's then kept and synced as when the game closes. The process still there doesn't start it again; the save changing again does. Two cases are deliberately left alone:
What's left is a game paused and minimised for over an hour, throttled under 1% and not saving. Its session ends and its save is kept and synced. If another device changes the save meanwhile, the user is asked when they come back and save. Tests:
🤖 Generated with Claude Code |
# Conflicts: # internal/p2p/syncengine/engine.go
|
Thanks, the new rules read well: five minutes without processor time, the hour-long backstop only for nearly idle sessions found from running programs, and outside sessions left alone. We'd like to merge it. It now conflicts with #114 (just merged) in |
|
Thanks! Done in d2161e4: today's |
The next standalone part of the play-sessions draft (#34), rebuilt on
dev-publicwithout save versions.The problem. A game autosaves every few minutes, and each autosave was synced as it came. One afternoon, a device took eleven versions of one game in 26 minutes from the device it was being played on, each with its own "synced from another device" snapshot. The playing device snapshotted every autosave too. The gameplay guard only waits while save files are locked, and many games don't keep them locked. And a device took other devices' versions of a game running on it, writing a save under the game.
The change (
syncengine/playing.go,daemon/sessions.go). While a game is being played on a device (the daemon's play sessions):SyncGamedeclines quietly (ErrPlayingHere, which counts asErrHeld);SettlingMessage, so an older build takes it as "busy, ask again";When the session ends, the existing "After playing" snapshot is taken and the save goes to the other devices in one sync.
A session must end when the game does (second commit), since it now holds the save. A process named like the game stayed behind for fifteen minutes after one closed, with under 1 MB and no processor time; a killed one left two that kept their memory and used none. On Windows, processes are now measured (memory and processor time). One holding under 32 MB that used no processor time since the last poll, or one that used none for a minute whatever it holds, is not the game. A game being played uses some all the time; a paused one still draws its menu. A process that can't be measured still counts.
Tests:
syncengine:TestPlaying_NotSyncedWhilePlayedHere,TestPlaying_PeerPlayingIsWaitedFordaemon: checkpoint timing,TestSessionEndsWhenOnlyAStubIsLeft(fails without the idle filter),TestActiveProcesses_HoldingMemoryDoingNothingsessions:TestIdleLeft for a follow-up (P2): finding a running game wherever it is installed, and protected games whose path can't be read.
Checks: the full unit and e2e suites pass locally on today's
dev-public.🤖 Generated with Claude Code