492bdb95e3
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
111 lines
7.8 KiB
Markdown
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.
|