Files
ThordekkCore/PHASE_2_5_IDLE_STRATEGY.md
T

610 lines
16 KiB
Markdown
Raw Normal View History

2026-01-20 21:33:16 -03:00
# Phase 2.5: Update IdleStrategy - Observer Pattern
**Duration**: 1 week (2025-02-17 to 2025-02-24)
**Status**: ⏳ PENDING
**Owner**: Development Team
---
## Objectives
Transform IdleStrategy from heavyweight delegator to lightweight observer:
1. Remove all Automation::instance() calls
2. Replace with fast manager state queries
3. Eliminate throttling timers (no longer needed)
4. Achieve <0.1ms per update (from 100ms+)
5. Implement true observer pattern
---
## Background
### Current Problem: Strategy Does Heavyweight Work
**IdleStrategy.cpp (Current)**:
```cpp
void IdleStrategy::UpdateBehavior(BotAI* ai, uint32 diff)
{
Player* bot = ai->GetBot();
uint32 currentTime = getMSTime();
// ❌ Calls expensive singleton operations
if (currentTime - _lastQuestUpdate > 2000)
{
QuestAutomation::instance()->UpdateBotAutomation(bot, diff); // 50-100ms!
_lastQuestUpdate = currentTime;
}
if (currentTime - _lastGatheringUpdate > 1000)
{
GatheringAutomation::instance()->Update(bot, diff); // 50-100ms!
_lastGatheringUpdate = currentTime;
}
// ... etc for Trade, Auction
}
```
**Problems**:
1. **Performance**: Even throttled, takes 0-144ms per update
2. **Architecture**: Strategy shouldn't do heavyweight work
3. **Complexity**: Strategy manages timers for external systems
4. **Redundancy**: Managers already have their own throttling in Phase 2.4
5. **Scalability**: Doesn't scale to 5000+ bots
### Solution: Lightweight Observer Pattern
**IdleStrategy.cpp (New)**:
```cpp
void IdleStrategy::UpdateBehavior(BotAI* ai, uint32 diff)
{
// ✅ Fast state queries only (atomic reads, <0.001ms each)
QuestManager* questMgr = ai->GetQuestManager();
GatheringManager* gatherMgr = ai->GetGatheringManager();
TradeManager* tradeMgr = ai->GetTradeManager();
// Priority 1: Quest behavior (observe state only)
if (questMgr && questMgr->IsQuestingActive())
{
// Quest movement/behavior handled by managers
// Strategy just observes and coordinates
return; // Questing has priority
}
// Priority 2: Gathering behavior
if (gatherMgr && gatherMgr->HasNearbyResources())
{
// Gathering handled by manager
return;
}
// Priority 3: Trading behavior
if (tradeMgr && tradeMgr->NeedsRepair())
{
// Trading handled by manager
return;
}
// Priority 4: Fallback - Simple wander
DoWander(ai, diff);
}
```
**Benefits**:
- ✅ <0.1ms per update (vs 100ms+)
- ✅ No throttling needed (queries are instant)
- ✅ True observer pattern (strategies observe, managers work)
- ✅ Clean separation of concerns
- ✅ Scalable to 5000+ bots
---
## Technical Requirements
### Performance Constraints
- IdleStrategy::UpdateBehavior(): <0.1ms per call
- Manager state query: <0.001ms per call (atomic read)
- Total idle strategy overhead: <0.1ms per bot per frame
- Must call every frame (no throttling)
### Observer Pattern Principles
1. **Strategies Observe**: Read state from managers (fast queries)
2. **Managers Work**: Do heavyweight operations (throttled)
3. **No Cross-Calling**: Strategies never call manager Update() methods
4. **Atomic State**: All state queries are atomic (thread-safe, lock-free)
### Integration Points
- Requires all 4 managers from Phase 2.4 (Quest, Trade, Gathering, Auction)
- Managers already updating via BotAI::UpdateManagers()
- IdleStrategy just observes results
---
## Deliverables
### 1. Refactored IdleStrategy.h
Location: `src/modules/Playerbot/AI/Strategy/IdleStrategy.h`
**Before** (with throttling timers):
```cpp
class IdleStrategy : public Strategy
{
private:
uint32 _lastWanderTime = 0;
uint32 _wanderInterval = 30000;
// ❌ Throttling timers - not needed anymore
uint32 _lastQuestUpdate = 0;
uint32 _questUpdateInterval = 2000;
uint32 _lastGatheringUpdate = 0;
uint32 _gatheringUpdateInterval = 1000;
uint32 _lastTradeUpdate = 0;
uint32 _tradeUpdateInterval = 5000;
uint32 _lastAuctionUpdate = 0;
uint32 _auctionUpdateInterval = 10000;
};
```
**After** (clean, no timers):
```cpp
class IdleStrategy : public Strategy
{
public:
IdleStrategy();
~IdleStrategy() override = default;
void InitializeActions() override;
void InitializeTriggers() override;
void InitializeValues() override;
void OnActivate(BotAI* ai) override;
void OnDeactivate(BotAI* ai) override;
bool IsActive(BotAI* ai) const override;
void UpdateBehavior(BotAI* ai, uint32 diff) override;
private:
// Wander behavior
void DoWander(BotAI* ai, uint32 diff);
// State
uint32 _lastWanderTime = 0;
uint32 _wanderInterval = 30000;
};
```
### 2. Refactored IdleStrategy.cpp
Location: `src/modules/Playerbot/AI/Strategy/IdleStrategy.cpp`
**Complete New Implementation**:
```cpp
#include "IdleStrategy.h"
#include "BotAI.h"
#include "Player.h"
#include "Log.h"
#include "Game/QuestManager.h"
#include "Social/TradeManager.h"
#include "Professions/GatheringManager.h"
#include "Economy/AuctionManager.h"
namespace Playerbot
{
IdleStrategy::IdleStrategy() : Strategy("idle")
{
SetPriority(50); // Lower than group strategies
}
void IdleStrategy::InitializeActions()
{
// TODO: Add idle actions if needed
}
void IdleStrategy::InitializeTriggers()
{
// TODO: Add idle triggers if needed
}
void IdleStrategy::InitializeValues()
{
// TODO: Add idle values if needed
}
void IdleStrategy::OnActivate(BotAI* ai)
{
if (!ai || !ai->GetBot())
return;
TC_LOG_INFO("module.playerbot", "Idle strategy activated for bot {}", ai->GetBot()->GetName());
SetActive(true);
}
void IdleStrategy::OnDeactivate(BotAI* ai)
{
if (!ai || !ai->GetBot())
return;
TC_LOG_INFO("module.playerbot", "Idle strategy deactivated for bot {}", ai->GetBot()->GetName());
SetActive(false);
}
bool IdleStrategy::IsActive(BotAI* ai) const
{
if (!ai || !ai->GetBot())
return false;
// Active when not in a group
return _active && !ai->GetBot()->GetGroup();
}
void IdleStrategy::UpdateBehavior(BotAI* ai, uint32 diff)
{
if (!ai || !ai->GetBot())
return;
// OBSERVER PATTERN: Query manager state (fast atomic reads)
// Managers already updating via BotAI::UpdateManagers()
// Strategy just observes and coordinates
// Get manager references
QuestManager* questMgr = ai->GetQuestManager();
GatheringManager* gatherMgr = ai->GetGatheringManager();
TradeManager* tradeMgr = ai->GetTradeManager();
AuctionManager* auctionMgr = ai->GetAuctionManager();
// Priority 1: Active questing (highest priority)
if (questMgr && questMgr->IsQuestingActive())
{
// Quest manager is handling quest logic
// Bot is moving to objectives, killing mobs, etc.
// Strategy just needs to observe - don't interfere
static uint32 questCounter = 0;
if (++questCounter % 100 == 0)
{
TC_LOG_DEBUG("module.playerbot", "🎯 IdleStrategy: Bot {} actively questing",
ai->GetBot()->GetName());
}
return;
}
// Priority 2: Gathering resources (if available)
if (gatherMgr && gatherMgr->HasNearbyResources())
{
// Gathering manager is handling resource collection
// Strategy just observes
static uint32 gatherCounter = 0;
if (++gatherCounter % 100 == 0)
{
TC_LOG_DEBUG("module.playerbot", "⛏️ IdleStrategy: Bot {} gathering resources",
ai->GetBot()->GetName());
}
return;
}
// Priority 3: Trading/repair (if needed)
if (tradeMgr && tradeMgr->NeedsRepair())
{
// Trade manager is handling vendor interaction
// Strategy just observes
static uint32 tradeCounter = 0;
if (++tradeCounter % 100 == 0)
{
TC_LOG_DEBUG("module.playerbot", "🔧 IdleStrategy: Bot {} needs repair",
ai->GetBot()->GetName());
}
return;
}
// Priority 4: Auction house (if near and has items to sell)
if (auctionMgr && auctionMgr->HasActiveAuctions())
{
// Auction manager is handling AH interaction
static uint32 auctionCounter = 0;
if (++auctionCounter % 100 == 0)
{
TC_LOG_DEBUG("module.playerbot", "💰 IdleStrategy: Bot {} using auction house",
ai->GetBot()->GetName());
}
return;
}
// Priority 5: Fallback - Simple wandering behavior
DoWander(ai, diff);
}
void IdleStrategy::DoWander(BotAI* ai, uint32 diff)
{
if (!ai || !ai->GetBot())
return;
uint32 currentTime = getMSTime();
if (currentTime - _lastWanderTime < _wanderInterval)
return;
_lastWanderTime = currentTime;
// TODO: Implement proper wandering with pathfinding
// For now, just log that bot is idle
static uint32 idleCounter = 0;
if (++idleCounter % 100 == 0)
{
TC_LOG_DEBUG("module.playerbot", "💤 IdleStrategy: Bot {} is wandering (truly idle)",
ai->GetBot()->GetName());
}
}
} // namespace Playerbot
```
### 3. Remove Automation Includes
Location: `src/modules/Playerbot/AI/Strategy/IdleStrategy.cpp`
**Before**:
```cpp
#include "Quest/QuestAutomation.h"
#include "Professions/GatheringAutomation.h"
#include "Social/TradeAutomation.h"
#include "Social/AuctionAutomation.h"
```
**After**:
```cpp
#include "Game/QuestManager.h"
#include "Social/TradeManager.h"
#include "Professions/GatheringManager.h"
#include "Economy/AuctionManager.h"
```
### 4. Performance Tests
Location: `tests/performance/IdleStrategyPerformanceTest.cpp`
**Test Benchmark**:
```cpp
TEST(IdleStrategyPerformance, UpdateBehaviorUnder100Microseconds)
{
// Setup: 100 bots with idle strategy
std::vector<Bot*> bots;
for (int i = 0; i < 100; ++i)
{
Bot* bot = CreateTestBot(CLASS_WARRIOR, 30);
bot->GetAI()->ActivateStrategy("idle");
bots.push_back(bot);
}
// Benchmark: 1000 update cycles
auto start = std::chrono::high_resolution_clock::now();
for (int cycle = 0; cycle < 1000; ++cycle)
{
for (Bot* bot : bots)
{
bot->GetAI()->GetStrategy("idle")->UpdateBehavior(bot->GetAI(), 100);
}
}
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
// Calculate average per-bot update time
double avgPerUpdate = duration.count() / (100.0 * 1000.0);
TC_LOG_INFO("test", "IdleStrategy avg update time: {} microseconds", avgPerUpdate);
// Assert: Must be under 100 microseconds (0.1ms)
ASSERT_LT(avgPerUpdate, 100.0) << "IdleStrategy update took " << avgPerUpdate << "us, exceeds 100us limit";
}
```
### 5. Integration Tests
Location: `tests/integration/IdleStrategyIntegrationTest.cpp`
**Test Scenarios**:
```cpp
TEST(IdleStrategyIntegration, PrioritizesQuesting)
{
// Setup: Bot with active quest
Bot* bot = CreateTestBot(CLASS_WARRIOR, 20);
bot->GetAI()->GetQuestManager()->AddQuest(12345);
bot->GetAI()->ActivateStrategy("idle");
// Execute 100 updates
for (int i = 0; i < 100; ++i)
bot->GetAI()->UpdateAI(100);
// Verify: Bot is following quest logic
ASSERT_TRUE(bot->GetAI()->GetQuestManager()->IsQuestingActive());
}
TEST(IdleStrategyIntegration, FallsBackToWander)
{
// Setup: Bot with no active tasks
Bot* bot = CreateTestBot(CLASS_WARRIOR, 20);
bot->GetAI()->ActivateStrategy("idle");
// Verify: No active systems
ASSERT_FALSE(bot->GetAI()->GetQuestManager()->IsQuestingActive());
ASSERT_FALSE(bot->GetAI()->GetGatheringManager()->HasNearbyResources());
// Execute updates
for (int i = 0; i < 100; ++i)
bot->GetAI()->UpdateAI(100);
// Verify: Bot executes wander behavior (logged)
// (Check logs for wander messages)
}
```
### 6. Documentation
Location: `docs/OBSERVER_PATTERN_GUIDE.md`
**Content**:
- Observer pattern explanation
- Manager/Strategy separation principles
- State query best practices
- Performance benefits explanation
- Migration from delegation to observation
- Troubleshooting guide
---
## Implementation Steps
### Day 1: Remove Automation Calls
1. Open IdleStrategy.cpp
2. Delete all Automation::instance() calls
3. Delete all throttling timer logic
4. Add manager state queries
5. Verify compilation
### Day 2: Implement Observer Pattern
1. Rewrite UpdateBehavior() with priority logic
2. Use manager state queries only
3. Add debug logging (temporary)
4. Test with 10 bots
5. Verify <0.1ms per update
### Day 3: Test Priority System
1. Test quest priority (highest)
2. Test gathering priority
3. Test trade priority
4. Test auction priority
5. Test wander fallback
### Day 4: Performance Testing
1. Benchmark with 100 bots
2. Benchmark with 500 bots
3. Benchmark with 1000 bots
4. Profile UpdateBehavior()
5. Verify <0.1ms per update
### Day 5: Integration Testing
1. Test with all managers active
2. Test priority transitions (quest → wander)
3. Test with mixed bot activities
4. Verify no regressions
5. Test 5000 bots (stress test)
### Day 6: Cleanup and Optimization
1. Remove temporary debug logging
2. Optimize state query order
3. Clean up code comments
4. Final code review
5. Performance validation
### Day 7: Documentation
1. Write observer pattern guide
2. Document priority system
3. Create migration examples
4. Update architecture docs
5. Final review
---
## Success Criteria
### Performance Requirements
- ✅ IdleStrategy::UpdateBehavior() <0.1ms per call
- ✅ Manager state queries <0.001ms each
- ✅ No throttling needed (called every frame)
- ✅ 100 bots: No performance degradation
- ✅ 500 bots: <1% CPU increase for idle strategy
- ✅ 1000 bots: <2% CPU increase
- ✅ 5000 bots: <10% CPU increase
### Functional Requirements
- ✅ All Automation::instance() calls removed
- ✅ All throttling timers removed
- ✅ Priority system works correctly
- ✅ Quest priority is highest
- ✅ Wander is fallback
- ✅ State queries are atomic
- ✅ No blocking operations
### Architecture Quality
- ✅ True observer pattern implemented
- ✅ Clean separation: strategies observe, managers work
- ✅ No cross-calling between systems
- ✅ Follows Phase 2.1 architecture design
- ✅ All managers from Phase 2.4 integrated
### Code Quality
- ✅ Clean, readable code
- ✅ Full documentation
- ✅ No compiler warnings
- ✅ Comprehensive tests
- ✅ No memory leaks
---
## Dependencies
### Requires
- Phase 2.1 complete (BehaviorManager base class)
- Phase 2.4 complete (All 4 managers refactored)
- Manager state query methods implemented
- BotAI::UpdateManagers() working
### Blocks
- Phase 2.6 (Integration testing needs observer pattern working)
- Full idle bot functionality
- Performance scalability to 5000+ bots
---
## Risk Mitigation
### Risk: Breaking idle bot behavior
**Mitigation**:
- Implement new code alongside old code temporarily
- Test extensively before removing old code
- Create rollback plan
### Risk: Performance regression
**Mitigation**:
- Benchmark before/after
- Profile every step
- Test with 5000 bots
### Risk: Priority system too complex
**Mitigation**:
- Keep priority logic simple (if/return pattern)
- Document priority order clearly
- Test each priority level independently
### Risk: State queries not thread-safe
**Mitigation**:
- Use atomic state flags in managers (from Phase 2.4)
- No locks needed (single-threaded access)
- Test with high bot counts
---
## Before/After Comparison
### Performance
| Metric | Before | After | Improvement |
|--------|--------|-------|-------------|
| Avg update time | 100-144ms | <0.1ms | 1000x faster |
| Min update time | 0ms (throttled) | <0.1ms | Consistent |
| Max update time | 600ms | <0.1ms | 6000x faster |
| CPU usage (100 bots) | 15-20% | <1% | 95% reduction |
### Code Complexity
| Metric | Before | After | Improvement |
|--------|--------|-------|-------------|
| LOC in UpdateBehavior() | 83 lines | 60 lines | Simpler |
| Timer variables | 8 timers | 1 timer | Cleaner |
| External dependencies | 4 singletons | 4 managers | Better |
| Blocking calls | 4 calls | 0 calls | No blocking |
---
## Next Phase
After completion, proceed to **Phase 2.6: Integration Testing**
---
**Last Updated**: 2025-01-13
**Next Review**: 2025-02-24