11 KiB
11 KiB
Group Event System - BotAI Integration Guide
Purpose: This guide explains how to integrate the Group Event System with BotAI when it becomes available.
📋 Prerequisites
Before integrating, ensure:
- ✅ Group Event System is compiled (GroupEventBus, GroupStateMachine, GroupEventHandler, PlayerbotGroupScript)
- ✅ BotAI class exists with Update() method
- ✅ Bots are managed by BotSpawner or similar lifecycle manager
🔧 Integration Steps
Step 1: Subscribe to Group Events (BotAI Constructor)
Add event subscriptions in BotAI::BotAI() constructor:
// In BotAI.cpp constructor
#include "Group/GroupEventBus.h"
BotAI::BotAI(Player* bot) : _bot(bot)
{
// ... existing initialization ...
// Subscribe to group events
auto eventBus = Playerbot::GroupEventBus::instance();
// Subscribe to member join/leave events
eventBus->Subscribe(_bot->GetGUID(), Playerbot::GroupEventType::MEMBER_JOINED,
[this](Playerbot::GroupEvent const& event) {
OnGroupMemberJoined(event);
});
eventBus->Subscribe(_bot->GetGUID(), Playerbot::GroupEventType::MEMBER_LEFT,
[this](Playerbot::GroupEvent const& event) {
OnGroupMemberLeft(event);
});
// Subscribe to leader change events
eventBus->Subscribe(_bot->GetGUID(), Playerbot::GroupEventType::LEADER_CHANGED,
[this](Playerbot::GroupEvent const& event) {
OnGroupLeaderChanged(event);
});
// Subscribe to loot method changes
eventBus->Subscribe(_bot->GetGUID(), Playerbot::GroupEventType::LOOT_METHOD_CHANGED,
[this](Playerbot::GroupEvent const& event) {
OnLootMethodChanged(event);
});
// Subscribe to raid conversion
eventBus->Subscribe(_bot->GetGUID(), Playerbot::GroupEventType::RAID_CONVERTED,
[this](Playerbot::GroupEvent const& event) {
OnRaidConverted(event);
});
// Subscribe to difficulty changes
eventBus->Subscribe(_bot->GetGUID(), Playerbot::GroupEventType::DIFFICULTY_CHANGED,
[this](Playerbot::GroupEvent const& event) {
OnDifficultyChanged(event);
});
// Add more subscriptions as needed...
}
Step 2: Unsubscribe on Destruction (BotAI Destructor)
// In BotAI.cpp destructor
BotAI::~BotAI()
{
// Unsubscribe from all events
Playerbot::GroupEventBus::instance()->UnsubscribeAll(_bot->GetGUID());
// ... existing cleanup ...
}
Step 3: Enable Per-Bot Group Polling (BotAI::Update)
// In BotAI::Update()
#include "Group/PlayerbotGroupScript.h"
void BotAI::Update(uint32 diff)
{
// ... existing update logic ...
// Poll group state changes (if bot is in a group)
if (Group* group = _bot->GetGroup())
{
Playerbot::PlayerbotGroupScript::PollGroupStateChanges(group, diff);
}
// ... rest of update logic ...
}
Step 4: Implement Event Handler Methods
Add handler methods to BotAI class:
// In BotAI.h
class BotAI
{
private:
// Group event handlers
void OnGroupMemberJoined(Playerbot::GroupEvent const& event);
void OnGroupMemberLeft(Playerbot::GroupEvent const& event);
void OnGroupLeaderChanged(Playerbot::GroupEvent const& event);
void OnLootMethodChanged(Playerbot::GroupEvent const& event);
void OnRaidConverted(Playerbot::GroupEvent const& event);
void OnDifficultyChanged(Playerbot::GroupEvent const& event);
// Add more as needed...
};
// In BotAI.cpp
void BotAI::OnGroupMemberJoined(Playerbot::GroupEvent const& event)
{
TC_LOG_DEBUG("bot.ai", "BotAI: Group member joined - Group: {}, Member: {}",
event.groupGuid.ToString(), event.targetGuid.ToString());
// Example: Update formation when new member joins
if (_combatCoordinator)
_combatCoordinator->UpdateFormation();
}
void BotAI::OnGroupMemberLeft(Playerbot::GroupEvent const& event)
{
TC_LOG_DEBUG("bot.ai", "BotAI: Group member left - Group: {}, Member: {}",
event.groupGuid.ToString(), event.targetGuid.ToString());
// Example: Adjust tactics when member leaves
if (_combatCoordinator)
_combatCoordinator->RecalculateGroupComposition();
}
void BotAI::OnGroupLeaderChanged(Playerbot::GroupEvent const& event)
{
TC_LOG_INFO("bot.ai", "BotAI: Group leader changed - New leader: {}",
event.targetGuid.ToString());
// Example: Update assist target if leader changed
if (event.targetGuid == _bot->GetGUID())
{
// Bot is now the leader
TC_LOG_INFO("bot.ai", "BotAI: Bot {} is now group leader", _bot->GetName());
}
}
void BotAI::OnLootMethodChanged(Playerbot::GroupEvent const& event)
{
TC_LOG_DEBUG("bot.ai", "BotAI: Loot method changed - Method: {}", event.data1);
// Example: Adjust looting behavior based on loot method
// data1 contains the new LootMethod value
}
void BotAI::OnRaidConverted(Playerbot::GroupEvent const& event)
{
bool isRaid = (event.data1 != 0);
TC_LOG_INFO("bot.ai", "BotAI: Group converted to {} - Group: {}",
isRaid ? "RAID" : "PARTY", event.groupGuid.ToString());
// Example: Switch to raid formations if converted to raid
if (isRaid && _combatCoordinator)
_combatCoordinator->SwitchToRaidFormation();
}
void BotAI::OnDifficultyChanged(Playerbot::GroupEvent const& event)
{
TC_LOG_DEBUG("bot.ai", "BotAI: Difficulty changed - Difficulty: {}", event.data1);
// Example: Adjust AI aggressiveness based on difficulty
// data1 contains the new difficulty ID
}
🎯 Event Types and Data
Available Events (via hooks + polling)
| Event Type | Trigger | data1 | data2 | targetGuid | Description |
|---|---|---|---|---|---|
| MEMBER_JOINED | OnAddMember hook | - | - | Member GUID | New member joined group |
| MEMBER_LEFT | OnRemoveMember hook | RemoveMethod | - | Member GUID | Member left group |
| LEADER_CHANGED | OnChangeLeader hook | - | - | New leader GUID | Leadership changed |
| GROUP_DISBANDED | OnDisband hook | - | - | - | Group disbanded |
| LOOT_METHOD_CHANGED | Polling | LootMethod | - | - | Loot method changed |
| LOOT_THRESHOLD_CHANGED | Polling | ItemQualities | - | - | Loot threshold changed |
| MASTER_LOOTER_CHANGED | Polling | - | - | Master looter GUID | Master looter changed |
| DIFFICULTY_CHANGED | Polling | Difficulty ID | - | - | Instance difficulty changed |
| RAID_CONVERTED | Polling | 1=raid, 0=party | - | - | Party/raid conversion |
| SUBGROUP_CHANGED | Polling | New subgroup | - | Member GUID | Member subgroup changed |
Unavailable Events (TrinityCore limitations)
- ❌ TARGET_ICON_CHANGED (no Group::GetTargetIcon() accessor)
- ❌ READY_CHECK_* (no Group::IsReadyCheckActive() accessor)
Workaround: Implement packet sniffing for SMSG_RAID_TARGET_UPDATE and SMSG_READY_CHECK packets
📊 Performance Considerations
Polling Overhead
- Per-bot polling: ~200 CPU cycles per poll
- Poll interval: 100-500ms recommended (configurable in BotAI::Update)
- 500 bots: ~0.03% CPU @ 3GHz with 100ms polling
Memory Usage
- GroupState cache: ~150 bytes per group
- Event queue: ~64 bytes per queued event
- Total: <10MB for 500 bot groups
Optimization Tips
- Poll only when in group: Check
_bot->GetGroup()before polling - Adjust poll interval: Use higher interval (500ms) for idle bots
- Batch event processing: GroupEventBus processes events in batches
- Unsubscribe unused events: Only subscribe to events you actually use
🧪 Testing the Integration
Test 1: Member Join/Leave
// Create test scenario
Bot1->InvitePlayerToGroup(Bot2);
Bot2->AcceptGroupInvite();
// Verify: OnGroupMemberJoined() called for both bots
Bot2->LeaveGroup();
// Verify: OnGroupMemberLeft() called for Bot1
Test 2: Loot Method Change
Group* group = Bot1->GetGroup();
group->SetLootMethod(LootMethod::MASTER_LOOT);
// Wait for next poll cycle (100ms)
// Verify: OnLootMethodChanged() called for all group bots
Test 3: Raid Conversion
Group* group = Bot1->GetGroup();
for (int i = 0; i < 5; i++)
group->AddMember(TestBot[i]);
group->ConvertToRaid();
// Verify: OnRaidConverted() called with data1 = 1
🔍 Debugging
Enable Debug Logging
// In worldserver.conf
Logger.Group = 4, Console Server # Enable group event logging
Logger.BotAI = 4, Console Server # Enable bot AI logging
Event Bus Statistics
// Get event statistics
auto stats = Playerbot::GroupEventBus::instance()->GetStatistics();
TC_LOG_INFO("bot.ai", "Event Bus Stats:\n"
" Total Published: {}\n"
" Total Delivered: {}\n"
" Pending: {}\n"
" Avg Process Time: {} μs",
stats.totalPublished, stats.totalDelivered, stats.pendingCount, stats.avgProcessTimeUs);
Dump Event Queue
// Dump pending events for debugging
Playerbot::GroupEventBus::instance()->DumpEventQueue();
✅ Integration Checklist
Before deploying, verify:
- BotAI subscribes to events in constructor
- BotAI unsubscribes in destructor
- BotAI::Update() calls PollGroupStateChanges()
- All event handlers implemented
- Event handlers update bot behavior appropriately
- Performance testing with 100+ bots
- Memory leak testing (no leaked subscriptions)
- Event delivery tested for all event types
🚀 Advanced Usage
Custom Event Priorities
// Publish custom high-priority event
Playerbot::GroupEvent event;
event.type = Playerbot::GroupEventType::MEMBER_JOINED;
event.priority = Playerbot::EventPriority::CRITICAL; // Process immediately
event.groupGuid = group->GetGUID();
event.targetGuid = newMember->GetGUID();
Playerbot::GroupEventBus::instance()->PublishEvent(event);
Event Filtering
// Subscribe with filter predicate
eventBus->Subscribe(_bot->GetGUID(), Playerbot::GroupEventType::MEMBER_JOINED,
[this](Playerbot::GroupEvent const& event) {
// Only handle if this is MY group
if (event.groupGuid == _bot->GetGroup()->GetGUID())
OnGroupMemberJoined(event);
});
Batch Event Processing
// Process multiple events at once
std::vector<Playerbot::GroupEvent> events;
eventBus->GetPendingEvents(_bot->GetGUID(), events, 10); // Get up to 10 events
for (auto const& event : events)
{
switch (event.type)
{
case Playerbot::GroupEventType::MEMBER_JOINED:
OnGroupMemberJoined(event);
break;
// ... handle other events ...
}
}
📚 Related Documentation
- GROUP_EVENT_SYSTEM_STATUS.md - System implementation status
- Group/GroupEventBus.h - Event bus API documentation
- Group/GroupStateMachine.h - State machine documentation
- Group/GroupEventHandler.h - Event handler implementations
- Group/PlayerbotGroupScript.h - TrinityCore integration