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

7.8 KiB

Phase 3: Built-in Game Adapters

Status: Complete Parent plan: 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

  • 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)
  • 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
  • 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
  • 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
  • Task 5: Register all built-in adapters in core/game_integration/adapters/__init__.py
  • 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
  • Task 7: Community adapter loader — scan data/game_adapters/ on startup, register as available
  • Task 8: Write tests for CS2 adapter (payload parsing, auth validation, diff detection)
  • Task 9: Write tests for Dota 2 adapter
  • Task 10: Write tests for LoL adapter (mock HTTP responses)
  • Task 11: Write tests for generic webhook adapter
  • 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

  • All tasks completed
  • Code follows project conventions
  • No unintended side effects
  • 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.pyCS2Adapter 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.pyDota2Adapter 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.pyLoLAdapter 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.pyGenericWebhookAdapter 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.