Skip to content

device settings: find a game's settings by what they hold, and give back what is a save - #118

Open
ChakraFusion wants to merge 7 commits into
Liquid-co:dev-publicfrom
ChakraFusion:pr/settings-by-content
Open

ChakraFusion wants to merge 7 commits into
Liquid-co:dev-publicfrom
ChakraFusion:pr/settings-by-content

Conversation

@ChakraFusion

Copy link
Copy Markdown

The follow-up #84 left out: settings files the game database doesn't list are recognised by their contents.

Detection (presets/detectsettings.go):

  • A file is taken for a game's settings by its contents: the vocabulary of one machine's display (resolution, vsync, shadow quality, upscaler), matched inside identifiers split at case changes and separators (_enableFrameGeneration, r_materialAniso), in UTF-8 or UTF-16. Unreal Engine's Config/<platform> folder is taken by convention.
  • Names only add weight. Measured over the game database's own labels, "settings" names 1,521 settings files and 70 saves, and ".ini" 1,831 and 137, so a name never decides on its own.
  • A name with a save word in it rules a file out unless the contents are overwhelming. The game database's save locations, logs, binaries, long files that are only partly settings, and Syncthing's conflict copies are never taken.

Giving back what is a save: a file taken for settings that changes like a save is given back for good (verdict "save", which also overrules the game database):

  • before it is left out, and at every later run, the game's snapshots are asked whether it changed in most of the snapshots in which the rest of the save changed (snapshot/history.go);
  • while it is left out, every change here and every manifest another device sends count whether it changed when the rest of the save did. Three such changes, outnumbering the times it stayed put, give it back (daemon/detectsettings.go).

Storage and UI: verdicts are kept per game (migration 0042: settings_files, settings_observations, and marks for when each game was last judged). They're applied through the device settings from #84, so hashes recorded under the old list are carried over as before. The game's file list says why each excluded file is excluded, and unticking one syncs it. The configuration tab's new text is in the catalogs: en, plus zh-Hans and zh-Hant as a first attempt for @Janlhin to correct. SyncIgnoreEditor isn't extracted yet, so its new labels are plain English like the rest of it.

On my machine: 74 files in 25 games detected, among them Crimson Desert's, DOOM Eternal's and Elden Ring's settings, and 12 games the game database doesn't cover. None of them is a file the snapshot history shows changing with the save.

Tests: presets/detectsettings_test.go, snapshot/history_test.go, daemon/devicesettings_test.go (TestObserveSettings_GivesBackWhatChangesLikeASave).

Checks: the full unit and e2e suites pass locally on today's dev-public.

🤖 Generated with Claude Code

…ack what is a save

The game database names the settings files of the games it knows (Liquid-co#84).
For the rest, a file is now taken for a game's settings by its contents
(presets/detectsettings.go): the vocabulary of one machine's display -
resolution, vsync, shadow quality, upscaler - matched inside identifiers
split at case changes and separators ("_enableFrameGeneration",
"r_materialAniso"), in UTF-8 or UTF-16. Unreal Engine's Config/<platform>
folder is taken by convention.

Names only add weight, by how reliably the game database's own labels show
them to mean settings: over its 16,700 settings and 9,000 save entries,
"settings" names 1,521 settings files and 70 saves, ".ini" 1,831 and 137,
so a name never decides on its own. A name with a save word in it rules a
file out unless the contents are overwhelming; the game database's save
locations are never taken, and nor are logs, binaries, long files that are
only partly settings, or Syncthing's conflict copies.

A file taken for settings that changes like a save is given back for good
(verdict "save", which also overrules the game database):
- before it is left out, and at every later run, the game's snapshots are
  asked whether it changed in most of the snapshots in which the rest of
  the save changed (snapshot/history.go);
- while it is left out, every change here and every manifest another
  device sends count whether it changed when the rest of the save did;
  three such changes, outnumbering the times it stayed put, give it back
  (daemon/detectsettings.go).

Detected files and verdicts are kept per game (migration 0042:
settings_files, settings_observations, and marks for when each game was
last judged) and applied through the device settings, so hashes recorded
under the old list are carried over as before. The game's file list says
why each excluded file is excluded, and unticking one there syncs it. The
configuration tab's new text is in the catalogs (en, zh-Hans, zh-Hant).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@vercel

vercel Bot commented Oct 21, 2026

Copy link
Copy Markdown

@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.

@Omar-VF

Omar-VF commented Oct 23, 2026

Copy link
Copy Markdown
Collaborator

Thanks, the detection and the give-back checks are thorough. Two changes before we can merge:

  1. 0042_settings_files.sql creates settings_files, settings_observations and marks with plain CREATE TABLE. Builds of the earlier draft stack created settings_files (0043) and marks (0041) with the same schema, and a database from those builds would now fail to start with "table already exists". We saw exactly this last week with sync_device_settings. Please use CREATE TABLE IF NOT EXISTS for these.
  2. Files found by their contents should not be left out of sync automatically. A wrong guess means part of a save stops syncing without the user knowing, which is a bigger risk than Keep each device's game settings out of sync #84's database-listed files. In this version, detected files should be shown in the game's configuration tab as suggested settings files, and keep syncing until the user confirms them there. The game database's files stay automatic, as they are now.

ChakraFusion and others added 3 commits October 26, 2026 08:52
…y-content

# Conflicts:
#	cmd/opensave-app/frontend/src/locales/en.json
#	cmd/opensave-app/frontend/src/locales/zh-Hans.json
#	cmd/opensave-app/frontend/src/locales/zh-Hant.json
Asked in review: builds of the earlier draft of this change made
settings_files and settings_observations (0043) and marks (0041) with the
same schema, and a database from one of them would not start under a
plain CREATE TABLE ("table already exists"), as happened with
sync_device_settings. IF NOT EXISTS for all three, and a test that opens a
database holding them with 0042 unrecorded; it fails without.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
…c until confirmed

Asked in review: a wrong guess would stop part of a save syncing without
anyone knowing, a bigger risk than the game database's listed files. So a
file found by its contents is now only suggested (verdict "suggested"): it
keeps syncing, and the game's configuration tab lists it with "Keep on
this device" and "Keep syncing". Keeping it on this device leaves it out
of syncing as the database's files are, and the give-back checks watch it
as before; keeping it syncing records "declined", and it is not suggested
again. A suggestion that the snapshots show changing with the save is
withdrawn. The game database's files stay automatic.

POST /api/games/{id}/suggested-settings {path, keepHere} answers one; the
game's JSON carries suggestedSettings. The tab's new text is in the
catalogs (en, zh-Hans, zh-Hant).

TestSuggestedSettings_SyncUntilConfirmed: detection suggests the settings
files and leaves them syncing, an answer each way does what it says, and
detecting again changes neither.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@ChakraFusion

Copy link
Copy Markdown
Author

Thanks, both done:

  1. Migration (cb5c9e7): CREATE TABLE IF NOT EXISTS for all three tables. TestSettingsFilesMigrationOpensOverTheDraftsTables opens a database that holds them with 0042 unrecorded; with a plain CREATE TABLE it fails with "table settings_files already exists".

  2. Suggestions instead of automatic (1a4c278): a file found by its contents is now only suggested. It keeps syncing, and the game's configuration tab lists it with Keep on this device and Keep syncing.

    • Keep on this device leaves it out like the database's files, and the give-back checks watch it as before.
    • Keep syncing records declined, so it isn't suggested again.
    • A suggestion that the snapshots show changing with the save is withdrawn.
    • The game database's files stay automatic.

    The tab answers through POST /api/games/{id}/suggested-settings. The new text is in all three catalogs, and the Chinese is a first attempt for @Janlhin to check. TestSuggestedSettings_SyncUntilConfirmed covers detection leaving files syncing, both answers, and a second detection run changing neither.

The branch also has today's dev-public merged in (the catalogs from #112).

🤖 Generated with Claude Code

Asked about in Liquid-co#106's review for its own lookup: a large save must not be
read into memory snapshot by snapshot. ChangesLikeASave read every file of
each of a game's last 40 snapshots to tell whether the rest of the save
changed - some ten million rows for a quarter-million-file save, each time
detection ran - and for a snapshot without a recorded file list it
unpacked and hashed the whole archive. Now the database compares two
snapshots (store.SnapshotsDiffer, stopping at the first difference), only
the files being judged are read (SnapshotPathHashes), and a snapshot with
no recorded list is left out (SnapshotHasFiles). ArchiveFiles and filesOf,
used only here, are gone.

TestSnapshotsDiffer covers the comparison; TestChangesLikeASave passes
unchanged.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
ChakraFusion added a commit to ChakraFusion/OpenSave that referenced this pull request Oct 26, 2026
The fixes.9 test build made peer_links, with the same schema, as 0038. A
database from it would not start under a plain CREATE TABLE ("table
already exists"), the problem the review on Liquid-co#118 found for its tables.
IF NOT EXISTS, and a test that opens a database holding it with 0039
unrecorded; it fails without.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@ChakraFusion

Copy link
Copy Markdown
Author

One more change, in the spirit of your review on #106: ChangesLikeASave (the snapshot-history check that gives back a file changing like a save) read every file of each of a game's last 40 snapshots, just to tell whether the rest of the save changed. For a quarter-million-file save that's some ten million rows each time detection ran. For a snapshot without a recorded file list, it even unpacked and hashed the whole archive.

In 899ccce it changed:

  • The database compares two snapshots (store.SnapshotsDiffer, stopping at the first difference).
  • Only the files being judged are read (SnapshotPathHashes).
  • A snapshot without a recorded list is left out instead of unpacked. ArchiveFiles and filesOf are gone.

TestChangesLikeASave passes unchanged, and TestSnapshotsDiffer covers the comparison. Full unit and e2e suites pass locally.

🤖 Generated with Claude Code

ChakraFusion and others added 2 commits October 28, 2026 08:21
A game cannot replace a file another program has open - Windows refuses
the rename whatever share mode the reader asked for - and many games save
by writing a new file and renaming it over the old. Detection read every
candidate file at each run, with os.ReadFile, whose handle on Windows also
keeps the game from deleting it. Now:
- it reads through fsx.OpenShared, so deleting is never blocked;
- a file that already has a verdict is not opened again: after the first
  run, only new files are read;
- it does not run while a session of the game is open, when the game
  writes its saves; that game's turn comes after the session.

Tests: a judged file is left alone; while a file is being judged the
game can delete it (Windows; fails with os.Open); no detection while the
game is played, and detection after it (fails without the check).

Co-Authored-By: Claude Opus 5.5 <[email protected]>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants