- UID
- 112
- 阅读权限
- 60
- 回帖
- 365
- 喵币
- 0
- 有爱
- 278
- DKP
- 631
- 金币
- 89318
- 在线时间
- 866 小时
- 注册时间
- 2016-8-6
- 最后登录
- 2026-2-16
声望: 6487   虚弱: 0

Lv.6(觉醒者)
     
|

楼主 |
发表于 2025-10-5 20:07:28
|
显示全部楼层
- PlayerbotsDatabase.h/cpp - 包含了对Playerbots相关数据库表的操作语句,如PLAYERBOTS_SEL_RANDOM_BOTS_VALUE等
- PlayerbotsScript.cpp - 包含了一些Playerbot脚本事件的接口,如OnPlayerbotUpdate等
---
- ``PlayerbotsDatabase.h`` 和``PlayerbotsDatabase.cpp`` - 这两个文件包含了Playerbots模块的数据库操作,包括随机机器人相关的SQL语句。从这些文件可以看出,有专门的表和操作来管理随机机器人,包括创建、删除、更新等操作。
- ``PlayerbotsScript.cpp`` - 这个文件定义了Playerbots模块的一些脚本接口,包括LFG队列检查、任务处理、会话更新等功能。特别是``OnPlayerbotUpdate(uint32 diff)`` 函数,这可能是机器人更新的关键入口点。
---
1. ``PlayerbotAI.cpp`` - 机器人AI逻辑实现
2. ``PlayerbotMgr.cpp`` - 机器人管理器实现
3. ``RandomPlayerbotMgr.cpp`` - 随机机器人管理器实现
4. ``PlayerbotAIConfig.cpp`` - AI配置实现
---
- ``PlayerbotHolder`` - 基础的机器人持有者类,管理机器人的登录、登出等
- ``PlayerbotMgr`` - 每个主玩家(master)对应的机器人管理器
- ``PlayerbotsMgr`` - 全局的机器人管理器单例
---
1. PlayerbotAI.cpp(约6604行):
- 这是机器人AI的核心实现文件
- 包含了AI对象上下文创建、不同状态(战斗/非战斗/死亡)的引擎初始化
- 有大量的包处理程序(masterIncomingPacketHandlers和botOutgoingPacketHandlers)
- 包含了与游戏对象交互、任务、组队、LFG等系统的集成
2. RandomPlayerbotMgr.cpp(约3578行):
- 这是随机机器人管理器的实现
- 包含了城市和银行家的映射数据
- 定义了多个线程函数:PrintStatsThread、CheckBgQueueThread、CheckLfgQueueThread、CheckPlayersThread
- 实现了一个PID控制器(botPID),可能用于控制机器人数量的平衡
这两个文件中与更新循环相关的部分,特别是PlayerbotAI中的更新方法和RandomPlayerbotMgr中的线程管理部分,因为这些很可能是导致性能堵塞的关键区域。
---
1. 在``PlayerbotAI.cpp`` 中,重要的``UpdateAI`` 方法(209行开始),这是机器人AI的核心更新循环。这个方法包含了:
- 检查机器人状态的逻辑
- 处理作弊选项的逻辑(设置生命值和能量)
- 处理当前法术的逻辑
- 处理移动的逻辑
- 更新AI内部状态的``UpdateAIInternal`` 调用
- 最重要的是,在每个更新结束时,调用了``YieldThread(GetReactDelay())`` (329行),这可能是控制线程执行的关键部分。
2. 在``RandomPlayerbotMgr.cpp`` 中,查看``UpdateAIInternal`` 方法的开始部分,这个方法是管理所有随机机器人的核心逻辑。它有性能监控``totalPmo = sPerformanceMonitor->start(PERF_MON_TOTAL, "RandomPlayerbotMgr::FullTick");`` 和一些机器人数量管理的逻辑。
---
1. PlayerbotAI::UpdateAI方法 :
- 这是机器人AI更新的主循环
- 每次更新结束时调用``YieldThread(GetReactDelay())``
- 这个方法在每次世界更新时都会为每个机器人调用,可能是性能瓶颈
2. RandomPlayerbotMgr::UpdateAIInternal方法 :
- 负责管理所有随机机器人的更新和登录/登出
- 包含复杂的机器人创建、登录、更新逻辑
- 有一些性能监控代码``PerformanceMonitorOperation`` ,这表明开发者已经意识到性能问题
3. YieldThread方法 :
- 在PlayerbotAI.cpp中被调用,但我还没有看到它的具体实现
- 它可能是导致线程阻塞的关键,因为它看起来是在控制线程执行和延迟
4. ProcessBot方法 :
- 在RandomPlayerbotMgr.cpp中被调用,但我还没有看到它的具体实现
- 它似乎负责处理单个机器人的更新逻辑
---
- 在``PlayerbotAIConfig.cpp`` 第165行:``iterationsPerTick = sConfigMgr->GetOption<int32>("AiPlayerbot.IterationsPerTick", 100);``
- 这表明默认值是100,但在配置文件中被覆盖为10
- 在``PlayerbotAIConfig.h`` 第295行:``uint32 iterationsPerTick;``
- 这是配置类中对该参数的定义
- 在``strategy\Engine.cpp`` 第156-158行:
- 这是该参数实际被使用的地方,特别是在AI引擎的循环中
- 这里使用的是``queue.Size() * sPlayerbotAIConfig->iterationsPerTick`` 作为循环次数上限
- 这意味着队列越大,循环次数越多,可能导致性能问题
---
1. iterationsPerTick的计算方式是:``queue.Size() * (minimal ? 2 : sPlayerbotAIConfig->iterationsPerTick)``
2. 然后有一个while循环,会执行iterationsPerTick次循环迭代
这里非常重要,因为:
- 当有500个机器人时,假设每个机器人的动作队列都有一定大小
- 目前配置的AiPlayerbot.IterationsPerTick=10,这会导致每个机器人每次更新都执行大量循环
- 当系统中有大量机器人时,这会造成严重的性能压力
---
1. 在Engine.cpp中,``iterationsPerTick`` 被计算为``queue.Size() * (minimal ? 2 : sPlayerbotAIConfig->iterationsPerTick)``
- 当不是minimal模式时,它会将动作队列的大小乘以配置文件中的``AiPlayerbot.IterationsPerTick`` 值
- 这个值用于限制每次AI更新循环中的最大迭代次数
2. 当前配置文件中的``AiPlayerbot.IterationsPerTick`` 设置为10,这意味着:
- 对于包含N个动作的队列,每个tick最多会处理N*10次迭代
- 当机器人数量增加时(如配置中设置的最大20个),这个计算会导致迭代次数成比例增加
- 如果机器人数量较多且动作队列较大,这可能会导致性能问题
3. 当我们考虑到用户提到的服务器在机器人数量达到20左右时会出现卡顿问题,这个参数很可能是导致性能下降的原因之一。
---.
Engine.cpp中的关键部分:
1. 这个循环是PlayerbotAI执行动作的核心部分。
2. 关键的性能问题点:
- ``iterationsPerTick = queue.Size() * (minimal ? 2 : sPlayerbotAIConfig->iterationsPerTick)`` - 这里将迭代次数与队列大小相乘,当队列很大时,这会导致迭代次数变得非常大。
- 没有看到任何的线程yield或sleep机制,这意味着循环会尽可能快地执行所有迭代,可能导致CPU占用过高。
- 有一个性能监控检查:``if (time(nullptr) - currentTime > 1)`` ,当执行时间超过1秒时会记录日志,但这只是监控而不是限制。
---
1. YieldThread函数的实现在PlayerbotAIBase.cpp中,它实际上只是设置了nextAICheckDelay参数,而不是真正的线程让出。
2. 这个函数的目的是通过设置检查延迟来控制AI更新的频率,而不是直接让出CPU时间片。
3. 从PlayerbotAIBase.cpp的UpdateAI函数可以看到,在每次AI更新后会调用YieldThread(),这会设置默认的检查延迟。
1. Engine.cpp中的循环会执行iterationsPerTick次迭代,这个值默认为10
2. 更重要的是,这个循环没有任何线程yield机制,导致CPU占用率高。
3. YieldThread函数只是设置延迟,并不会真正让出CPU时间片。
优化方案应该是:
1. 在Engine.cpp的循环中添加真正的线程yield机制,例如使用std::this_thread::yield()或std::this_thread::sleep_for()。
2. 或者修改YieldThread函数,使其不仅设置延迟,还实际让出CPU时间片。
---
1. iterationsPerTick 参数在 PlayerbotAIConfig.cpp 中被设置为配置文件中的值``AiPlayerbot.IterationsPerTick`` ,默认值是 100。
2. 在 Engine.cpp 中,iterationsPerTick 被用来控制每个 tick 中执行的操作数量:
3. 这是一个关键的性能问题 - iterationsPerTick 是一个乘数,乘以队列大小,这会导致大量的迭代操作。
4. YieldThread 函数并不是真正的线程让出,它只是设置下一次检查的延迟时间。
优化方案:
1. 减小 AiPlayerbot.IterationsPerTick 的值,从配置文件中的 10 改为更小的值,比如 2。
2. 在 Engine.cpp 的循环中添加真正的线程让出机制,使用 boost::this_thread::yield() 或其他适当的线程让出函数。
---
1. 修改了配置文件,将``AiPlayerbot.IterationsPerTick`` 从默认的 10 减小到了 2,这会显著减少每个AI更新周期中的迭代次数,从而减轻服务器负担。
2. 修改了``PlayerbotAIBase.cpp`` 中的``YieldThread`` 函数,添加了``boost::this_thread::yield()`` 调用,使其真正实现线程让出机制,而不仅仅是设置延迟。这样可以让Playerbot的AI线程更好地与其他线程共享CPU资源,避免长时间占用CPU导致其他线程饥饿。
优化的效果:
1. 减小``IterationsPerTick`` 会直接减少每个AI更新周期中的操作次数,从而减少CPU占用。当前配置为 2,相比默认的 10,理论上可以减少约 80% 的迭代操作。
2. 添加``boost::this_thread::yield()`` 调用会让AI线程在执行完一定操作后主动让出CPU时间片,允许其他线程有机会执行,从而改善整体系统的响应性和稳定性。
---
|
|