Skip to content

Commit 6be860c

Browse files
hermensbasRegrad
andauthored
[Stability] Various crash fixes based on Regrad fixes and crashlogs. (mod-playerbots#1928)
These contains various fixes, fixes that have history worked one in past more then once as person as group, aswell @Wishmaster117. But due various reasons we had to drop them due priority or simply timewise. These fixes have recollected again by @Regrad based on his crash logs. Most crash logs we have, i am talking 30+ of them, to many to post here. @Regrad running a larger server 100+ real players with bots, which means he will walk into issues that most of us wont or are extremely difficult to reproduce. @Regrad used LLM to solve them based on crash log and mentioned his server crashes almost disappeared, instead of redoing every single PR and pull them apart. I tried to keep his bunch of changes together as whole, reviewed them, some redone, verified again etc etc. This is not how would normally do this. But since i want @Regrad being able to confirm, we need this in a package as a whole. Pulling them apart in the current situation is simply to much, to complicated in the verification process. So this PR is open and in my opinion has priority above others, but @Regrad is only person who can give the green light for the mod-playerbot changes for now. I, we spend huge amount of time into these issues over last couple of months. I will put other PR's on hold for abit. --------- Signed-off-by: Engardium <regradius@gmail.com> Co-authored-by: Engardium <regradius@gmail.com>
1 parent 9971622 commit 6be860c

12 files changed

Lines changed: 545 additions & 429 deletions

src/PlayerbotAI.cpp

Lines changed: 92 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -242,8 +242,8 @@ void PlayerbotAI::UpdateAI(uint32 elapsed, bool minimal)
242242
nextAICheckDelay = 0;
243243

244244
// Early return if bot is in invalid state
245-
if (!bot || !bot->IsInWorld() || !bot->GetSession() || bot->GetSession()->isLogingOut() ||
246-
bot->IsDuringRemoveFromWorld())
245+
if (!bot || !bot->GetSession() || !bot->IsInWorld() || bot->IsBeingTeleported() ||
246+
bot->GetSession()->isLogingOut() || bot->IsDuringRemoveFromWorld())
247247
return;
248248

249249
// Handle cheat options (set bot health and power if cheats are enabled)
@@ -713,39 +713,59 @@ void PlayerbotAI::HandleTeleportAck()
713713
if (IsRealPlayer())
714714
return;
715715

716+
// Clearing motion generators and stopping movement which prevents
717+
// conflicts between teleport and any active motion (walk, run, swim, flight, etc.)
716718
bot->GetMotionMaster()->Clear(true);
717719
bot->StopMoving();
720+
721+
// Near teleport (within map/instance)
718722
if (bot->IsBeingTeleportedNear())
719723
{
720-
// Temporary fix for instance can not enter
721-
if (!bot->IsInWorld())
722-
{
723-
bot->GetMap()->AddPlayerToMap(bot);
724-
}
725-
while (bot->IsInWorld() && bot->IsBeingTeleportedNear())
726-
{
727-
Player* plMover = bot->m_mover->ToPlayer();
728-
if (!plMover)
729-
return;
730-
WorldPacket p = WorldPacket(MSG_MOVE_TELEPORT_ACK, 20);
731-
p << plMover->GetPackGUID();
732-
p << (uint32)0; // supposed to be flags? not used currently
733-
p << (uint32)0; // time - not currently used
734-
bot->GetSession()->HandleMoveTeleportAck(p);
735-
};
724+
// Previous versions manually added the bot to the map if it was not in the world.
725+
// not needed: HandleMoveTeleportAckOpcode() safely attaches the player to the map
726+
// and clears IsBeingTeleportedNear().
727+
728+
Player* plMover = bot->m_mover->ToPlayer();
729+
if (!plMover)
730+
return;
731+
732+
// Send the near teleport ACK packet
733+
WorldPacket p(MSG_MOVE_TELEPORT_ACK, 20);
734+
p << plMover->GetPackGUID();
735+
p << uint32(0);
736+
p << uint32(0);
737+
bot->GetSession()->HandleMoveTeleportAck(p);
738+
739+
// Simulate teleport latency and prevent AI from running too early (used cmangos delays)
740+
SetNextCheckDelay(urand(1000, 2000));
736741
}
742+
743+
// Far teleport (worldport / different map)
737744
if (bot->IsBeingTeleportedFar())
738745
{
739-
while (bot->IsBeingTeleportedFar())
746+
// Handle far teleport ACK:
747+
// Moves the bot to the new map, clears IsBeingTeleportedFar(), updates session/map references
748+
bot->GetSession()->HandleMoveWorldportAck();
749+
750+
// Ensure bot now has a valid map. If this fails, there is a core/session bug?
751+
if (!bot->GetMap())
740752
{
741-
bot->GetSession()->HandleMoveWorldportAck();
753+
LOG_ERROR("playerbot", "Bot {} has no map after worldport ACK", bot->GetGUID().ToString());
742754
}
743-
// SetNextCheckDelay(urand(2000, 5000));
755+
756+
// Instance strategies after teleport
744757
if (sPlayerbotAIConfig->applyInstanceStrategies)
745758
ApplyInstanceStrategies(bot->GetMapId(), true);
759+
760+
// healer DPS strategies if restrictions are enabled
746761
if (sPlayerbotAIConfig->restrictHealerDPS)
747762
EvaluateHealerDpsStrategy();
763+
764+
// Reset AI state to to before teleport conditions
748765
Reset(true);
766+
767+
// Slightly longer delay to simulate far teleport latency (used cmangos delays)
768+
SetNextCheckDelay(urand(2000, 5000));
749769
}
750770

751771
SetNextCheckDelay(sPlayerbotAIConfig->globalCoolDown);
@@ -988,10 +1008,10 @@ void PlayerbotAI::HandleBotOutgoingPacket(WorldPacket const& packet)
9881008
{
9891009
if (packet.empty())
9901010
return;
1011+
9911012
if (!bot || !bot->IsInWorld() || bot->IsDuringRemoveFromWorld())
992-
{
9931013
return;
994-
}
1014+
9951015
switch (packet.GetOpcode())
9961016
{
9971017
case SMSG_SPELL_FAILURE:
@@ -4103,8 +4123,7 @@ Player* PlayerbotAI::FindNewMaster()
41034123
for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next())
41044124
{
41054125
Player* member = gref->GetSource();
4106-
if (!member || member == bot || !member->IsInWorld() ||
4107-
!member->IsInSameRaidWith(bot))
4126+
if (!member || member == bot || !member->IsInWorld() || !member->IsInSameRaidWith(bot))
41084127
continue;
41094128

41104129
PlayerbotAI* memberBotAI = GET_PLAYERBOT_AI(member);
@@ -4339,6 +4358,11 @@ inline bool ZoneHasRealPlayers(Player* bot)
43394358

43404359
bool PlayerbotAI::AllowActive(ActivityType activityType)
43414360
{
4361+
// Early return if bot is in invalid state
4362+
if (!bot || !bot->GetSession() || !bot->IsInWorld() || bot->IsBeingTeleported() ||
4363+
bot->GetSession()->isLogingOut() || bot->IsDuringRemoveFromWorld())
4364+
return false;
4365+
43424366
// when botActiveAlone is 100% and smartScale disabled
43434367
if (sPlayerbotAIConfig->botActiveAlone >= 100 && !sPlayerbotAIConfig->botActiveAloneSmartScale)
43444368
{
@@ -4429,10 +4453,8 @@ bool PlayerbotAI::AllowActive(ActivityType activityType)
44294453
for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next())
44304454
{
44314455
Player* member = gref->GetSource();
4432-
if ((!member || !member->IsInWorld()) && member->GetMapId() != bot->GetMapId())
4433-
{
4456+
if (!member || !member->IsInWorld() || member->GetMapId() != bot->GetMapId())
44344457
continue;
4435-
}
44364458

44374459
if (member == bot)
44384460
{
@@ -4483,23 +4505,23 @@ bool PlayerbotAI::AllowActive(ActivityType activityType)
44834505
// HasFriend
44844506
if (sPlayerbotAIConfig->BotActiveAloneForceWhenIsFriend)
44854507
{
4486-
if (!bot || !bot->IsInWorld() || !bot->GetGUID())
4508+
// shouldnt be needed analyse in future
4509+
if (!bot->GetGUID())
44874510
return false;
44884511

44894512
for (auto& player : sRandomPlayerbotMgr->GetPlayers())
44904513
{
4491-
if (!player || !player->IsInWorld())
4514+
if (!player || !player->GetSession() || !player->IsInWorld() || player->IsDuringRemoveFromWorld() ||
4515+
player->GetSession()->isLogingOut())
44924516
continue;
44934517

4494-
Player* connectedPlayer = ObjectAccessor::FindPlayer(player->GetGUID());
4495-
if (!connectedPlayer)
4518+
PlayerbotAI* playerAI = GET_PLAYERBOT_AI(player);
4519+
if (!playerAI || !playerAI->IsRealPlayer())
44964520
continue;
44974521

4522+
// if a real player has the bot as a friend
44984523
PlayerSocial* social = player->GetSocial();
4499-
if (!social)
4500-
continue;
4501-
4502-
if (social->HasFriend(bot->GetGUID()))
4524+
if (social && social->HasFriend(bot->GetGUID()))
45034525
return true;
45044526
}
45054527
}
@@ -4513,7 +4535,7 @@ bool PlayerbotAI::AllowActive(ActivityType activityType)
45134535
}
45144536
}
45154537

4516-
// Bots don't need to move using PathGenerator.
4538+
// Bots don't need react to PathGenerator activities
45174539
if (activityType == DETAILED_MOVE_ACTIVITY)
45184540
{
45194541
return false;
@@ -4549,15 +4571,25 @@ bool PlayerbotAI::AllowActive(ActivityType activityType)
45494571

45504572
bool PlayerbotAI::AllowActivity(ActivityType activityType, bool checkNow)
45514573
{
4552-
if (!allowActiveCheckTimer[activityType])
4553-
allowActiveCheckTimer[activityType] = time(nullptr);
4574+
const int activityIndex = static_cast<int>(activityType);
45544575

4555-
if (!checkNow && time(nullptr) < (allowActiveCheckTimer[activityType] + 5))
4556-
return allowActive[activityType];
4576+
// Unknown/out-of-range avoid blocking, added logging for further analysing should not happen in the first place.
4577+
if (activityIndex <= 0 || activityIndex >= MAX_ACTIVITY_TYPE)
4578+
{
4579+
LOG_ERROR("playerbots", "AllowActivity received invalid activity type value: {}", activityIndex);
4580+
return true;
4581+
}
4582+
4583+
if (!allowActiveCheckTimer[activityIndex])
4584+
allowActiveCheckTimer[activityIndex] = time(nullptr);
4585+
4586+
if (!checkNow && time(nullptr) < (allowActiveCheckTimer[activityIndex] + 5))
4587+
return allowActive[activityIndex];
4588+
4589+
const bool allowed = AllowActive(activityType);
4590+
allowActive[activityIndex] = allowed;
4591+
allowActiveCheckTimer[activityIndex] = time(nullptr);
45574592

4558-
bool allowed = AllowActive(activityType);
4559-
allowActive[activityType] = allowed;
4560-
allowActiveCheckTimer[activityType] = time(nullptr);
45614593
return allowed;
45624594
}
45634595

@@ -5341,15 +5373,13 @@ Item* PlayerbotAI::FindStoneFor(Item* weapon) const
53415373
if (!item_template)
53425374
return nullptr;
53435375

5344-
static const std::vector<uint32_t> uPrioritizedSharpStoneIds = {
5345-
ADAMANTITE_SHARPENING_STONE, FEL_SHARPENING_STONE, ELEMENTAL_SHARPENING_STONE, DENSE_SHARPENING_STONE,
5346-
SOLID_SHARPENING_STONE, HEAVY_SHARPENING_STONE, COARSE_SHARPENING_STONE, ROUGH_SHARPENING_STONE
5347-
};
5376+
static const std::vector<uint32_t> uPrioritizedSharpStoneIds = {
5377+
ADAMANTITE_SHARPENING_STONE, FEL_SHARPENING_STONE, ELEMENTAL_SHARPENING_STONE, DENSE_SHARPENING_STONE,
5378+
SOLID_SHARPENING_STONE, HEAVY_SHARPENING_STONE, COARSE_SHARPENING_STONE, ROUGH_SHARPENING_STONE};
53485379

5349-
static const std::vector<uint32_t> uPrioritizedWeightStoneIds = {
5350-
ADAMANTITE_WEIGHTSTONE, FEL_WEIGHTSTONE, DENSE_WEIGHTSTONE, SOLID_WEIGHTSTONE,
5351-
HEAVY_WEIGHTSTONE, COARSE_WEIGHTSTONE, ROUGH_WEIGHTSTONE
5352-
};
5380+
static const std::vector<uint32_t> uPrioritizedWeightStoneIds = {
5381+
ADAMANTITE_WEIGHTSTONE, FEL_WEIGHTSTONE, DENSE_WEIGHTSTONE, SOLID_WEIGHTSTONE,
5382+
HEAVY_WEIGHTSTONE, COARSE_WEIGHTSTONE, ROUGH_WEIGHTSTONE};
53535383

53545384
Item* stone = nullptr;
53555385
ItemTemplate const* pProto = weapon->GetTemplate();
@@ -5385,7 +5415,6 @@ static const std::vector<uint32_t> uPrioritizedWeightStoneIds = {
53855415

53865416
Item* PlayerbotAI::FindOilFor(Item* weapon) const
53875417
{
5388-
53895418
if (!weapon)
53905419
return nullptr;
53915420

@@ -5394,12 +5423,12 @@ Item* PlayerbotAI::FindOilFor(Item* weapon) const
53945423
return nullptr;
53955424

53965425
static const std::vector<uint32_t> uPrioritizedWizardOilIds = {
5397-
BRILLIANT_WIZARD_OIL, SUPERIOR_WIZARD_OIL, WIZARD_OIL, LESSER_WIZARD_OIL, MINOR_WIZARD_OIL,
5398-
BRILLIANT_MANA_OIL, SUPERIOR_MANA_OIL, LESSER_MANA_OIL, MINOR_MANA_OIL};
5426+
BRILLIANT_WIZARD_OIL, SUPERIOR_WIZARD_OIL, WIZARD_OIL, LESSER_WIZARD_OIL, MINOR_WIZARD_OIL,
5427+
BRILLIANT_MANA_OIL, SUPERIOR_MANA_OIL, LESSER_MANA_OIL, MINOR_MANA_OIL};
53995428

54005429
static const std::vector<uint32_t> uPrioritizedManaOilIds = {
5401-
BRILLIANT_MANA_OIL, SUPERIOR_MANA_OIL, LESSER_MANA_OIL, MINOR_MANA_OIL,
5402-
BRILLIANT_WIZARD_OIL, SUPERIOR_WIZARD_OIL, WIZARD_OIL, LESSER_WIZARD_OIL, MINOR_WIZARD_OIL};
5430+
BRILLIANT_MANA_OIL, SUPERIOR_MANA_OIL, LESSER_MANA_OIL, MINOR_MANA_OIL, BRILLIANT_WIZARD_OIL,
5431+
SUPERIOR_WIZARD_OIL, WIZARD_OIL, LESSER_WIZARD_OIL, MINOR_WIZARD_OIL};
54035432

54045433
Item* oil = nullptr;
54055434
int botClass = bot->getClass();
@@ -5415,22 +5444,22 @@ Item* PlayerbotAI::FindOilFor(Item* weapon) const
54155444
prioritizedOils = &uPrioritizedWizardOilIds;
54165445
break;
54175446
case CLASS_DRUID:
5418-
if (specTab == 0) // Balance
5447+
if (specTab == 0) // Balance
54195448
prioritizedOils = &uPrioritizedWizardOilIds;
5420-
else if (specTab == 1) // Feral
5449+
else if (specTab == 1) // Feral
54215450
prioritizedOils = nullptr;
5422-
else // Restoration (specTab == 2) or any other/unspecified spec
5451+
else // Restoration (specTab == 2) or any other/unspecified spec
54235452
prioritizedOils = &uPrioritizedManaOilIds;
54245453
break;
54255454
case CLASS_HUNTER:
54265455
prioritizedOils = &uPrioritizedManaOilIds;
54275456
break;
54285457
case CLASS_PALADIN:
5429-
if (specTab == 1) // Protection
5458+
if (specTab == 1) // Protection
54305459
prioritizedOils = &uPrioritizedWizardOilIds;
5431-
else if (specTab == 2) // Retribution
5460+
else if (specTab == 2) // Retribution
54325461
prioritizedOils = nullptr;
5433-
else // Holy (specTab == 0) or any other/unspecified spec
5462+
else // Holy (specTab == 0) or any other/unspecified spec
54345463
prioritizedOils = &uPrioritizedManaOilIds;
54355464
break;
54365465
default:

0 commit comments

Comments
 (0)