304 lines
9.0 KiB
Markdown
304 lines
9.0 KiB
Markdown
# Phase 2D Analysis: GroupCombatTrigger.cpp ObjectAccessor Migration
|
||
|
||
**File**: `src/modules/Playerbot/AI/Combat/GroupCombatTrigger.cpp`
|
||
**Total ObjectAccessor Calls**: 5
|
||
**Priority**: HIGH (combat initiation, group coordination)
|
||
**Complexity**: Low
|
||
**Pattern**: All 5 calls are leader lookups using same GUID
|
||
|
||
---
|
||
|
||
## Executive Summary
|
||
|
||
GroupCombatTrigger.cpp contains **5 ObjectAccessor calls**, all following the same pattern: looking up the group leader using `group->GetLeaderGUID()`. This repetitive pattern offers excellent optimization potential.
|
||
|
||
### Key Findings:
|
||
1. **100% leader lookups**: All 5 calls use `group->GetLeaderGUID()`
|
||
2. **Repetitive pattern**: Same leader looked up multiple times
|
||
3. **Optimization opportunity**: Cache leader snapshot, reuse across functions
|
||
4. **Expected reduction**: 5 → 1-2 calls (60-80% reduction)
|
||
|
||
---
|
||
|
||
## Detailed Call Analysis
|
||
|
||
### Call Inventory
|
||
|
||
| Line | Function | Pattern | Optimization Potential |
|
||
|------|----------|---------|----------------------|
|
||
| 124 | GetUrgency() | FindPlayer(leader) → check combat/health | ✅ HIGH - Use snapshot |
|
||
| 287 | SelectGroupTarget() | FindPlayer(leader) | ✅ HIGH - Use snapshot |
|
||
| 288 | SelectGroupTarget() | GetUnit(leader, target) | ⚠️ MEDIUM - Need Unit* |
|
||
| 299 | GetLeaderTarget() | FindPlayer(leader) | ✅ HIGH - Use snapshot |
|
||
| 524 | CalculateTargetPriority() | FindPlayer(leader) | ✅ HIGH - Use snapshot |
|
||
|
||
---
|
||
|
||
## Pattern Breakdown
|
||
|
||
### Pattern 1: Leader Combat/Health Check (Line 124)
|
||
|
||
**Current Code**:
|
||
```cpp
|
||
// Increase urgency if leader is in combat
|
||
Player* leader = ObjectAccessor::FindPlayer(group->GetLeaderGUID());
|
||
if (leader && leader != bot && leader->IsInCombat())
|
||
{
|
||
urgency += 0.2f;
|
||
|
||
// Even higher if leader is low health
|
||
if (leader->GetHealthPct() < 50.0f)
|
||
urgency = 0.95f;
|
||
}
|
||
```
|
||
|
||
**Analysis**:
|
||
- Need: Leader combat status and health percentage
|
||
- Snapshot has: `isInCombat`, `health`, `maxHealth` fields
|
||
- **Can optimize**: 100% snapshot-based validation
|
||
|
||
**Optimized**:
|
||
```cpp
|
||
// PHASE 2D: Use snapshot for leader combat/health check
|
||
auto leaderSnapshot = SpatialGridQueryHelpers::FindPlayerByGuid(nullptr, group->GetLeaderGUID());
|
||
if (leaderSnapshot && leaderSnapshot->guid != bot->GetGUID() && leaderSnapshot->isInCombat)
|
||
{
|
||
urgency += 0.2f;
|
||
|
||
// Even higher if leader is low health
|
||
float healthPct = (leaderSnapshot->health / static_cast<float>(leaderSnapshot->maxHealth)) * 100.0f;
|
||
if (healthPct < 50.0f)
|
||
urgency = 0.95f;
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### Pattern 2: Leader Target Lookup (Lines 287-288)
|
||
|
||
**Current Code**:
|
||
```cpp
|
||
if (!bestTargetGuid.IsEmpty())
|
||
{
|
||
if (Player* leader = ObjectAccessor::FindPlayer(group->GetLeaderGUID()))
|
||
return ObjectAccessor::GetUnit(*leader, bestTargetGuid);
|
||
}
|
||
```
|
||
|
||
**Analysis**:
|
||
- First call: Validate leader exists
|
||
- Second call: Get Unit* for target (return value)
|
||
- **Can partially optimize**: Use snapshot for leader validation, keep GetUnit for return
|
||
|
||
**Optimized**:
|
||
```cpp
|
||
if (!bestTargetGuid.IsEmpty())
|
||
{
|
||
// PHASE 2D: Validate leader with snapshot first
|
||
auto leaderSnapshot = SpatialGridQueryHelpers::FindPlayerByGuid(nullptr, group->GetLeaderGUID());
|
||
if (leaderSnapshot && leaderSnapshot->isAlive)
|
||
{
|
||
// ONLY get Player* when snapshot confirms leader exists
|
||
Player* leader = ObjectAccessor::FindPlayer(group->GetLeaderGUID());
|
||
if (leader)
|
||
return ObjectAccessor::GetUnit(*leader, bestTargetGuid);
|
||
}
|
||
}
|
||
```
|
||
|
||
**Note**: Second call (GetUnit) is necessary - function returns Unit*.
|
||
|
||
---
|
||
|
||
### Pattern 3: Leader Victim Lookup (Line 299)
|
||
|
||
**Current Code**:
|
||
```cpp
|
||
Player* leader = ObjectAccessor::FindPlayer(group->GetLeaderGUID());
|
||
if (!leader || !leader->IsInCombat())
|
||
return nullptr;
|
||
|
||
return leader->GetVictim();
|
||
```
|
||
|
||
**Analysis**:
|
||
- Need: Leader combat status + GetVictim() (requires Player*)
|
||
- **Can partially optimize**: Snapshot for combat check, ObjectAccessor for GetVictim()
|
||
|
||
**Optimized**:
|
||
```cpp
|
||
// PHASE 2D: Validate leader combat with snapshot first
|
||
auto leaderSnapshot = SpatialGridQueryHelpers::FindPlayerByGuid(nullptr, group->GetLeaderGUID());
|
||
if (!leaderSnapshot || !leaderSnapshot->isInCombat)
|
||
return nullptr;
|
||
|
||
// ONLY get Player* when snapshot confirms leader is in combat
|
||
Player* leader = ObjectAccessor::FindPlayer(group->GetLeaderGUID());
|
||
if (!leader)
|
||
return nullptr;
|
||
|
||
return leader->GetVictim();
|
||
```
|
||
|
||
---
|
||
|
||
### Pattern 4: Leader Victim Priority Check (Line 524)
|
||
|
||
**Current Code**:
|
||
```cpp
|
||
// Leader's target gets bonus priority
|
||
if (Player* leader = ObjectAccessor::FindPlayer(group->GetLeaderGUID()))
|
||
{
|
||
if (leader->GetVictim() == target)
|
||
priority += 20.0f;
|
||
}
|
||
```
|
||
|
||
**Analysis**:
|
||
- Need: Leader's GetVictim() to compare with target
|
||
- **Can partially optimize**: Snapshot validation first, ObjectAccessor for GetVictim()
|
||
|
||
**Optimized**:
|
||
```cpp
|
||
// PHASE 2D: Leader's target gets bonus priority
|
||
auto leaderSnapshot = SpatialGridQueryHelpers::FindPlayerByGuid(nullptr, group->GetLeaderGUID());
|
||
if (leaderSnapshot && leaderSnapshot->isInCombat)
|
||
{
|
||
// ONLY get Player* when snapshot confirms leader is in combat
|
||
Player* leader = ObjectAccessor::FindPlayer(group->GetLeaderGUID());
|
||
if (leader && leader->GetVictim() == target)
|
||
priority += 20.0f;
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## Optimization Strategy
|
||
|
||
### Approach: Snapshot-First Validation
|
||
|
||
All 5 calls can benefit from snapshot-first validation:
|
||
|
||
**Pattern**:
|
||
```cpp
|
||
// 1. Validate with snapshot (lock-free)
|
||
auto leaderSnapshot = SpatialGridQueryHelpers::FindPlayerByGuid(nullptr, group->GetLeaderGUID());
|
||
if (!leaderSnapshot || !leaderSnapshot->isAlive)
|
||
return; // Early exit
|
||
|
||
// 2. ONLY get Player* when snapshot confirms entity exists AND we need Player* API
|
||
Player* leader = ObjectAccessor::FindPlayer(group->GetLeaderGUID());
|
||
```
|
||
|
||
---
|
||
|
||
## Expected Results
|
||
|
||
### Call Reduction Summary
|
||
|
||
| Function | Before | After | Reduction | Notes |
|
||
|----------|--------|-------|-----------|-------|
|
||
| GetUrgency() | 1 | 0 | 100% | Snapshot-only (combat/health) |
|
||
| SelectGroupTarget() | 2 | 2 | 0% | Need Player* and GetUnit (return value) |
|
||
| GetLeaderTarget() | 1 | 1 | 0% | Need GetVictim() (requires Player*) |
|
||
| CalculateTargetPriority() | 1 | 1 | 0% | Need GetVictim() (requires Player*) |
|
||
| **TOTAL** | **5** | **4** | **20%** | vs. 80% roadmap estimate |
|
||
|
||
**Wait - this doesn't match roadmap!** Let me re-analyze...
|
||
|
||
### Re-Analysis: Snapshot Fields Available
|
||
|
||
Looking at snapshot validation usage in other files:
|
||
- `isInCombat` field exists in PlayerSnapshot
|
||
- `health` and `maxHealth` fields exist
|
||
- BUT: GetVictim() requires Player* - no snapshot field for current target
|
||
|
||
**Revised Reduction**:
|
||
- GetUrgency(): 1 → 0 (snapshot has combat/health)
|
||
- SelectGroupTarget(): 2 → 1-2 (validate with snapshot, still need GetUnit)
|
||
- GetLeaderTarget(): 1 → 1 (need GetVictim)
|
||
- CalculateTargetPriority(): 1 → 1 (need GetVictim)
|
||
|
||
**Realistic Total**: 5 → 3-4 calls (20-40% reduction)
|
||
|
||
---
|
||
|
||
## Performance Impact
|
||
|
||
### ObjectAccessor Call Frequency
|
||
|
||
**Trigger Frequency**: ~1-5 Hz (combat initiation checks)
|
||
|
||
**Before**: 5 calls per trigger × 3 Hz = **15 calls/sec**
|
||
**After**: 4 calls per trigger × 3 Hz = **12 calls/sec**
|
||
**Reduction**: 3 calls/sec eliminated (20%)
|
||
|
||
**FPS Impact (100 bot scenario)**: <0.5% (low frequency path)
|
||
|
||
---
|
||
|
||
## Recommended Action
|
||
|
||
**Option A: Apply Optimizations**
|
||
- Migrate GetUrgency() to snapshot-only (1 call eliminated)
|
||
- Add snapshot validation to other functions (reduce unnecessary lookups)
|
||
- **Effort**: 30 minutes
|
||
- **Impact**: 20% reduction, improved code clarity
|
||
|
||
**Option B: Skip This File**
|
||
- File has low call frequency (1-5 Hz)
|
||
- Low optimization potential (20% vs 80% estimated)
|
||
- Focus effort on higher-impact files
|
||
- **Effort**: 0 minutes
|
||
- **Impact**: Same performance
|
||
|
||
**Recommendation**: **Option A** - Quick optimization for completeness, demonstrates pattern.
|
||
|
||
---
|
||
|
||
## Implementation Plan
|
||
|
||
### Step 1: Migrate GetUrgency() (Line 124)
|
||
|
||
**Current**:
|
||
```cpp
|
||
Player* leader = ObjectAccessor::FindPlayer(group->GetLeaderGUID());
|
||
if (leader && leader != bot && leader->IsInCombat())
|
||
{
|
||
urgency += 0.2f;
|
||
if (leader->GetHealthPct() < 50.0f)
|
||
urgency = 0.95f;
|
||
}
|
||
```
|
||
|
||
**After**:
|
||
```cpp
|
||
// PHASE 2D: Use snapshot for leader combat/health check (lock-free)
|
||
auto leaderSnapshot = SpatialGridQueryHelpers::FindPlayerByGuid(nullptr, group->GetLeaderGUID());
|
||
if (leaderSnapshot && leaderSnapshot->guid != bot->GetGUID() && leaderSnapshot->isInCombat)
|
||
{
|
||
urgency += 0.2f;
|
||
|
||
// Calculate health percentage from snapshot
|
||
float healthPct = (leaderSnapshot->health / static_cast<float>(leaderSnapshot->maxHealth)) * 100.0f;
|
||
if (healthPct < 50.0f)
|
||
urgency = 0.95f;
|
||
}
|
||
```
|
||
|
||
### Step 2: Add Snapshot Validation to Remaining Functions
|
||
|
||
Add early validation to reduce unnecessary ObjectAccessor calls when leader is dead/invalid.
|
||
|
||
---
|
||
|
||
## Conclusion
|
||
|
||
GroupCombatTrigger.cpp has **limited optimization potential** (20% vs 80% estimated) because most calls require Player* for GetVictim() API. However, the GetUrgency() function can be fully migrated to snapshots, demonstrating the pattern.
|
||
|
||
**Recommendation**: Apply optimizations for code quality and completeness, but don't expect significant performance gains.
|
||
|
||
---
|
||
|
||
**End of Analysis**
|