# Phase 3: Built-in Game Adapters **Status:** ✅ Complete **Parent plan:** [PLAN.md](./PLAN.md) **Domain:** backend ## Objective Implement built-in adapters for popular games and ship example community adapter YAML files. Each adapter translates a game's native data format into standardized GameEvents. ## Tasks - [x] Task 1: CS2 Game State Integration adapter (`core/game_integration/adapters/cs2_adapter.py`) - Parse CS2 GSI JSON payload (player.state, round, map sections) - Events: player_health, player_armor, player_ammo, player_money, kill, death, round_start, round_end, bomb_planted, bomb_defused, flashbang, team - Auth: validate payload["auth"]["token"] against adapter_config["auth_token"] - Diff-based detection for kills (compare match_stats.kills with prev_state) - Setup instructions: how to create gamestate_integration_*.cfg in CS2 - Config schema: auth_token (string) - [x] Task 2: Dota 2 Game State Integration adapter (`core/game_integration/adapters/dota2_adapter.py`) - Similar to CS2 GSI format but different payload structure - Events: player_health, player_mana, kill, death, match_start, match_end, gold - Auth: same pattern as CS2 - Config schema: auth_token - [x] Task 3: League of Legends Live Client Data API adapter (`core/game_integration/adapters/lol_adapter.py`) - Poll-based: fetches from https://127.0.0.1:2999/liveclientdata/allgamedata - Events: player_health, player_mana, player_level, death, respawn, gold, game_time - Adapter manages its own polling thread (started/stopped with integration enable/disable) - Config schema: poll_interval_ms (int, default 500) - Note: LoL uses self-signed SSL cert — needs verify=False or custom cert handling - [x] Task 4: Generic webhook adapter (`core/game_integration/adapters/generic_webhook_adapter.py`) - User-defined JSON path mappings (configured in adapter_config) - Config schema: mappings list (same format as MappingAdapter YAML) - Effectively a MappingAdapter configured via API rather than YAML file - [x] Task 5: Register all built-in adapters in `core/game_integration/adapters/__init__.py` - [x] Task 6: Create example community adapter YAML files - `server/src/wled_controller/data/game_adapters/minecraft.yaml` — via webhook mod - `server/src/wled_controller/data/game_adapters/valorant.yaml` — via Overwolf/Insights API - `server/src/wled_controller/data/game_adapters/rocket_league.yaml` — via SOS plugin - [x] Task 7: Community adapter loader — scan data/game_adapters/ on startup, register as available - [x] Task 8: Write tests for CS2 adapter (payload parsing, auth validation, diff detection) - [x] Task 9: Write tests for Dota 2 adapter - [x] Task 10: Write tests for LoL adapter (mock HTTP responses) - [x] Task 11: Write tests for generic webhook adapter - [x] Task 12: Write tests for community YAML adapter loading ## Files to Modify/Create - `server/src/wled_controller/core/game_integration/adapters/cs2_adapter.py` - `server/src/wled_controller/core/game_integration/adapters/dota2_adapter.py` - `server/src/wled_controller/core/game_integration/adapters/lol_adapter.py` - `server/src/wled_controller/core/game_integration/adapters/generic_webhook_adapter.py` - `server/src/wled_controller/core/game_integration/adapters/__init__.py` — register all - `server/src/wled_controller/data/game_adapters/minecraft.yaml` - `server/src/wled_controller/data/game_adapters/valorant.yaml` - `server/src/wled_controller/data/game_adapters/rocket_league.yaml` - `server/tests/core/test_cs2_adapter.py` - `server/tests/core/test_dota2_adapter.py` - `server/tests/core/test_lol_adapter.py` - `server/tests/core/test_generic_webhook_adapter.py` - `server/tests/core/test_community_adapter_loader.py` ## Acceptance Criteria - CS2 adapter correctly parses real GSI payloads into standardized events - Dota 2 adapter handles its GSI format - LoL adapter can poll (mocked) and produce events - Generic webhook adapter translates arbitrary JSON using user-defined mappings - Community YAML files load and register correctly - Auth validation works per adapter - All tests pass with realistic payload samples ## Notes - Use real CS2/Dota2 GSI payload samples from documentation for tests - LoL polling thread must be stoppable (daemon thread or event flag) - Community adapter directory should be configurable (default: data/game_adapters/) - Generic webhook adapter reuses MappingAdapter logic from Phase 1 ## Review Checklist - [x] All tasks completed - [x] Code follows project conventions - [x] No unintended side effects - [x] Tests pass ## Handoff to Next Phase **Completed:** All 12 tasks implemented and tested. 92 tests pass, 0 failures. Ruff clean. ### What was built - `adapters/cs2_adapter.py` — `CS2Adapter` parsing CS2 GSI payloads. Continuous events: health, armor, money (gold), ammo. Diff-based triggers: kill, death. Phase triggers: round_start, round_end, bomb_planted (objective_captured), bomb_defused (objective_lost). Flash detection (blinded). Team affiliation (team_a/team_b). Auth via `payload["auth"]["token"]`. - `adapters/dota2_adapter.py` — `Dota2Adapter` parsing Dota 2 GSI payloads. Continuous: health (hp/max_hp ratio), mana (mp/max_mp ratio), gold (configurable max). Diff-based: kill, death. Match flow: match_start (PRE_GAME/GAME_IN_PROGRESS), match_end (POST_GAME/DISCONNECT). Auth via `payload["auth"]["token"]`. - `adapters/lol_adapter.py` — `LoLAdapter` parsing LoL Live Client Data payloads + `LoLPoller` daemon thread for polling `https://127.0.0.1:2999/liveclientdata/allgamedata`. Continuous: health, mana, level (speed), gold. Triggers: death (health drops to 0), respawn (objective_progress). No auth (local-only API). Poller uses `threading.Event` for clean stop, `ssl.CERT_NONE` for self-signed cert. - `adapters/generic_webhook_adapter.py` — `GenericWebhookAdapter` delegating to `MappingAdapter` internally. User defines mappings in `adapter_config["mappings"]`. Auth via configurable header (default: Authorization with Bearer prefix support). - `adapters/__init__.py` — Registers all 4 built-in adapters with `AdapterRegistry` on import. - `community_loader.py` — Scans `data/game_adapters/` for `.yaml`/`.yml` files, loads them as `MappingAdapter` instances keyed as `community_`. Module-level registry with `register_community_adapters()`, `get_community_adapter()`, `get_community_adapter_info()`. - `data/game_adapters/minecraft.yaml` — Webhook-based, maps health/armor/food/XP/kills/deaths. - `data/game_adapters/valorant.yaml` — Webhook-based via Overwolf, maps health/shield/money/kills/deaths/round/spike. - `data/game_adapters/rocket_league.yaml` — Webhook-based via SOS plugin bridge, maps boost/speed/goals/time/teams. ### Key design decisions - **CS2/Dota2 auth** uses `payload["auth"]["token"]` (not HTTP headers) — matches how Valve's GSI actually sends the token. - **LoL polling** is opt-in via `LoLPoller` class, not auto-started by the adapter. The integration manager (Phase 4+) should instantiate and manage poller lifecycle. - **Generic webhook** creates a transient `MappingAdapter` per `parse_payload` call. This is simple and stateless — the adapter_config is the source of truth. For high-frequency usage, caching the MappingAdapter instance could be a future optimization. - **Community adapters** are separate from `AdapterRegistry` (which holds class-based adapters). They live in `community_loader._community_adapters` since they're instance-based MappingAdapters. ### What Phase 4+ needs - Import `wled_controller.core.game_integration.adapters` in `main.py` to trigger built-in adapter registration. - Call `register_community_adapters()` from `community_loader` during app startup. - The adapter listing endpoint (`GET /api/v1/game-adapters`) should also include `get_community_adapter_info()` results. - LoL polling needs lifecycle management — start `LoLPoller` when a LoL integration is enabled, stop when disabled.