# Fragalytics MVP Fragalytics is a self-hosted MVP for Counter-Strike 2 demo analysis and highlight generation built in C# on ASP.NET Core. You upload `.dem` files manually, the app stores them, parses them in a background worker, scores highlight moments with explainable rules, persists the results in a local JSON-backed store, and creates clip job records for future rendering. ## Why this C# stack - Web app: ASP.NET Core Razor Pages. It keeps the UI simple, local, and maintainable without adding a separate frontend build. - Background work: `BackgroundService`. Good enough for a single-node MVP and easy to replace later with a queue worker. - Persistence: file-backed JSON store. It avoids external package dependencies in this environment while still giving durable local storage and a clean repository boundary. - Parsing: parser adapter abstraction. The app can run today with the sample adapter and can be pointed at an external parser process later. - Scoring: explicit service layer with versioned rules and explainable output. ## What is implemented - Manual `.dem` upload - Background demo parsing - Persistent storage - Models for demos, matches, rounds, players, events, highlights, scoring profiles, and clip jobs - Explainable highlight scoring - Highlight tags: `multikill`, `headshot`, `opening_kill`, `clutch`, `1vX`, `wallbang`, `smoke_kill`, `round_win` - Clip job records with `start_tick`, `end_tick`, `status`, `renderer`, and payload - Web UI for demos, match details, highlights, and clip jobs - Rescoring when scoring rules change - Parser adapter abstraction - Future render-worker abstraction - Docker setup - Tests for scoring and basic ingest flow ## Run locally ```bash DOTNET_CLI_HOME=/tmp/dotnet-home DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1 dotnet run --project src/Fragalytics.Web ``` Open `http://localhost:5000` or the URL printed by ASP.NET Core. ## Run the render worker The render worker is a separate process: ```bash DOTNET_CLI_HOME=/tmp/dotnet-home DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1 dotnet run --project src/Fragalytics.RenderWorker ``` Right now it uses the `sample-copy` backend. That backend does not render from a `.dem`; it claims pending clip jobs and attaches a configured sample `.mp4` or `.webm` file so the end-to-end worker flow is in place. ## Docker ```bash docker compose up --build ``` Open `http://localhost:8080`. ## Configuration Main settings live in [appsettings.json](/home/hz/source/fragalytics/src/Fragalytics.Web/appsettings.json). - `Fragalytics:DataDirectory`: persistent app state root - `Fragalytics:UploadDirectory`: uploaded demo path - `Fragalytics:WorkerPollSeconds`: polling interval for the hosted worker - `Fragalytics:Parser:Mode`: `sample` or `external` - `Fragalytics:Parser:ExternalCommand`: executable for a real parser sidecar - `Fragalytics:Parser:ExternalArguments`: argument template, with `{demoPath}` placeholder - `Fragalytics:RenderWorker:Backend`: render backend name, currently `sample-copy` - `Fragalytics:RenderWorker:SampleClipPath`: source media file used by the stub backend - `Fragalytics:RenderWorker:TempDirectory`: worker scratch directory - `Fragalytics:RenderWorker:PollSeconds`: clip-job polling interval ## Scoring Scoring rules live in [scoring-rules.json](/home/hz/source/fragalytics/src/Fragalytics.Web/scoring-rules.json). Change the weights or threshold, then use `Rescore All Matches` from the home page. The app stores a hashed scoring version on each highlight so rule changes are traceable. ## Rendering Roadmap The app can now play uploaded clip files, but it still does not render video directly from CS2 demos. The step-by-step implementation plan for real demo-to-video rendering is documented in [docs/rendering-from-cs2-demo.md](/home/hz/source/fragalytics/docs/rendering-from-cs2-demo.md). ## Assumptions - This MVP is kill-centric. It does not yet score utility-only, positioning-only, or movement-only moments. - The default parser is the deterministic sample adapter so the app is runnable offline and testable in this environment. - Real CS2 parsing is expected to come from an external parser process wired through `IDemoParserAdapter`. That keeps the ASP.NET Core app stable even if the parsing implementation changes. - The persistent store is intentionally simple for a local MVP. If this moves beyond single-node use, replace the store with SQLite or Postgres behind the same service boundary. - Rendering is intentionally not implemented. Clip jobs are created as durable work intents for a future render worker. Current status: the separate render-worker scaffold exists, but the only backend today is a stub that copies a known video file instead of driving CS2. ## Tests ```bash DOTNET_CLI_HOME=/tmp/dotnet-home DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1 dotnet run --project tests/Fragalytics.Web.Tests ``` ## Extension path - Replace the JSON store with SQLite or Postgres - Move background processing into a separate worker process - Add a real parser sidecar that emits the normalized `ParsedDemo` JSON contract - Replace the `sample-copy` render backend with real CS2-driven capture - Add richer event ingestion and rule editing in the UI