Skip to content

Keep each local user’s game saves separate - #945

Open
thehobbit85 wants to merge 5 commits into
seedds:mainfrom
thehobbit85:codex/local-user-saves
Open

thehobbit85 wants to merge 5 commits into
seedds:mainfrom
thehobbit85:codex/local-user-saves

Conversation

@thehobbit85

@thehobbit85 thehobbit85 commented Nov 2, 2026 •

Copy link
Copy Markdown
Contributor

What and why

Two players starting the same title previously saw the same local account and wrote to the same save. The host now gives each account a stable ID and mounts the personal save for the ID the game requests. Console-wide data remains shared, including a device-only title's existing save.

The built Mac and iOS launchers can create, rename and select up to eight local users. A rename changes only the nickname. Rebuilding an app with the same title and data directory continues to use the original account ID and the existing save/ and, where present, save_device/. Additional accounts use separate sibling directories. Additional accounts' personal saves are outside the existing cloud archive.

Players can also pick one top-level save folder. A fresh per-title destination copies committed saves, including those of users no longer listed, and preserves guest filename metadata. Reopening a populated destination keeps its progress. Returning to the app default reopens the original files. An iOS reinstall that receives a new app container still requires restoring the old data or reselecting its external library.

External frontends can query, create and rename users through the packed Mac launcher's JSON commands, preselect an ID with DM_USER_ID, or request a save library with DM_SAVE_ROOT. Embedded launchers have the same operations through the C interface. Explicit DM_SAVE continues to take precedence.

The first three commits implement the feature in order. The first adds the account and save model and launcher selection. The second adds editing and management commands. The third adds save-folder selection and migration. Each intermediate commit built the Mac host and passed its focused tests.

Revision 786256765573e1749faf2a6b328091a224a220af includes main e5620b1a05a5b7374e0135d6312d3e90994eab81. Main does not provide the multi-user UID model, separate personal saves, or save library selection.

Validation on 786256765573. python3 -m unittest dm.tools.tests.test_docs dm.tools.tests.test_local_users dm.tools.tests.test_local_save_root dm.tools.tests.test_env_switches. All 30 tests passed.

The native local-account and local-save fixtures passed.

GitHub checks for this revision

Earlier local validation on 0342343baeb5. python3 -m unittest dm.tools.tests.test_docs dm.tools.tests.test_local_users dm.tools.tests.test_local_save_root dm.tools.tests.test_env_switches dm.tools.tests.test_nsp_mac_app dm.ios.tests.test_launcher_storage. All 80 tests passed. The native local-users target passed the account and save fixtures. The NVN table fixture passed all 227 checks, covering both cases that failed at the earlier base. This integration used synthetic and packaged-app fixtures. Physical iOS picker behavior, a full current game run and the supported Metal compiler remain separate validation.

Author validation on earlier revisions

All eight GitHub checks passed on 46af5af0. The follow-up commit fixed the clang-tidy diagnostics in the save path, cloud initialization and test helpers. The LLVM 23.1.4 baseline fell from 271 findings to 269. C analyzer run · Build and test run

  • Upgrade from the old engine. Built separate executables against upstream 68a0ac67 and the candidate using the real guest account and filesystem functions. The old executable committed identifiable account and device data on APFS. The candidate read back the same bytes with the default configuration and after renaming the original account. A second UID opened an empty personal save and read the shared device data. A second run exercised the original device-only layout.
  • Host. The complete make -C dm/host/tests GAME=010015100B514000 run suite passed. New cases exercise two mounted accounts, close/reopen, aliasing, failed commits, unknown IDs, and cloud recovery on launcher exit. A secondary-only session leaves the legacy cloud archive alone.
  • Launchers, storage and documentation. 79 tests passed across test_nsp_mac_app, test_local_users, test_local_save_root, test_docs and test_env_switches. The native and packaged paths cover first create, ID-preserving rename, concurrent edits, relative manifest paths, external selection, importing a fresh library and reopening an existing one. The packaged app's storage test also verified that a second library copied newer progress from the first library and left the original app save untouched. The pinned Python lint/type ratchet reported zero new findings.
  • iOS. All 42 iOS tests passed. The iOS compiler checked 188 sources with zero failures, and the production project linked an unsigned app with synthetic resources. The Files picker, bookmark persistence and gameplay have not been exercised on a physical iPhone or iPad.
  • Game regression. Ran scripts/regress.py impact 68a0ac67..956a19d6 and the full scripts/regress.py check --games 0100F43008C44000. Pokémon Legends Z-A 2.0.2 (v393216, NVN) was the only local Mac build recognized by the harness. It has no committed smoke route. The other affected titles were unavailable for a local comparison. On Apple M2 Max, macOS 26.6 and Xcode 27.0, the harness stopped during baseline shader preparation because this Mac has Metal 32023.921 and the repository pins 32023.922. No candidate gameplay, FPS or timing comparison was obtained. Real-game validation with the pinned toolchain remains outstanding.
  • Graphics suite. make -C dm/gfx/tests GAME=010015100B514000 run stopped at two NVN2 version assertions in t_nvn. The candidate and unmodified PR base both returned API 231.39 where the test expects 231.30, and both failed the GPU code-range assertion (2 of 225 checks).

Docs

  • Updated the player and settings guides, the external frontend contract, docs/development.md and the environment-switch reference.
  • No shader chain changes, game files, keys or extracted data.

Shared geometry fixture CI fix

This branch retains the #959 prerequisite as commit 786256765573. The patch and fixture contents match the shared fix exactly.

Prior published commit bb1494b75b37 applied #959 as a separate prerequisite. On 965be8264715, LLVM 23.1.4 reproduced both C lint findings in the geometry hazard fixture inherited from main. At that earlier head, the focused lint check passed and the Mac GPU fixture passed all 65 assertions in each of three cut configurations with Metal API validation. The prerequisite changes only the synthetic test. The feature validation and its limitations above still apply to the feature revision they name.

@thehobbit85
thehobbit85 marked this pull request as ready for review November 2, 2026 08:41
@thehobbit85
thehobbit85 force-pushed the codex/local-user-saves branch 2 times, most recently from 0342343 to 965be82 Compare November 2, 2026 11:36
Give the account service a stable roster and let the game and its launcher select users by ID. Route each account mount to its own transaction while preserving the original player's files and the title's shared device save. Only a host that reconciled a cloud save may ask its launcher to publish on exit.
Let players manage a roster on Mac and iOS before starting the game. Renaming keeps the account ID and save directory. Merge edits under a shared file lock so that two launchers can add players without losing either change. Expose the same operations and JSON result on the packaged Mac launcher for external frontends.
Let each built launcher select a save library, with one directory per title and separate account saves inside it. Copy committed saves to a fresh destination before publishing it, and reopen existing progress without overwriting it. Keep the original single-user location as the default and honor explicit frontend launch overrides.
Use the ROM overlay and title path components that were validated, and
require a returned guest handle before the save tests use it. Run the
first cloud reconciliation with dispatch_once so concurrent mounts
still wait for it without holding a separate mutex across file I/O.

The pinned analyzer passes with two fewer baseline findings. Local
account, save transaction, cloud sync and launcher checks pass.
@thehobbit85
thehobbit85 force-pushed the codex/local-user-saves branch from bb1494b to 7862567 Compare November 2, 2026 13:34
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