Skip to content

Demo Analyzer: players of a folder read before show at once, and cloud demos aren't downloaded to list them (#992) - #996

Merged
ccoventry merged 10 commits into
devfrom
fix/analyzer-players-once-992
Oct 25, 2026
Merged

ccoventry merged 10 commits into
devfrom
fix/analyzer-players-once-992

Conversation

@ccoventry

Copy link
Copy Markdown
Owner

Release note: Fixed: the Demo Analyzer shows the players of a folder it has read before at once, and no longer downloads a whole cloud folder (OneDrive, Google Drive) just to list its players.

Closes #992

What changes

  • A folder read before shows its players at once. A new lookup_demo_players command sorts the folder's demos in one call without opening any: demos whose players file is current come back together, so there is no "Reading players" pass. A lookup that takes over 300 ms shows "Looking up players…".
  • Only the demos not read yet are read, and the counter counts just those: "Reading players: 3 / 38 not read yet".
  • Cloud demos wait for the user. A demo with no cache entry on a cloud drive isn't read, because reading it downloads it. The status line says how many and that Cache all reads them. Detection opens no file: Windows cloud-files placeholder attributes (RECALL_ON_DATA_ACCESS, RECALL_ON_OPEN, OFFLINE, used by OneDrive, Dropbox, Box and Google Drive's mirror mode), or a volume labelled "Google Drive" (Drive for desktop's streaming drive, which sets none of them). A demo that has a cache entry is read as before, from the cache.
  • Cache all and the player read don't analyse the same demo twice. While Cache all runs, the player read skips the demos it has queued and picks up each one's players as Cache all caches it (last in demo_cache_progress). If a read was already going when Cache all started, it stops. When Cache all ends (finished or stopped), whatever is still missing gets looked up again and read here, unless it's in the cloud.
  • New logic is in studio/src/folder_players.js (createFolderPlayers), native::player_index::lookup_players and native::sys::cloud_file. main.js is untouched.

Decisions

  • A full analysis is still how an unread demo gets its players (point 4 of the issue). A cheaper pass would still have to read the whole file, since players join mid-demo, so on a cloud drive it downloads just as much. The full analysis also fills the analyzer cache, so opening that demo later is instant. Not worth a second parser.
  • Every file on a "Google Drive" volume with no cache entry counts as in the cloud, even one Drive has kept on this PC: Drive's streaming drive doesn't say which files it has locally. The batch still answers the cached ones, and Cache all reads the rest.
  • Only the Analyzer lane changed. The Master Queue and Configuration > Players lookups read the project's own demos and still use index_demo_players as before.
  • If Cache all starts while a demo is being read, that one demo can still be analysed by both: the read stops between demos.
  • If the lookup fails, every demo is read the old way.
  • No cache format change: no SCHEMA_VERSION bump.

Test done

  • cargo test -p native --lib -- player_index cloud_file: 4 new tests pass. They cover the lookup sorting cached, cloud and to-read demos against a real cache store, the attribute masks, the volume label, and a local file reading as not online-only.
  • cargo clippy --workspace --exclude goldsrc-hooks --exclude web-analyzer --all-targets --locked -D warnings (1.98.1): clean. cargo fmt --all: clean.
  • npm run test:unit: 95 files / 853 tests pass, including 8 new ones in folder_players.test.js and main_js_budget.
  • Playwright: analyzer-tree (2 new tests: a folder opened twice only reads the uncached demo the first time and nothing the second time with an empty status line; a cloud folder reads nothing and says so), plus demo-cache, players, player-filter, analyzer-loading: all pass.
  • Not tested live: no real Studio run and no real cloud placeholder. Every OneDrive file on this PC is pinned (on device), and Google Drive for desktop wasn't running, so the Google Drive volume-label check is untested on a real drive. Live check for the user: open an uncached local folder twice (the second time should show players with no counter), open I:\My Drive\... with Drive running (nothing downloads, the status line points to Cache all), then press Cache all there (players fill in as it goes).

🤖 Generated with Claude Code

…che all's (#992)

A folder's demos are sorted in one lookup_demo_players call that opens no
demo: players already cached come back together, demos on a cloud drive with
no cache entry are left unread, and only the rest are walked one by one.
While Cache all runs, the demos it has queued are left to it and their
players are taken from the entry it writes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ccoventry and others added 2 commits October 24, 2026 04:26
…ast demo by full path

- initDemoCache returned nothing when the Cache all button exists (the real
  app), so analyzer_pane's destructure threw and the rest of
  initAnalyzerPane never ran. app-boot.spec.js (from #1004) caught it.
- demo_cache_progress's `last` was a bare file name, so the player read's
  `paths.includes(last)` never matched and players only filled in when
  Cache all ended. It is now the full path given (nothing else reads it).
- CloudCheck looks a folder's volume up once, not once per demo.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ccoventry

Copy link
Copy Markdown
Owner Author

Review (unattended pass)

Merged origin/dev in (#1004's main.js slim-down and the rest; no conflicts), then fixed two real bugs. New head: 36b590cf.

Fixed

  1. The Demo Analyzer stopped booting in the real app. initDemoCache returned api only from its early exit (no Cache all button). When the button exists, which it does in the real app, it returned undefined, so ({ isCaching } = initDemoCache(...)) threw and everything after that line in initAnalyzerPane (Browse button and the rest) never ran. The analyzer-tree harness has no Cache all button, so the PR's own e2e tests missed it. app-boot.spec.js from main.js only wires: eight startup blocks move into their own modules (part of #683) #1004 caught it once dev was merged in. Fix: return api; at the end, plus a demo-cache e2e test that isCaching names a running run's demos and none once it ends.
  2. Players never filled in while Cache all ran. demo_cache_progress.last was the bare file name (path.file_name()), so folder_players' paths.includes(last) never matched. Players only showed up when the run ended, through the leftovers lookup. The unit test used 'b.dem' for both, which hid it. last is now the full path as given (nothing else reads it: frontend, hooks and native checked), and a Rust test covers it.

Small improvement

  • CloudCheck cached the volume label per volume, but still called GetVolumePathNameW for every uncached demo. It now looks the volume up once per folder.

Checked, no change needed

  • Cache key: lookup_players and load_players both go through cache::stamp (size + mtime), and is_fresh uses the same stamp. An older-schema entry counts as in the cloud, which is right: run_analyzer_cached doesn't upgrade, so reading that demo would download it.
  • Attribute flags are right (OFFLINE 0x1000, RECALL_ON_OPEN 0x40000, RECALL_ON_DATA_ACCESS 0x400000). GetFileAttributesW and GetVolumeInformationW read metadata only and don't hydrate. Non-Windows stubs and the wasm32 gate are fine.
  • lookup_demo_players is async + spawn_blocking, .catch() is on the invoke, main.js is untouched, and there are no new error strings with paths.
  • Folder changes: every await re-checks folder, a stale walk is stopped by the new request id, and last from another folder's run is dropped because it isn't in paths.
  • Cache all stopped midway: the backend always sends a finished event (also when cancelled), and queued is cleared there. The leftovers are looked up again and read, or wait if they're in the cloud.

Judgement calls left for you

  • When Cache all takes over a running read, the event for the demo in flight carries the old request id and is dropped. Its players still arrive through Cache all's last for that demo, or through the lookup when the run ends. Not worth more code.
  • While Cache all runs, the player status line says nothing about the demos left to it. Cache all's own status shows progress.

Tests run (worktree, after the merge)

  • cargo test -p native --locked: 769 passed.
  • clippy 1.98.1 --workspace --exclude goldsrc-hooks --exclude web-analyzer --all-targets --locked -D warnings: clean. fmt 1.98.1: clean.
  • npm run test:unit: 96 files / 859 tests passed.
  • Full Playwright suite (CI=1): 393 passed, including app-boot, analyzer-tree, demo-cache, players, player-filter and analyzer-loading.
  • Not tested live, same as the PR: no real cloud placeholder and no running Studio.

🤖 Generated with Claude Code

ccoventry and others added 2 commits October 25, 2026 08:22
The "Players not read for N demos on a cloud drive" line already leaves
out demos Cache all has queued, but nothing redrew it until the first
demo was cached, which on a cloud drive is after that demo's whole
download. The run's first event now redraws it once.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ccoventry

Copy link
Copy Markdown
Owner Author

Live test (dev build, real Studio, isolated appdata)

Merged origin/dev in (clean, 80521f2) and fixed one small bug (f22aa54). Studio ran from a worktree with a copy of the user's settings and analyzer cache in an isolated appdata; the real settings.json was unchanged afterwards. Driven over WebView2 CDP. No game launch.

Measured

  • A folder read before shows its players at once. PRE install's dod (486 demos, all cached): lookup_demo_players took 64-75 ms (486 found, 0 to read, 0 cloud). Reopening the folder in the Analyzer: rows at 104 ms, all 186 player names at 219 ms, and the status line stayed empty the whole time.
  • Only unread demos get read. On a test folder of ktps8w3 anzio POV copies: first open of 5 uncached demos showed "Reading players: 0 / 5 not read yet" and counted up to 5 (64 s, debug-build parses). On the second open the players were there at 18 ms with no counter. After adding 2 demos, the 5 known ones showed at 18 ms and the counter read "0 / 2 not read yet".
  • Boot: the Analyzer booted, Cache all works, and the code after initDemoCache runs (sub-tabs switch).
  • Cloud: there is no real placeholder on this PC. OneDrive is set up but not running, none of its 6,349 files is a placeholder, and its one demo is pinned, so the lookup counts it as local (to read), which is right. No Google Drive volume is mounted. I simulated a cloud drive with local copies flagged FILE_ATTRIBUTE_OFFLINE. On open nothing was read, and the line read "Players not read for 3 demos on a cloud drive… Cache all reads them." With Cache all, the players arrived 25 ms after the first demo was cached, well before the run ended, so the full-path last match works.

Fixed

While Cache all ran, the cloud line stayed up until the first demo was cached (14 s here; on a real cloud drive, that demo's whole download). status() already leaves out queued demos, but nothing redrew the line. Now the run's first demo_cache_progress event redraws it once. Re-run live: the line cleared 27 ms after the click. There's a new unit test in folder_players.test.js.

Tests

  • npm run test:unit: 887 passed.
  • Playwright (CI=1): analyzer-tree, demo-cache, app-boot, players, player-filter and analyzer-loading, 31 passed.
  • cargo test -p native --lib -- player_index cloud_file demo_cache: 13 passed.

Saved formats

No change. SCHEMA_VERSION stays 12, and no settings or project file changes. Only the demo_cache_progress event's last changed, and an event isn't saved.

Not tested

  • A real OneDrive or Google Drive placeholder. The Drive volume-label check is still unexercised.
  • Outside this PR: the Demos list's map and type columns still read each demo's first 4 KB (read_demo_heads), which on OneDrive Files On-Demand may download the file.

🤖 Generated with Claude Code

@ccoventry
ccoventry merged commit 4ff05a0 into dev Oct 25, 2026
5 checks passed
@ccoventry
ccoventry deleted the fix/analyzer-players-once-992 branch October 25, 2026 14:47
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.

The Demo Analyzer reads every demo's players again each time a folder is opened

1 participant