Harnais QA PlayerBot : 4 fixes du module bot + Z-fix et logs du runner artefacts

- PlayerBotSession : simulation de l ack MSG_MOVE_WORLDPORT pour un bot en jeu
  (IsBeingTeleportedFar) ; sans ca tout .tele name sur un bot restait bloque
- BotFieldAI : eviction hors instance gatee par la config pbotqa (harnais scenarios)
- Diagnostics QA-BOTLOG/QA-RUNLOG (login bot, holder async, cycle du runner)
- Runner artefacts : Z des spawns cale au sol via GetHeight avec repli sur l ancre,
  offsets resserres (plateformes type Skywall)
- Cause racine login bots : noms generes invalides (chiffres) rejetes par
  CheckPlayerName -> persos QA renommes en DB (Botqa/Botqb, compte 32)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
SylvaniaCore deploy
2026-07-22 12:57:14 +00:00
parent d3c0436ad5
commit f783cfd073
5 changed files with 54 additions and 6 deletions
@@ -16,6 +16,7 @@
*/
#include "BotFieldAI.h"
#include "Config.h"
#include "MoveSplineInit.h"
#include "BotBGAIMovement.h"
#include "PlayerBotMgr.h"
@@ -269,8 +270,12 @@ bool BotFieldAI::ProcessGroupInvite()
return false;
if (me->IsInWorld() && me->GetMap()->IsDungeon())
{
BotUtility::TryTeleportHome(this);
return true;
// pbotqa=1 : harnais QA, autorise les bots field a rester en instance
if (!sConfigMgr->GetIntDefault("pbotqa", 0))
{
BotUtility::TryTeleportHome(this);
return true;
}
}
Group* pGroup = me->GetGroupInvite();
if (!pGroup)
@@ -870,14 +870,19 @@ void WorldSession::HandleContinuePlayerLogin()
std::shared_ptr<LoginQueryHolder> holder = std::make_shared<LoginQueryHolder>(GetAccountId(), m_playerLoading);
if (!holder->Initialize())
{
TC_LOG_ERROR("server.worldserver", "QA-BOTLOG2: holder Initialize KO compte %u", GetAccountId()); // QA-BOTLOG2
m_playerLoading.Clear();
return;
}
if (IsBotSession())
TC_LOG_ERROR("server.worldserver", "QA-BOTLOG2: holder pose compte %u", GetAccountId());
SendPacket(WorldPackets::Auth::ResumeComms(CONNECTION_TYPE_INSTANCE).Write());
AddQueryHolderCallback(CharacterDatabase.DelayQueryHolder(holder)).AfterComplete([this](SQLQueryHolderBase const& holder)
{
if (IsBotSession())
TC_LOG_ERROR("server.worldserver", "QA-BOTLOG2: callback holder compte %u", GetAccountId());
HandlePlayerLogin(dynamic_cast<LoginQueryHolder const&>(holder));
});
}
@@ -910,6 +915,7 @@ void WorldSession::HandlePlayerLogin(LoginQueryHolder const& holder)
// "GetAccountId() == db stored account id" checked in LoadFromDB (prevent login not own character using cheating tools)
if (!pCurrChar->LoadFromDB(playerGuid, holder))
{
TC_LOG_ERROR("server.worldserver", "QA-BOTLOG3: LoadFromDB KO pour %s compte %u", playerGuid.ToString().c_str(), GetAccountId()); // QA-BOTLOG3
SetPlayer(NULL);
KickPlayer(); // disconnect client, player no set to session and it will not deleted or saved at kick
delete pCurrChar; // delete it manually
@@ -1722,9 +1722,15 @@ bool PlayerBotMgr::LoginBotByAccountIndex(uint32 account, uint32 index)
#ifndef INCOMPLETE_BOT
PlayerBotBaseInfo* botInfo = GetPlayerBotAccountInfo(account);
if (!botInfo)
{
TC_LOG_ERROR("server.worldserver", "QA-BOTLOG: pas de botInfo pour compte %u", account); // QA-BOTLOG
return false;
}
if (index >= botInfo->characters.size())
{
TC_LOG_ERROR("server.worldserver", "QA-BOTLOG: compte %u index %u >= %u persos", account, index, (uint32)botInfo->characters.size());
return false;
}
for (auto itChar = botInfo->characters.begin();
itChar != botInfo->characters.end();
itChar++)
@@ -1738,7 +1744,15 @@ bool PlayerBotMgr::LoginBotByAccountIndex(uint32 account, uint32 index)
WorldSession* pWorldSession = sWorld->FindSession(account);
PlayerBotSession* pSession = dynamic_cast<PlayerBotSession*>(pWorldSession);
if (!pSession || pSession->PlayerLoading() || pSession->HasSchedules() || pSession->GetPlayer())
{
TC_LOG_ERROR("server.worldserver", "QA-BOTLOG: compte %u session=%d cast=%d loading=%d schedules=%d player=%d",
account, pWorldSession ? 1 : 0, pSession ? 1 : 0,
pSession ? (int)pSession->PlayerLoading() : -1,
pSession ? (int)pSession->HasSchedules() : -1,
(pSession && pSession->GetPlayer()) ? 1 : 0);
return false;
}
TC_LOG_ERROR("server.worldserver", "QA-BOTLOG: schedule Online_GUID pousse pour guid %u", (uint32)charInfo.guid);
BotGlobleSchedule schedule1(BotGlobleScheduleType::BGSType_Online_GUID, charInfo.guid);
pSession->PushScheduleToQueue(schedule1);
return true;
@@ -163,6 +163,15 @@ void PlayerBotSession::ProcessNoWorld(uint32 diff)
if (!player)
return;
// Un client reel confirme le changement de monde par MSG_MOVE_WORLDPORT_ACK ;
// sans cette simulation, un bot en jeu vise par .tele reste bloque sur le semaphore.
if (player->IsBeingTeleportedFar())
{
HandleMoveWorldportAck();
m_NoWorldTick = 500;
return;
}
if (player->IsInWorld())
{
m_NoWorldTick = 0;
@@ -226,6 +235,7 @@ void PlayerBotSession::CastSchedule(uint32 diff)
break;
case BGSType_Online_GUID:
result = ProcessOnlineByGUID(schedule);
TC_LOG_ERROR("server.worldserver", "QA-BOTLOG: ProcessOnlineByGUID resultat=%d", (int)result); // QA-BOTLOG
break;
case BGSType_Offline:
result = ProcessOffline(schedule);
@@ -99,6 +99,7 @@ struct scenario_artifact_runner : public InstanceScript
{
SetBossNumber(12);
config = ArtifactRunner::GetConfig(instance->GetId());
TC_LOG_ERROR("server.worldserver", "QA-RUNLOG: Initialize map %u config=%d", instance->GetId(), config ? 1 : 0); // QA-RUNLOG
stage = 0;
aliveCount = 0;
introDone = false;
@@ -109,6 +110,7 @@ struct scenario_artifact_runner : public InstanceScript
InstanceScript::OnPlayerEnter(player);
PhasingHandler::AddPhase(player, 169, true);
TC_LOG_ERROR("server.worldserver", "QA-RUNLOG: OnPlayerEnter map %u introDone=%d config=%d", instance->GetId(), (int)introDone, config ? 1 : 0);
if (!introDone && config)
{
introDone = true;
@@ -144,6 +146,7 @@ struct scenario_artifact_runner : public InstanceScript
return;
}
TC_LOG_ERROR("server.worldserver", "QA-RUNLOG: StartStage %u map %u", (uint32)newStage, instance->GetId());
stage = newStage;
aliveCount = 0;
stageEntries.clear();
@@ -162,11 +165,21 @@ struct scenario_artifact_runner : public InstanceScript
}
for (uint8 i = 0; i < s.count; ++i)
{
float dx = (i % 3) * 6.0f - 6.0f;
float dy = (i / 3) * 6.0f - 3.0f;
Position pos = { s.x + dx + 12.0f, s.y + dy + 8.0f, s.z, s.o };
if (TempSummon* summon = instance->SummonCreature(s.entry, pos))
float dx = (i % 3) * 4.0f - 4.0f;
float dy = (i / 3) * 4.0f - 2.0f;
Position pos = { s.x + dx + 6.0f, s.y + dy + 4.0f, s.z, s.o };
// cale le Z au sol : les ancres sont sures mais les offsets peuvent sortir des plateformes
float gz = instance->GetHeight(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ() + 8.0f, true, 60.0f);
if (gz > INVALID_HEIGHT && std::abs(gz - s.z) < 25.0f)
pos.m_positionZ = gz + 0.5f;
else
pos.Relocate(s.x, s.y, s.z); // repli : pile sur l ancre officielle
TempSummon* summon = instance->SummonCreature(s.entry, pos);
if (!summon)
TC_LOG_ERROR("server.worldserver", "QA-RUNLOG: summon KO entry %u map %u", s.entry, instance->GetId());
if (summon)
{
TC_LOG_ERROR("server.worldserver", "QA-RUNLOG: summon POS entry %u (%.1f, %.1f, %.1f)", s.entry, summon->GetPositionX(), summon->GetPositionY(), summon->GetPositionZ());
if (s.level)
{
summon->SetLevel(s.level);