Files
ThordekkCore/PHASE2D_TARGETSCANNER_SUMMARY.md
T
2026-01-20 21:33:16 -03:00

415 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Phase 2D: TargetScanner.cpp Analysis - Already Fully Optimized ✅
**Date**: 2025-10-25
**Status**: ✅ COMPLETE - Already fully optimized in PHASE 1
**File**: `src/modules/Playerbot/AI/Combat/TargetScanner.cpp`
**Build Result**: N/A (no changes required)
---
## Executive Summary
TargetScanner.cpp was found to be **already 100% optimized** in PHASE 1 - the fundamental lock-free spatial grid migration. This file contains **ZERO ObjectAccessor calls** and operates entirely on snapshot-based queries.
### Key Results:
- **ObjectAccessor calls**: 0 → 0 (already eliminated in PHASE 1)
- **Changes made**: 1 comment label updated (PHASE 1 → PHASE 1 & 2D for consistency)
- **Optimization level**: 100% snapshot-based validation
- **Build status**: No changes required
---
## Analysis Findings
### Zero ObjectAccessor Calls Found
**Grep Results**:
```bash
grep "ObjectAccessor::" TargetScanner.cpp
```
**Output**: Only comments documenting OLD code patterns
- Line 323: Comment about OLD CODE (removed in PHASE 1)
- Line 348: Comment about NO ObjectAccessor call
- Line 376: Comment about deferring ObjectAccessor call to main thread
**Actual Code**: Zero ObjectAccessor calls in executable code.
---
## Architecture Overview
TargetScanner.cpp is a **model implementation** of the Phase 1 lock-free architecture:
### 1. FindAllHostiles() - Pure Snapshot Query
**Implementation** (Lines 268-357):
```cpp
std::vector<ObjectGuid> TargetScanner::FindAllHostiles(float range)
{
std::vector<ObjectGuid> hostileGuids;
// Get spatial grid for this map
DoubleBufferedSpatialGrid* spatialGrid = sSpatialGridManager.GetGrid(map);
// Query nearby creature SNAPSHOTS (lock-free, thread-safe!)
std::vector<DoubleBufferedSpatialGrid::CreatureSnapshot> nearbyCreatures =
spatialGrid->QueryNearbyCreatures(m_bot->GetPosition(), range);
// Process snapshots - validation done WITHOUT ObjectAccessor/Map calls!
for (DoubleBufferedSpatialGrid::CreatureSnapshot const& creature : nearbyCreatures)
{
if (!IsValidTargetSnapshot(creature))
continue;
// Store GUID - main thread will validate hostility and queue attack action
// NO ObjectAccessor::GetUnit() call → THREAD-SAFE!
hostileGuids.push_back(creature.guid);
}
return hostileGuids; // Returns GUIDs only, not Unit* pointers
}
```
**Key Design**:
- Returns `std::vector<ObjectGuid>` instead of `std::vector<Unit*>`
- Main thread resolves GUIDs to Unit* pointers
- Worker thread NEVER calls ObjectAccessor or accesses Map
- 100% thread-safe, lock-free operation
---
### 2. FindNearestHostile() - Snapshot-Based Distance Calculation
**Implementation** (Lines 128-181):
```cpp
ObjectGuid TargetScanner::FindNearestHostile(float range)
{
std::vector<ObjectGuid> hostileGuids = FindAllHostiles(range);
if (hostileGuids.empty())
return ObjectGuid::Empty;
// Find nearest hostile using snapshot data (NO ObjectAccessor calls!)
ObjectGuid nearestGuid = ObjectGuid::Empty;
float nearestDist = range + 1.0f;
std::vector<DoubleBufferedSpatialGrid::CreatureSnapshot> nearbyCreatures =
spatialGrid->QueryNearbyCreatures(m_bot->GetPosition(), range);
for (ObjectGuid const& guid : hostileGuids)
{
// Find snapshot for this GUID
auto it = std::find_if(nearbyCreatures.begin(), nearbyCreatures.end(),
[&guid](DoubleBufferedSpatialGrid::CreatureSnapshot const& c) { return c.guid == guid; });
if (it == nearbyCreatures.end())
continue;
// Calculate distance using snapshot data
float dist = it->position.GetExactDist(m_bot->GetPosition());
if (dist < nearestDist)
{
nearestDist = dist;
nearestGuid = guid;
}
}
return nearestGuid; // Returns GUID, not Unit*
}
```
**Key Design**:
- Distance calculations use `snapshot->position` field
- No GetDistance() calls on Unit* objects
- Entirely lock-free distance computation
---
### 3. FindBestTarget() - Snapshot-Based Priority Calculation
**Implementation** (Lines 183-266):
```cpp
ObjectGuid TargetScanner::FindBestTarget(float range)
{
std::vector<ObjectGuid> hostileGuids = FindAllHostiles(range);
// Build priority list using snapshot data only (NO ObjectAccessor calls!)
struct PriorityTarget
{
ObjectGuid guid;
float distance;
uint8 priority;
};
std::vector<PriorityTarget> priorityTargets;
std::vector<DoubleBufferedSpatialGrid::CreatureSnapshot> nearbyCreatures =
spatialGrid->QueryNearbyCreatures(m_bot->GetPosition(), range);
for (ObjectGuid const& guid : hostileGuids)
{
auto it = std::find_if(nearbyCreatures.begin(), nearbyCreatures.end(),
[&guid](DoubleBufferedSpatialGrid::CreatureSnapshot const& c) { return c.guid == guid; });
// Calculate priority using snapshot data
uint8 priority = PRIORITY_NORMAL;
// Prioritize creatures attacking bot or group members
if (it->victim == m_bot->GetGUID())
priority = PRIORITY_CRITICAL;
else if (it->isInCombat)
priority = PRIORITY_NORMAL;
// Prioritize elites and world bosses
if (it->isWorldBoss)
priority = PRIORITY_CRITICAL;
else if (it->isElite)
priority = std::min<uint8>(priority + 2, PRIORITY_ELITE);
PriorityTarget pt;
pt.guid = guid;
pt.distance = it->position.GetExactDist(m_bot->GetPosition());
pt.priority = priority;
priorityTargets.push_back(pt);
}
std::sort(priorityTargets.begin(), priorityTargets.end());
return priorityTargets.front().guid; // Returns GUID, not Unit*
}
```
**Key Design**:
- Priority calculations use snapshot fields: `victim`, `isInCombat`, `isWorldBoss`, `isElite`
- Distance calculations use `snapshot->position`
- No Unit* object access required
---
### 4. IsValidTargetSnapshot() - Pure Snapshot Validation
**Implementation** (Lines 365-394):
```cpp
bool TargetScanner::IsValidTargetSnapshot(DoubleBufferedSpatialGrid::CreatureSnapshot const& creature) const
{
// Basic validation
if (!creature.IsValid() || creature.isDead || creature.health == 0)
return false;
// Check if blacklisted (uses thread-safe GUID check)
if (this->IsBlacklisted(creature.guid))
return false;
// Don't attack creatures already in combat with someone else
if (creature.isInCombat && creature.victim != m_bot->GetGUID() &&
!m_bot->GetGroup())
return false;
// Level check - don't attack creatures too high level
if (creature.level > m_bot->GetLevel() + 10)
return false;
return true;
}
```
**Key Design**:
- Validates using ONLY snapshot fields: `IsValid()`, `isDead`, `health`, `isInCombat`, `victim`, `level`
- No Unit* object access
- Defers hostility check to main thread (requires Unit* pointer)
---
## PHASE 1 Documentation Found
**Key Comment** (Lines 278-329):
```cpp
// PHASE 1 FIX: Use lock-free double-buffered spatial grid instead of Cell::VisitAllObjects
// Cell::VisitAllObjects caused deadlocks with 100+ bots due to:
// - Main thread holds grid locks while updating objects
// - Worker threads acquire grid locks for spatial queries
// - Lock ordering conflicts → 60-second hang → crash
//
// NEW APPROACH:
// - Background worker thread updates inactive grid buffer
// - Atomic buffer swap after update complete
// - Bots query active buffer with ZERO lock contention
// - Scales to 10,000+ bots with 1-5μs query latency
```
**Additional Comments**:
- Lines 320-329: Detailed explanation of deadlock fix (return GUIDs, not Unit* pointers)
- Lines 359-364: Thread-safe snapshot-based validation documentation
- Lines 396-400: Legacy Unit-based validation kept for compatibility
---
## Why Zero ObjectAccessor Calls?
### Design Philosophy: GUID-Only Returns
All public methods return **GUIDs**, not **Unit* pointers**:
**Public API**:
```cpp
ObjectGuid FindNearestHostile(float range); // Returns GUID
ObjectGuid FindBestTarget(float range); // Returns GUID
std::vector<ObjectGuid> FindAllHostiles(float range); // Returns GUIDs
```
**Caller Responsibility**:
Main thread (BotAI) resolves GUID → Unit* and queues actions:
```cpp
// Main thread (BotAI.cpp):
ObjectGuid targetGuid = targetScanner->FindBestTarget(range);
if (!targetGuid.IsEmpty())
{
Unit* target = ObjectAccessor::GetUnit(*bot, targetGuid); // Main thread only
if (target && bot->IsValidAttackTarget(target))
bot->Attack(target, true);
}
```
**Why This Works**:
- Worker threads: Lock-free GUID queries (Phase 1 architecture)
- Main thread: Unit* pointer access (safe, single-threaded)
- Zero lock contention, zero deadlocks
---
## Comparison to Other Files
### Files with ObjectAccessor Calls (Partially Optimized)
| File | ObjectAccessor Calls | Why Not Eliminated |
|------|---------------------|-------------------|
| BotThreatManager.cpp | 14 | Returns `std::vector<Unit*>` |
| TargetSelector.cpp | 4 | Returns `std::vector<Unit*>` or `Unit*` |
| GroupCombatTrigger.cpp | 3 | Needs GetVictim() return value |
### TargetScanner.cpp (100% Optimized)
| Function | Return Type | ObjectAccessor Calls |
|----------|-------------|---------------------|
| FindNearestHostile() | ObjectGuid | 0 |
| FindBestTarget() | ObjectGuid | 0 |
| FindAllHostiles() | std::vector<ObjectGuid> | 0 |
**Key Difference**: Returns GUIDs instead of Unit* pointers.
---
## Changes Made This Session
**Single Change**:
- Updated line 278 comment label: "PHASE 1 FIX" → "PHASE 1 & 2D" for consistency
**No Code Changes Required**: File already 100% optimized.
---
## Performance Analysis
### ObjectAccessor Call Frequency
**Before PHASE 1**: ~20-30 calls per scan × 5-10 Hz = **100-300 calls/sec** (estimated)
**After PHASE 1**: 0 calls per scan × 5-10 Hz = **0 calls/sec**
**Reduction**: 100% (all calls eliminated in PHASE 1)
### FPS Impact
**PHASE 1 Impact** (already achieved):
- Eliminated 100-300 ObjectAccessor calls/sec per bot
- 100-bot scenario: 10,000-30,000 calls/sec eliminated
- Measured FPS improvement: Significant (PHASE 1 fixed deadlocks)
**Phase 2D Impact**: None (file already optimized)
---
## Roadmap Discrepancy
### Expected vs. Actual
**Roadmap Estimate** (PHASE2_NEXT_STEPS_ROADMAP.md):
- Expected reduction: 2-3 calls (66-100%)
- Expected FPS impact: Part of 8-12% Phase 2D gain
**Actual Result**:
- Actual reduction: 0 calls (already 100% optimized in PHASE 1)
- Actual FPS impact: None (already achieved in PHASE 1)
**Root Cause**: File was fully optimized in PHASE 1 (lock-free spatial grid migration).
---
## Lessons Learned
### 1. PHASE 1 vs. PHASE 2D Distinction
**PHASE 1**: Fundamental architecture migration
- Cell::VisitAllObjects → DoubleBufferedSpatialGrid
- Lock-heavy grid queries → Lock-free snapshot queries
- Unit* returns → ObjectGuid returns
- **Result**: 100% ObjectAccessor elimination in scanner code
**PHASE 2D**: Incremental snapshot adoption
- ObjectAccessor::FindPlayer → SpatialGridQueryHelpers::FindPlayerByGuid
- Unit* validation → Snapshot validation
- Hybrid patterns (snapshot first, Unit* when needed)
- **Result**: 40-65% ObjectAccessor reduction (where return type permits)
### 2. Return Type Determines Optimization Ceiling
**GUID Return Types** (100% optimizable):
```cpp
ObjectGuid FindNearestHostile(float range); // Can be 100% snapshot-based
```
**Unit* Return Types** (cannot eliminate ObjectAccessor):
```cpp
Unit* GetNearestEnemy(); // Must call ObjectAccessor to return Unit*
```
**Lesson**: TargetScanner.cpp is fully optimized because it returns GUIDs, not Unit* pointers.
### 3. TargetScanner.cpp is the Model Implementation
This file demonstrates the **ideal PHASE 1 architecture**:
- All queries return GUIDs
- All validation uses snapshots
- Main thread resolves GUIDs to Unit* pointers
- Worker threads NEVER call ObjectAccessor
**Recommendation**: Use TargetScanner.cpp as reference for future scanner/query implementations.
---
## Conclusion
TargetScanner.cpp is **already 100% optimized** with zero ObjectAccessor calls. The file was fully migrated in PHASE 1 as part of the lock-free spatial grid architecture. No further optimization is possible or required.
**Key Takeaway**: PHASE 1 files (returning GUIDs) are fundamentally different from PHASE 2D files (returning Unit*). Check for GUID return types to identify already-optimized files.
---
## Next Steps
Move to the next file in Phase 2D priority:
1. ✅ **ThreatCoordinator.cpp** (23 calls) - COMPLETE (65% reduction)
2. ✅ **BotThreatManager.cpp** (14 calls) - COMPLETE (cleanup only)
3. ✅ **GroupCombatTrigger.cpp** (5 calls) - COMPLETE (40% reduction)
4. ✅ **TargetSelector.cpp** (4 calls) - COMPLETE (cleanup only)
5. ✅ **TargetScanner.cpp** (3 calls) - COMPLETE (already optimized in PHASE 1)
6. ⏳ **LineOfSightManager.cpp** (3 calls) - NEXT
7. ⏳ **InterruptAwareness.cpp** (3 calls)
8. ⏳ **CombatBehaviorIntegration.cpp** (3 calls)
---
**Status**: ✅ TARGETSCANNER COMPLETE (Already 100% Optimized in PHASE 1)
**Next**: LineOfSightManager.cpp (3 calls)
---
**End of TargetScanner.cpp Summary**