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
7.8 KiB
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 modserver/src/wled_controller/data/game_adapters/valorant.yaml— via Overwolf/Insights APIserver/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.pyserver/src/wled_controller/core/game_integration/adapters/dota2_adapter.pyserver/src/wled_controller/core/game_integration/adapters/lol_adapter.pyserver/src/wled_controller/core/game_integration/adapters/generic_webhook_adapter.pyserver/src/wled_controller/core/game_integration/adapters/__init__.py— register allserver/src/wled_controller/data/game_adapters/minecraft.yamlserver/src/wled_controller/data/game_adapters/valorant.yamlserver/src/wled_controller/data/game_adapters/rocket_league.yamlserver/tests/core/test_cs2_adapter.pyserver/tests/core/test_dota2_adapter.pyserver/tests/core/test_lol_adapter.pyserver/tests/core/test_generic_webhook_adapter.pyserver/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.py—CS2Adapterparsing 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 viapayload["auth"]["token"].adapters/dota2_adapter.py—Dota2Adapterparsing 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 viapayload["auth"]["token"].adapters/lol_adapter.py—LoLAdapterparsing LoL Live Client Data payloads +LoLPollerdaemon thread for pollinghttps://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 usesthreading.Eventfor clean stop,ssl.CERT_NONEfor self-signed cert.adapters/generic_webhook_adapter.py—GenericWebhookAdapterdelegating toMappingAdapterinternally. User defines mappings inadapter_config["mappings"]. Auth via configurable header (default: Authorization with Bearer prefix support).adapters/__init__.py— Registers all 4 built-in adapters withAdapterRegistryon import.community_loader.py— Scansdata/game_adapters/for.yaml/.ymlfiles, loads them asMappingAdapterinstances keyed ascommunity_<stem>. Module-level registry withregister_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
LoLPollerclass, not auto-started by the adapter. The integration manager (Phase 4+) should instantiate and manage poller lifecycle. - Generic webhook creates a transient
MappingAdapterperparse_payloadcall. 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 incommunity_loader._community_adapterssince they're instance-based MappingAdapters.
What Phase 4+ needs
- Import
wled_controller.core.game_integration.adaptersinmain.pyto trigger built-in adapter registration. - Call
register_community_adapters()fromcommunity_loaderduring app startup. - The adapter listing endpoint (
GET /api/v1/game-adapters) should also includeget_community_adapter_info()results. - LoL polling needs lifecycle management — start
LoLPollerwhen a LoL integration is enabled, stop when disabled.