Compare commits

...

3 Commits

Author SHA1 Message Date
bash
752ed07742 Dont wait to travel when in combat.
Prevents bot adding a travel delay when in combat
2025-08-10 23:30:26 +02:00
bash
380312ffd2 nullptr fix (#1523) 2025-08-10 22:59:34 +02:00
Alex Dcnh
872e417613 Playerbots/LFG: fix false not eligible & dungeon 0/type 0, add clear diagnostics (#1521)
Tested
2025-08-10 21:23:02 +02:00
4 changed files with 155 additions and 14 deletions

View File

@@ -529,6 +529,9 @@ void PlayerbotHolder::OnBotLogin(Player* const bot)
{ {
botAI->ResetStrategies(!sRandomPlayerbotMgr->IsRandomBot(bot)); botAI->ResetStrategies(!sRandomPlayerbotMgr->IsRandomBot(bot));
} }
botAI->Reset(true); // Reset transient states (incl. LFG "proposal") to avoid the "one or more players are not eligible" error after reconnect.
sPlayerbotDbStore->Load(botAI); sPlayerbotDbStore->Load(botAI);
if (master && !master->HasUnitState(UNIT_STATE_IN_FLIGHT)) if (master && !master->HasUnitState(UNIT_STATE_IN_FLIGHT))
@@ -548,16 +551,21 @@ void PlayerbotHolder::OnBotLogin(Player* const bot)
if (master && master->GetGroup() && !group) if (master && master->GetGroup() && !group)
{ {
Group* mgroup = master->GetGroup(); Group* mgroup = master->GetGroup();
if (mgroup->GetMembersCount() >= 5) // if (mgroup->GetMembersCount() >= 5)
if (mgroup->GetMembersCount() + 1 > 5) // only convert in raid if the add of THIS bot make group > 5
{ {
if (!mgroup->isRaidGroup() && !mgroup->isLFGGroup() && !mgroup->isBGGroup() && !mgroup->isBFGroup()) if (!mgroup->isRaidGroup() && !mgroup->isLFGGroup() && !mgroup->isBGGroup() && !mgroup->isBFGroup())
{ {
mgroup->ConvertToRaid(); mgroup->ConvertToRaid();
} }
if (mgroup->isRaidGroup()) //if (mgroup->isRaidGroup())
{ //{
mgroup->AddMember(bot); //mgroup->AddMember(bot);
} //}
mgroup->AddMember(bot);
LOG_DEBUG("playerbots", "[GROUP] after add: members={}, isRaid={}, isLFG={}",
(int)mgroup->GetMembersCount(), mgroup->isRaidGroup() ? 1 : 0, mgroup->isLFGGroup() ? 1 : 0);
} }
else else
{ {

View File

@@ -30,6 +30,7 @@
#include "cs_playerbots.h" #include "cs_playerbots.h"
#include "cmath" #include "cmath"
#include "BattleGroundTactics.h" #include "BattleGroundTactics.h"
#include "ObjectAccessor.h"
class PlayerbotsDatabaseScript : public DatabaseScript class PlayerbotsDatabaseScript : public DatabaseScript
{ {
@@ -311,7 +312,7 @@ class PlayerbotsScript : public PlayerbotScript
public: public:
PlayerbotsScript() : PlayerbotScript("PlayerbotsScript") {} PlayerbotsScript() : PlayerbotScript("PlayerbotsScript") {}
bool OnPlayerbotCheckLFGQueue(lfg::Lfg5Guids const& guidsList) override /*bool OnPlayerbotCheckLFGQueue(lfg::Lfg5Guids const& guidsList) override
{ {
bool nonBotFound = false; bool nonBotFound = false;
for (ObjectGuid const& guid : guidsList.guids) for (ObjectGuid const& guid : guidsList.guids)
@@ -325,7 +326,137 @@ public:
} }
return nonBotFound; return nonBotFound;
}*/
// New LFG Function
bool OnPlayerbotCheckLFGQueue(lfg::Lfg5Guids const& guidsList)
{
const size_t totalSlots = guidsList.guids.size();
size_t ignoredEmpty = 0, ignoredNonPlayer = 0;
size_t offlinePlayers = 0, botPlayers = 0, realPlayers = 0;
bool groupGuidSeen = false;
LOG_DEBUG("playerbots", "[LFG] check start: slots={}", totalSlots);
for (size_t i = 0; i < totalSlots; ++i)
{
ObjectGuid const& guid = guidsList.guids[i];
// 1) Placeholders to ignore
if (guid.IsEmpty())
{
++ignoredEmpty;
LOG_DEBUG("playerbots", "[LFG] slot {}: <empty> -> ignored", i);
continue;
}
// Group GUID: in the original implementation this counted as "non-bot found"
if (guid.IsGroup())
{
groupGuidSeen = true;
LOG_DEBUG("playerbots", "[LFG] slot {}: <GROUP GUID> -> counts as having a real player (compat)", i);
continue;
}
// Other non-Player GUIDs: various placeholders, ignore them
if (!guid.IsPlayer())
{
++ignoredNonPlayer;
LOG_DEBUG("playerbots", "[LFG] slot {}: guid={} (non-player/high={}) -> ignored", i,
static_cast<uint64>(guid.GetRawValue()), (unsigned)guid.GetHigh());
continue;
}
// 2) Player present?
Player* player = ObjectAccessor::FindPlayer(guid);
if (!player)
{
++offlinePlayers;
LOG_DEBUG("playerbots", "[LFG] slot {}: player guid={} is offline/not in world", i,
static_cast<uint64>(guid.GetRawValue()));
continue;
}
// 3) Bot or real player?
if (GET_PLAYERBOT_AI(player) != nullptr)
{
++botPlayers;
LOG_DEBUG("playerbots", "[LFG] slot {}: BOT {} (lvl {}, class {})", i, player->GetName().c_str(),
player->GetLevel(), player->getClass());
}
else
{
++realPlayers;
LOG_DEBUG("playerbots", "[LFG] slot {}: REAL {} (lvl {}, class {})", i, player->GetName().c_str(),
player->GetLevel(), player->getClass());
}
}
// "Ultra-early phase" detection: only placeholders => DO NOT VETO
const bool onlyPlaceholders = (realPlayers + botPlayers + (groupGuidSeen ? 1 : 0)) == 0 &&
(ignoredEmpty + ignoredNonPlayer) == totalSlots;
// "Soft" LFG preflight if we actually see players AND at least one offline
if (!onlyPlaceholders && offlinePlayers > 0)
{
// Find a plausible leader: prefer a real online player, otherwise any online player
Player* leader = nullptr;
for (ObjectGuid const& guid : guidsList.guids)
if (guid.IsPlayer())
if (Player* p = ObjectAccessor::FindPlayer(guid))
if (GET_PLAYERBOT_AI(p) == nullptr)
{
leader = p;
break;
}
if (!leader)
for (ObjectGuid const& guid : guidsList.guids)
if (guid.IsPlayer())
if (Player* p = ObjectAccessor::FindPlayer(guid))
{
leader = p;
break;
}
if (leader)
{
Group* g = leader->GetGroup();
if (g)
{
LOG_DEBUG("playerbots", "[LFG-RESET] group members={}, isRaid={}, isLFGGroup={}",
(int)g->GetMembersCount(), g->isRaidGroup() ? 1 : 0, g->isLFGGroup() ? 1 : 0);
// "Soft" reset of LFG states on the bots' AI side (proposal/role-check, etc.)
for (GroupReference* ref = g->GetFirstMember(); ref; ref = ref->next())
{
Player* member = ref->GetSource();
if (!member)
continue;
if (PlayerbotAI* ai = GET_PLAYERBOT_AI(member))
ai->Reset(true);
}
}
}
LOG_DEBUG("playerbots", "[LFG] preflight soft-reset triggered (offline detected) -> allowQueue=no (retry)");
return false; // ask the client to retry right after the reset
}
// "Hybrid" policy: permissive if only placeholders; otherwise original logic
bool allowQueue = onlyPlaceholders ? true : ((offlinePlayers == 0) && (realPlayers >= 1 || groupGuidSeen));
LOG_DEBUG("playerbots",
"[LFG] summary: slots={}, real={}, bots={}, offline={}, ignored(empty+nonPlayer)={}, "
"groupGuidSeen={} -> allowQueue={}",
totalSlots, realPlayers, botPlayers, offlinePlayers, (ignoredEmpty + ignoredNonPlayer),
(groupGuidSeen ? "yes" : "no"), (allowQueue ? "yes" : "no"));
return allowQueue;
} }
// End LFG
void OnPlayerbotCheckKillTask(Player* player, Unit* victim) override void OnPlayerbotCheckKillTask(Player* player, Unit* victim) override
{ {

View File

@@ -1020,14 +1020,16 @@ void PlayerbotFactory::ClearSkills()
} }
bot->SetUInt32Value(PLAYER_SKILL_INDEX(0), 0); bot->SetUInt32Value(PLAYER_SKILL_INDEX(0), 0);
bot->SetUInt32Value(PLAYER_SKILL_INDEX(1), 0); bot->SetUInt32Value(PLAYER_SKILL_INDEX(1), 0);
// unlearn default race/class skills // unlearn default race/class skills
PlayerInfo const* info = sObjectMgr->GetPlayerInfo(bot->getRace(), bot->getClass()); if (PlayerInfo const* info = sObjectMgr->GetPlayerInfo(bot->getRace(), bot->getClass())) {
for (PlayerCreateInfoSkills::const_iterator itr = info->skills.begin(); itr != info->skills.end(); ++itr) for (PlayerCreateInfoSkills::const_iterator itr = info->skills.begin(); itr != info->skills.end(); ++itr)
{ {
uint32 skillId = itr->SkillId; uint32 skillId = itr->SkillId;
if (!bot->HasSkill(skillId)) if (!bot->HasSkill(skillId))
continue; continue;
bot->SetSkill(skillId, 0, 0, 0); bot->SetSkill(skillId, 0, 0, 0);
}
} }
} }

View File

@@ -18,7 +18,7 @@ bool MoveToTravelTargetAction::Execute(Event event)
WorldLocation location = *target->getPosition(); WorldLocation location = *target->getPosition();
Group* group = bot->GetGroup(); Group* group = bot->GetGroup();
if (group && !urand(0, 1) && bot == botAI->GetGroupMaster()) if (group && !urand(0, 1) && bot == botAI->GetGroupMaster() && !bot->IsInCombat())
{ {
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
{ {