WIP(refactoring): GameSystemsManager, CombatCoordinationIntegrator, and subsystem registry planning

Work-in-progress from recovery-refactoring-work branch including:
- GameSystemsManager interface and implementation
- CombatCoordinationIntegrator scaffolding
- BotAI and BotMessage header updates
- CMakeLists.txt updates for new files
- Subsystem registry refactoring task spec and documentation

Co-Authored-By: Claude Opus 4.6 <[email protected]>
Signed-off-by: luis <[email protected]>
This commit is contained in:
agatho
2026-02-06 13:53:50 -03:00
committed by luis
co-authored by Claude Opus 4.6
parent 858235aec4
commit 04fbdf7242
14 changed files with 5279 additions and 0 deletions
+711
View File
@@ -0,0 +1,711 @@
# Orchestration Architecture - Zenflow + Obsidian for TrinityCore
## System Overview
```
┌─────────────────────────────────────────────────────────────────┐
│ Zenflow Desktop (GUI) │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Project │ │ Workflow │ │ DAG │ │
│ │ Dashboard │ │ Designer │ │ Visualizer │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │ │
│ │ Orchestrates │
│ ▼ │
└───────────────────────────────────────────────────────────────-─┘
│
┌───────────────────┼───────────────────┐
│ │ │
▼ ▼ ▼
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ Claude Instance 1│ │ Claude Instance 2│ │ Claude Instance 3│
│ (Analysis) │ │ (Implementation) │ │ (Review) │
│ │ │ │ │ │
│ Branch: │ │ Branch: │ │ Branch: │
│ zenflow/agent-1/ │ │ zenflow/agent-2/ │ │ zenflow/agent-3/ │
└────────┬─────────┘ └────────┬─────────┘ └────────┬─────────┘
│ │ │
│ Shared Tasks │ Shared Memory │
│ (via env var) │ (via MCP) │
│ │ │
└────────────────────┼────────────────────┘
│
▼
┌───────────────────────────────┐
│ CLAUDE_CODE_TASK_LIST_ID │
│ "trinitycore-playerbot-shared"│
│ │
│ Task Coordination Layer │
│ • DAG dependencies │
│ • Status updates │
│ • Blocking relationships │
└───────────────┬───────────────┘
│
▼
┌───────────────────────────────┐
│ Obsidian Vault (MCP) │
│ C:\...\PlayerBotProjectMemory│
│ │
│ ┌─────────────────────────┐ │
│ │ Architecture/ │ │
│ │ • System Overview.md │ │
│ │ • Component Docs │ │
│ └─────────────────────────┘ │
│ │
│ ┌─────────────────────────┐ │
│ │ Tasks/ │ │
│ │ • Active Instances.md │ │
│ │ • Task definitions │ │
│ └─────────────────────────┘ │
│ │
│ ┌─────────────────────────┐ │
│ │ Refactorings/ │ │
│ │ • DI Cleanup.md │ │
│ │ • Sprint Plans │ │
│ └─────────────────────────┘ │
│ │
│ ┌─────────────────────────┐ │
│ │ Sessions/ │ │
│ │ • Implementation notes │ │
│ │ • Build reports │ │
│ │ • Handover docs │ │
│ └─────────────────────────┘ │
│ │
│ ┌─────────────────────────┐ │
│ │ Gotchas/ │ │
│ │ • Git Workflow rules │ │
│ │ • Build system issues │ │
│ └─────────────────────────┘ │
└───────────────┬───────────────┘
│
│ All agents read/write
│ Persistent memory
│ Graph visualization
│
▼
┌───────────────────────────────┐
│ TrinityCore Repository │
│ C:\TrinityBots\TrinityCore │
│ │
│ Branches: │
│ • playerbot-dev (main) │
│ • zenflow/agent-1/sprint-2 │
│ • zenflow/agent-2/sprint-2 │
│ • zenflow/agent-3/review │
└───────────────────────────────┘
```
---
## Data Flow Diagram
### Workflow Execution Flow
```
User Action (Zenflow GUI)
│
│ Click "Run Workflow: DI Cleanup Sprint 2"
│
▼
Zenflow Orchestrator
│
│ 1. Create workflow instance
│ 2. Read DAG configuration
│ 3. Identify first stage (Analysis)
│
▼
Stage 1: Analysis Agent (Claude Opus 4.5)
│
│ Actions:
│ • Create branch: zenflow/agent-1/sprint-2
│ • Read Obsidian: "Refactorings/DI Cleanup.md"
│ • Analyze dependencies
│ • Write Obsidian: "Refactorings/Analysis Sprint 2.md"
│ • Update task: Status = COMPLETE
│
▼
Zenflow Orchestrator
│
│ Stage 1 complete ✅
│ Check dependencies: Stage 2 unblocked
│
▼
Stage 2: Specification Agent (Claude Opus 4.5)
│
│ Actions:
│ • Create branch: zenflow/agent-2/sprint-2
│ • Read Obsidian: "Refactorings/Analysis Sprint 2.md"
│ • Generate technical spec
│ • Write Obsidian: "Refactorings/Spec Sprint 2.md"
│ • Update task: Status = COMPLETE
│
▼
Zenflow Orchestrator
│
│ Stage 2 complete ✅
│ Check dependencies: Stage 3 unblocked
│
▼
Stage 3: Implementation Agent (Claude Sonnet 4.5)
│
│ Actions:
│ • Create branch: zenflow/agent-3/sprint-2-impl
│ • Read Obsidian: "Refactorings/Spec Sprint 2.md"
│ • Check lock: .claude/locks/CMakeLists.txt.lock
│ • Implement code changes
│ • Update CMakeLists.txt
│ • Commit changes
│ • Write Obsidian: "Sessions/Implementation Sprint 2.md"
│ • Update task: Status = COMPLETE
│
▼
Zenflow Orchestrator
│
│ Stage 3 complete ✅
│ Check dependencies: Stage 4 unblocked
│
▼
Stage 4: Build Verification Agent (Claude Sonnet 4.5)
│
│ Actions:
│ • Checkout impl branch
│ • Run CMake configure
│ • Build worldserver
│ • Check for errors
│ • Write Obsidian: "Sessions/Build Report Sprint 2.md"
│ • Update task: Status = COMPLETE or FAILED
│
▼
Zenflow Orchestrator
│
│ Stage 4 status check
│ If FAILED: Alert user, stop workflow
│ If COMPLETE: Continue to Stage 5
│
▼
Stage 5: Review Agent (Claude Opus 4.5)
│
│ Actions:
│ • Read all previous Obsidian notes
│ • Review git diff
│ • Check for issues:
│ - Security vulnerabilities
│ - Memory leaks
│ - API misuse
│ • Write Obsidian: "Sessions/Review Sprint 2.md"
│ • Decision: APPROVE or REQUEST_CHANGES
│ • Update task: Status = COMPLETE
│
▼
Zenflow Orchestrator
│
│ Workflow complete ✅
│ All stages passed
│
▼
User Notification (Zenflow GUI)
│
│ "Sprint 2 workflow complete - Ready for merge"
│
▼
User Reviews Obsidian Notes + Git Branches
│
│ Manually verify
│ Merge when satisfied
│
▼
Done ✅
```
---
## Communication Patterns
### Pattern 1: Task Coordination (via Shared Task List)
```
┌─────────────────┐ ┌─────────────────┐
│ Instance 1 │ │ Instance 2 │
│ (Terminal 1) │ │ (Terminal 2) │
└────────┬────────┘ └────────┬────────┘
│ │
│ CLAUDE_CODE_TASK_LIST_ID │
│ = "trinitycore-..." │
│ │
└─────────┬─────────────────┘
│
▼
┌──────────────────────┐
│ Shared Task List │
│ │
│ 1. [COMPLETE] Stage1│
│ 2. [IN_PROGRESS] S2 │◄─── Instance 1 updates
│ 3. [PENDING] Stage3 │
│ 4. [BLOCKED_BY: 3] │◄─── Instance 2 sees this
└──────────────────────┘
│
│ Both instances
│ read/write same list
│
┌─────────┴──────────┐
│ │
▼ ▼
Instance 1 Instance 2
Knows: S2 active Knows: Wait for S2
```
### Pattern 2: Memory Sharing (via Obsidian MCP)
```
┌─────────────────┐ ┌─────────────────┐
│ Agent 1 │ │ Agent 2 │
│ (Analysis) │ │ (Impl) │
└────────┬────────┘ └────────┬────────┘
│ │
│ MCP: obsidian_write_note │ MCP: obsidian_read_note
│ │
└─────────┬─────────────────┘
│
▼
┌──────────────────────────────┐
│ Obsidian Vault │
│ │
│ "Analysis Sprint 2.md" │
│ ┌────────────────────────┐ │
│ │ ## Dependencies Found │ │
│ │ - File1 depends on File2│ │◄── Agent 1 writes
│ │ - CMakeLists needs update│ │
│ └────────────────────────┘ │
│ │
│ │◄── Agent 2 reads
│ Agent 2 sees this │
│ Uses it for implementation │
└──────────────────────────────┘
```
### Pattern 3: Conflict Prevention (via File Locks)
```
Agent 1 wants to edit CMakeLists.txt
│
│ 1. Check Obsidian for lock file
▼
Obsidian: ".claude/locks/CMakeLists.txt.lock"
│
├─ EXISTS? ───────────────┐
│ │
YES NO
│ │
▼ ▼
Read lock info Create lock file
│ │
├─ Expired? Write:
│ • locked_at {
NO • release_after "locked_by": "agent-1",
│ "locked_at": "2026-02-06T10:00:00Z",
▼ "release_after": "2026-02-06T12:00:00Z"
⚠️ STOP }
Report conflict │
Exit │
▼
Edit CMakeLists.txt
│
▼
Commit changes
│
▼
Delete lock file
│
▼
Done ✅
```
---
## State Management
### Workflow State Transitions
```
PENDING ──────────────────────────────────┐
│ │
│ User triggers workflow │
│ │
▼ │
IN_PROGRESS │
│ │
├─ Stage 1 ─→ RUNNING ─→ COMPLETE ───┤
│ │
├─ Stage 2 ─→ RUNNING ─→ COMPLETE ───┤
│ │
├─ Stage 3 ─→ RUNNING ─→ COMPLETE ───┤
│ │
├─ Stage 4 ─→ RUNNING ─→ FAILED ─────┼─→ WORKFLOW_FAILED
│ │ │ │
│ │ │ ▼
│ │ │ Alert user
│ │ │ Save logs to Obsidian
│ └─ Retry? │ Stop execution
│ │ │
│ YES │
│ │ │
│ RUNNING ───┤
│ │ │
└──────────────────────────────┘ │
│
All stages COMPLETE ──────────────────────┘
│
▼
WORKFLOW_COMPLETE ✅
│
│ Notification to Zenflow GUI
│ Update Obsidian summary
│ Mark all tasks COMPLETE
│
▼
Ready for manual review & merge
```
### Task State Transitions
```
PENDING
│
│ Agent claims task
▼
IN_PROGRESS
│
├─ Work continues ───┐
│ │
│ ◄──────────────────┘
│
├─ Blocker encountered?
│ YES ──→ BLOCKED
│ │
│ │ Blocker resolved
│ ▼
│ PENDING (restart)
│
├─ Work completes
│ YES ──→ COMPLETE ✅
│
└─ Abandoned?
YES ──→ DELETED
```
---
## Security & Isolation
### Branch Isolation Strategy
```
Main Branch: playerbot-dev
│
│ Zenflow creates isolated branches per agent
│
├─ zenflow/agent-1/sprint-2
│ └─ Changes: Analysis only
│
├─ zenflow/agent-2/sprint-2
│ └─ Changes: Spec generation only
│
├─ zenflow/agent-3/sprint-2-impl
│ └─ Changes: Implementation only
│
└─ zenflow/agent-4/sprint-2-build
└─ Changes: Build fixes only
All isolated → Merge strategy:
1. Review each branch independently
2. Merge agent-1 first (analysis)
3. Merge agent-2 second (spec)
4. Merge agent-3 third (impl)
5. Merge agent-4 last (fixes)
No cross-contamination ✅
Clear audit trail ✅
Easy rollback ✅
```
### File Ownership Protection
```
┌────────────────────────────────────────┐
│ Obsidian: File Ownership │
│ │
│ CMakeLists.txt │
│ ├─ Owner: Agent 3 │
│ ├─ Lock expires: 2026-02-06 12:00 │
│ └─ Status: 🔒 LOCKED │
│ │
│ BattlegroundAI.cpp │
│ ├─ Owner: Agent 5 │
│ ├─ Lock expires: Never (completed) │
│ └─ Status: ✅ AVAILABLE │
│ │
│ PlayerbotModule.cpp │
│ ├─ Owner: None │
│ ├─ Last modified: 2026-02-05 │
│ └─ Status: ✅ AVAILABLE │
└────────────────────────────────────────┘
│
│ Agent 2 tries to edit CMakeLists.txt
▼
Check ownership → LOCKED by Agent 3
│
▼
⚠️ CONFLICT DETECTED
│
├─ Option 1: Wait for lock to expire
├─ Option 2: Request unlock from Agent 3
└─ Option 3: Choose different file
Conflict PREVENTED before it happens ✅
```
---
## Performance & Scalability
### Parallel Execution Capacity
```
Sequential (Before):
Analysis → Spec → Impl → Build → Review
│ │ │ │ │
2h + 1h + 4h + 1h + 1h = 9 hours total
Parallel (With Zenflow):
Analysis (Agent 1) ──────────┐
│ │
▼ │
Spec (Agent 2) ──────────┐ │
│ │ │
▼ │ │
Impl (Agent 3) ───┐ │ │
│ │ │ │
▼ ▼ ▼ ▼
Build + Review in parallel after impl
│
▼
2h + 1h + 4h (max) + max(1h, 1h) = ~7 hours total
Savings: 22% time reduction
Benefits:
• Different models per stage (cost optimization)
• Each agent specializes
• Failures isolated to stage
```
### Resource Usage
```
┌───────────────────────────────────────────┐
│ Resource Consumption │
│ │
│ Zenflow Desktop: │
│ • CPU: ~5% (idle) / ~20% (active) │
│ • RAM: ~200MB │
│ • Disk: ~500MB │
│ │
│ Obsidian: │
│ • CPU: ~2% (idle) / ~10% (indexing) │
│ • RAM: ~100MB │
│ • Disk: ~50MB (vault) + 500MB (plugins) │
│ │
│ MCP Server: │
│ • CPU: ~1% (idle) / ~5% (requests) │
│ • RAM: ~50MB │
│ • Disk: Negligible │
│ │
│ Claude Code (per instance): │
│ • CPU: ~10-30% (during execution) │
│ • RAM: ~500MB │
│ • Disk: Cache + logs (~1GB) │
│ │
│ Total System Impact: │
│ • 3 Claude instances running: ~1.5GB RAM│
│ • Acceptable on 16GB+ system │
└───────────────────────────────────────────┘
```
---
## Disaster Recovery
### What Happens When Things Go Wrong
```
Scenario: Agent 3 crashes during implementation
Before (No Orchestration):
• All work lost
• No record of progress
• Must restart from scratch
• Unknown what was attempted
❌ Hours wasted
After (With Zenflow + Obsidian):
│
│ Agent 3 crashes
▼
Zenflow detects failure
│
├─ Saves logs to Obsidian: "Sessions/Crash Sprint 2.md"
├─ Marks task FAILED
├─ Preserves git branch: zenflow/agent-3/sprint-2-impl
├─ Updates Active Instances: Agent 3 = CRASHED
└─ Alerts user
User can:
│
├─ Read crash logs in Obsidian
├─ Review partial git branch
├─ Understand what was attempted
├─ Restart from last checkpoint
└─ OR manually fix and continue
✅ Zero work lost
✅ Full audit trail
✅ Easy recovery
```
### Rollback Procedure
```
Problem: Sprint 2 implementation broke build
With Orchestration:
│
1. Check Obsidian: "Sessions/Build Report Sprint 2.md"
└─ See exact error
│
2. Check git: zenflow/agent-3/sprint-2-impl
└─ See all changes
│
3. Options:
│
├─ A. Rollback entire sprint
│ git reset --hard HEAD~N
│ Delete Obsidian notes (archive)
│
├─ B. Fix specific issue
│ Read build logs
│ Spawn new agent: "Fix build error X"
│ Let agent fix specific problem
│
└─ C. Rollback to specific stage
git checkout zenflow/agent-2/sprint-2 (spec was good)
Re-run implementation stage with fixes
All options are safe ✅
All changes are tracked ✅
Easy to find root cause ✅
```
---
## Cost Analysis
### Setup Costs (One-Time)
```
Time Investment:
• Obsidian setup: 30 minutes
• MCP server setup: 20 minutes
• Zenflow setup: 20 minutes
• Claude Code config: 15 minutes
• First workflow: 30 minutes
• Testing: 20 minutes
─────────────────────────────────────
Total: ~2-3 hours
Monetary Costs:
• Obsidian: FREE
• Zenflow: FREE (desktop version)
• MCP server: FREE (open source)
• Claude Code: INCLUDED (with Claude subscription)
─────────────────────────────────────
Total: $0 extra
```
### Ongoing Costs
```
Per Sprint/Workflow:
• Zenflow desktop: FREE
• Obsidian storage: ~50MB per sprint (negligible)
• MCP overhead: Negligible CPU/RAM
• Claude API calls: SAME (you're using Claude anyway)
Actually LESS due to better coordination
(no rework, no conflicts)
Time Savings Per Sprint:
• Setup overhead: +5 minutes (create workflow)
• Execution: -22% (parallel execution)
• Debugging: -50% (clear logs, no conflicts)
• Rework: -90% (spec-driven, no disasters)
─────────────────────────────────────
Net savings: ~2-3 hours per sprint
ROI: POSITIVE after first real sprint ✅
```
---
## Comparison with Alternatives
| Approach | Setup Time | Coordination | Memory | GUI | Cost | Best For |
|----------|-----------|--------------|--------|-----|------|----------|
| **No Orchestration** | 0 min | ❌ Manual | ❌ None | ❌ | $0 | Solo, simple tasks |
| **Manual Git Branches** | 10 min | ⚠️ Manual | ❌ Docs only | ❌ | $0 | 2-3 instances max |
| **Built-in Task Lists** | 5 min | ✅ Automatic | ❌ None | ❌ | $0 | Basic coordination |
| **Zenflow + Obsidian** | 2-3 hrs | ✅ Automated | ✅ Full | ✅ | $0 | Complex projects |
| **Claude-Flow** | 1 hr | ✅ Swarms | ✅ Shared | ⚠️ Minimal | $0 | AI-first teams |
| **CrewAI** | 2 hrs | ✅ Crews | ✅ Shared | ✅ Visual | $0 | Multi-tool agents |
| **Custom Solution** | 40+ hrs | ✅ Your design | ✅ Your design | ✅ Your design | Dev time | Specific needs |
**Recommended:** Zenflow + Obsidian for TrinityCore project ✅
---
## Success Metrics
### How to Measure Success
```
Metric: Git Conflicts
Before: ~2-3 per week (manual coordination)
After: ~0-1 per month (orchestrated)
Target: 90% reduction ✅
Metric: Rework Rate
Before: ~30% of code rewritten due to conflicts/misunderstandings
After: ~5% (only genuine design changes)
Target: 80% reduction ✅
Metric: Context Switching Time
Before: 15-30 min to understand what others are doing
After: 2-5 min (read Obsidian notes)
Target: 75% reduction ✅
Metric: Disaster Recovery Time
Before: 2-4 hours (recreate lost work)
After: 10-15 min (restore from Obsidian + git)
Target: 90% reduction ✅
Metric: Onboarding New Instance
Before: 30-60 min (figure out state, conflicts)
After: 5 min (read Obsidian "Active Instances")
Target: 85% reduction ✅
```
---
**Document Version:** 1.0
**Last Updated:** 2026-02-06
**Architecture Stability:** Stable
**Recommended Review:** After completing 3 sprints
+250
View File
@@ -0,0 +1,250 @@
# Sprint 1: Code-Audit & Duplikat-Analyse - RESULTS
**Date:** 2026-02-06
**Status:** COMPLETED
## Executive Summary
The comprehensive audit of the Playerbot coordination systems revealed significant findings:
1. **~12,550 lines of code are NOT COMPILED** (dead code in Raid and Arena directories)
2. **Two KitingManager classes exist** but are NOT duplicates - they serve different architectural layers
3. **Event Bus infrastructure is active** but some domain buses need integration
4. **Battleground coordination is fully active** with comprehensive BG script system
---
## Critical Finding: Orphaned Code Not In CMakeLists.txt
### Raid Coordination (AI/Coordination/Raid/)
**Status: DEAD CODE - NOT COMPILED**
**Files: 9 .cpp files + headers (~5,266 lines)**
| File | Lines | Description |
|------|-------|-------------|
| RaidCoordinator.cpp | ~788 | Main raid orchestrator |
| RaidTankCoordinator.cpp | ~637 | Tank assignment/swap |
| RaidHealCoordinator.cpp | ~636 | Healer assignment |
| RaidCooldownRotation.cpp | ~310 | Bloodlust/defensive rotation |
| RaidGroupManager.cpp | ~227 | Split raid mechanics |
| RaidPositioningManager.cpp | ~213 | Position assignment |
| RaidEncounterManager.cpp | ~309 | Boss phase tracking |
| KitingManager.cpp | ~297 | Raid-level waypoint kiting |
| AddManagementSystem.cpp | ~335 | Add priority/assignment |
**Root Cause:** Files exist but are not listed in CMakeLists.txt
**Impact:** All raid coordination features are non-functional
**Resolution:** Add to CMakeLists.txt in Sprint 5 (Raid Coordination)
### Arena Coordination (AI/Coordination/Arena/)
**Status: DEAD CODE - NOT COMPILED**
**Files: 6 .cpp files + headers (~7,284 lines)**
| File | Lines | Description |
|------|-------|-------------|
| ArenaCoordinator.cpp | ~1373 | Main arena orchestrator |
| ArenaPositioning.cpp | ~1175 | Pillar/LOS management |
| BurstCoordinator.cpp | ~686 | Offensive sync |
| CCChainManager.cpp | ~849 | Crowd control chains |
| DefensiveCoordinator.cpp | ~876 | Survivability management |
| KillTargetManager.cpp | ~422 | Target selection |
**Root Cause:** Files exist but are not listed in CMakeLists.txt
**Impact:** All arena coordination features are non-functional
**Resolution:** Add to CMakeLists.txt in Sprint 6 (PvP Coordination)
---
## Kiting System Analysis
**Finding: NOT DUPLICATES - Different Architectural Layers**
### Combat KitingManager (AI/Combat/KitingManager)
- **Purpose:** Single-bot dynamic kiting
- **Architecture:** Real-time decision-based, 9 kiting patterns
- **Status:** Orphaned (Phase 2 disabled in CombatAIIntegrator)
- **Lines:** ~1,157
### Raid KitingManager (AI/Coordination/Raid/KitingManager)
- **Purpose:** Raid-level waypoint coordination
- **Architecture:** Path-based, pre-planned routes
- **Status:** Active but not compiled (Raid/ not in CMakeLists)
- **Lines:** ~297
**Recommendation:** Keep separate - they serve different purposes
---
## Dungeon Coordination Analysis
**Status: ACTIVE but PARTIAL**
**Location:** AI/Coordination/Dungeon/
**Files:** 6 (all in CMakeLists.txt)
### Active Components
- ✅ **DungeonCoordinator** - Fully implemented, instantiated via DungeonAutonomyManager
- ✅ **Event Bus Integration** - Subscribes to CombatEventRouter (6 event types)
- ✅ **State Machine** - 8 states: IDLE → ENTERING → READY_CHECK → CLEARING_TRASH → PRE_BOSS → BOSS_COMBAT → POST_BOSS → COMPLETED
- ✅ **Wipe Recovery** - Complete 6-phase recovery system
### Gaps Requiring Sprint 4 Work
- ⚠️ **Boss Strategy Database** - `LoadBossStrategies()` is empty
- ⚠️ **Trash Pack Pre-Loading** - Packs must be registered dynamically
- ⚠️ **M+ Affix Handling** - Only 4/18 affixes active (Quaking, Explosive, Volcanic, Sanguine)
- ⚠️ **Creature Forces DB** - Enemy forces not loaded from database
- ⚠️ **Route Optimization** - Framework present but not implemented
- ⚠️ **Marker Application** - Raid markers not actually set on targets
---
## Systems Status Matrix
| System | Location | CMake Status | Event Bus | Functional |
|--------|----------|--------------|-----------|------------|
| Dungeon Coordination | AI/Coordination/Dungeon/ | ✅ COMPILED | ✅ Active | ⚠️ PARTIAL |
| Battleground Coordination | AI/Coordination/Battleground/ | ✅ COMPILED | ✅ Active | ✅ PRODUCTION-READY |
| Raid Orchestrator | AI/Coordination/RaidOrchestrator | ✅ COMPILED | Limited | Partial |
| Messaging (Sprint 2) | AI/Coordination/Messaging/ | ✅ COMPILED | ✅ Active | ✅ YES |
| ContentContext (Sprint 2) | AI/Coordination/ContentContextManager | ✅ COMPILED | N/A | ✅ YES |
| Raid Coordination | AI/Coordination/Raid/ | ❌ NOT COMPILED | N/A | ❌ NO |
| Arena Coordination | AI/Coordination/Arena/ | ❌ NOT COMPILED | N/A | ❌ NO |
---
## Event Bus Infrastructure Status
### Event Bus Infrastructure Status
**ALL 12+ EVENT BUSES ARE ACTIVE** - Production-ready infrastructure
| Bus | Publishers | Processing | Status |
|-----|------------|------------|--------|
| CombatEventBus | 10 packet handlers | 50/cycle | ✅ ACTIVE |
| GroupEventBus | 6 packet handlers | 100/cycle | ✅ ACTIVE |
| LootEventBus | 9 packet handlers | 50/cycle | ✅ ACTIVE |
| QuestEventBus | 13 packet handlers | 50/cycle | ✅ ACTIVE |
| AuctionEventBus | 6 packet handlers | 20/cycle | ✅ ACTIVE |
| ResourceEventBus | 3 packet handlers | 30/cycle | ✅ ACTIVE |
| SocialEventBus | 6 packet handlers | 30/cycle | ✅ ACTIVE |
| InstanceEventBus | 7 packet handlers | 20/cycle | ✅ ACTIVE |
| NPCEventBus | 8 packet handlers | 30/cycle | ✅ ACTIVE |
| AuraEventBus | 4 packet handlers | 30/cycle | ✅ ACTIVE |
| ProfessionEventBus | Multi | 20/cycle | ✅ ACTIVE |
| **CooldownEventBus** | **5 packet handlers** | **30/cycle** | ✅ **ACTIVE - GAP 1 WAS ALREADY RESOLVED** |
**GAP 1 Finding:** CooldownEventBus was **NOT dead** - it has 5 active packet handlers:
- ParseTypedSpellCooldown() - SMSG_SPELL_COOLDOWN
- ParseTypedCooldownEvent() - SMSG_COOLDOWN_EVENT
- ParseTypedClearCooldown() - SMSG_CLEAR_COOLDOWN
- ParseTypedClearCooldowns() - SMSG_CLEAR_COOLDOWNS
- ParseTypedModifyCooldown() - SMSG_MODIFY_COOLDOWN
### Sprint 2 Infrastructure (Enhancements)
- ✅ MajorCooldownTracker - Adds Major CD detection on top of working CooldownEventBus
- ✅ BotMessageBus - Per-group message routing
- ✅ ClaimResolver - First-Claim-Wins with priority override
- ✅ ContentContextManager - Content type detection
---
## Recommendations
### Immediate Actions
1. ✅ Merge conflicts resolved (CooldownEvents.h/cpp, PlayerbotModule.cpp)
2. ⏳ Build validation for Sprint 2 changes
### Sprint 5 (Raid Coordination)
- Add all Raid/ files to CMakeLists.txt
- Wire RaidCoordinator instantiation
- Integrate with event bus system
### Sprint 6 (PvP Coordination)
- Add all Arena/ files to CMakeLists.txt
- Wire ArenaCoordinator instantiation
- Connect to existing ArenaAI/ArenaBotManager
### Low Priority
- Consider extracting shared kiting utilities (optional ~150 lines dedup)
---
---
## Battleground Coordination Analysis
**Status: PRODUCTION-READY - FULLY IMPLEMENTED**
**Location:** AI/Coordination/Battleground/
**Total Code:** ~9,300 lines
The BG system is the **most complete coordination system** in the codebase:
| Component | Files | Status |
|-----------|-------|--------|
| Core Managers | 10/10 | ✅ ACTIVE |
| Script System | 4/4 | ✅ ACTIVE |
| BG Scripts | 21/21 | ✅ ACTIVE |
| BG Coverage | 13/13 maps | ✅ COMPLETE |
### Key Features Implemented
- **BattlegroundCoordinator** - Central orchestrator, instantiated per BG
- **BGRoleManager** - 20+ role types with suitability scoring
- **BGStrategyEngine** - 7 strategic options with momentum tracking
- **FlagCarrierManager** - Complete CTF mechanics
- **NodeController** - Domination node management
- **BGSpatialQueryCache** - O(1) player lookups (~80x faster)
- **21 BG Scripts** - All major battlegrounds covered
### Event Integration
- Subscribes to CombatEventRouter (priority 35)
- Script event system with 25+ event types
**This system requires NO additional work for Sprint 6 BG portion.**
---
## CombatBehaviors System Analysis
**Location:** AI/CombatBehaviors/
**Status:** COMPILED but NOT INTEGRATED
All 5 CombatBehaviors managers are in CMakeLists.txt but have **zero external instantiation** - they exist but are never created in BotAI.
| File | Lines | Status | Gap Coverage |
|------|-------|--------|--------------|
| AoEDecisionManager | 800+ | ✅ Complete | Target clustering, DoT spread |
| CooldownStackingOptimizer | 1500+ | ✅ Complete | 12-class cooldown DB |
| DefensiveBehaviorManager | 200+ | ⚠️ Headers only | GAP 3 partial |
| DispelCoordinator | 450+ | ✅ Complete | **GAP 2 READY** |
| InterruptRotationManager | 420+ | ✅ Complete | Rotation queue |
### GAP 2 (Dispel Rotation) - READY FOR INTEGRATION
- `DispelCoordinator` has full implementation with:
- `DispelAssignment` struct for claim tracking
- `IsBeingDispelled()` to prevent overlap
- 6-tier priority system
- 200+ debuff database
### GAP 3 (External Defensive CD) - PARTIAL
- `DefensiveBehaviorManager` has architecture but procedural logic incomplete
- External defensive request/response infrastructure exists
- Needs procedural logic in .cpp
### Integration Required
- Add member variables to BotAI.h
- Create instantiation in BotAI constructor
- Wire into BotAI::UpdateAI() flow
- Connect CombatCoordinationIntegrator to bridge to BotMessageBus
---
## Conclusion
Sprint 1 audit reveals that the coordination infrastructure has significant code that needs activation:
1. **Dead Code (not compiled):** ~12,550 lines (Raid/ + Arena/)
2. **Orphaned Code (compiled but not used):** ~3,500 lines (CombatBehaviors)
**Quality of Code:** Production-ready (not stubs)
**Recovery Effort:**
- Raid/Arena: Add to CMakeLists.txt + instantiation
- CombatBehaviors: Wire into BotAI lifecycle
File diff suppressed because it is too large Load Diff
+238
View File
@@ -0,0 +1,238 @@
# Zenflow + Obsidian Setup - Quick Start Checklist
**Goal:** Get orchestration running in 2-3 hours
**Full Guide:** See `ZENFLOW_OBSIDIAN_SETUP_GUIDE.md` for detailed instructions
---
## ☑️ Pre-Setup (15 minutes)
- [ ] **Download Obsidian** from https://obsidian.md/download
- [ ] **Download Zenflow** from https://zencoder.ai/zenflow
- [ ] **Check Node.js:** `node --version` (need v18+)
- If not installed: https://nodejs.org/
- [ ] **Find Anthropic API Key** (for Zenflow)
- Dashboard: https://console.anthropic.com/
---
## ☑️ Obsidian Setup (30 minutes)
- [ ] **Install Obsidian Desktop**
- [ ] **Create new vault:**
- Name: `TrinityCore-Playerbot-Memory`
- Location: `C:\TrinityBots\PlayerBotProjectMemory`
- [ ] **Enable Community Plugins:**
- Settings → Community plugins → Turn off Restricted mode
- [ ] **Install "Local REST API" plugin:**
- Browse plugins → Search "Local REST API"
- Install and enable
- Copy API key from settings (SAVE THIS!)
- [ ] **Install "Dataview" plugin** (optional but recommended)
- [ ] **Create folder structure:**
- `Architecture/`
- `Refactorings/`
- `Gotchas/`
- `Tasks/`
- `Sessions/`
- `Decisions/`
- [ ] **Create core files** (copy from setup guide):
- `Architecture/System Overview.md`
- `Gotchas/Git Workflow Gotchas.md`
- `Refactorings/DI Cleanup.md`
- `Tasks/Active Instances.md`
- `Tasks/Task Template.md`
---
## ☑️ MCP Server Setup (20 minutes)
- [ ] **Clone repository:**
```bash
cd C:\TrinityBots
git clone https://github.com/cyanheads/obsidian-mcp-server.git
cd obsidian-mcp-server
```
- [ ] **Install dependencies:**
```bash
npm install
```
- [ ] **Build:**
```bash
npm run build
```
- [ ] **Create config.json:**
- Location: `C:\TrinityBots\obsidian-mcp-server\config.json`
- Content: See setup guide Section 2.4
- **IMPORTANT:** Use your Obsidian API key!
- [ ] **Test server:**
```bash
node dist/index.js
# Should start without errors
# Ctrl+C to stop
```
---
## ☑️ Zenflow Setup (20 minutes)
- [ ] **Install Zenflow Desktop**
- [ ] **Sign in / Create account**
- [ ] **Create project:**
- Name: `TrinityCore Playerbot`
- Path: `C:\TrinityBots\TrinityCore`
- Type: C++ / CMake
- [ ] **Configure AI Provider:**
- Settings → AI Providers → Add Anthropic
- Enter API key
- Select models: Opus 4.5, Sonnet 4.5, Haiku
- [ ] **Connect Git:**
- Project Settings → Version Control
- Enable "Branch per agent" ✅
- Prefix: `zenflow/agent-{agent-id}/`
---
## ☑️ Claude Code Integration (15 minutes)
- [ ] **Edit MCP config:**
- Location: `%APPDATA%\Roaming\claude-code\mcp.json`
- Add `obsidian-memory` server (see setup guide 4.1)
- [ ] **Set environment variable:**
- Windows: Environment Variables → New
- Name: `CLAUDE_CODE_TASK_LIST_ID`
- Value: `trinitycore-playerbot-shared`
- [ ] **Restart all terminals**
- [ ] **Test MCP:**
```bash
claude-code
> Can you list available MCP tools?
# Should see obsidian_* tools
```
- [ ] **Test Obsidian read:**
```bash
> Read "Architecture/System Overview" from Obsidian vault
# Should display content
```
---
## ☑️ Create First Workflow (30 minutes)
- [ ] **Open Zenflow Desktop**
- [ ] **New Workflow:**
- Name: `DI Cleanup Refactoring`
- Template: Spec-Driven Development
- [ ] **Configure 5 stages:**
1. Analyze Dependencies (Opus 4.5)
2. Create Technical Spec (Opus 4.5)
3. Implement Changes (Sonnet 4.5)
4. Verify Build (Sonnet 4.5)
5. Code Review (Opus 4.5)
- [ ] **Set dependencies:**
- Stage 2 depends on Stage 1
- Stage 3 depends on Stage 2
- Stage 4 depends on Stage 3
- Stage 5 depends on Stage 4
- [ ] **Configure agent settings:**
- Enable "Isolated Branches" ✅
- Enable "Shared Memory" → Obsidian ✅
- Vault: `C:\TrinityBots\PlayerBotProjectMemory`
- [ ] **Add instructions to each stage** (see setup guide 5.2)
---
## ☑️ Run Test (20 minutes)
- [ ] **Create test task in Obsidian:**
- File: `Tasks/TEST-2026-02-06-01 - Verify Setup.md`
- Copy template from setup guide 6.1
- [ ] **Start workflow in Zenflow:**
- Run Workflow → DI Cleanup Refactoring
- Sprint: `TEST`
- Click Start
- [ ] **Watch progress:**
- Zenflow: See DAG visualization
- Obsidian: Open Graph View (Ctrl+G)
- Watch new notes appear in `Sessions/`
- [ ] **Verify results:**
- Check for 5 new markdown files in Obsidian
- Check git branches: `git branch | grep zenflow`
- Check task list: `claude-code` → `/tasks`
- [ ] **Cleanup test:**
```bash
git branch -D zenflow/di-cleanup-sprint-TEST/*
# Delete test notes in Obsidian
```
---
## ☑️ Verify Everything Works
- [ ] **Multiple instances can share tasks** ✅
- Open 2 Claude Code terminals
- Both should see same tasks: `/tasks`
- [ ] **Obsidian memory is accessible** ✅
- Claude can read notes: `> Read "Architecture/System Overview"`
- Claude can write notes: `> Write a test note to Obsidian`
- [ ] **Zenflow shows progress** ✅
- GUI displays running workflows
- Stage logs are visible
- [ ] **No git conflicts** ✅
- Each agent uses separate branch
- Clean merge possible
---
## ✅ You're Done! What's Next?
### Immediate Next Steps:
1. **Update Obsidian notes** with your TrinityCore-specific info
2. **Run real refactoring sprint** (not TEST)
3. **Monitor and refine** workflows based on experience
### Advanced Features to Explore:
- File locking pattern (Section 7.1)
- Session handover (Section 7.2)
- Emergency stop (Section 7.3)
- Cross-repository coordination (Section 7.5)
### Daily Workflow:
See Section "Daily Workflow" in full guide for:
- Morning startup checklist
- Starting new tasks
- Hourly checks
- End of day routine
---
## 🆘 Quick Troubleshooting
| Problem | Solution |
|---------|----------|
| MCP server won't start | Check Obsidian REST API plugin enabled, API key correct |
| Zenflow can't find repo | Re-scan repository in project settings |
| Tasks not shared | Set `CLAUDE_CODE_TASK_LIST_ID` env var, restart terminals |
| Obsidian links don't show | Use `[[Note]]` syntax not `[Note](path)` |
| Workflow hangs | Check logs in Zenflow, cancel and restart stage |
See "Troubleshooting" section in full guide for detailed fixes.
---
## 📚 Resources
- **Full Setup Guide:** `.claude/ZENFLOW_OBSIDIAN_SETUP_GUIDE.md`
- **Zenflow Docs:** https://zencoder.ai/docs
- **Obsidian MCP Server:** https://github.com/cyanheads/obsidian-mcp-server
- **Claude Code Docs:** https://code.claude.com/docs
- **MCP Protocol:** https://modelcontextprotocol.io/
---
**Total Time:** ~2-3 hours for complete setup
**Complexity:** Moderate (some terminal/config work)
**Payoff:** Massive (prevents disasters, enables safe parallel work)
**Last Updated:** 2026-02-06
+820
View File
@@ -0,0 +1,820 @@
# Zenflow Workflow: TOK Lighthouse Battleground
**Complete workflow configuration for Temple of Kotmogu - 100% correct & fully dynamic**
---
## Workflow Metadata
**Workflow Name:** `TOK Lighthouse Battleground`
**Description:** Make Temple of Kotmogu the first 100% correct, fully dynamic battleground. This will serve as the template/blueprint for all 13 other battlegrounds.
**Branch Prefix:** `lighthouse-bg/tok/`
**Project:** TrinityCore Playerbot
**Agent:** Claude Code (for all automated stages)
---
## Stage 1: Research TOK Data
**Stage Name:** `Research TOK Data (DBC/DB2/SQL)`
**Agent:** Claude Code
**Depends On:** None (first stage)
**Instructions:**
```
Read from Obsidian: "Refactorings/TOK Lighthouse BG.md"
Research and verify:
1. Orb GameObject entries (should be 212091-212094)
- Query: SELECT * FROM gameobject WHERE id IN (212091,212092,212093,212094)
- Verify spawn positions, display IDs, states
2. Orb aura IDs (should be 121175-121178)
- Check spell data for each aura
- Verify buff mechanics (1x, 2x, 3x, 4x multipliers)
3. Victory conditions
- Confirm 1600 points to win
- Score calculation formula
- Center control multiplier (5x)
4. Map data
- Zone ID for Temple of Kotmogu
- Center area coordinates
- Orb spawn positions (exact coordinates)
5. Current implementation analysis
- Read: src/modules/Playerbot/PvP/BattlegroundAI.cpp (TOK functions)
- Read: src/modules/Playerbot/AI/Coordination/Battleground/Scripts/Domination/TempleOfKotmoguScript.cpp
- Identify all bugs and issues
Document findings:
- Write to Obsidian: "Sessions/TOK Sprint 1 - Data Research.md"
- Include: All GameObject data, aura data, current bugs found
- List: What needs to be fixed vs what's already correct
```
---
## Stage 2: Create Perfect Specification
**Stage Name:** `Create TOK Perfect Spec`
**Agent:** Claude Code
**Depends On:** Stage 1 ✓
**Instructions:**
```
Read from Obsidian:
- "Sessions/TOK Sprint 1 - Data Research.md" (Stage 1 output)
- "Refactorings/TOK Lighthouse BG.md" (requirements)
Create detailed technical specification covering:
1. GameObject Interaction Fix
- Exact code changes needed for orb pickup
- Correct GameObject::Use() parameters
- State checks required
- Error handling approach
2. Combat Engagement Implementation
- Where to add Attack() calls
- Target selection logic
- Combat state management
- Integration with existing combat system
3. Dynamic Strategy State Machine
- State definitions (e.g., AGGRESSIVE, DEFENSIVE, BALANCED)
- Transition triggers (score difference, orb count, time)
- Actions per state
- Role assignment per state
4. Center Control Logic
- Detection of center area
- Movement to center
- 5x multiplier awareness
- Risk/reward calculation
5. Edge Case Handling
- Bot death with orb (orb drop behavior)
- Orb respawn timing (30 seconds)
- Multiple bots targeting same orb
- Player disconnect/AFK
- Uneven team sizes
6. Data Validation
- Confirm all GameObject entries
- Confirm all aura IDs
- Confirm spawn positions
- Confirm victory conditions
7. Testing Criteria
- Success metrics (>95% orb pickup, 100% combat engagement)
- Performance requirements (<0.1% CPU per bot)
- Zero crashes requirement
Write specification to Obsidian: "Refactorings/TOK Lighthouse Spec.md"
Specification must be:
- Complete (no TODOs or "to be determined")
- Precise (exact line numbers, exact code changes)
- Testable (clear acceptance criteria)
- Reviewable (clear rationale for each decision)
```
---
## Stage 3: Fix GameObject & Combat
**Stage Name:** `Fix Orb Pickup & Combat Engagement`
**Agent:** Claude Code
**Depends On:** Stage 2 ✓
**Instructions:**
```
Read specification: "Refactorings/TOK Lighthouse Spec.md"
Implementation tasks:
1. Fix GameObject Orb Pickup
File: src/modules/Playerbot/PvP/BattlegroundAI.cpp
- Locate PickupOrb() function (around line 1750)
- Fix GameObject search to use correct entries:
* 212091 (Orange Orb)
* 212092 (Purple Orb)
* 212093 (Green Orb)
* 212094 (Blue Orb)
- Add proper GameObject state checks (GAMEOBJECT_STATE_READY)
- Add distance validation (must be within USE_RANGE)
- Add proper error handling
- Add detailed logging for debugging
2. Merge Combat Engagement Fix
- Check if fix exists in recovery-refactoring-work branch
- If yes: Cherry-pick combat engagement commits
- If no: Implement from scratch:
* HuntEnemyOrbCarrier() - Add player->Attack(enemyCarrier, true)
* DefendOrbCarrier() - Add player->Attack(enemy, true)
- Ensure Attack() is called AFTER SetSelection()
- Add combat state validation (IsAlive, IsHostileTo)
- Add detailed logging
3. Add Center Control Awareness
- Define center area coordinates
- Add IsInCenterArea() helper function
- Modify movement logic to prioritize center when carrying orb
- Add 5x multiplier awareness to decision making
4. Code Quality
- Remove all TODOs
- Add comprehensive error handling
- Add null pointer checks
- Add bounds checking
- Follow TrinityCore coding style
5. Testing Preparation
- Add detailed logging statements for:
* Orb pickup attempts (success/failure with reason)
* Combat engagements (target, result)
* Center control decisions
* State transitions
Write implementation notes to Obsidian: "Sessions/TOK Sprint 2 - GameObject & Combat Fix.md"
Include:
- Exact changes made (file:line references)
- Rationale for each change
- Any deviations from spec (with justification)
- Known limitations or edge cases not yet handled
```
---
## Stage 4: Implement Dynamic Strategy
**Stage Name:** `Implement Dynamic Tactics & Strategy`
**Agent:** Claude Code
**Depends On:** Stage 3 ✓
**Instructions:**
```
Read from Obsidian:
- "Refactorings/TOK Lighthouse Spec.md" (strategy design)
- "Sessions/TOK Sprint 2 - GameObject & Combat Fix.md" (current state)
Implement dynamic strategy system:
1. Create Strategy State Machine
File: src/modules/Playerbot/AI/Coordination/Battleground/Scripts/Domination/TempleOfKotmoguScript.cpp
States:
- AGGRESSIVE: Ahead in score, hunt enemy carriers
- DEFENSIVE: Behind in score, protect our carriers
- BALANCED: Even score, standard play
- DESPERATION: Far behind + time running out
- PRESERVATION: Far ahead + time running out
Transitions:
- Based on score difference (thresholds: 200, 400 points)
- Based on orb distribution (who has more orbs)
- Based on time remaining
- Based on player count advantage
2. Implement Score-Based Tactics
- Add GetCurrentScore() for both teams
- Add GetScoreDifference() helper
- Add GetOrbDistribution() (count orbs per team)
- Implement tactics per state:
* AGGRESSIVE: Focus on enemy carriers, center control
* DEFENSIVE: Protect carriers, avoid risky plays
* BALANCED: Standard role distribution
* DESPERATION: All-in aggressive, ignore defense
* PRESERVATION: Avoid combat, run down clock
3. Implement Orb Distribution Analysis
- Track which team holds which orbs
- Count total orbs per team
- Identify priority targets (enemy with multiple orbs)
- Adjust strategy based on orb count
4. Implement Role Reassignment
File: src/modules/Playerbot/AI/Coordination/Battleground/BGRoleManager.cpp
- Dynamic role changes based on strategy state
- Role distribution per state:
* AGGRESSIVE: More hunters, fewer defenders
* DEFENSIVE: More defenders/escorts, fewer hunters
* BALANCED: Even distribution
- Smooth role transitions (don't abandon mid-action)
5. Add Strategy Logging
- Log state transitions (old state → new state + reason)
- Log strategy decisions (why choosing target X)
- Log role reassignments
- Performance metrics (state duration, effectiveness)
6. Integration with BGCoordinator
File: src/modules/Playerbot/AI/Coordination/Battleground/BattlegroundCoordinator.cpp
- Connect strategy state to coordinator
- Broadcast strategy changes to all bots
- Coordinate multi-bot actions (synchronized attacks)
Write to Obsidian: "Sessions/TOK Sprint 3 - Dynamic Strategy.md"
Include:
- State machine diagram (text-based)
- Transition logic explanation
- Role distribution tables
- Example scenarios showing strategy adaptation
```
---
## Stage 5A: Build Verification & Test Prep
**Stage Name:** `Build & Prepare Tests`
**Agent:** Claude Code
**Depends On:** Stage 4 ✓
**Instructions:**
```
Read from Obsidian: "Refactorings/TOK Testing Plan.md"
Tasks:
1. Build Verification
- Navigate to build directory
- Run CMake configuration:
cmake --build . --config RelWithDebInfo --target worldserver -- /maxcpucount:4
- Check for compilation errors
- Check for warnings (should be minimal)
- Verify binary created successfully
- Check binary size (compare to previous build)
2. Static Code Analysis
- Check all modified files for:
* Null pointer dereferences
* Uninitialized variables
* Memory leaks (missing deletes)
* Buffer overflows
* Race conditions (missing locks)
* Exception safety
- Verify all TODOs removed
- Verify all error paths handled
- Check for code smells (duplicated code, long functions)
3. Generate bg.log Configuration
- Create snippet for worldserver.conf:
Logger.BG.TOK=6,Console Server File
Logger.BG.TOK.File=bg
Logger.BG.TOK.TimeStamp=1
Logger.playerbots.bg=6,Console Server File
Logger.playerbots.bg.File=bg
4. Create Manual Test Checklist
Generate detailed test instructions in Obsidian: "Sessions/TOK Test Instructions.md"
Include:
- Server startup instructions
- How to add bg.log config
- Exact test scenarios:
* Test 1: Orange orb pickup (bot approaches, uses, confirms aura)
* Test 2: Purple orb pickup
* Test 3: Green orb pickup
* Test 4: Blue orb pickup
* Test 5: Combat engagement (bot attacks enemy carrier)
* Test 6: Center control (bot moves to center with orb)
* Test 7: Strategy switch (force score difference, observe behavior)
* Test 8: Bot death with orb (verify orb drops)
* Test 9: Orb respawn (verify 30 second timer)
* Test 10: Full game (10v10, complete to victory condition)
- Metrics to track:
* Orb pickup attempts vs successes (target: >95%)
* Combat engagements vs failures to attack (target: 100%)
* Crashes (target: ZERO)
* Strategy transitions observed
* Victory condition correctness
- Log collection instructions:
* Where to find logs/bg.log
* What log excerpts to copy
* How to save to Obsidian
5. Create Test Results Template
Create file in Obsidian: "Sessions/TOK Manual Test Results.md"
Template with sections:
- Test Environment (build hash, date, duration)
- Orb Pickup Results (table with attempts/successes per orb)
- Combat Engagement Results
- Strategy Transition Observations
- Crashes / Errors
- Performance Notes
- Log Excerpts
- Overall Assessment
Write to Obsidian:
- "Sessions/TOK Build Report.md" - Build results, warnings, binary info
- "Sessions/TOK Code Analysis.md" - Static analysis findings
- "Sessions/TOK Test Instructions.md" - Complete test checklist (ALREADY DESCRIBED ABOVE)
Output should be:
- Build report: ✅ PASS or ❌ FAIL (if fail, stop workflow here)
- Code analysis: List of issues found (if critical, stop workflow)
- Test instructions: Complete, clear, ready for human tester
```
---
## Stage 5B: MANUAL TESTING PAUSE
**Stage Name:** `⏸️ PAUSE: User Manual Testing`
**Agent:** None (Human Task)
**Depends On:** Stage 5A ✓
**Type:** Manual Pause Point
**Instructions:**
```
⏸️⏸️⏸️ WORKFLOW PAUSES HERE ⏸️⏸️⏸️
This is a manual testing phase. The workflow will resume after user completes testing.
USER TASKS:
1. Read Test Instructions
- Open Obsidian: "Sessions/TOK Test Instructions.md"
- Review all test scenarios
- Understand success criteria
2. Configure Server
- Add bg.log configuration to worldserver.conf (snippet provided in Stage 5A)
- Ensure Playerbot module is enabled
- Start worldserver
3. Execute Tests
- Follow each test scenario in order
- Track metrics for each test
- Note any unexpected behavior
- Collect screenshots if issues occur
4. Collect Logs
- After testing, copy logs/bg.log
- Extract relevant sections (orb pickup events, combat events, errors)
- Save to a file for analysis
5. Fill Results Template
- Open Obsidian: "Sessions/TOK Manual Test Results.md"
- Fill in all sections:
* Test environment details
* Results for each test scenario
* Metrics (success rates)
* Crashes (if any)
* Log excerpts (paste relevant logs)
* Overall observations
6. Resume Workflow
- After completing all tests and documenting results
- Resume workflow to Stage 5C
- Stage 5C will analyze your results
ESTIMATED TIME: 30-60 minutes
SUCCESS CRITERIA TO PROCEED:
- ✅ All test scenarios executed
- ✅ Results documented in Obsidian
- ✅ Logs collected and excerpts saved
- ✅ Ready for automated analysis
NOTE: If major issues found during testing:
- Document them clearly
- Stage 5C will identify them
- Workflow may loop back to fix issues
```
---
## Stage 5C: Log Analysis & Validation
**Stage Name:** `Analyze Test Results & Validate`
**Agent:** Claude Code
**Depends On:** Stage 5B ✓
**Instructions:**
```
Read from Obsidian:
- "Sessions/TOK Manual Test Results.md" (user's test data)
- "Refactorings/TOK Testing Plan.md" (success criteria)
- Any attached log files referenced in test results
Analysis tasks:
1. Parse Test Results
- Extract metrics from user's results:
* Orb pickup success rate (per orb and overall)
* Combat engagement success rate
* Crash count
* Strategy transitions observed
* Victory condition correctness
- Compare against targets:
* Orb pickup: >95% required
* Combat: 100% required
* Crashes: ZERO required
2. Log File Analysis
- Parse log excerpts for:
* Error messages (ERROR, FATAL levels)
* Warning messages (relevant to TOK)
* Exception traces
* Performance issues (long delays, timeouts)
- Search for specific events:
* "Orb pickup attempt" → success/failure reasons
* "Combat engagement" → target acquired, attack started
* "Strategy transition" → old state → new state
* GameObject interaction failures
* Null pointer accesses
* Memory allocation failures
3. Performance Analysis
- Check for:
* Excessive CPU usage indicators
* Memory leaks (increasing allocations)
* Slow operations (>100ms for simple actions)
* Thread contention / deadlocks
- Verify performance requirements met:
* <0.1% CPU per bot
* <10MB memory per bot
* <50ms response time for decisions
4. Edge Case Verification
- Verify each edge case was tested:
* Bot death with orb → orb dropped correctly
* Orb respawn timing → 30 seconds confirmed
* Multiple bots same orb → conflict resolution
* Disconnect/AFK → system handled gracefully
- Check if edge cases passed
5. Decision Matrix
Based on analysis, make decision:
✅ PASS - Proceed to Stage 6 if ALL true:
- Orb pickup ≥95%
- Combat engagement = 100%
- Crashes = 0
- No critical errors in logs
- Performance acceptable
- All edge cases handled
⚠️ NEEDS FIXES - Create fix tasks if:
- Orb pickup <95% (identify failure reasons)
- Combat engagement <100% (identify causes)
- Crashes = 1-2 (not critical, but needs fixing)
- Some errors in logs (non-critical)
- Performance issues (but not blocking)
❌ FAIL - Recommend rollback if:
- Crashes ≥3 (critical stability issue)
- Critical errors in logs (data corruption, memory corruption)
- Orb pickup <50% (fundamental design flaw)
- Performance unacceptable (>1% CPU per bot, >50MB memory)
6. Fix Task Generation (if NEEDS FIXES)
- For each issue found, create task in Obsidian:
* "Tasks/TOK-FIX-[NN] - [Issue Description].md"
* Include: Root cause, proposed fix, affected files
- Update "Refactorings/TOK Lighthouse BG.md" with fix status
Write comprehensive analysis to Obsidian: "Sessions/TOK Log Analysis Report.md"
Include:
- Executive summary (PASS/NEEDS FIXES/FAIL + rationale)
- Detailed metrics breakdown
- Log analysis findings (errors, warnings, patterns)
- Performance assessment
- Edge case verification results
- If NEEDS FIXES: List of issues + fix tasks created
- If FAIL: Recommendations for next steps (rollback vs redesign)
- If PASS: Confirmation to proceed to blueprint stage
WORKFLOW DECISION:
- If PASS: Continue to Stage 6
- If NEEDS FIXES: Pause workflow, alert user, wait for fixes
- If FAIL: Stop workflow, alert user, recommend rollback
```
---
## Stage 6: Create Transfer Blueprint
**Stage Name:** `Create Transfer Blueprint for All BGs`
**Agent:** Claude Code
**Depends On:** Stage 5C ✓ (PASS status required)
**Instructions:**
```
Read from Obsidian:
- "Refactorings/TOK Lighthouse BG.md" (original requirements)
- "Refactorings/TOK Lighthouse Spec.md" (design decisions)
- All session notes (Sprint 1-3, test results, analysis)
- "Sessions/TOK Log Analysis Report.md" (validation confirmation)
Create comprehensive blueprint for transferring TOK patterns to all 13 other battlegrounds.
Blueprint document: "Architecture/Lighthouse BG Blueprint.md"
Structure:
1. Executive Summary
- What makes TOK the lighthouse
- Success metrics achieved
- Key patterns to transfer
- Estimated effort to apply to other BGs
2. Data Accuracy Pattern
- How we researched TOK data (DBC/DB2/SQL queries)
- GameObject verification process
- Aura/spell verification process
- Victory condition verification
- Template queries for other BGs
- Checklist for data validation
3. GameObject Interaction Pattern
- The correct way to interact with BG objects
- GameObject::Use() best practices
- State checking requirements
- Distance validation
- Error handling pattern
- Code template for object interaction
4. Combat Engagement Pattern
- SetSelection() + Attack() sequence
- Target validation (IsAlive, IsHostileTo)
- Combat state management
- Integration with existing combat system
- Code template for combat engagement
5. Dynamic Strategy Pattern
- State machine architecture
- Transition logic design
- How to define BG-specific states
- How to define transition triggers
- Role assignment integration
- Code template for strategy system
6. Center Control / Objective Pattern
- How to define objective areas
- Priority calculation
- Risk/reward analysis
- Movement coordination
- Code template for objective control
7. Edge Case Handling Pattern
- Common edge cases across all BGs
- BG-specific edge cases
- Testing methodology
- Code patterns for edge case handling
8. Coordination Pattern
- BGCoordinator integration
- Role manager usage
- Multi-bot synchronization
- Communication patterns
- Code template for coordination
9. Logging & Debugging Pattern
- What to log and why
- Log levels for different events
- Performance logging
- Debugging tools used
- Log configuration template
10. Testing Pattern
- Build verification process
- Static code analysis checklist
- Manual testing scenarios template
- Log analysis methodology
- Success criteria definition
- Metrics tracking template
11. Performance Pattern
- CPU usage targets
- Memory usage targets
- Response time requirements
- Performance profiling tools
- Optimization techniques used
12. Code Quality Pattern
- TrinityCore coding standards applied
- Error handling requirements
- Null safety practices
- Resource management (RAII)
- Code review checklist
13. Transfer Process
- Step-by-step guide to apply patterns to new BG
- Customization points (what's BG-specific vs generic)
- Estimated time per BG
- Dependency order (which BGs to do first)
- Risk mitigation (testing, validation)
14. BG Priority Matrix
- List all 13 other battlegrounds
- Categorize by type:
* CTF: Warsong Gulch, Twin Peaks
* Domination: Arathi Basin, Battle for Gilneas, Eye of the Storm
* Resource Race: Silvershard Mines, Deepwind Gorge
* Siege: Alterac Valley, Isle of Conquest, Strand of the Ancients
* Epic: Ashran
- Recommend order of implementation
- Complexity assessment per BG
15. Success Metrics for Transfer
- How to measure if pattern was applied correctly
- Validation checklist per BG
- Testing requirements per BG
- Sign-off criteria
16. Lessons Learned
- What worked well in TOK development
- What challenges were faced
- What would be done differently
- Tips for applying to other BGs
17. Code Appendix
- Complete code templates (copy-paste ready)
- Configuration snippets
- SQL queries for data verification
- Testing scripts
Write blueprint to Obsidian: "Architecture/Lighthouse BG Blueprint.md"
Also create quick reference:
- "Architecture/BG Transfer Checklist.md" - Quick checklist for applying patterns
Update project documentation:
- "Refactorings/TOK Lighthouse BG.md" - Mark as ✅ COMPLETE
- "Tasks/Active Instances.md" - Update status
Blueprint should be:
- Complete (covers all aspects)
- Actionable (anyone can follow it)
- Template-based (minimal customization per BG)
- Validated (based on proven TOK implementation)
- Maintainable (easy to update if patterns improve)
FINAL OUTPUT:
- Blueprint document (50-100 pages equivalent)
- Transfer checklist (2-3 pages)
- Updated project status
- Ready for application to next BG
```
---
## Workflow Settings
**If Zenflow allows these configurations:**
- ✅ **Branch Strategy:** Create branch per stage
- Format: `lighthouse-bg/tok/stage-{N}-{stage-name}`
- Merge after validation
- ✅ **Shared Memory:** Obsidian Vault
- Path: `C:\TrinityBots\PlayerBotProjectMemory\TrinityCore-Playerbot-Memory`
- All stages read/write to Obsidian for coordination
- ✅ **Parallelization:** Sequential (not parallel)
- Max parallel: 1
- Stages must complete in order
- ✅ **Auto-commit:** After each stage
- Commit message format: `[Zenflow] Stage {N}: {stage-name} - {brief summary}`
- ✅ **Failure Handling:**
- On build failure (Stage 5A): Stop workflow, alert user
- On test failure (Stage 5C): Pause workflow, create fix tasks
- On any critical error: Stop workflow, alert user
- ✅ **Notifications:**
- Stage complete: Update Obsidian "Tasks/Active Instances.md"
- Workflow paused: Alert user with reason
- Workflow complete: Summary report to Obsidian
---
## Expected Timeline
- **Stage 1 (Research):** ~30-45 minutes
- **Stage 2 (Spec):** ~45-60 minutes
- **Stage 3 (GameObject/Combat):** ~60-90 minutes
- **Stage 4 (Dynamic Strategy):** ~90-120 minutes
- **Stage 5A (Build & Prep):** ~15-30 minutes
- **Stage 5B (Manual Testing):** ~30-60 minutes (USER)
- **Stage 5C (Analysis):** ~20-30 minutes
- **Stage 6 (Blueprint):** ~60-90 minutes
**Total Automated Time:** ~6-8 hours
**Total Manual Time:** ~30-60 minutes (your testing)
**Total Workflow Time:** ~7-9 hours
---
## Workflow Success Criteria
To consider the workflow successful:
- ✅ All stages complete without critical errors
- ✅ Build passes (Stage 5A)
- ✅ Manual tests pass (Stage 5B/5C):
- Orb pickup ≥95%
- Combat engagement = 100%
- Zero crashes
- ✅ Blueprint created (Stage 6)
- ✅ Code merged to main branch
- ✅ Documentation complete in Obsidian
**Result:** Temple of Kotmogu is production-ready and serves as the blueprint for all other battlegrounds.
---
**End of Workflow Definition**
@@ -0,0 +1,503 @@
# Task: PlayerbotModule Subsystem Registry Refactoring + Startup Log Harmonisierung
## Ziel
Refactoring der drei "God Functions" in `PlayerbotModule.cpp`:
- `InitializeManagers()` (~300 Zeilen Boilerplate)
- `OnWorldUpdate()` (~200 Zeilen mit manuellen Timer-Variablen t1–t17)
- `Shutdown()` (~100 Zeilen Copy-Paste Shutdown-Calls)
Ersetze das manuelle, hart-codierte Subsystem-Management durch ein **SubsystemRegistry-Pattern**, das Init, Update und Shutdown automatisch verwaltet. Zusätzlich: **Harmonisierung und Verschlankung des Startup-Log-Outputs** für bessere Übersichtlichkeit.
---
## Kontext
### Projekt
- **Pfad**: `C:\TrinityBots\TrinityCore`
- **Modul**: `src/modules/Playerbot/`
- **Sprache**: C++20, MSVC (Visual Studio 2025)
- **Build**: `cmake --build build --config RelWithDebInfo -- /m`
- **Branch**: `playerbot-dev`
- **Codebasis**: ~636K LOC, ~1010 Dateien im Playerbot-Modul
### Wichtige Dateien
- `src/modules/Playerbot/PlayerbotModule.h` — Hauptklasse (Header)
- `src/modules/Playerbot/PlayerbotModule.cpp` — Hauptklasse (Implementation, ~600 Zeilen)
- `src/modules/Playerbot/PlayerbotModuleAdapter.h/.cpp` — ModuleManager-Integration
- `src/modules/Common/Update/ModuleUpdateManager.h/.cpp` — Bestehendes Update-Registry (NICHT ändern)
- `src/modules/Playerbot/CMakeLists.txt` — Build-Konfiguration (Multi-Library Architektur)
### Bestehende Architektur
`PlayerbotModuleAdapter` registriert sich beim `ModuleManager`. Bei jedem World-Tick wird `PlayerbotModuleAdapter::OnModuleUpdate()` → `PlayerbotModule::OnWorldUpdate()` aufgerufen. PlayerbotModule ruft dann manuell 17+ Manager-Update-Funktionen auf.
### Coding Standards (aus CLAUDE.md)
- **Naming**: PascalCase Klassen, PascalCase Methoden, camelCase Variablen, `m_` Prefix für Members
- **Threading**: `std::shared_mutex` für read-heavy, `std::mutex` für write-heavy
- **Memory**: Smart Pointers bevorzugen
- **Include Order**: Own header → Project headers → TrinityCore headers → External → STL
- **Singletons**: Nutze `#define sXxxManager XxxManager::instance()` Pattern wie im restlichen TC-Code
---
## Problem-Analyse
### 1. OnWorldUpdate() — Manuelles Timing-Boilerplate
Aktuell sieht die Funktion so aus (gekürzt):
```cpp
void PlayerbotModule::OnWorldUpdate(uint32 diff)
{
auto timeStart = std::chrono::high_resolution_clock::now();
auto lastTime = timeStart;
sBotAccountMgr->Update(diff);
auto t1 = std::chrono::high_resolution_clock::now();
auto accountTime = std::chrono::duration_cast<std::chrono::microseconds>(t1 - lastTime).count();
lastTime = t1;
Playerbot::sBotSpawner->Update(diff);
auto t2 = std::chrono::high_resolution_clock::now();
auto spawnerTime = std::chrono::duration_cast<std::chrono::microseconds>(t2 - lastTime).count();
lastTime = t2;
// ... 15 weitere identische Blöcke mit t3–t17 ...
if (totalUpdateTime > 100000)
{
TC_LOG_WARN("module.playerbot.performance",
"PERFORMANCE: OnWorldUpdate took {:.2f}ms - Account:{:.2f}ms, ...",
totalUpdateTime / 1000.0f, accountTime / 1000.0f, ...); // 17 Parameter!
}
}
```
**Probleme**: Massiver Code-Duplikat, jedes neue Subsystem erfordert Änderungen an 3 Stellen (Init, Update, Shutdown + neue Timer-Variable + Log-Format-String).
### 2. InitializeManagers() — Identisches Boilerplate × 25
```cpp
TC_LOG_INFO("server.loading", "Initializing Bot Protection Registry...");
if (!sBotProtectionRegistry->Initialize())
{
TC_LOG_WARN("server.loading", "Bot Protection Registry initialization failed...");
}
else
{
TC_LOG_INFO("server.loading", "Bot Protection Registry initialized successfully");
}
```
Dieses Pattern wiederholt sich ~25 Mal mit minimalem Unterschied.
### 3. Shutdown() — Spiegelbildliches Boilerplate
```cpp
TC_LOG_INFO("server.loading", "Shutting down Bot Protection Registry...");
sBotProtectionRegistry->Shutdown();
TC_LOG_INFO("server.loading", "Bot Protection Registry shutdown complete");
```
~20 Mal wiederholt.
### 4. Startup-Logging — Unübersichtlich und inkonsistent
- Manche Subsysteme loggen auf `"server.loading"`, andere auf `"module.playerbot"`
- Einige nutzen `TC_LOG_ERROR` für nicht-kritische Meldungen (z.B. `PlayerbotModuleAdapter::OnModuleStartup` loggt mit `TC_LOG_ERROR`)
- Jedes Subsystem produziert 2-3 Zeilen (init start + init success/fail)
- Bei 25+ Subsystemen sind das ~75 Log-Zeilen nur für Init
- Kein visueller Überblick — alles sieht gleich aus
---
## Anforderungen
### A. Neues SubsystemRegistry-Interface
Erstelle ein `IPlayerbotSubsystem`-Interface und eine `PlayerbotSubsystemRegistry`:
```cpp
// Core/PlayerbotSubsystem.h
namespace Playerbot
{
enum class SubsystemPriority : uint8
{
CRITICAL = 0, // Muss initialisiert werden, Fehler = Abort
HIGH = 1, // Sollte initialisiert werden, Fehler = Warning
NORMAL = 2, // Standard-Subsystem
LOW = 3, // Optional, Fehler wird ignoriert
};
struct SubsystemInfo
{
std::string name; // Display-Name für Logging
SubsystemPriority priority; // Init-Priorität
uint32 initOrder; // Init-Reihenfolge (niedrig = zuerst)
uint32 updateOrder; // Update-Reihenfolge (niedrig = zuerst)
uint32 shutdownOrder; // Shutdown-Reihenfolge (niedrig = zuerst)
bool needsUpdate; // Ob dieses Subsystem per-tick updates braucht
};
class IPlayerbotSubsystem
{
public:
virtual ~IPlayerbotSubsystem() = default;
virtual SubsystemInfo GetInfo() const = 0;
virtual bool Initialize() = 0;
virtual void Update(uint32 diff) = 0;
virtual void Shutdown() = 0;
};
} // namespace Playerbot
```
Die Registry:
```cpp
// Core/PlayerbotSubsystemRegistry.h
namespace Playerbot
{
class PlayerbotSubsystemRegistry
{
public:
static PlayerbotSubsystemRegistry* instance();
// Registrierung
void RegisterSubsystem(std::unique_ptr<IPlayerbotSubsystem> subsystem);
// Lifecycle
bool InitializeAll(); // Ersetzt InitializeManagers()
void UpdateAll(uint32 diff); // Ersetzt OnWorldUpdate()-Inhalt
void ShutdownAll(); // Ersetzt Shutdown()-Inhalt
// Diagnostik
struct SubsystemMetrics
{
std::string name;
uint64 totalUpdateTimeUs = 0;
uint64 lastUpdateTimeUs = 0;
uint64 maxUpdateTimeUs = 0;
uint32 updateCount = 0;
};
std::vector<SubsystemMetrics> GetMetrics() const;
uint32 GetSubsystemCount() const;
private:
PlayerbotSubsystemRegistry() = default;
struct SubsystemEntry
{
std::unique_ptr<IPlayerbotSubsystem> subsystem;
SubsystemInfo info;
SubsystemMetrics metrics;
bool initialized = false;
};
std::vector<SubsystemEntry> m_subsystems;
bool m_allInitialized = false;
};
} // namespace Playerbot
#define sPlayerbotSubsystemRegistry Playerbot::PlayerbotSubsystemRegistry::instance()
```
### B. Subsystem-Adapter für bestehende Manager
Da die bestehenden Singletons (sBotAccountMgr, sBotSpawner, etc.) NICHT geändert werden sollen, erstelle Thin-Wrapper-Adapter. Beispiel:
```cpp
class BotAccountMgrSubsystem : public IPlayerbotSubsystem
{
public:
SubsystemInfo GetInfo() const override
{
return { "BotAccountMgr", SubsystemPriority::CRITICAL,
/*initOrder=*/100, /*updateOrder=*/100, /*shutdownOrder=*/900,
/*needsUpdate=*/true };
}
bool Initialize() override { return sBotAccountMgr->Initialize(); }
void Update(uint32 diff) override { sBotAccountMgr->Update(diff); }
void Shutdown() override { sBotAccountMgr->Shutdown(); }
};
```
Erstelle Adapter für ALLE ~25 Subsysteme die aktuell in InitializeManagers/OnWorldUpdate/Shutdown manuell aufgerufen werden. Die Adapter können in einer einzigen Datei leben: `Core/SubsystemAdapters.cpp`.
**WICHTIG**: Analysiere die aktuelle Reihenfolge in PlayerbotModule.cpp sorgfältig und bilde sie exakt in den Order-Werten ab!
### C. Vereinfachte PlayerbotModule-Funktionen
Nach dem Refactoring sollte PlayerbotModule.cpp drastisch schrumpfen:
```cpp
bool PlayerbotModule::InitializeManagers()
{
RegisterAllSubsystems();
return sPlayerbotSubsystemRegistry->InitializeAll();
}
void PlayerbotModule::OnWorldUpdate(uint32 diff)
{
if (!_enabled || !_initialized)
return;
try
{
// One-time trigger to complete login for existing sessions
static bool loginTriggered = false;
static uint32 totalTime = 0;
totalTime += diff;
if (!loginTriggered && totalTime > 5000)
{
TriggerBotCharacterLogins();
loginTriggered = true;
}
sPlayerbotSubsystemRegistry->UpdateAll(diff);
}
catch (std::exception const& ex)
{
TC_LOG_ERROR("module.playerbot", "CRITICAL EXCEPTION in OnWorldUpdate: {}", ex.what());
_enabled = false;
}
catch (...)
{
TC_LOG_ERROR("module.playerbot", "CRITICAL UNKNOWN EXCEPTION in OnWorldUpdate");
_enabled = false;
}
}
void PlayerbotModule::Shutdown()
{
if (!_initialized)
return;
TC_LOG_INFO("module.playerbot", "Shutting down Playerbot Module...");
sPlayerbotSubsystemRegistry->ShutdownAll();
ShutdownDatabase();
sPlayerbotCharDB->Shutdown();
_initialized = false;
_enabled = false;
TC_LOG_INFO("module.playerbot", "Playerbot Module shutdown complete.");
}
```
### D. Automatisches Performance-Profiling in der Registry
Die Registry misst die Update-Zeit pro Subsystem automatisch. Bei Überschreitung von 100ms werden die **Top-5 langsamsten Subsysteme** geloggt (statt alle 17):
```
PERFORMANCE: UpdateAll took 142.5ms - Top offenders:
BotWorldSessionMgr: 68.3ms, BotSpawner: 32.1ms, DomainEventBus: 18.7ms, ...
```
### E. Startup-Log Harmonisierung
**Ziel**: Kompakter, übersichtlicher Startup-Output statt ~75 Zeilen.
#### Format-Vorgabe:
```
[module.playerbot] ═══════════════════════════════════════════════════════════════
[module.playerbot] Playerbot Module v1.0.0 initializing...
[module.playerbot] ───────────────────────────────────────────────────────────────
[module.playerbot] Database : Connected (127.0.0.1:3306/characters)
[module.playerbot] Migrations : 12 applied (schema v12)
[module.playerbot] ───────────────────────────────────────────────────────────────
[module.playerbot] Initializing 25 subsystems...
[module.playerbot] ✓ BotAccountMgr [ 45ms]
[module.playerbot] ✓ BotNameMgr [ 12ms]
[module.playerbot] ✓ BotCharacterDistribution [ 89ms]
[module.playerbot] ✓ BotWorldSessionMgr [ 23ms]
[module.playerbot] ⚠ BotProtectionRegistry [ 3ms] (non-critical, continuing)
[module.playerbot] ✓ QuestHubDatabase [ 234ms] (847 quest hubs)
[module.playerbot] ... (weitere Subsysteme)
[module.playerbot] ───────────────────────────────────────────────────────────────
[module.playerbot] Result: 24/25 OK | 1 warning | 0 failed
[module.playerbot] Total init time: 1,247ms
[module.playerbot] ═══════════════════════════════════════════════════════════════
[module.playerbot] Playerbot Module v1.0.0 ready.
```
#### Regeln:
1. **Einheitlicher Log-Channel**: Alles über `"module.playerbot"` (nicht `"server.loading"`)
2. **Log-Level**: `TC_LOG_INFO` für normale Meldungen, `TC_LOG_WARN` für non-fatal, `TC_LOG_ERROR` nur für fatale Fehler
3. **Kein doppeltes Logging**: Subsysteme selbst loggen NICHT mehr "Initializing..." / "...initialized". Die Registry macht das zentral.
4. **Alle manuellen TC_LOG_INFO/WARN-Aufrufe** vor/nach Init-Calls in PlayerbotModule.cpp ENTFERNEN
#### PlayerbotModuleAdapter aufräumen:
- `TC_LOG_ERROR("server.loading", "=== PlayerbotModuleAdapter::OnModuleStartup() CALLED ===")` → ENTFERNEN
- `if (++logCounter % 100000 == 0)` periodic logging → ENTFERNEN
- Alle verbleibenden Logs auf `"module.playerbot"` Channel umstellen
---
## Schritte
### Phase 1: Interface & Registry erstellen
1. Erstelle `src/modules/Playerbot/Core/PlayerbotSubsystem.h` (Interface)
2. Erstelle `src/modules/Playerbot/Core/PlayerbotSubsystemRegistry.h/.cpp` (Registry)
3. Füge die neuen Dateien zum CMakeLists.txt hinzu (in `PLAYERBOT_CORE_SOURCES`)
### Phase 2: Subsystem-Adapter erstellen
4. Erstelle `src/modules/Playerbot/Core/SubsystemAdapters.h/.cpp` — Thin-Wrapper für alle Manager
5. Erstelle `RegisterAllSubsystems()` Funktion die alle Adapter registriert
**KRITISCH**: Die Reihenfolge der Registrierung (initOrder, updateOrder, shutdownOrder) muss die aktuelle Reihenfolge in PlayerbotModule.cpp exakt beibehalten! Analysiere die aktuelle Reihenfolge sorgfältig.
### Phase 3: PlayerbotModule.cpp refactorn
6. `InitializeManagers()` → `RegisterAllSubsystems()` + `sPlayerbotSubsystemRegistry->InitializeAll()`
7. `OnWorldUpdate()` → `sPlayerbotSubsystemRegistry->UpdateAll(diff)` (Login-Trigger und Exception-Safety beibehalten)
8. `Shutdown()` → `sPlayerbotSubsystemRegistry->ShutdownAll()`
9. Entferne ALLE manuellen Init/Shutdown-Log-Aufrufe
### Phase 4: Log-Harmonisierung
10. Implementiere formatierte Startup-Ausgabe in `InitializeAll()`
11. Implementiere Performance-Warning (Top-5) in `UpdateAll()`
12. Räume `PlayerbotModuleAdapter.cpp` auf (Debug-Leftovers, Log-Channel)
13. Stelle alle Playerbot-Logs in `PlayerbotModule.cpp` auf `"module.playerbot"` um
14. Database-Init-Meldungen (`InitializeDatabase()`) ebenfalls harmonisieren
### Phase 5: Build & Verify
15. Build: `cmake --build build --config RelWithDebInfo -- /m`
16. Alle Compile-Fehler beheben
17. Verifiziere: Keine funktionalen Änderungen — reines Refactoring
---
## Spezielle Anforderungen
### Was NICHT geändert werden darf
- **Bestehende Singleton-Interfaces** (sBotAccountMgr, sBotSpawner, etc.) — nur Wrapper erstellen
- **ModuleUpdateManager** in `src/modules/Common/` — fremder Code
- **ModuleManager-Integration** — PlayerbotModuleAdapter bleibt Einstiegspunkt
- **Die funktionale Reihenfolge** von Init, Update und Shutdown
- **EventBus-Processing** in OnWorldUpdate() — als Subsystem wrappen
- **Queue Health Monitoring** — als Teil des EventBus-Subsystems wrappen
### Was besonders beachtet werden muss
- Die **Bot-Login-Trigger-Logik** (`static bool loginTriggered`) muss erhalten bleiben
- **Dependency-Wiring** zwischen Subsystemen muss erhalten bleiben. Z.B.:
```cpp
sDemandCalculator->SetActivityTracker(sPlayerActivityTracker);
sDemandCalculator->SetProtectionRegistry(sBotProtectionRegistry);
sDemandCalculator->SetFlowPredictor(sBracketFlowPredictor);
```
Diese Calls gehören in den `Initialize()` des DemandCalculator-Adapters.
- Die **BotRetirementManager** braucht ebenfalls Wiring: `sBotRetirementManager->SetProtectionRegistry(sBotProtectionRegistry)`
- **Exception-Safety** in OnWorldUpdate() beibehalten (try-catch mit Module-Disable)
- Das **NOTE über doppelte Updates** (ModuleUpdateManager vs ModuleManager) beachten
### Shutdown-Reihenfolge
Die Shutdown-Reihenfolge ist NICHT einfach die umgekehrte Init-Reihenfolge. Die Registry braucht ein separates `shutdownOrder`-Feld. Analysiere die aktuelle Shutdown-Reihenfolge in `PlayerbotModule::Shutdown()` und bilde sie korrekt ab.
### Thread-Safety
Die Registry wird NUR vom World Thread aufgerufen — kein Multi-Thread-Zugriff. Daher braucht die Registry selbst KEINE Mutex-Protection.
---
## Subsystem-Liste (aus aktueller PlayerbotModule.cpp extrahiert)
### InitializeManagers() Reihenfolge:
1. BotAccountMgr (CRITICAL)
2. BotNameMgr (CRITICAL)
3. BotCharacterDistribution (CRITICAL)
4. BotWorldSessionMgr (CRITICAL)
5. BotPacketRelay
6. BotChatCommandHandler
7. ProfessionDatabase
8. ProfessionEventBus (lazy init, evtl. kein Adapter nötig)
9. ClassBehaviorTreeRegistry
10. QuestHubDatabase
11. PortalDatabase (non-fatal)
12. BotGearFactory
13. PlayerbotPacketSniffer
14. BG/LFG Typed Packet Handlers
15. MajorCooldownTracker
16. BotActionManager
17. BotProtectionRegistry (non-fatal)
18. BotRetirementManager (non-fatal, braucht ProtectionRegistry)
19. BracketFlowPredictor (non-fatal)
20. PlayerActivityTracker (non-fatal)
21. DemandCalculator (non-fatal, braucht ActivityTracker/ProtectionRegistry/FlowPredictor)
22. PopulationLifecycleController (non-fatal)
23. ContentRequirementDb (non-fatal)
24. BotTemplateRepository (non-fatal)
25. BotCloneEngine (non-fatal)
26. BotPostLoginConfigurator (non-fatal)
27. InstanceBotPool (non-fatal)
28. JITBotFactory (non-fatal)
29. QueueStatePoller (non-fatal)
30. QueueShortageSubscriber (non-fatal)
31. InstanceBotOrchestrator (non-fatal)
32. InstanceBotHooks (non-fatal)
33. BotOperationTracker
### OnWorldUpdate() Reihenfolge:
1. BotAccountMgr
2. BotSpawner
3. BotWorldSessionMgr
4. PlayerbotCharDB
5. GroupEventBus
6. Domain EventBuses (Combat, Loot, Quest, Aura, Cooldown, Resource, Social, Auction, NPC, Instance, Profession)
7. Queue Health Monitoring (every 60s, kann Teil des EventBus-Subsystems sein)
8. BotProtectionRegistry
9. BotRetirementManager
10. BracketFlowPredictor
11. PlayerActivityTracker
12. DemandCalculator
13. PopulationLifecycleController
14. InstanceBotPool
15. InstanceBotOrchestrator
16. JITBotFactory
17. QueueStatePoller
### Shutdown() Reihenfolge (aktuell):
1. BotActionManager
2. PopulationLifecycleController
3. InstanceBotHooks
4. InstanceBotOrchestrator
5. QueueShortageSubscriber
6. QueueStatePoller
7. JITBotFactory
8. InstanceBotPool
9. BotCloneEngine
10. BotPostLoginConfigurator
11. BotTemplateRepository
12. BotOperationTracker (PrintStatus + Shutdown)
13. DemandCalculator
14. PlayerActivityTracker
15. BracketFlowPredictor
16. BotRetirementManager
17. BotProtectionRegistry
18. UnregisterHooks + UnregisterModule
19. BotChatCommandHandler
20. BotPacketRelay
21. PlayerbotPacketSniffer
22. BotWorldSessionMgr
23. BotNameMgr
24. BotAccountMgr
25. PlayerbotDatabase (ShutdownDatabase)
26. PlayerbotCharDB
---
## Definition of Done
- [ ] Alle 3 God-Functions (Init/Update/Shutdown) sind auf je ~10-20 Zeilen geschrumpft
- [ ] SubsystemRegistry existiert mit automatischem Performance-Profiling
- [ ] Alle Subsysteme sind als Adapter registriert
- [ ] Init/Update/Shutdown-Reihenfolge ist exakt wie vorher
- [ ] Startup-Log ist kompakt und übersichtlich (gruppiert, mit Timing, mit Zusammenfassung)
- [ ] Alle Logs nutzen einheitlich `"module.playerbot"` Channel
- [ ] Keine `TC_LOG_ERROR` für informative Meldungen
- [ ] Debug-Leftovers in PlayerbotModuleAdapter entfernt
- [ ] Performance-Warning zeigt Top-5 langsamste Subsysteme
- [ ] Build kompiliert fehlerfrei mit `RelWithDebInfo`
- [ ] Keine funktionalen Änderungen — reines Refactoring
- [ ] Dependency-Wiring zwischen Subsystemen funktioniert korrekt
- [ ] Bot-Login-Trigger-Logik funktioniert weiterhin
+57
View File
@@ -584,6 +584,63 @@ public:
Events::EventDispatcher* GetEventDispatcher() { return _gameSystems ? _gameSystems->GetEventDispatcher() : nullptr; }
Events::EventDispatcher const* GetEventDispatcher() const { return _gameSystems ? _gameSystems->GetEventDispatcher() : nullptr; }
// ========================================================================
// SPRINT 3: COMBAT COORDINATION - Cross-bot coordination via claims
// ========================================================================
/**
* @brief Get Combat Coordination Integrator
* Bridges existing combat managers with BotMessageBus claim system
* @return Pointer to CombatCoordinationIntegrator, or nullptr if not initialized
*/
class CombatCoordinationIntegrator* GetCombatCoordinationIntegrator()
{
return _gameSystems ? _gameSystems->GetCombatCoordinationIntegrator() : nullptr;
}
class CombatCoordinationIntegrator const* GetCombatCoordinationIntegrator() const
{
return _gameSystems ? _gameSystems->GetCombatCoordinationIntegrator() : nullptr;
}
/**
* @brief Get Dispel Coordinator for dispel rotation (GAP 2 fix)
* @return Pointer to DispelCoordinator, or nullptr if not initialized
*/
class DispelCoordinator* GetDispelCoordinator()
{
return _gameSystems ? _gameSystems->GetDispelCoordinator() : nullptr;
}
class DispelCoordinator const* GetDispelCoordinator() const
{
return _gameSystems ? _gameSystems->GetDispelCoordinator() : nullptr;
}
/**
* @brief Get Interrupt Rotation Manager
* @return Pointer to InterruptRotationManager, or nullptr if not initialized
*/
class InterruptRotationManager* GetInterruptRotationManager()
{
return _gameSystems ? _gameSystems->GetInterruptRotationManager() : nullptr;
}
class InterruptRotationManager const* GetInterruptRotationManager() const
{
return _gameSystems ? _gameSystems->GetInterruptRotationManager() : nullptr;
}
/**
* @brief Get Defensive Behavior Manager for external CD coordination (GAP 3 fix)
* @return Pointer to DefensiveBehaviorManager, or nullptr if not initialized
*/
class DefensiveBehaviorManager* GetDefensiveBehaviorManager()
{
return _gameSystems ? _gameSystems->GetDefensiveBehaviorManager() : nullptr;
}
class DefensiveBehaviorManager const* GetDefensiveBehaviorManager() const
{
return _gameSystems ? _gameSystems->GetDefensiveBehaviorManager() : nullptr;
}
// ========================================================================
// ST-1: ADAPTIVE AI UPDATE THROTTLING - Performance optimization
// ========================================================================
@@ -0,0 +1,800 @@
/*
* Copyright (C) 2024 TrinityCore <https://www.trinitycore.org/>
*
* Sprint 3: Combat Coordination Integration Layer Implementation
*/
#include "CombatCoordinationIntegrator.h"
#include "InterruptCoordinator.h"
#include "AI/CombatBehaviors/DefensiveBehaviorManager.h"
#include "CrowdControlManager.h"
#include "AI/CombatBehaviors/DispelCoordinator.h"
#include "AI/BotAI.h"
#include "Group/GroupRoleEnums.h"
#include "Player.h"
#include "Group.h"
#include "Unit.h"
#include "SpellInfo.h"
#include "SpellMgr.h"
#include "SpellHistory.h"
#include "ObjectAccessor.h"
#include "GameTime.h"
#include "Log.h"
namespace Playerbot
{
// ============================================================================
// EXTERNAL CD DATABASE
// ============================================================================
static std::unordered_map<uint32, ExternalCDInfo> s_externalCDDatabase;
static bool s_databaseInitialized = false;
const std::unordered_map<uint32, ExternalCDInfo>& ExternalCDInfo::GetDatabase()
{
if (!s_databaseInitialized)
{
// Major External CDs (single target, life-saving)
s_externalCDDatabase[47788] = { 47788, ExternalCDTier::TIER_MAJOR, 180000, 10000, false, true }; // Guardian Spirit
s_externalCDDatabase[33206] = { 33206, ExternalCDTier::TIER_MAJOR, 180000, 8000, false, true }; // Pain Suppression
s_externalCDDatabase[102342] = { 102342, ExternalCDTier::TIER_MAJOR, 90000, 12000, false, true }; // Ironbark
s_externalCDDatabase[116849] = { 116849, ExternalCDTier::TIER_MAJOR, 120000, 12000, false, true }; // Life Cocoon
// Moderate External CDs
s_externalCDDatabase[6940] = { 6940, ExternalCDTier::TIER_MODERATE, 120000, 12000, false, true }; // Blessing of Sacrifice
s_externalCDDatabase[114030] = { 114030, ExternalCDTier::TIER_MODERATE, 120000, 12000, false, true }; // Vigilance
// Minor/Group CDs
s_externalCDDatabase[62618] = { 62618, ExternalCDTier::TIER_MINOR, 180000, 10000, true, false }; // Power Word: Barrier
s_externalCDDatabase[196718] = { 196718, ExternalCDTier::TIER_MINOR, 180000, 8000, true, false }; // Darkness
s_externalCDDatabase[51052] = { 51052, ExternalCDTier::TIER_MINOR, 120000, 10000, true, false }; // Anti-Magic Zone
// Raid CDs
s_externalCDDatabase[97462] = { 97462, ExternalCDTier::TIER_RAID, 180000, 10000, true, false }; // Rallying Cry
s_externalCDDatabase[98008] = { 98008, ExternalCDTier::TIER_RAID, 180000, 6000, true, false }; // Spirit Link Totem
s_externalCDDatabase[31821] = { 31821, ExternalCDTier::TIER_RAID, 180000, 8000, true, false }; // Aura Mastery
s_externalCDDatabase[108280] = { 108280, ExternalCDTier::TIER_RAID, 180000, 10000, true, false }; // Healing Tide Totem
s_databaseInitialized = true;
}
return s_externalCDDatabase;
}
// ============================================================================
// CONSTRUCTOR / DESTRUCTOR
// ============================================================================
CombatCoordinationIntegrator::CombatCoordinationIntegrator(BotAI* ai)
: _ai(ai)
, _bot(ai ? ai->GetBot() : nullptr)
{
}
CombatCoordinationIntegrator::~CombatCoordinationIntegrator()
{
Shutdown();
}
// ============================================================================
// LIFECYCLE
// ============================================================================
void CombatCoordinationIntegrator::Initialize(
InterruptCoordinatorFixed* interruptCoord,
DefensiveBehaviorManager* defensiveMgr,
CrowdControlManager* ccMgr,
DispelCoordinator* dispelCoord)
{
_interruptCoord = interruptCoord;
_defensiveMgr = defensiveMgr;
_ccMgr = ccMgr;
_dispelCoord = dispelCoord;
// Ensure database is initialized
ExternalCDInfo::GetDatabase();
// Get group GUID for message bus subscription
if (_bot && _bot->GetGroup())
{
_groupGuid = _bot->GetGroup()->GetGUID();
SubscribeToMessageBus();
}
TC_LOG_DEBUG("playerbot.combat", "CombatCoordinationIntegrator::Initialize - Bot {} initialized",
_bot ? _bot->GetGUID().ToString() : "null");
}
void CombatCoordinationIntegrator::Shutdown()
{
UnsubscribeFromMessageBus();
_activeClaims.clear();
_protectionWindows.clear();
_interruptCoord = nullptr;
_defensiveMgr = nullptr;
_ccMgr = nullptr;
_dispelCoord = nullptr;
}
void CombatCoordinationIntegrator::Update(uint32 diff)
{
if (!_bot || !_bot->IsAlive())
return;
uint32 now = GameTime::GetGameTimeMS();
if (now - _lastUpdate < UPDATE_INTERVAL_MS)
return;
_lastUpdate = now;
// Update protection windows (expire old ones)
UpdateProtectionWindows();
// Check if group changed
if (_bot->GetGroup())
{
ObjectGuid newGroupGuid = _bot->GetGroup()->GetGUID();
if (newGroupGuid != _groupGuid)
{
UnsubscribeFromMessageBus();
_groupGuid = newGroupGuid;
SubscribeToMessageBus();
}
}
// Expire old claims
auto claimIt = _activeClaims.begin();
while (claimIt != _activeClaims.end())
{
auto elapsed = std::chrono::steady_clock::now() - claimIt->second.submitTime;
if (elapsed > std::chrono::milliseconds(_config.claimTimeoutMs * 10)) // 10x timeout for cleanup
{
claimIt = _activeClaims.erase(claimIt);
}
else
{
++claimIt;
}
}
}
// ============================================================================
// INTERRUPT COORDINATION (S3.4)
// ============================================================================
bool CombatCoordinationIntegrator::RequestInterrupt(ObjectGuid targetGuid, uint32 spellId, ClaimPriority priority)
{
if (!_config.enableInterruptClaims || !_bot || _groupGuid.IsEmpty())
return false;
// Create interrupt claim message
BotMessage msg = BotMessage::ClaimInterrupt(
_bot->GetGUID(),
_groupGuid,
targetGuid,
spellId,
priority
);
// Submit through BotMessageBus
auto callback = [this](BotMessage const& m, ClaimStatus s) {
OnClaimResolved(m, s);
};
ClaimStatus status = BotMessageBus::Instance().SubmitClaim(msg, callback);
// Track the claim
uint64 claimKey = targetGuid.GetRawValue() ^ (static_cast<uint64>(BotMessageType::CLAIM_INTERRUPT) << 56);
ActiveClaim claim;
claim.type = BotMessageType::CLAIM_INTERRUPT;
claim.targetGuid = targetGuid;
claim.spellOrAuraId = spellId;
claim.status = status;
claim.submitTime = std::chrono::steady_clock::now();
_activeClaims[claimKey] = claim;
_metrics.interruptClaimsSubmitted++;
return status != ClaimStatus::REJECTED;
}
bool CombatCoordinationIntegrator::ShouldInterrupt(ObjectGuid& targetGuid, uint32& spellId) const
{
// Find active interrupt claim that was accepted
for (auto const& [key, claim] : _activeClaims)
{
if (claim.type == BotMessageType::CLAIM_INTERRUPT &&
claim.status == ClaimStatus::ACCEPTED)
{
targetGuid = claim.targetGuid;
spellId = GetInterruptSpell();
return spellId != 0;
}
}
return false;
}
void CombatCoordinationIntegrator::OnInterruptExecuted(ObjectGuid targetGuid, uint32 spellId, bool success)
{
// Remove the claim
uint64 claimKey = targetGuid.GetRawValue() ^ (static_cast<uint64>(BotMessageType::CLAIM_INTERRUPT) << 56);
_activeClaims.erase(claimKey);
// Announce via message bus
if (_bot && !_groupGuid.IsEmpty())
{
BotMessage msg;
msg.type = BotMessageType::ANNOUNCE_CD_USAGE;
msg.senderGuid = _bot->GetGUID();
msg.groupGuid = _groupGuid;
msg.targetGuid = targetGuid;
msg.spellId = spellId;
msg.timestamp = std::chrono::steady_clock::now();
BotMessageBus::Instance().Publish(msg);
}
}
// ============================================================================
// DISPEL COORDINATION (S3.2 - GAP 2 Fix)
// ============================================================================
bool CombatCoordinationIntegrator::RequestDispel(ObjectGuid targetGuid, uint32 auraId, ClaimPriority priority)
{
if (!_config.enableDispelClaims || !_bot || _groupGuid.IsEmpty())
return false;
BotMessage msg = BotMessage::ClaimDispel(
_bot->GetGUID(),
_groupGuid,
targetGuid,
auraId,
priority
);
auto callback = [this](BotMessage const& m, ClaimStatus s) {
OnClaimResolved(m, s);
};
ClaimStatus status = BotMessageBus::Instance().SubmitClaim(msg, callback);
uint64 claimKey = targetGuid.GetRawValue() ^ (static_cast<uint64>(BotMessageType::CLAIM_DISPEL) << 56) ^ auraId;
ActiveClaim claim;
claim.type = BotMessageType::CLAIM_DISPEL;
claim.targetGuid = targetGuid;
claim.spellOrAuraId = auraId;
claim.status = status;
claim.submitTime = std::chrono::steady_clock::now();
_activeClaims[claimKey] = claim;
_metrics.dispelClaimsSubmitted++;
return status != ClaimStatus::REJECTED;
}
bool CombatCoordinationIntegrator::ShouldDispel(ObjectGuid& targetGuid, uint32& auraId) const
{
for (auto const& [key, claim] : _activeClaims)
{
if (claim.type == BotMessageType::CLAIM_DISPEL &&
claim.status == ClaimStatus::ACCEPTED)
{
targetGuid = claim.targetGuid;
auraId = claim.spellOrAuraId;
return true;
}
}
return false;
}
void CombatCoordinationIntegrator::OnDispelExecuted(ObjectGuid targetGuid, uint32 auraId, bool success)
{
uint64 claimKey = targetGuid.GetRawValue() ^ (static_cast<uint64>(BotMessageType::CLAIM_DISPEL) << 56) ^ auraId;
_activeClaims.erase(claimKey);
}
float CombatCoordinationIntegrator::CalculateDispelPriority(ObjectGuid targetGuid, uint32 auraId) const
{
if (!_bot)
return 0.0f;
float priority = 0.0f;
// Base priority from aura danger
if (_dispelCoord)
{
if (_dispelCoord->ShouldDispel(auraId))
priority += 50.0f;
}
// Can dispel type bonus (+100)
SpellInfo const* auraSpell = sSpellMgr->GetSpellInfo(auraId, DIFFICULTY_NONE);
if (auraSpell && CanDispelType(auraSpell->Dispel))
priority += 100.0f;
// Distance bonus (+50 if in range)
float distance = GetDistanceToTarget(targetGuid);
if (distance < 40.0f)
priority += 50.0f;
// CD available bonus (+200 if not on CD)
// This would check dispel cooldown
// Healer bonus (+30)
if (_bot && IsPlayerHealer(_bot))
priority += 30.0f;
// GCD free bonus (+20)
// Would check GCD state
return priority;
}
// ============================================================================
// EXTERNAL DEFENSIVE CD COORDINATION (S3.3 - GAP 3 Fix)
// ============================================================================
bool CombatCoordinationIntegrator::RequestExternalDefensive(ObjectGuid targetGuid, DangerLevel danger)
{
if (!_config.enableDefensiveClaims || !_bot || _groupGuid.IsEmpty())
return false;
// Don't request if target is already protected
if (IsTargetProtected(targetGuid))
return false;
// Determine appropriate CD tier for danger level
ExternalCDTier maxTier = GetAppropriateCDTier(danger);
// Get available CDs we can provide
std::vector<uint32> availableCDs = GetAvailableExternalCDs(maxTier);
if (availableCDs.empty())
return false;
// Select best CD for the situation
uint32 cdSpellId = availableCDs[0]; // Simple: use first available
ClaimPriority priority = ClaimPriority::MEDIUM;
if (danger >= DangerLevel::CRITICAL)
priority = ClaimPriority::CRITICAL;
else if (danger >= DangerLevel::HIGH)
priority = ClaimPriority::HIGH;
BotMessage msg = BotMessage::ClaimDefensiveCD(
_bot->GetGUID(),
_groupGuid,
targetGuid,
cdSpellId,
priority
);
auto callback = [this](BotMessage const& m, ClaimStatus s) {
OnClaimResolved(m, s);
};
ClaimStatus status = BotMessageBus::Instance().SubmitClaim(msg, callback);
uint64 claimKey = targetGuid.GetRawValue() ^ (static_cast<uint64>(BotMessageType::CLAIM_DEFENSIVE_CD) << 56);
ActiveClaim claim;
claim.type = BotMessageType::CLAIM_DEFENSIVE_CD;
claim.targetGuid = targetGuid;
claim.spellOrAuraId = cdSpellId;
claim.status = status;
claim.submitTime = std::chrono::steady_clock::now();
_activeClaims[claimKey] = claim;
_metrics.defensiveClaimsSubmitted++;
return status != ClaimStatus::REJECTED;
}
bool CombatCoordinationIntegrator::ShouldProvideExternalCD(ObjectGuid& targetGuid, uint32& spellId) const
{
for (auto const& [key, claim] : _activeClaims)
{
if (claim.type == BotMessageType::CLAIM_DEFENSIVE_CD &&
claim.status == ClaimStatus::ACCEPTED)
{
targetGuid = claim.targetGuid;
spellId = claim.spellOrAuraId;
return true;
}
}
return false;
}
void CombatCoordinationIntegrator::OnExternalCDUsed(ObjectGuid targetGuid, uint32 spellId)
{
// Remove claim
uint64 claimKey = targetGuid.GetRawValue() ^ (static_cast<uint64>(BotMessageType::CLAIM_DEFENSIVE_CD) << 56);
_activeClaims.erase(claimKey);
// Create protection window (GAP 3 fix)
auto& db = ExternalCDInfo::GetDatabase();
auto it = db.find(spellId);
ProtectionWindow window;
window.targetGuid = targetGuid;
window.protectorGuid = _bot ? _bot->GetGUID() : ObjectGuid::Empty;
window.spellId = spellId;
window.tier = (it != db.end()) ? it->second.tier : ExternalCDTier::TIER_MINOR;
window.startTime = std::chrono::steady_clock::now();
window.endTime = window.startTime + std::chrono::milliseconds(_config.protectionWindowMs);
_protectionWindows.push_back(window);
_metrics.protectionWindowsCreated++;
// Announce CD usage
if (_bot && !_groupGuid.IsEmpty())
{
BotMessage msg;
msg.type = BotMessageType::ANNOUNCE_CD_USAGE;
msg.senderGuid = _bot->GetGUID();
msg.groupGuid = _groupGuid;
msg.targetGuid = targetGuid;
msg.spellId = spellId;
msg.timestamp = std::chrono::steady_clock::now();
BotMessageBus::Instance().Publish(msg);
}
}
bool CombatCoordinationIntegrator::IsTargetProtected(ObjectGuid targetGuid) const
{
for (auto const& window : _protectionWindows)
{
if (window.targetGuid == targetGuid && window.IsActive())
return true;
}
return false;
}
uint32 CombatCoordinationIntegrator::GetProtectionRemaining(ObjectGuid targetGuid) const
{
for (auto const& window : _protectionWindows)
{
if (window.targetGuid == targetGuid && window.IsActive())
return window.GetRemainingMs();
}
return 0;
}
DangerLevel CombatCoordinationIntegrator::AssessDanger(ObjectGuid targetGuid) const
{
Unit* target = ObjectAccessor::GetUnit(*_bot, targetGuid);
if (!target || !target->IsAlive())
return DangerLevel::NONE;
float healthPct = target->GetHealthPct() / 100.0f;
if (healthPct < _config.dangerHealthCritical)
return DangerLevel::CRITICAL;
if (healthPct < _config.dangerHealthHigh)
return DangerLevel::HIGH;
if (healthPct < _config.dangerHealthModerate)
return DangerLevel::MODERATE;
return DangerLevel::NONE;
}
ExternalCDTier CombatCoordinationIntegrator::GetAppropriateCDTier(DangerLevel danger) const
{
// Per GAP 3: Don't waste major CDs on moderate danger
switch (danger)
{
case DangerLevel::CRITICAL:
case DangerLevel::PRE_DANGER:
return ExternalCDTier::TIER_MAJOR;
case DangerLevel::HIGH:
return ExternalCDTier::TIER_MODERATE;
case DangerLevel::MODERATE:
return ExternalCDTier::TIER_MINOR;
default:
return ExternalCDTier::TIER_MINOR;
}
}
// ============================================================================
// CC COORDINATION (S3.6)
// ============================================================================
bool CombatCoordinationIntegrator::RequestCC(ObjectGuid targetGuid, uint32 spellId, ClaimPriority priority)
{
if (!_config.enableCCClaims || !_bot || _groupGuid.IsEmpty())
return false;
BotMessage msg = BotMessage::ClaimCC(
_bot->GetGUID(),
_groupGuid,
targetGuid,
spellId,
priority
);
auto callback = [this](BotMessage const& m, ClaimStatus s) {
OnClaimResolved(m, s);
};
ClaimStatus status = BotMessageBus::Instance().SubmitClaim(msg, callback);
uint64 claimKey = targetGuid.GetRawValue() ^ (static_cast<uint64>(BotMessageType::CLAIM_CC) << 56);
ActiveClaim claim;
claim.type = BotMessageType::CLAIM_CC;
claim.targetGuid = targetGuid;
claim.spellOrAuraId = spellId;
claim.status = status;
claim.submitTime = std::chrono::steady_clock::now();
_activeClaims[claimKey] = claim;
_metrics.ccClaimsSubmitted++;
return status != ClaimStatus::REJECTED;
}
bool CombatCoordinationIntegrator::ShouldCC(ObjectGuid& targetGuid, uint32& spellId) const
{
for (auto const& [key, claim] : _activeClaims)
{
if (claim.type == BotMessageType::CLAIM_CC &&
claim.status == ClaimStatus::ACCEPTED)
{
targetGuid = claim.targetGuid;
spellId = claim.spellOrAuraId;
return true;
}
}
return false;
}
void CombatCoordinationIntegrator::OnCCExecuted(ObjectGuid targetGuid, uint32 spellId, bool success)
{
uint64 claimKey = targetGuid.GetRawValue() ^ (static_cast<uint64>(BotMessageType::CLAIM_CC) << 56);
_activeClaims.erase(claimKey);
// Update CC manager's DR tracking
if (_ccMgr && success)
{
_ccMgr->OnCCApplied(targetGuid, spellId);
}
}
// ============================================================================
// CLAIM CALLBACKS
// ============================================================================
void CombatCoordinationIntegrator::OnClaimResolved(BotMessage const& message, ClaimStatus status)
{
uint64 claimKey = 0;
switch (message.type)
{
case BotMessageType::CLAIM_INTERRUPT:
claimKey = message.targetGuid.GetRawValue() ^ (static_cast<uint64>(BotMessageType::CLAIM_INTERRUPT) << 56);
if (status == ClaimStatus::ACCEPTED)
_metrics.interruptClaimsWon++;
else
_metrics.interruptClaimsLost++;
break;
case BotMessageType::CLAIM_DISPEL:
claimKey = message.targetGuid.GetRawValue() ^ (static_cast<uint64>(BotMessageType::CLAIM_DISPEL) << 56) ^ message.auraId;
if (status == ClaimStatus::ACCEPTED)
_metrics.dispelClaimsWon++;
else
_metrics.dispelClaimsLost++;
break;
case BotMessageType::CLAIM_DEFENSIVE_CD:
claimKey = message.targetGuid.GetRawValue() ^ (static_cast<uint64>(BotMessageType::CLAIM_DEFENSIVE_CD) << 56);
if (status == ClaimStatus::ACCEPTED)
_metrics.defensiveClaimsWon++;
else
_metrics.defensiveClaimsLost++;
break;
case BotMessageType::CLAIM_CC:
claimKey = message.targetGuid.GetRawValue() ^ (static_cast<uint64>(BotMessageType::CLAIM_CC) << 56);
if (status == ClaimStatus::ACCEPTED)
_metrics.ccClaimsWon++;
else
_metrics.ccClaimsLost++;
break;
default:
return;
}
// Update claim status
auto it = _activeClaims.find(claimKey);
if (it != _activeClaims.end())
{
it->second.status = status;
it->second.resolveTime = std::chrono::steady_clock::now();
}
}
// ============================================================================
// INTERNAL METHODS
// ============================================================================
void CombatCoordinationIntegrator::SubscribeToMessageBus()
{
if (_subscribed || _groupGuid.IsEmpty() || !_bot)
return;
uint8 role = _ai ? _ai->GetRole() : 0;
uint8 subgroup = 0;
if (_bot->GetGroup())
{
Group::MemberSlot const* slot = _bot->GetGroup()->GetMemberSlot(_bot->GetGUID());
if (slot)
subgroup = slot->group;
}
_subscriptionId = BotMessageBus::Instance().Subscribe(
_groupGuid,
_bot->GetGUID(),
role,
subgroup,
[this](BotMessage const& msg) { OnBotMessage(msg); }
);
_subscribed = true;
}
void CombatCoordinationIntegrator::UnsubscribeFromMessageBus()
{
if (!_subscribed)
return;
BotMessageBus::Instance().Unsubscribe(_groupGuid, _bot->GetGUID());
_subscribed = false;
_subscriptionId = 0;
}
void CombatCoordinationIntegrator::OnBotMessage(BotMessage const& message)
{
// Handle incoming messages from other bots
switch (message.type)
{
case BotMessageType::ANNOUNCE_CD_USAGE:
// Track that another bot used a CD - could inform our CD planning
break;
case BotMessageType::REQUEST_HEAL:
// Could trigger external defensive if we have one
break;
case BotMessageType::REQUEST_EXTERNAL_CD:
// Another bot is requesting external CD
{
DangerLevel danger = AssessDanger(message.targetGuid);
if (danger >= DangerLevel::HIGH)
{
RequestExternalDefensive(message.targetGuid, danger);
}
}
break;
default:
break;
}
}
void CombatCoordinationIntegrator::UpdateProtectionWindows()
{
auto it = _protectionWindows.begin();
while (it != _protectionWindows.end())
{
if (!it->IsActive())
{
it = _protectionWindows.erase(it);
}
else
{
++it;
}
}
}
std::vector<uint32> CombatCoordinationIntegrator::GetAvailableExternalCDs(ExternalCDTier maxTier) const
{
std::vector<uint32> result;
if (!_bot)
return result;
auto const& db = ExternalCDInfo::GetDatabase();
for (auto const& [spellId, info] : db)
{
// Check tier
if (static_cast<uint8>(info.tier) > static_cast<uint8>(maxTier))
continue;
// Check if bot knows spell and it's not on cooldown
if (_bot->HasSpell(spellId) && !_bot->GetSpellHistory()->HasCooldown(spellId))
{
result.push_back(spellId);
}
}
return result;
}
bool CombatCoordinationIntegrator::CanDispelType(uint32 dispelMask) const
{
if (!_bot)
return false;
Classes botClass = static_cast<Classes>(_bot->GetClass());
// Check by class what dispel types are available
switch (botClass)
{
case CLASS_PRIEST:
// Priests can dispel Magic, Disease
return (dispelMask == DISPEL_MAGIC) || (dispelMask == DISPEL_DISEASE);
case CLASS_PALADIN:
// Paladins can dispel Disease, Poison, Magic (if Holy)
return (dispelMask == DISPEL_DISEASE) || (dispelMask == DISPEL_POISON) || (dispelMask == DISPEL_MAGIC);
case CLASS_DRUID:
// Druids can dispel Curse, Poison, Magic (if Resto)
return (dispelMask == DISPEL_CURSE) || (dispelMask == DISPEL_POISON) || (dispelMask == DISPEL_MAGIC);
case CLASS_SHAMAN:
// Shamans can dispel Curse, Magic (if Resto)
return (dispelMask == DISPEL_CURSE) || (dispelMask == DISPEL_MAGIC);
case CLASS_MAGE:
// Mages can dispel Curse
return (dispelMask == DISPEL_CURSE);
case CLASS_MONK:
// Monks can dispel Disease, Poison, Magic (if Mistweaver)
return (dispelMask == DISPEL_DISEASE) || (dispelMask == DISPEL_POISON) || (dispelMask == DISPEL_MAGIC);
case CLASS_EVOKER:
// Evokers can dispel Magic, Poison (Preservation)
return (dispelMask == DISPEL_MAGIC) || (dispelMask == DISPEL_POISON);
default:
return false;
}
}
uint32 CombatCoordinationIntegrator::GetInterruptSpell() const
{
if (!_bot)
return 0;
Classes botClass = static_cast<Classes>(_bot->GetClass());
// Return primary interrupt spell by class
switch (botClass)
{
case CLASS_WARRIOR: return 6552; // Pummel
case CLASS_PALADIN: return 96231; // Rebuke
case CLASS_HUNTER: return 147362; // Counter Shot
case CLASS_ROGUE: return 1766; // Kick
case CLASS_PRIEST: return 0; // No interrupt (Silence is 15487 but long CD)
case CLASS_DEATH_KNIGHT: return 47528; // Mind Freeze
case CLASS_SHAMAN: return 57994; // Wind Shear
case CLASS_MAGE: return 2139; // Counterspell
case CLASS_WARLOCK: return 119910; // Spell Lock (pet)
case CLASS_MONK: return 116705; // Spear Hand Strike
case CLASS_DRUID: return 106839; // Skull Bash
case CLASS_DEMON_HUNTER: return 183752; // Disrupt
case CLASS_EVOKER: return 351338; // Quell
default: return 0;
}
}
float CombatCoordinationIntegrator::GetDistanceToTarget(ObjectGuid targetGuid) const
{
if (!_bot)
return 999.0f;
Unit* target = ObjectAccessor::GetUnit(*_bot, targetGuid);
if (!target)
return 999.0f;
return _bot->GetDistance(target);
}
} // namespace Playerbot
@@ -0,0 +1,427 @@
/*
* Copyright (C) 2024 TrinityCore <https://www.trinitycore.org/>
*
* Sprint 3: Combat Coordination Integration Layer
* Bridges existing combat managers with BotMessageBus claim system
*/
#ifndef PLAYERBOT_COMBAT_COORDINATION_INTEGRATOR_H
#define PLAYERBOT_COMBAT_COORDINATION_INTEGRATOR_H
#include "Define.h"
#include "ObjectGuid.h"
#include "AI/Coordination/Messaging/BotMessageBus.h"
#include "AI/Coordination/Messaging/ClaimResolver.h"
#include "Cooldown/CooldownEventBus.h"
#include "Cooldown/MajorCooldownTracker.h"
#include <memory>
#include <functional>
#include <unordered_map>
#include <chrono>
class Player;
class Group;
class Unit;
namespace Playerbot
{
class BotAI;
class InterruptCoordinatorFixed;
class DefensiveBehaviorManager;
class CrowdControlManager;
class DispelCoordinator;
/**
* @brief External defensive cooldown tiers for coordination
*
* Per GAP 3: Major CDs should not be wasted on moderate danger
*/
enum class ExternalCDTier : uint8
{
TIER_MAJOR, // Guardian Spirit, Pain Suppression, Ironbark, Life Cocoon
TIER_MODERATE, // Blessing of Sacrifice, Vigilance
TIER_MINOR, // Power Word: Barrier (group), Darkness, AMZ
TIER_RAID, // Rallying Cry, Spirit Link, Devotion Aura, Healing Tide
};
/**
* @brief Danger level for external CD requests
*/
enum class DangerLevel : uint8
{
NONE = 0, // No danger
MODERATE = 1, // Sustained damage, manageable
HIGH = 2, // Spike incoming, need protection
CRITICAL = 3, // Death imminent without intervention
PRE_DANGER = 4, // Boss ability incoming (predictive)
};
/**
* @brief Tracked protection window for a target
*
* Per GAP 3: 6s window where target is "protected" after external CD
*/
struct ProtectionWindow
{
ObjectGuid targetGuid;
ObjectGuid protectorGuid;
uint32 spellId = 0;
ExternalCDTier tier = ExternalCDTier::TIER_MINOR;
std::chrono::steady_clock::time_point startTime;
std::chrono::steady_clock::time_point endTime;
bool IsActive() const
{
return std::chrono::steady_clock::now() < endTime;
}
uint32 GetRemainingMs() const
{
auto now = std::chrono::steady_clock::now();
if (now >= endTime) return 0;
return static_cast<uint32>(
std::chrono::duration_cast<std::chrono::milliseconds>(endTime - now).count());
}
};
/**
* @brief External CD database entry
*/
struct ExternalCDInfo
{
uint32 spellId;
ExternalCDTier tier;
uint32 cooldownMs;
uint32 durationMs;
bool isGroupwide; // AMZ, Barrier, Rallying Cry
bool requiresTarget; // Pain Suppression, Guardian Spirit
static const std::unordered_map<uint32, ExternalCDInfo>& GetDatabase();
};
/**
* @class CombatCoordinationIntegrator
* @brief Integrates combat managers with BotMessageBus claim system
*
* Sprint 3 Core Component:
* - Routes interrupt claims through BotMessageBus
* - Routes dispel claims through BotMessageBus
* - Routes external defensive CD claims
* - Manages protection windows (GAP 3 fix)
* - Coordinates CC claims with DR awareness
*
* Performance: <0.05ms per Update() per bot
*/
class TC_GAME_API CombatCoordinationIntegrator
{
public:
explicit CombatCoordinationIntegrator(BotAI* ai);
~CombatCoordinationIntegrator();
// ========================================================================
// LIFECYCLE
// ========================================================================
/**
* Initialize with references to existing managers
*/
void Initialize(
InterruptCoordinatorFixed* interruptCoord,
DefensiveBehaviorManager* defensiveMgr,
CrowdControlManager* ccMgr,
DispelCoordinator* dispelCoord
);
/**
* Shutdown and cleanup subscriptions
*/
void Shutdown();
/**
* Main update loop - processes claims and coordinates actions
* @param diff Milliseconds since last update
*/
void Update(uint32 diff);
// ========================================================================
// INTERRUPT COORDINATION (S3.4)
// ========================================================================
/**
* Request to interrupt a spell via claim system
* @param targetGuid Caster to interrupt
* @param spellId Spell being cast
* @param priority Interrupt priority
* @return true if claim was submitted
*/
bool RequestInterrupt(ObjectGuid targetGuid, uint32 spellId, ClaimPriority priority);
/**
* Check if this bot should interrupt (has active claim)
* @param targetGuid Out: target to interrupt
* @param spellId Out: spell to use
* @return true if this bot should interrupt now
*/
bool ShouldInterrupt(ObjectGuid& targetGuid, uint32& spellId) const;
/**
* Report interrupt result
*/
void OnInterruptExecuted(ObjectGuid targetGuid, uint32 spellId, bool success);
// ========================================================================
// DISPEL COORDINATION (S3.2 - GAP 2 Fix)
// ========================================================================
/**
* Request to dispel a debuff via claim system
* @param targetGuid Friendly with debuff
* @param auraId Debuff to dispel
* @param priority Dispel urgency
* @return true if claim was submitted
*/
bool RequestDispel(ObjectGuid targetGuid, uint32 auraId, ClaimPriority priority);
/**
* Check if this bot should dispel (has active claim)
* @param targetGuid Out: target to dispel
* @param auraId Out: aura to dispel
* @return true if this bot should dispel now
*/
bool ShouldDispel(ObjectGuid& targetGuid, uint32& auraId) const;
/**
* Report dispel result
*/
void OnDispelExecuted(ObjectGuid targetGuid, uint32 auraId, bool success);
/**
* Calculate dispel priority score
* Per S3.2: Priority = base + canDispelType + distance + cdStatus + isHealer + gcdFree
*/
float CalculateDispelPriority(ObjectGuid targetGuid, uint32 auraId) const;
// ========================================================================
// EXTERNAL DEFENSIVE CD COORDINATION (S3.3 - GAP 3 Fix)
// ========================================================================
/**
* Request external defensive CD via claim system
* @param targetGuid Friendly needing protection
* @param danger Current danger level
* @return true if claim was submitted
*/
bool RequestExternalDefensive(ObjectGuid targetGuid, DangerLevel danger);
/**
* Check if this bot should provide external CD
* @param targetGuid Out: who to protect
* @param spellId Out: which CD to use
* @return true if this bot should cast external CD
*/
bool ShouldProvideExternalCD(ObjectGuid& targetGuid, uint32& spellId) const;
/**
* Report external CD usage
*/
void OnExternalCDUsed(ObjectGuid targetGuid, uint32 spellId);
/**
* Check if target is currently protected (within danger window)
* @param targetGuid Target to check
* @return true if target has active protection
*/
bool IsTargetProtected(ObjectGuid targetGuid) const;
/**
* Get protection window remaining for target
* @return Milliseconds remaining or 0 if not protected
*/
uint32 GetProtectionRemaining(ObjectGuid targetGuid) const;
/**
* Assess danger level for a target
* @param targetGuid Target to assess
* @return Current danger level
*/
DangerLevel AssessDanger(ObjectGuid targetGuid) const;
/**
* Get appropriate CD tier for danger level
* Per GAP 3: Don't waste major CDs on moderate danger
*/
ExternalCDTier GetAppropriateCDTier(DangerLevel danger) const;
// ========================================================================
// CC COORDINATION (S3.6)
// ========================================================================
/**
* Request to CC a target via claim system
* @param targetGuid Enemy to CC
* @param spellId CC spell to use
* @param priority CC priority
* @return true if claim was submitted
*/
bool RequestCC(ObjectGuid targetGuid, uint32 spellId, ClaimPriority priority);
/**
* Check if this bot should CC (has active claim)
* @param targetGuid Out: target to CC
* @param spellId Out: CC spell to use
* @return true if this bot should CC now
*/
bool ShouldCC(ObjectGuid& targetGuid, uint32& spellId) const;
/**
* Report CC result
*/
void OnCCExecuted(ObjectGuid targetGuid, uint32 spellId, bool success);
// ========================================================================
// CLAIM CALLBACKS
// ========================================================================
/**
* Called when a claim we submitted is resolved
*/
void OnClaimResolved(BotMessage const& message, ClaimStatus status);
// ========================================================================
// CONFIGURATION
// ========================================================================
struct Config
{
uint32 protectionWindowMs = 6000; // GAP 3: 6 second danger window
uint32 claimTimeoutMs = 200; // First-claim-wins timeout
float dangerHealthCritical = 0.30f; // < 30% = CRITICAL
float dangerHealthHigh = 0.50f; // < 50% = HIGH
float dangerHealthModerate = 0.80f; // < 80% = MODERATE
uint32 incomingDPSThreshold = 5; // % max HP/sec for danger
bool enableInterruptClaims = true;
bool enableDispelClaims = true;
bool enableDefensiveClaims = true;
bool enableCCClaims = true;
};
void SetConfig(const Config& config) { _config = config; }
const Config& GetConfig() const { return _config; }
// ========================================================================
// METRICS
// ========================================================================
struct Metrics
{
uint32 interruptClaimsSubmitted = 0;
uint32 interruptClaimsWon = 0;
uint32 interruptClaimsLost = 0;
uint32 dispelClaimsSubmitted = 0;
uint32 dispelClaimsWon = 0;
uint32 dispelClaimsLost = 0;
uint32 defensiveClaimsSubmitted = 0;
uint32 defensiveClaimsWon = 0;
uint32 defensiveClaimsLost = 0;
uint32 ccClaimsSubmitted = 0;
uint32 ccClaimsWon = 0;
uint32 ccClaimsLost = 0;
uint32 protectionWindowsCreated = 0;
};
const Metrics& GetMetrics() const { return _metrics; }
void ResetMetrics() { _metrics = Metrics(); }
private:
// ========================================================================
// INTERNAL METHODS
// ========================================================================
/**
* Subscribe to BotMessageBus for this bot's group
*/
void SubscribeToMessageBus();
/**
* Unsubscribe from BotMessageBus
*/
void UnsubscribeFromMessageBus();
/**
* Handle incoming bot messages
*/
void OnBotMessage(BotMessage const& message);
/**
* Update protection windows (expire old ones)
*/
void UpdateProtectionWindows();
/**
* Get available external CDs this bot can provide
*/
std::vector<uint32> GetAvailableExternalCDs(ExternalCDTier maxTier) const;
/**
* Check if bot can dispel the given type
*/
bool CanDispelType(uint32 dispelMask) const;
/**
* Get bot's interrupt spell
*/
uint32 GetInterruptSpell() const;
/**
* Calculate distance to target
*/
float GetDistanceToTarget(ObjectGuid targetGuid) const;
// ========================================================================
// MEMBER VARIABLES
// ========================================================================
BotAI* _ai;
Player* _bot;
ObjectGuid _groupGuid;
// Manager references (owned by BotAI, not us)
InterruptCoordinatorFixed* _interruptCoord = nullptr;
DefensiveBehaviorManager* _defensiveMgr = nullptr;
CrowdControlManager* _ccMgr = nullptr;
DispelCoordinator* _dispelCoord = nullptr;
// Active claims for this bot
struct ActiveClaim
{
BotMessageType type;
ObjectGuid targetGuid;
uint32 spellOrAuraId = 0;
ClaimStatus status = ClaimStatus::PENDING;
std::chrono::steady_clock::time_point submitTime;
std::chrono::steady_clock::time_point resolveTime;
};
std::unordered_map<uint64, ActiveClaim> _activeClaims; // key: hash of target+type
// Protection windows (GAP 3 fix)
std::vector<ProtectionWindow> _protectionWindows;
// Configuration
Config _config;
// Metrics
Metrics _metrics;
// Subscription state
bool _subscribed = false;
uint32 _subscriptionId = 0;
// Update timing
uint32 _lastUpdate = 0;
static constexpr uint32 UPDATE_INTERVAL_MS = 100;
};
} // namespace Playerbot
#endif // PLAYERBOT_COMBAT_COORDINATION_INTEGRATOR_H
@@ -198,6 +198,54 @@ struct BotMessage
return msg;
}
static BotMessage ClaimCC(ObjectGuid sender, ObjectGuid group, ObjectGuid target,
uint32 spellId, ClaimPriority priority)
{
BotMessage msg{};
msg.type = BotMessageType::CLAIM_CC;
msg.scope = MessageScope::GROUP_BROADCAST;
msg.senderGuid = sender;
msg.groupGuid = group;
msg.targetGuid = target;
msg.spellId = spellId;
msg.claimPriority = priority;
msg.claimStatus = ClaimStatus::PENDING;
msg.timestamp = std::chrono::steady_clock::now();
msg.expiryTime = msg.timestamp + std::chrono::milliseconds(200);
return msg;
}
static BotMessage ClaimSoak(ObjectGuid sender, ObjectGuid group, ObjectGuid target,
ClaimPriority priority)
{
BotMessage msg{};
msg.type = BotMessageType::CLAIM_SOAK;
msg.scope = MessageScope::GROUP_BROADCAST;
msg.senderGuid = sender;
msg.groupGuid = group;
msg.targetGuid = target;
msg.claimPriority = priority;
msg.claimStatus = ClaimStatus::PENDING;
msg.timestamp = std::chrono::steady_clock::now();
msg.expiryTime = msg.timestamp + std::chrono::milliseconds(200);
return msg;
}
static BotMessage RequestExternalCD(ObjectGuid sender, ObjectGuid group, ObjectGuid target, float urgency)
{
BotMessage msg{};
msg.type = BotMessageType::REQUEST_EXTERNAL_CD;
msg.scope = MessageScope::ROLE_BROADCAST;
msg.senderGuid = sender;
msg.groupGuid = group;
msg.targetGuid = target;
msg.targetRole = 1; // Healers primarily provide externals
msg.value = urgency;
msg.timestamp = std::chrono::steady_clock::now();
msg.expiryTime = msg.timestamp + std::chrono::milliseconds(2000);
return msg;
}
static BotMessage AnnounceCDUsage(ObjectGuid sender, ObjectGuid group,
uint32 spellId, uint32 durationMs)
{
+2
View File
@@ -560,6 +560,8 @@ set(PLAYERBOT_COMBAT_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/AI/Combat/MechanicAwareness.h
${CMAKE_CURRENT_SOURCE_DIR}/AI/Combat/InterruptCoordinator.cpp
${CMAKE_CURRENT_SOURCE_DIR}/AI/Combat/InterruptCoordinator.h
${CMAKE_CURRENT_SOURCE_DIR}/AI/Combat/CombatCoordinationIntegrator.cpp
${CMAKE_CURRENT_SOURCE_DIR}/AI/Combat/CombatCoordinationIntegrator.h
${CMAKE_CURRENT_SOURCE_DIR}/AI/Combat/InterruptAwareness.cpp
${CMAKE_CURRENT_SOURCE_DIR}/AI/Combat/InterruptAwareness.h
${CMAKE_CURRENT_SOURCE_DIR}/AI/Combat/InterruptManager.cpp
@@ -68,10 +68,18 @@ GameSystemsManager::~GameSystemsManager()
// (managers may call UnsubscribeAll() in their destructors)
// 1. High-level systems first
_combatCoordinationIntegrator.reset(); // Sprint 3: Must shutdown before managers it references
_combatStateManager.reset();
_deathRecoveryManager.reset();
_unifiedMovementCoordinator.reset();
// Sprint 3: Combat Behaviors managers
_aoeDecisionManager.reset();
_cooldownStackingOptimizer.reset();
_defensiveBehaviorManager.reset();
_dispelCoordinator.reset();
_interruptRotationManager.reset();
// 2. Game system managers
_tradeManager.reset();
_gatheringManager.reset();
@@ -208,6 +216,28 @@ void GameSystemsManager::Initialize(Player* bot)
// Combat state manager
_combatStateManager = std::make_unique<CombatStateManager>(_bot, _botAI);
// ========================================================================
// SPRINT 3: COMBAT COORDINATION - Essential for group coordination
// ========================================================================
// Combat Behaviors managers - essential for group combat
_aoeDecisionManager = std::make_unique<AoEDecisionManager>(_botAI);
_cooldownStackingOptimizer = std::make_unique<CooldownStackingOptimizer>(_botAI);
_defensiveBehaviorManager = std::make_unique<DefensiveBehaviorManager>(_botAI);
_dispelCoordinator = std::make_unique<DispelCoordinator>(_botAI);
_interruptRotationManager = std::make_unique<InterruptRotationManager>(_botAI);
// Combat Coordination Integrator - bridges managers with BotMessageBus claim system
_combatCoordinationIntegrator = std::make_unique<CombatCoordinationIntegrator>(_botAI);
// Initialize CombatCoordinationIntegrator with references to combat managers
_combatCoordinationIntegrator->Initialize(
nullptr, // InterruptCoordinatorFixed (not yet migrated)
_defensiveBehaviorManager.get(),
nullptr, // CrowdControlManager (not yet migrated)
_dispelCoordinator.get()
);
// Manager creation complete - no logging to avoid GetName() during init
// ========================================================================
@@ -676,6 +706,69 @@ void GameSystemsManager::UpdateManagers(uint32 diff)
_pvpCombatAI->Update(diff);
}
// ========================================================================
// SPRINT 3: COMBAT COORDINATION - Essential for all bots in groups
// Updates claim system, dispel coordination, interrupt rotation, etc.
// ========================================================================
// Combat Coordination Integrator - bridges to BotMessageBus claim system
// 100ms throttle - fast enough for responsive coordination
_combatCoordTimer += diff;
if (_combatCoordTimer >= 100)
{
_combatCoordTimer = 0;
if (_combatCoordinationIntegrator)
_combatCoordinationIntegrator->Update(diff);
}
// Combat Behaviors managers - essential for group combat
// 200ms throttle - responsive but not every frame
// DispelCoordinator - handles dispel rotation and claims (GAP 2 fix)
_dispelTimer += diff;
if (_dispelTimer >= 200)
{
_dispelTimer = 0;
if (_dispelCoordinator)
_dispelCoordinator->Update(diff);
}
// InterruptRotationManager - handles interrupt assignment and rotation
_interruptTimer += diff;
if (_interruptTimer >= 100) // Interrupts need faster response
{
_interruptTimer = 0;
if (_interruptRotationManager)
_interruptRotationManager->Update(diff);
}
// AoEDecisionManager - target clustering decisions (500ms throttle)
_aoeTimer += diff;
if (_aoeTimer >= 500)
{
_aoeTimer = 0;
if (_aoeDecisionManager)
_aoeDecisionManager->Update(diff);
}
// CooldownStackingOptimizer - optimal CD stacking (500ms throttle)
_cdStackTimer += diff;
if (_cdStackTimer >= 500)
{
_cdStackTimer = 0;
if (_cooldownStackingOptimizer)
_cooldownStackingOptimizer->Update(diff);
}
// DefensiveBehaviorManager - external CD coordination (200ms throttle)
_defenseTimer += diff;
if (_defenseTimer >= 200)
{
_defenseTimer = 0;
if (_defensiveBehaviorManager)
_defensiveBehaviorManager->Update(diff);
}
// ========================================================================
// MORE WORLD BOT ONLY - PROFESSION AUTOMATION - Check every 15 seconds
// ========================================================================
@@ -72,6 +72,16 @@
#include "AI/HybridAIController.h"
#include "BehaviorPriorityManager.h"
// Sprint 3: Combat Coordination
#include "AI/Combat/CombatCoordinationIntegrator.h"
// Combat Behaviors managers
#include "AI/CombatBehaviors/AoEDecisionManager.h"
#include "AI/CombatBehaviors/CooldownStackingOptimizer.h"
#include "AI/CombatBehaviors/DefensiveBehaviorManager.h"
#include "AI/CombatBehaviors/DispelCoordinator.h"
#include "AI/CombatBehaviors/InterruptRotationManager.h"
namespace Playerbot
{
@@ -223,6 +233,16 @@ public:
HybridAIController* GetHybridAI() const override { return _hybridAI.get(); }
BehaviorPriorityManager* GetPriorityManager() const override { return _priorityManager.get(); }
// Sprint 3: Combat Coordination
CombatCoordinationIntegrator* GetCombatCoordinationIntegrator() const override { return _combatCoordinationIntegrator.get(); }
// Combat Behaviors managers
AoEDecisionManager* GetAoEDecisionManager() const override { return _aoeDecisionManager.get(); }
CooldownStackingOptimizer* GetCooldownStackingOptimizer() const override { return _cooldownStackingOptimizer.get(); }
DefensiveBehaviorManager* GetDefensiveBehaviorManager() const override { return _defensiveBehaviorManager.get(); }
DispelCoordinator* GetDispelCoordinator() const override { return _dispelCoordinator.get(); }
InterruptRotationManager* GetInterruptRotationManager() const override { return _interruptRotationManager.get(); }
private:
// ========================================================================
// MANAGER INSTANCES - All 26 managers owned by facade
@@ -294,6 +314,16 @@ private:
// Behavior management
std::unique_ptr<BehaviorPriorityManager> _priorityManager;
// Sprint 3: Combat Coordination
std::unique_ptr<CombatCoordinationIntegrator> _combatCoordinationIntegrator;
// Combat Behaviors managers
std::unique_ptr<AoEDecisionManager> _aoeDecisionManager;
std::unique_ptr<CooldownStackingOptimizer> _cooldownStackingOptimizer;
std::unique_ptr<DefensiveBehaviorManager> _defensiveBehaviorManager;
std::unique_ptr<DispelCoordinator> _dispelCoordinator;
std::unique_ptr<InterruptRotationManager> _interruptRotationManager;
// ========================================================================
// INTERNAL STATE
// ========================================================================
@@ -321,6 +351,14 @@ private:
uint32 _professionBridgeTimer{0}; // 5000ms - selling/buying materials
uint32 _farmingUpdateTimer{0}; // 2000ms - farming coordination
// Sprint 3: Combat Coordination throttle timers
uint32 _combatCoordTimer{0}; // 100ms - responsive coordination
uint32 _dispelTimer{0}; // 200ms - dispel rotation
uint32 _interruptTimer{0}; // 100ms - fast interrupt response
uint32 _aoeTimer{0}; // 500ms - target clustering
uint32 _cdStackTimer{0}; // 500ms - cooldown optimization
uint32 _defenseTimer{0}; // 200ms - defensive coordination
// ========================================================================
// HELPER METHODS
// ========================================================================
@@ -75,6 +75,16 @@ class ManagerRegistry;
class HybridAIController;
class BehaviorPriorityManager;
// Combat Coordination (Sprint 3)
class CombatCoordinationIntegrator;
// Combat Behaviors managers
class AoEDecisionManager;
class CooldownStackingOptimizer;
class DefensiveBehaviorManager;
class DispelCoordinator;
class InterruptRotationManager;
namespace Advanced
{
class GroupCoordinator;
@@ -312,6 +322,56 @@ public:
* @return Non-owning pointer to BehaviorPriorityManager (owned by facade)
*/
virtual BehaviorPriorityManager* GetPriorityManager() const = 0;
// ========================================================================
// COMBAT COORDINATION (Sprint 3)
// ========================================================================
/**
* @brief Get combat coordination integrator
* Bridges combat managers with BotMessageBus claim system
* @return Non-owning pointer to CombatCoordinationIntegrator (owned by facade)
*/
virtual CombatCoordinationIntegrator* GetCombatCoordinationIntegrator() const = 0;
// ========================================================================
// COMBAT BEHAVIORS MANAGERS
// ========================================================================
/**
* @brief Get AoE decision manager
* Target clustering and DoT spread decisions
* @return Non-owning pointer to AoEDecisionManager (owned by facade)
*/
virtual AoEDecisionManager* GetAoEDecisionManager() const = 0;
/**
* @brief Get cooldown stacking optimizer
* 12-class cooldown database for optimal CD stacking
* @return Non-owning pointer to CooldownStackingOptimizer (owned by facade)
*/
virtual CooldownStackingOptimizer* GetCooldownStackingOptimizer() const = 0;
/**
* @brief Get defensive behavior manager
* External defensive CD coordination (GAP 3 fix)
* @return Non-owning pointer to DefensiveBehaviorManager (owned by facade)
*/
virtual DefensiveBehaviorManager* GetDefensiveBehaviorManager() const = 0;
/**
* @brief Get dispel coordinator
* Dispel rotation and claim system (GAP 2 fix)
* @return Non-owning pointer to DispelCoordinator (owned by facade)
*/
virtual DispelCoordinator* GetDispelCoordinator() const = 0;
/**
* @brief Get interrupt rotation manager
* Interrupt assignment and rotation queue
* @return Non-owning pointer to InterruptRotationManager (owned by facade)
*/
virtual InterruptRotationManager* GetInterruptRotationManager() const = 0;
};
} // namespace Playerbot