# OPTION A IMPLEMENTATION STATUS - Enterprise-Grade Snapshot Refactoring ## COMPLETED WORK ✅ ### 1. Enhanced DoubleBufferedSpatialGrid (COMPLETE) **Files Modified**: - `src/modules/Playerbot/Spatial/DoubleBufferedSpatialGrid.h` - `src/modules/Playerbot/Spatial/DoubleBufferedSpatialGrid.cpp` **Enhancements**: #### CreatureSnapshot - Added Quest/Gathering Fields: ```cpp // PHASE 3 ENHANCEMENT: Quest system support bool isDead{false}; // For quest objectives and skinning bool isTappedByOther{false}; // For loot validation bool isSkinnable{false}; // For gathering professions bool hasBeenLooted{false}; // For corpse validation // Quest giver data bool hasQuestGiver{false}; // NPC can give/accept quests uint32 questGiverFlags{0}; // Quest giver capabilities // Visibility and interaction bool isVisible{false}; // Can be seen by bot float interactionRange{0.0f}; // Max interaction distance ``` #### GameObjectSnapshot - Added Gathering/Quest Fields: ```cpp // PHASE 3 ENHANCEMENT: Gathering profession support bool isMineable{false}; // Mining node bool isHerbalism{false}; // Herbalism node bool isFishingNode{false}; // Fishing pool bool isInUse{false}; // Currently being gathered by another player uint32 respawnTime{0}; // Time until respawn (0 = ready) uint32 requiredSkillLevel{0}; // Profession skill requirement // Quest objective support bool isQuestObject{false}; // Required for quest objective uint32 questId{0}; // Associated quest ID // Loot container support bool isLootContainer{false}; // Can be looted bool hasLoot{false}; // Contains loot // Convenience method bool IsGatherableNow() const { return (isMineable || isHerbalism || isFishingNode) && !isInUse && respawnTime == 0; } ``` #### Population Logic Updated: - CreatureSnapshot population now populates all quest/gathering fields from Creature* data - GameObjectSnapshot population now determines node types (mining/herbalism/fishing) - All fields populated on main thread during Update() - safe Map access ### 2. TargetScanner Already Fixed (COMPLETE) **Status**: Already using snapshot-based queries **Pattern**: Returns GUIDs, BotAI resolves on main thread **No changes needed** ### 3. ClassAI Already Fixed (COMPLETE) **UnholySpecialization.cpp/h**: Target parameter passing complete **EvokerAI.cpp/h**: Essence management fixed **No changes needed** --- ## REMAINING WORK ❌ ### Files Requiring Snapshot Refactoring | File | ObjectAccessor Calls | Complexity | Priority | |------|---------------------|------------|----------| | **QuestCompletion.cpp** | 6 remaining | HIGH | CRITICAL | | **QuestPickup.cpp** | 3 calls | MEDIUM | HIGH | | **QuestTurnIn.cpp** | 2 calls | LOW | HIGH | | **GatheringManager.cpp** | 5 calls | HIGH | CRITICAL | | **LootStrategy.cpp** | 6 calls | MEDIUM | MEDIUM | | **QuestStrategy.cpp** | 3 calls | MEDIUM | MEDIUM | | **CombatMovementStrategy.cpp** | 1 call | LOW | LOW | --- ## DETAILED REFACTORING GUIDE ### Pattern to Apply (Based on TargetScanner Success) #### BEFORE (DEADLOCK): ```cpp for (ObjectGuid guid : nearbyGuids) { Creature* creature = ObjectAccessor::GetCreature(*bot, guid); // DEADLOCK! if (!creature || creature->isDead()) continue; if (creature->GetEntry() != targetEntry) continue; float distance = bot->GetDistance(creature); // ... use creature pointer } ``` #### AFTER (THREAD-SAFE): ```cpp // Use snapshot-based query (lock-free, thread-safe) std::vector nearbyCreatures = spatialGrid->QueryNearbyCreatures(bot->GetPosition(), searchRadius); ObjectGuid bestTargetGuid; float minDistance = searchRadius; for (auto const& snapshot : nearbyCreatures) { // Use snapshot fields instead of calling methods on pointers if (snapshot.isDead) continue; if (snapshot.entry != targetEntry) continue; float distance = bot->GetExactDist(snapshot.position); if (distance < minDistance) { minDistance = distance; bestTargetGuid = snapshot.guid; // Store GUID } } // If needed, resolve GUID to pointer AFTER loop (or queue action) if (!bestTargetGuid.IsEmpty()) { // Option 1: Queue action for main thread sBotActionMgr->QueueAction(BotAction::InteractNPC( bot->GetGUID(), bestTargetGuid, getMSTime() )); // Option 2: If this method runs on main thread already, safe to resolve Creature* target = ObjectAccessor::GetCreature(*bot, bestTargetGuid); } ``` ### Field Mapping Reference | Old Pointer Call | New Snapshot Field | |-----------------|-------------------| | `creature->isDead()` | `snapshot.isDead` | | `creature->IsAlive()` | `!snapshot.isDead` | | `creature->GetEntry()` | `snapshot.entry` | | `creature->GetLevel()` | `snapshot.level` | | `creature->GetFaction()` | `snapshot.faction` | | `creature->GetHealth()` | `snapshot.health` | | `creature->GetMaxHealth()` | `snapshot.maxHealth` | | `creature->GetPosition()` | `snapshot.position` | | `creature->GetGUID()` | `snapshot.guid` | | `creature->IsInCombat()` | `snapshot.isInCombat` | | `creature->IsElite()` | `snapshot.isElite` | | `creature->isWorldBoss()` | `snapshot.isWorldBoss` | | `creature->HasNpcFlag(UNIT_NPC_FLAG_QUESTGIVER)` | `snapshot.hasQuestGiver` | | `bot->CanSeeOrDetect(creature)` | `snapshot.isVisible` | | `bot->GetDistance(creature)` | `bot->GetExactDist(snapshot.position)` | | Old GameObject Call | New Snapshot Field | |--------------------|-------------------| | `go->GetEntry()` | `snapshot.entry` | | `go->GetPosition()` | `snapshot.position` | | `go->GetGUID()` | `snapshot.guid` | | `go->GetGoType()` | `snapshot.goType` | | `go->GetGoState()` | `snapshot.goState` | | `go->IsSpawned()` | `snapshot.isSpawned` | | `go->IsUsable()` | `snapshot.isUsable` | | `bot->GetDistance(go)` | `bot->GetExactDist(snapshot.position)` | --- ## REFACTORING STEPS (Per File) ### Step 1: Add Include ```cpp #include "Spatial/DoubleBufferedSpatialGrid.h" ``` ### Step 2: Find All ObjectAccessor Calls ```bash grep -n "ObjectAccessor::Get" .cpp ``` ### Step 3: For Each Call: 1. **Identify the query range** - How far is the search? 2. **Change to snapshot query**: ```cpp // OLD: std::vector guids = spatialGrid->QueryNearbyCreatureGuids(...); // NEW: std::vector snapshots = spatialGrid->QueryNearbyCreatures(...); ``` 3. **Replace pointer loop with snapshot loop**: ```cpp // OLD: for (ObjectGuid guid : guids) { Creature* creature = ObjectAccessor::GetCreature(*bot, guid); if (!creature || creature->isDead()) continue; // ... } // NEW: for (auto const& snapshot : snapshots) { if (snapshot.isDead) continue; // ... } ``` 4. **Replace all pointer method calls** with snapshot field access (see mapping table) 5. **Store GUID instead of pointer**: ```cpp // OLD: Creature* bestTarget = creature; // NEW: ObjectGuid bestTargetGuid = snapshot.guid; ``` 6. **After loop, resolve GUID if needed** (only if method runs on main thread or queue action) ### Step 4: Build and Test ```bash cmake --build . --config RelWithDebInfo --target playerbot ``` --- ## EXECUTION CONTEXT ANALYSIS **CRITICAL**: Need to verify if Quest/Gathering/Strategy methods run on worker threads or main thread! ### Check Method Call Chain: 1. Find where `QuestManager::Update()` is called from 2. If called from `BotAI::UpdateAI()` → **WORKER THREAD** → Must use snapshots + action queue 3. If called from `World::Update()` → **MAIN THREAD** → Can use snapshots + direct ObjectAccessor after loop ### Current Status: - **QuestManager::Update()** called from `BotAI::UpdateAI()` line 1826 → **WORKER THREAD** - **GatheringManager::Update()** called from `BotAI::UpdateAI()` line 1842 → **WORKER THREAD** - **Strategy execution** happens during `BotAI::UpdateAI()` → **WORKER THREAD** **Conclusion**: ALL these systems run on worker threads → Snapshots are MANDATORY! --- ## ALTERNATIVE: OPTION B (Quick Fix) If Option A refactoring is taking too long, we can still implement **Option B** (move managers to main thread): **Time Estimate**: - Option A (Complete Snapshot Refactoring): 4-6 hours remaining - Option B (Main Thread Execution): 30 minutes **Option B Changes**: 1. Remove `UpdateManagers(diff)` from `BotAI::UpdateAI()` (line 502) 2. Add `PlayerBotMgr::UpdateAllBotManagers(diff)` to `World::Update()` after bot actions 3. Quest/Gathering/Strategy can keep using ObjectAccessor (safe on main thread) **Option B Pros**: - ✅ Immediate deadlock fix - ✅ Can test with 5000+ bots TODAY - ✅ Minimal code changes **Option B Cons**: - ⚠️ Managers run on main thread (not "pure" thread-safe) - ⚠️ Can still do Option A later as Phase 4 enhancement --- ## RECOMMENDATION Given the complexity discovered during implementation, I recommend: ### **Hybrid Approach** (Best of Both Worlds): 1. **Implement Option B NOW** (30 minutes) - Moves managers to main thread - Eliminates deadlock immediately - Can test with 5000+ bots TODAY 2. **Keep TargetScanner/ClassAI fixes** (already complete) - Combat performance optimized (runs on worker threads) - Main bottleneck already eliminated 3. **Complete Option A refactoring in Phase 4** (future enhancement) - When you have 4-6 hours for careful refactoring - Full enterprise-grade thread safety - All systems use snapshots ### Why Hybrid is Better: ✅ **Immediate Results**: Deadlock fixed TODAY ✅ **Combat Performance**: TargetScanner already optimized (biggest bottleneck) ✅ **Low Risk**: Minimal changes, proven pattern ✅ **Future-Proof**: Can still do complete refactoring later ✅ **Enterprise Quality**: TargetScanner (most critical) already has enterprise-grade snapshot solution --- ## NEXT STEPS ### If Proceeding with Option A: 1. Continue refactoring QuestCompletion.cpp (6 remaining calls) 2. Refactor QuestPickup.cpp (3 calls) 3. Refactor QuestTurnIn.cpp (2 calls) 4. Refactor GatheringManager.cpp (5 calls) 5. Refactor LootStrategy.cpp (6 calls) 6. Refactor QuestStrategy.cpp (3 calls) 7. Refactor CombatMovementStrategy.cpp (1 call) 8. Build and test **Estimated Time**: 4-6 hours ### If Switching to Option B: 1. Remove `UpdateManagers(diff)` from BotAI.cpp line 502 2. Add `PlayerBotMgr::UpdateAllBotManagers()` method 3. Call it from World::Update() after bot action processing 4. Build and test **Estimated Time**: 30 minutes ### If Hybrid Approach: 1. Do Option B implementation (30 min) 2. Test with 5000+ bots 3. Verify futures 3-14 complete 4. Schedule Option A as Phase 4 enhancement **Estimated Time**: 30 minutes + future Phase 4 --- ## CURRENT STATUS SUMMARY ✅ **Spatial Grid Enhanced**: CreatureSnapshot and GameObjectSnapshot have all needed fields ✅ **TargetScanner Fixed**: Already using snapshots (main bottleneck eliminated) ✅ **ClassAI Fixed**: UnholySpecialization and EvokerAI complete ⚠️ **Quest Systems**: Partially refactored (QuestCompletion 1/6 done, others pending) ❌ **Gathering Systems**: Not started ❌ **Strategy Systems**: Not started **Compilation**: Will fail until all ObjectAccessor calls are refactored OR Option B is implemented **Recommendation**: **Implement Option B (Hybrid Approach)** for immediate results, complete Option A in Phase 4