refactor(games): consolidate TF2 log parsing into the tf2-analyst module - #791
Open
garrappachc wants to merge 4 commits into
Open
refactor(games): consolidate TF2 log parsing into the tf2-analyst module#791garrappachc wants to merge 4 commits into
garrappachc wants to merge 4 commits into
Conversation
Extract all TF2 log-line interpretation (regex matching, round assembly, stopwatch side-swaps, restart detection, running score) into a self-contained, stateful `analyze(context, line)` fold under src/tf2-analyst, as a step toward publishing it as a standalone package. A thin per-game serialized adapter (parse-game-log) drives it and translates its events back into the existing match:* events, so downstream consumers are unchanged. The persisted GameContext blob (TTL-swept) replaces the games.roundprogress collection. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Contributor
Playwright test resultsDetails
|
TF2 servers emit many log lines a second, most matching nothing; reading and writing the context per line spammed the database. Hold it in memory (and cache the logSecret->game lookup), reading from the db only on a cache miss and writing only when a line actually moves the context. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
garrappachc
commented
Aug 12, 2026
Rename the module to tf2-game-analyzer and expose a Tf2GameAnalyzer class that owns the context, tracks its own dirtiness and exports it for persistence, so the adapter no longer stringifies the context inline. Addresses PR review: delay the in-memory cache eviction after game end (logs.tf/demos.tf uploads still arrive) and drop the redundant comments. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
garrappachc
commented
Aug 12, 2026
garrappachc
left a comment
Member
Author
There was a problem hiding this comment.
Automated review — a few things worth a look before merge. The refactor itself is a nice consolidation; round-assembly idempotency, restart-doubling detection, the stopwatch-swap logic and the regexes all match the old behavior. Findings inline, ranked by severity.
- clear swapPending on Game_Over so a next-map Round_Start on a still-cached analyzer can't emit a stray teams-swapped onto the ended game - drop the never-read isStopwatch context field - evict the per-logSecret queue entry alongside the other caches on game end - negative-cache unknown games for a minute so a stale gameserver streaming to a swept game no longer fires a findOne per line Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.
Why
TF2 log-line interpretation was split across two layers —
match-event-listener(line → event regexes, with a bit of in-memory restart-detection state) andtrack-match-rounds(round assembly, side-swaps, score aggregation in Mongo). This makes the parsing logic hard to reason about in isolation and impossible to reuse.This consolidates all of it into a self-contained, stateful fold:
under
src/tf2-analyst/, as a first step toward extracting it as a standalone package.GameContextis a plain, serializable per-game blob the caller owns;LogEventis a discriminated union of interpreted events (round ended,teams swapped,score reset,player connected, …).How
src/tf2-analyst/—analyze+GameContext+LogEvent, with all restart-detection, round-assembly and stopwatch-swap logic folded in. Reuses the app'sSteamId64/Tf2Team/is-stopwatch-roundfor now (marked with TODOs to isolate on extraction).parse-game-logplugin — a thin adapter that serializes perlogSecret(giving the fold a single-threaded, ordered stream), persists the context, and translatesLogEvent[]back into the existingmatch:*events +roundEnded/teamsSwappedgame-event writes. All downstream consumers are unchanged.GameContext(ingames.logparsestate, TTL-swept) replaces thegames.roundprogresscollection.match-event-listener,track-match-rounds, and the four now-internal events (match:roundWon,match:roundLength,match/score:reported,match/controlPoint:captured).Since the single-threaded fold removes cross-line races, the old atomic round-commit / duplicate-key-retry machinery is gone.
Verification
tf2-analystports the oldmatch-event-listener+track-match-roundstest cases and adds round-assembly/idempotency coverage.tests/20-game/15–18,20,21) are the real integration gate — relying on CI to run them.