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

27 KiB

TrinityCore Playerbot API Reference

Version: WoW 11.2 Compatible Target: 5000 concurrent bots Module Location: src/modules/Playerbot/


Table of Contents

  1. Core APIs
  2. Subsystems
  3. Configuration
  4. Extension Points

Core APIs

BotSpawner

File: src/modules/Playerbot/Lifecycle/BotSpawner.h Singleton Access: sBotSpawner or BotSpawner::instance()

Bot lifecycle manager responsible for creating, spawning, and despawning bots with support for 5000 concurrent bots.

Core Methods

Initialize()
bool BotSpawner::Initialize()

Initializes the bot spawner system.

Returns: true on success, false on failure

Example:

if (!sBotSpawner->Initialize()) {
    TC_LOG_ERROR("module.playerbot", "Failed to initialize BotSpawner");
    return false;
}

SpawnBot(SpawnRequest const& request)
bool BotSpawner::SpawnBot(SpawnRequest const& request)

Spawns a single bot based on a spawn request.

Parameters:

  • request - Spawn request containing zone, level, class requirements

Returns: true if spawn initiated successfully

Thread Safety: Thread-safe with internal mutex protection

Example:

SpawnRequest request;
request.type = SpawnRequest::SPECIFIC_ZONE;
request.zoneId = 12; // Elwynn Forest
request.minLevel = 1;
request.maxLevel = 10;

if (sBotSpawner->SpawnBot(request)) {
    TC_LOG_INFO("module.playerbot", "Bot spawn request submitted");
}

Spawn Request Types:

enum SpawnRequestType {
    RANDOM,          // Spawn random bot in any zone
    SPECIFIC_ZONE,   // Spawn in specific zone
    SPECIFIC_MAP,    // Spawn in specific map
    FOLLOW_PLAYER    // Spawn near player for following
};

SpawnBots(std::vector<SpawnRequest> const& requests)
uint32 BotSpawner::SpawnBots(std::vector<SpawnRequest> const& requests)

Batch spawn multiple bots for better performance.

Parameters:

  • requests - Vector of spawn requests

Returns: Number of successfully queued requests

Performance: Optimized for batch processing with configurable batch size (default: 10 bots/batch)

Example:

std::vector<SpawnRequest> batch;
for (uint32 i = 0; i < 50; ++i) {
    SpawnRequest req;
    req.type = SpawnRequest::RANDOM;
    batch.push_back(req);
}

uint32 queued = sBotSpawner->SpawnBots(batch);
TC_LOG_INFO("module.playerbot", "Queued {} bot spawn requests", queued);

DespawnBot(ObjectGuid guid, bool forced = false)
void BotSpawner::DespawnBot(ObjectGuid guid, bool forced = false)

Despawns a specific bot.

Parameters:

  • guid - Character GUID of bot to despawn
  • forced - If true, force immediate despawn regardless of state

Thread Safety: Thread-safe

Example:

ObjectGuid botGuid = ...; // Get from player
sBotSpawner->DespawnBot(botGuid, false);

GetActiveBotCount()
uint32 BotSpawner::GetActiveBotCount() const

Returns the total number of active bots.

Returns: Number of currently active bots

Performance: Lock-free atomic operation - safe to call frequently

Example:

uint32 activeBots = sBotSpawner->GetActiveBotCount();
TC_LOG_INFO("module.playerbot", "Currently {} bots active", activeBots);

CreateAndSpawnBot()
bool BotSpawner::CreateAndSpawnBot(
    uint32 masterAccountId,
    uint8 classId,
    uint8 race,
    uint8 gender,
    std::string const& name,
    ObjectGuid& outCharacterGuid)

Creates a new bot character and spawns it immediately.

Parameters:

  • masterAccountId - Owner account ID
  • classId - Character class (1-13)
  • race - Character race
  • gender - Character gender (0=male, 1=female)
  • name - Character name
  • outCharacterGuid - Output parameter for created character GUID

Returns: true on success, false on failure

Used By: .bot spawn command

Example:

ObjectGuid characterGuid;
if (sBotSpawner->CreateAndSpawnBot(
    accountId,
    CLASS_WARRIOR,
    RACE_HUMAN,
    GENDER_MALE,
    "BotWarrior",
    characterGuid))
{
    TC_LOG_INFO("module.playerbot", "Created and spawned bot: {}", characterGuid.ToString());
}

GetStats()
SpawnStats const& BotSpawner::GetStats() const

Retrieves spawn statistics for monitoring.

Returns: Reference to spawn statistics structure

Statistics Available:

struct SpawnStats {
    std::atomic<uint32> totalSpawned;      // Total bots spawned
    std::atomic<uint32> totalDespawned;    // Total bots despawned
    std::atomic<uint32> currentlyActive;   // Currently active
    std::atomic<uint32> peakConcurrent;    // Peak concurrent count
    std::atomic<uint32> failedSpawns;      // Failed spawn attempts
    std::atomic<uint64> totalSpawnTime;    // Total spawn time (µs)
    std::atomic<uint32> spawnAttempts;     // Total attempts

    float GetAverageSpawnTime() const;     // Average time in ms
    float GetSuccessRate() const;          // Success rate %
};

Example:

SpawnStats const& stats = sBotSpawner->GetStats();
TC_LOG_INFO("module.playerbot", "Bot Statistics:");
TC_LOG_INFO("module.playerbot", "  Total Spawned: {}", stats.totalSpawned.load());
TC_LOG_INFO("module.playerbot", "  Currently Active: {}", stats.currentlyActive.load());
TC_LOG_INFO("module.playerbot", "  Success Rate: {:.2f}%", stats.GetSuccessRate());
TC_LOG_INFO("module.playerbot", "  Avg Spawn Time: {:.2f}ms", stats.GetAverageSpawnTime());

Configuration

struct SpawnConfig {
    uint32 maxBotsTotal = 500;           // Global bot limit
    uint32 maxBotsPerZone = 50;          // Per-zone limit
    uint32 maxBotsPerMap = 200;          // Per-map limit
    uint32 spawnBatchSize = 10;          // Batch processing size
    uint32 spawnDelayMs = 100;           // Delay between batches
    bool enableDynamicSpawning = true;   // Auto-spawn based on players
    bool respectPopulationCaps = true;   // Enforce limits
    float botToPlayerRatio = 2.0f;       // Bots per player
};

Access:

SpawnConfig const& config = sBotSpawner->GetConfig();

Modify:

sBotSpawner->SetMaxBots(1000);
sBotSpawner->SetBotToPlayerRatio(3.0f);

BotSession

File: src/modules/Playerbot/Session/BotSession.h Inherits: WorldSession

Represents a bot's world session without network socket.

Core Methods

Create(uint32 bnetAccountId)
static std::shared_ptr<BotSession> BotSession::Create(uint32 bnetAccountId)

Factory method to create a bot session.

Parameters:

  • bnetAccountId - Battle.net account ID

Returns: Shared pointer to created session

Thread Safety: Thread-safe

Example:

auto session = BotSession::Create(accountId);
if (!session) {
    TC_LOG_ERROR("module.playerbot", "Failed to create bot session");
    return;
}

LoginCharacter(ObjectGuid characterGuid)
bool BotSession::LoginCharacter(ObjectGuid characterGuid)

Asynchronously loads and logs in a bot character.

Parameters:

  • characterGuid - Character GUID to login

Returns: true if login initiated successfully

Async: Uses TrinityCore's async query holder pattern

Example:

if (botSession->LoginCharacter(characterGuid)) {
    TC_LOG_INFO("module.playerbot", "Bot login initiated for {}", characterGuid.ToString());
    // Login completes asynchronously via HandleBotPlayerLogin callback
}

Update(uint32 diff, PacketFilter& updater)
bool BotSession::Update(uint32 diff, PacketFilter& updater)

Updates bot session (called every frame).

Parameters:

  • diff - Time since last update (milliseconds)
  • updater - Packet filter for processing

Returns: true if session is still active

Called By: BotWorldSessionMgr::Update()

Performance: Includes comprehensive memory safety checks


GetAI()
BotAI* BotSession::GetAI() const

Retrieves the bot's AI controller.

Returns: Pointer to BotAI or nullptr

Example:

if (BotAI* ai = botSession->GetAI()) {
    ai->SetAIState(BotAIState::COMBAT);
}

SendPacket(WorldPacket const* packet, bool forced = false)
void BotSession::SendPacket(WorldPacket const* packet, bool forced = false) override

Sends a packet to the bot (stored in queue, no network).

Parameters:

  • packet - Packet to send
  • forced - Ignored for bots (no network)

Special Handling: Intercepts SMSG_PARTY_INVITE for automatic group joining

Example:

WorldPacket packet(SMSG_PARTY_INVITE);
// ... build packet
botSession->SendPacket(&packet);

Login State Management

enum class LoginState : uint8 {
    NONE,                // Not logging in
    LOGIN_IN_PROGRESS,   // Async login in progress
    LOGIN_COMPLETE,      // Login successful
    LOGIN_FAILED         // Login failed
};

State Queries:

LoginState state = botSession->GetLoginState();
bool loggedIn = botSession->IsLoginComplete();
bool failed = botSession->IsLoginFailed();

BotAI

File: src/modules/Playerbot/AI/BotAI.h Factory: sBotAIFactory->CreateAI(Player* bot)

Base AI controller for all bots with strategy-based behavior system.

Core Methods

UpdateAI(uint32 diff)
virtual void BotAI::UpdateAI(uint32 diff)

Main update method - SINGLE entry point for all AI updates.

Parameters:

  • diff - Time since last update (milliseconds)

Update Flow:

  1. Update core behaviors (strategies, movement, idle)
  2. Check combat state transitions
  3. If in combat, call OnCombatUpdate() for class-specific logic

Performance: Runs every frame - no throttling for smooth movement

Override: Do NOT override - use OnCombatUpdate() instead


OnCombatUpdate(uint32 diff)
virtual void BotAI::OnCombatUpdate(uint32 diff)

Virtual method for class-specific COMBAT ONLY updates.

Parameters:

  • diff - Time since last update (milliseconds)

Called When: Bot is in combat state

Override: ClassAI implementations should override for combat rotations

Must Not:

  • Control movement (handled by strategies)
  • Throttle updates (causes following issues)
  • Call base UpdateAI (would cause recursion)

Example (in ClassAI):

void WarriorAI::OnCombatUpdate(uint32 diff) {
    Unit* target = GetTargetUnit();
    if (!target)
        return;

    // Execute rotation
    UpdateRotation(target);

    // Update cooldowns
    UpdateCooldowns(diff);
}

Strategy Management
AddStrategy(std::unique_ptr<Strategy> strategy)
void BotAI::AddStrategy(std::unique_ptr<Strategy> strategy)

Registers a new strategy with the AI.

Parameters:

  • strategy - Unique pointer to strategy (ownership transferred)

Example:

auto followStrategy = std::make_unique<LeaderFollowBehavior>();
botAI->AddStrategy(std::move(followStrategy));

ActivateStrategy(std::string const& name)
void BotAI::ActivateStrategy(std::string const& name)

Activates a registered strategy.

Parameters:

  • name - Strategy name

Thread Safety: Thread-safe with mutex

Example:

botAI->ActivateStrategy("follow");

DeactivateStrategy(std::string const& name)
void BotAI::DeactivateStrategy(std::string const& name)

Deactivates an active strategy.

Parameters:

  • name - Strategy name

Example:

botAI->DeactivateStrategy("follow");

Group Management
OnGroupJoined(Group* group)
void BotAI::OnGroupJoined(Group* group)

Called when bot joins a group.

Parameters:

  • group - Group joined

Actions:

  • Activates "follow" strategy
  • Activates "group_combat" strategy
  • Sets AI state to FOLLOWING

Example:

if (Group* group = bot->GetGroup()) {
    botAI->OnGroupJoined(group);
}

OnGroupLeft()
void BotAI::OnGroupLeft()

Called when bot leaves a group.

Actions:

  • Deactivates "follow" strategy
  • Deactivates "group_combat" strategy
  • Sets AI state to IDLE

State Management
GetAIState()
BotAIState BotAI::GetAIState() const

Returns current AI state.

AI States:

enum class BotAIState {
    IDLE,        // Doing nothing
    COMBAT,      // In combat
    DEAD,        // Dead
    TRAVELLING,  // Moving to destination
    QUESTING,    // Doing quests
    GATHERING,   // Gathering resources
    TRADING,     // At vendor/trading
    FOLLOWING,   // Following group leader
    FLEEING,     // Running away
    RESTING      // Resting for health/mana
};

SetAIState(BotAIState state)
void BotAI::SetAIState(BotAIState state)

Sets AI state with logging.

Parameters:

  • state - New AI state

Action System
QueueAction(std::shared_ptr<Action> action, ActionContext const& context)
void BotAI::QueueAction(std::shared_ptr<Action> action, ActionContext const& context = {})

Queues an action for execution.

Parameters:

  • action - Shared pointer to action
  • context - Optional action context

Example:

auto healAction = std::make_shared<HealAction>(targetGuid, spellId);
botAI->QueueAction(healAction);

CancelCurrentAction()
void BotAI::CancelCurrentAction()

Cancels currently executing action.


Performance Metrics
struct PerformanceMetrics {
    std::atomic<uint32> totalUpdates;
    std::atomic<uint32> actionsExecuted;
    std::atomic<uint32> triggersProcessed;
    std::atomic<uint32> strategiesEvaluated;
    std::chrono::microseconds averageUpdateTime;
    std::chrono::microseconds maxUpdateTime;
    std::chrono::steady_clock::time_point lastUpdate;
};

Access:

PerformanceMetrics const& metrics = botAI->GetPerformanceMetrics();
TC_LOG_INFO("module.playerbot", "AI Performance:");
TC_LOG_INFO("module.playerbot", "  Total Updates: {}", metrics.totalUpdates.load());
TC_LOG_INFO("module.playerbot", "  Avg Update Time: {}µs", metrics.averageUpdateTime.count());

ClassAI

File: src/modules/Playerbot/AI/ClassAI/ClassAI.h Inherits: BotAI Factory: ClassAIFactory::CreateClassAI(Player* bot)

Base class for class-specific combat AI implementations.

Design Principles

  1. Combat Only: ClassAI handles ONLY combat specialization
  2. No Base Override: Never override UpdateAI() - use OnCombatUpdate()
  3. No Movement Control: Let BotAI strategies handle movement
  4. No Throttling: Don't throttle updates - causes following issues

Pure Virtual Methods

UpdateRotation(Unit* target)
virtual void ClassAI::UpdateRotation(::Unit* target) = 0

Executes class-specific combat rotation.

Parameters:

  • target - Current combat target

Must Implement: All derived ClassAI classes

Example (Warrior):

void WarriorAI::UpdateRotation(Unit* target) {
    // Execute warrior rotation
    if (CanUseAbility(SPELL_MORTAL_STRIKE) && IsSpellReady(SPELL_MORTAL_STRIKE)) {
        CastSpell(target, SPELL_MORTAL_STRIKE);
        return;
    }

    if (GetBot()->GetRage() > 30 && CanUseAbility(SPELL_HEROIC_STRIKE)) {
        CastSpell(target, SPELL_HEROIC_STRIKE);
    }
}

UpdateBuffs()
virtual void ClassAI::UpdateBuffs() = 0

Applies class-specific buffs.

Called: When not in combat or between combats

Example (Paladin):

void PaladinAI::UpdateBuffs() {
    // Maintain seal
    if (!HasAura(SPELL_SEAL_OF_RIGHTEOUSNESS)) {
        CastSpell(SPELL_SEAL_OF_RIGHTEOUSNESS);
    }

    // Apply blessing
    if (Group* group = GetBot()->GetGroup()) {
        for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) {
            Player* member = ref->GetSource();
            if (member && !HasAura(SPELL_BLESSING_OF_KINGS, member)) {
                CastSpell(member, SPELL_BLESSING_OF_KINGS);
            }
        }
    }
}

HasEnoughResource(uint32 spellId)
virtual bool ClassAI::HasEnoughResource(uint32 spellId) = 0

Checks if bot has sufficient resources for a spell.

Parameters:

  • spellId - Spell to check

Returns: true if sufficient resources


GetOptimalRange(Unit* target)
virtual float ClassAI::GetOptimalRange(::Unit* target) = 0

Returns optimal combat range for the class.

Parameters:

  • target - Combat target

Returns: Preferred combat range in yards

Example:

float MageAI::GetOptimalRange(Unit* target) {
    return 30.0f; // Ranged caster - maintain distance
}

float RogueAI::GetOptimalRange(Unit* target) {
    return 5.0f; // Melee - close combat
}

Utility Methods

Spell Casting
bool CastSpell(::Unit* target, uint32 spellId);  // Cast on target
bool CastSpell(uint32 spellId);                  // Self-cast
bool IsSpellReady(uint32 spellId);               // Check cooldown
bool IsInRange(::Unit* target, uint32 spellId);  // Check range
bool IsSpellUsable(uint32 spellId);              // Check usability
uint32 GetSpellCooldown(uint32 spellId);         // Get remaining CD

Example:

if (IsSpellReady(SPELL_FIREBALL) && IsInRange(target, SPELL_FIREBALL)) {
    if (CastSpell(target, SPELL_FIREBALL)) {
        TC_LOG_DEBUG("module.playerbot", "Cast Fireball on {}", target->GetName());
    }
}

Target Selection
::Unit* GetBestAttackTarget();                   // Best DPS target
::Unit* GetBestHealTarget();                     // Lowest health ally
::Unit* GetNearestEnemy(float maxRange = 30.0f); // Nearest enemy
::Unit* GetLowestHealthAlly(float maxRange = 40.0f); // For healing

Example:

Unit* healTarget = GetBestHealTarget();
if (healTarget && healTarget->GetHealthPct() < 50.0f) {
    CastSpell(healTarget, SPELL_HEAL);
}

Aura Management
bool HasAura(uint32 spellId, ::Unit* target = nullptr);
uint32 GetAuraStacks(uint32 spellId, ::Unit* target = nullptr);
uint32 GetAuraRemainingTime(uint32 spellId, ::Unit* target = nullptr);

Example:

if (HasAura(SPELL_BLOODLUST)) {
    // Execute burst rotation
}

uint32 stacks = GetAuraStacks(SPELL_SUNDER_ARMOR, target);
if (stacks < 5) {
    CastSpell(target, SPELL_SUNDER_ARMOR);
}

Subsystems

Group Coordination

File: src/modules/Playerbot/Group/GroupInvitationHandler.h

GroupInvitationHandler

Handles automatic group invitation acceptance for bots.

class GroupInvitationHandler {
public:
    bool HandleInvitation(WorldPackets::Party::PartyInvite const& invite);
    void Update(uint32 diff);
    void ProcessPendingInvitations();
};

Usage:

if (BotAI* ai = botSession->GetAI()) {
    GroupInvitationHandler* handler = ai->GetGroupInvitationHandler();
    handler->Update(diff);
}

Movement & Positioning

File: src/modules/Playerbot/Movement/LeaderFollowBehavior.h

LeaderFollowBehavior

Strategy for following group leader.

class LeaderFollowBehavior : public Strategy {
public:
    void UpdateFollowBehavior(BotAI* ai, uint32 diff);
    float GetFollowDistance() const;
    void SetFollowDistance(float distance);
};

Default Distances:

  • Melee: 5 yards
  • Ranged: 15 yards
  • Healers: 20 yards

Usage:

auto* followStrategy = botAI->GetStrategy("follow");
if (auto* followBehavior = dynamic_cast<LeaderFollowBehavior*>(followStrategy)) {
    followBehavior->SetFollowDistance(10.0f);
}

Combat Mechanics

File: src/modules/Playerbot/AI/Combat/InterruptCoordinator.h

InterruptCoordinator

Coordinates spell interrupts across multiple bots to prevent overlap.

class InterruptCoordinator {
public:
    bool RegisterInterrupt(ObjectGuid botGuid, ObjectGuid targetGuid, uint32 spellId);
    bool CanInterrupt(ObjectGuid botGuid, ObjectGuid targetGuid);
    void ClearInterrupts(ObjectGuid targetGuid);
};

Usage:

if (sInterruptCoordinator->CanInterrupt(bot->GetGUID(), target->GetGUID())) {
    CastSpell(target, SPELL_KICK);
    sInterruptCoordinator->RegisterInterrupt(bot->GetGUID(), target->GetGUID(), SPELL_KICK);
}

Database Integration

File: src/modules/Playerbot/Database/PlayerbotCharacterDBInterface.h

PlayerbotCharacterDBInterface

Provides safe async/sync database access routing.

class PlayerbotCharacterDBInterface {
public:
    PreparedQueryResult ExecuteSync(CharacterDatabasePreparedStatement* stmt);
    void ExecuteAsync(CharacterDatabasePreparedStatement* stmt, std::function<void(PreparedQueryResult)> callback);
    CharacterDatabaseTransaction BeginTransaction();
    void CommitTransaction(CharacterDatabaseTransaction transaction);
};

Singleton: sPlayerbotCharDB

Example:

CharacterDatabasePreparedStatement* stmt = sPlayerbotCharDB->GetPreparedStatement(CHAR_SEL_CHARACTER);
stmt->setUInt64(0, characterGuid.GetCounter());

sPlayerbotCharDB->ExecuteAsync(stmt, [](PreparedQueryResult result) {
    if (result) {
        // Process result
    }
});

Performance Monitoring

File: src/modules/Playerbot/Performance/BotPerformanceMonitor.h

BotPerformanceMonitor

Monitors bot performance metrics.

class BotPerformanceMonitor {
public:
    void RecordBotUpdate(ObjectGuid botGuid, uint32 updateTimeMs);
    void RecordMemoryUsage(ObjectGuid botGuid, uint64 memoryBytes);
    PerformanceReport GenerateReport();
};

Metrics Tracked:

  • Update time per bot
  • Memory usage per bot
  • CPU usage estimation
  • Database query performance
  • AI decision time

Example:

auto report = sBotPerformanceMonitor->GenerateReport();
TC_LOG_INFO("module.playerbot", "Performance Report:");
TC_LOG_INFO("module.playerbot", "  Average Update Time: {:.2f}ms", report.avgUpdateTime);
TC_LOG_INFO("module.playerbot", "  Total Memory: {} MB", report.totalMemoryMB);

Configuration

File: playerbots.conf C++ API: src/modules/Playerbot/Config/PlayerbotConfig.h

Configuration Access

bool enabled = sPlayerbotConfig->GetBool("Playerbot.Enable", false);
uint32 maxBots = sPlayerbotConfig->GetUInt("Playerbot.MaxBotsPerAccount", 10);
float ratio = sPlayerbotConfig->GetFloat("Playerbot.BotToPlayerRatio", 2.0f);
std::string logFile = sPlayerbotConfig->GetString("Playerbot.Log.File", "Playerbot.log");

Key Configuration Options

# Core Settings
Playerbot.Enable = 1
Playerbot.MaxBotsPerAccount = 10
Playerbot.GlobalMaxBots = 1000

# Performance
Playerbot.UpdateInterval = 100
Playerbot.AIDecisionTimeLimit = 50
Playerbot.SpawnBatchSize = 10

# Database
Playerbot.Database.Timeout = 30
Playerbot.Database.EnablePooling = 1

# Logging
Playerbot.Log.Level = 4
Playerbot.Log.File = "Playerbot.log"

Performance Cache

Frequently accessed config values are cached for performance:

template<typename T>
T GetCached(std::string const& key, T defaultValue) const;

Example:

// Fast cached access - no mutex lock
uint32 maxBots = sPlayerbotConfig->GetCached<uint32>("Playerbot.MaxBotsPerAccount", 10);

Extension Points

Creating Custom Strategies

File: src/modules/Playerbot/AI/Strategy/Strategy.h

class MyCustomStrategy : public Strategy {
public:
    std::string GetName() const override { return "my_custom"; }

    bool IsActive(BotAI const* ai) const override {
        // Determine if strategy should be active
        return true;
    }

    void UpdateBehavior(BotAI* ai, uint32 diff) override {
        // Implement custom behavior
    }

    void OnActivate(BotAI* ai) override {
        TC_LOG_INFO("module.playerbot", "Custom strategy activated");
    }

    void OnDeactivate(BotAI* ai) override {
        TC_LOG_INFO("module.playerbot", "Custom strategy deactivated");
    }
};

// Register and use
auto customStrategy = std::make_unique<MyCustomStrategy>();
botAI->AddStrategy(std::move(customStrategy));
botAI->ActivateStrategy("my_custom");

Creating Custom Actions

File: src/modules/Playerbot/AI/Actions/Action.h

class MyCustomAction : public Action {
public:
    bool IsPossible(BotAI* ai) override {
        // Check if action can be executed
        return true;
    }

    bool IsUseful(BotAI* ai) override {
        // Check if action should be executed
        return true;
    }

    ActionResult Execute(BotAI* ai, ActionContext const& context) override {
        // Execute action logic
        TC_LOG_INFO("module.playerbot", "Executing custom action");
        return ActionResult::SUCCESS;
    }
};

// Queue action
auto action = std::make_shared<MyCustomAction>();
botAI->QueueAction(action);

Creating Custom Triggers

File: src/modules/Playerbot/AI/Triggers/Trigger.h

class MyCustomTrigger : public Trigger {
public:
    bool Check(BotAI const* ai) override {
        // Fast pre-check
        return ai->GetBot()->GetHealthPct() < 50.0f;
    }

    TriggerResult Evaluate(BotAI* ai) override {
        TriggerResult result;
        result.triggered = true;
        result.urgency = 100.0f;
        result.suggestedAction = std::make_shared<MyCustomAction>();
        return result;
    }
};

// Register trigger
auto trigger = std::make_shared<MyCustomTrigger>();
botAI->RegisterTrigger(trigger);

Performance Considerations

Thread Safety

Thread-Safe Components:

  • BotSpawner (internal mutex)
  • BotSession (packet queue mutex)
  • BotAI (strategy mutex)

Lock-Free Components:

  • GetActiveBotCount() (atomic)
  • Spawn statistics (atomic counters)
  • Bot state tracking (atomic flags)

Memory Optimization

Target: <10MB per bot

Strategies:

  • Object pooling for frequently created objects
  • Shared spell info caching
  • Lazy loading of AI components
  • Smart pointer usage for RAII cleanup

Database Optimization

Async Pattern:

// GOOD - Async with callback
sPlayerbotCharDB->ExecuteAsync(stmt, [](PreparedQueryResult result) {
    // Process result on callback thread
});

// BAD - Synchronous blocking
PreparedQueryResult result = CharacterDatabase.Query(stmt); // Blocks!

Connection Pooling:

  • Dedicated connection pool for bot operations
  • Configurable pool size
  • Automatic connection recycling

Error Handling

Common Patterns

// Validate inputs
if (!bot || !bot->IsInWorld()) {
    TC_LOG_ERROR("module.playerbot", "Invalid bot state");
    return false;
}

// Handle async failures
sPlayerbotCharDB->ExecuteAsync(stmt, [this](PreparedQueryResult result) {
    if (!result) {
        TC_LOG_ERROR("module.playerbot", "Database query failed");
        return;
    }
    // Process result
});

// Memory safety checks
if (!_active.load() || _destroyed.load()) {
    return; // Session being destroyed
}

Logging Categories

TC_LOG_ERROR("module.playerbot.spawner", "Spawn failed");
TC_LOG_WARN("module.playerbot.session", "Session timeout");
TC_LOG_INFO("module.playerbot.ai", "AI state changed");
TC_LOG_DEBUG("module.playerbot.combat", "Spell cast: {}", spellId);
TC_LOG_TRACE("module.playerbot.movement", "Position updated");

Version History

Current: WoW 11.2 Compatible Last Updated: 2025-10-03 API Stability: Stable (production-ready)

For bug reports and feature requests, see project issue tracker.