Files
ledgrab/plans/game-integration/phase-3-adapters.md
T
alexei.dolgolyov 492bdb95e3 feat: game integration system
Receive real-time events from games (CS2, Dota 2, LoL, etc.) and drive
LED effects through the existing color strip and value source pipelines.

Core:
- GameEventBus (thread-safe pub/sub) with standardized 23-type event vocabulary
- GameAdapter ABC + AdapterRegistry + MappingAdapter (YAML-driven)
- Built-in adapters: CS2 GSI, Dota 2 GSI, LoL Live Client, Generic Webhook
- Community YAML adapters: Minecraft, Valorant, Rocket League
- GameEventColorStripStream with 5 effects (flash/pulse/sweep/color_shift/breathing)
- GameEventValueSource with EMA smoothing and timeout
- 4 built-in effect presets (FPS Combat, MOBA Health, Racing, Generic Alert)
- Auto-setup for Valve GSI games (Steam path detection, cfg file writing)
- Demo capture engine exposed to non-demo mode

Frontend:
- Game tab in Streams tree navigation with integration cards
- Game integration editor modal with adapter picker, config fields, event mappings
- game_event source type in CSS and ValueSource editors
- Setup instructions overlay (markdown rendered)
- Live event monitor and connection test

API:
- Full CRUD for game integrations
- Event ingestion endpoint (adapter-level auth)
- Adapter metadata, presets, auto-setup, status/diagnostics endpoints
2026-03-31 13:17:52 +03:00

111 lines
7.8 KiB
Markdown

# 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_<stem>`. 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.