fix bot crash
This commit is contained in:
@@ -292,8 +292,8 @@ CharacterDatabasePreparedStatement* BotDatabasePool::GetPreparedStatement(uint32
|
||||
468, // CHAR_SEL_GM_SUGGESTIONS (duplicate entry for clarity)
|
||||
536, // CHAR_SEL_CHARACTER_AURA_FROZEN
|
||||
537, // CHAR_SEL_CHARACTER_ONLINE
|
||||
539, // CHAR_SEL_CHAR_DEL_INFO_BY_NAME
|
||||
540, // CHAR_SEL_CHAR_DEL_INFO
|
||||
368, // CHAR_SEL_CHAR_DEL_INFO_BY_NAME
|
||||
369, // CHAR_SEL_CHAR_DEL_INFO
|
||||
541, // CHAR_SEL_CHARS_BY_ACCOUNT_ID
|
||||
542, // CHAR_SEL_CHAR_PINFO
|
||||
543, // CHAR_SEL_PINFO_BANS
|
||||
|
||||
@@ -520,8 +520,8 @@ void PlayerbotCharacterDBInterface::InitializeStatementClassification()
|
||||
// Character info queries
|
||||
536, // CHAR_SEL_CHARACTER_AURA_FROZEN
|
||||
537, // CHAR_SEL_CHARACTER_ONLINE
|
||||
539, // CHAR_SEL_CHAR_DEL_INFO_BY_NAME
|
||||
540, // CHAR_SEL_CHAR_DEL_INFO
|
||||
368, // CHAR_SEL_CHAR_DEL_INFO_BY_NAME
|
||||
369, // CHAR_SEL_CHAR_DEL_INFO
|
||||
541, // CHAR_SEL_CHARS_BY_ACCOUNT_ID
|
||||
542, // CHAR_SEL_CHAR_PINFO
|
||||
543, // CHAR_SEL_PINFO_BANS
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "SpellAuraEffects.h"
|
||||
#include "SpellMgr.h"
|
||||
#include "TemporarySummon.h"
|
||||
#include "../../modules/Playerbot/Core/PlayerBotHooks.h"
|
||||
#include <boost/heap/fibonacci_heap.hpp>
|
||||
|
||||
const CompareThreatLessThan ThreatManager::CompareThreat;
|
||||
@@ -39,12 +40,17 @@ void ThreatReference::AddThreat(float amount)
|
||||
{
|
||||
if (amount == 0.0f)
|
||||
return;
|
||||
float oldThreat = _baseAmount;
|
||||
_baseAmount = std::max<float>(_baseAmount + amount, 0.0f);
|
||||
if (amount > 0.0f)
|
||||
HeapNotifyIncreased();
|
||||
else
|
||||
HeapNotifyDecreased();
|
||||
_mgr._needClientUpdate = true;
|
||||
|
||||
// PLAYERBOT HOOK: Notify bots of threat change
|
||||
if (Playerbot::PlayerBotHooks::OnThreatChanged)
|
||||
Playerbot::PlayerBotHooks::OnThreatChanged(_mgr.GetOwner(), _victim, oldThreat, _baseAmount);
|
||||
}
|
||||
|
||||
void ThreatReference::ScaleThreat(float factor)
|
||||
@@ -154,7 +160,7 @@ void ThreatReference::UnregisterAndFree()
|
||||
class ThreatReferenceImpl : public ThreatReference
|
||||
{
|
||||
public:
|
||||
explicit ThreatReferenceImpl(ThreatManager* mgr, Unit* victim) : ThreatReference(mgr, victim) { }
|
||||
explicit ThreatReferenceImpl(ThreatManager* mgr, Unit* victim) : ThreatReference(mgr, victim) {}
|
||||
|
||||
ThreatManager::Heap::handle_type _handle;
|
||||
};
|
||||
@@ -190,7 +196,7 @@ void ThreatReference::HeapNotifyDecreased()
|
||||
}
|
||||
|
||||
ThreatManager::ThreatManager(Unit* owner) : _owner(owner), _ownerCanHaveThreatList(false), _needClientUpdate(false), _needThreatClearUpdate(false), _updateTimer(THREAT_UPDATE_INTERVAL),
|
||||
_sortedThreatList(std::make_unique<Heap>()), _currentVictimRef(nullptr), _fixateRef(nullptr)
|
||||
_sortedThreatList(std::make_unique<Heap>()), _currentVictimRef(nullptr), _fixateRef(nullptr)
|
||||
{
|
||||
for (int8 i = 0; i < MAX_SPELL_SCHOOL; ++i)
|
||||
_singleSchoolModifiers[i] = 1.0f;
|
||||
@@ -295,12 +301,12 @@ Trinity::IteratorPair<ThreatManager::ThreatListIterator, std::nullptr_t> ThreatM
|
||||
auto itr = _myThreatListEntries.begin();
|
||||
auto end = _myThreatListEntries.end();
|
||||
std::function<ThreatReference const* ()> generator = [itr, end]() mutable -> ThreatReference const*
|
||||
{
|
||||
if (itr == end)
|
||||
return nullptr;
|
||||
{
|
||||
if (itr == end)
|
||||
return nullptr;
|
||||
|
||||
return (itr++)->second;
|
||||
};
|
||||
return (itr++)->second;
|
||||
};
|
||||
return { ThreatListIterator{ std::move(generator) }, nullptr };
|
||||
}
|
||||
|
||||
@@ -309,12 +315,12 @@ Trinity::IteratorPair<ThreatManager::ThreatListIterator, std::nullptr_t> ThreatM
|
||||
auto itr = _sortedThreatList->ordered_begin();
|
||||
auto end = _sortedThreatList->ordered_end();
|
||||
std::function<ThreatReference const* ()> generator = [itr, end]() mutable -> ThreatReference const*
|
||||
{
|
||||
if (itr == end)
|
||||
return nullptr;
|
||||
{
|
||||
if (itr == end)
|
||||
return nullptr;
|
||||
|
||||
return *(itr++);
|
||||
};
|
||||
return *(itr++);
|
||||
};
|
||||
return { ThreatListIterator{ std::move(generator) }, nullptr };
|
||||
}
|
||||
|
||||
@@ -482,7 +488,7 @@ void ThreatManager::ScaleThreat(Unit* target, float factor)
|
||||
{
|
||||
auto it = _myThreatListEntries.find(target->GetGUID());
|
||||
if (it != _myThreatListEntries.end())
|
||||
it->second->ScaleThreat(std::max<float>(factor,0.0f));
|
||||
it->second->ScaleThreat(std::max<float>(factor, 0.0f));
|
||||
}
|
||||
|
||||
void ThreatManager::MatchUnitThreatToHighestThreat(Unit* target)
|
||||
@@ -675,7 +681,7 @@ void ThreatManager::RegisterForAIUpdate(ObjectGuid const& guid)
|
||||
return a->_online < b->_online;
|
||||
if (a->_taunted != b->_taunted) // taunt state precedence (TAUNT > NONE > DETAUNT)
|
||||
return a->_taunted < b->_taunted;
|
||||
return (a->GetThreat()*aWeight < b->GetThreat());
|
||||
return (a->GetThreat() * aWeight < b->GetThreat());
|
||||
}
|
||||
|
||||
/*static*/ float ThreatManager::CalculateModifiedThreat(float threat, Unit const* victim, SpellInfo const* spell)
|
||||
@@ -696,40 +702,40 @@ void ThreatManager::RegisterForAIUpdate(ObjectGuid const& guid)
|
||||
SpellSchoolMask const mask = spell ? spell->GetSchoolMask() : SPELL_SCHOOL_MASK_NORMAL;
|
||||
switch (mask)
|
||||
{
|
||||
case SPELL_SCHOOL_MASK_NORMAL:
|
||||
threat *= victimMgr._singleSchoolModifiers[SPELL_SCHOOL_NORMAL];
|
||||
break;
|
||||
case SPELL_SCHOOL_MASK_HOLY:
|
||||
threat *= victimMgr._singleSchoolModifiers[SPELL_SCHOOL_HOLY];
|
||||
break;
|
||||
case SPELL_SCHOOL_MASK_FIRE:
|
||||
threat *= victimMgr._singleSchoolModifiers[SPELL_SCHOOL_FIRE];
|
||||
break;
|
||||
case SPELL_SCHOOL_MASK_NATURE:
|
||||
threat *= victimMgr._singleSchoolModifiers[SPELL_SCHOOL_NATURE];
|
||||
break;
|
||||
case SPELL_SCHOOL_MASK_FROST:
|
||||
threat *= victimMgr._singleSchoolModifiers[SPELL_SCHOOL_FROST];
|
||||
break;
|
||||
case SPELL_SCHOOL_MASK_SHADOW:
|
||||
threat *= victimMgr._singleSchoolModifiers[SPELL_SCHOOL_SHADOW];
|
||||
break;
|
||||
case SPELL_SCHOOL_MASK_ARCANE:
|
||||
threat *= victimMgr._singleSchoolModifiers[SPELL_SCHOOL_ARCANE];
|
||||
break;
|
||||
default:
|
||||
case SPELL_SCHOOL_MASK_NORMAL:
|
||||
threat *= victimMgr._singleSchoolModifiers[SPELL_SCHOOL_NORMAL];
|
||||
break;
|
||||
case SPELL_SCHOOL_MASK_HOLY:
|
||||
threat *= victimMgr._singleSchoolModifiers[SPELL_SCHOOL_HOLY];
|
||||
break;
|
||||
case SPELL_SCHOOL_MASK_FIRE:
|
||||
threat *= victimMgr._singleSchoolModifiers[SPELL_SCHOOL_FIRE];
|
||||
break;
|
||||
case SPELL_SCHOOL_MASK_NATURE:
|
||||
threat *= victimMgr._singleSchoolModifiers[SPELL_SCHOOL_NATURE];
|
||||
break;
|
||||
case SPELL_SCHOOL_MASK_FROST:
|
||||
threat *= victimMgr._singleSchoolModifiers[SPELL_SCHOOL_FROST];
|
||||
break;
|
||||
case SPELL_SCHOOL_MASK_SHADOW:
|
||||
threat *= victimMgr._singleSchoolModifiers[SPELL_SCHOOL_SHADOW];
|
||||
break;
|
||||
case SPELL_SCHOOL_MASK_ARCANE:
|
||||
threat *= victimMgr._singleSchoolModifiers[SPELL_SCHOOL_ARCANE];
|
||||
break;
|
||||
default:
|
||||
{
|
||||
auto it = victimMgr._multiSchoolModifiers.find(mask);
|
||||
if (it != victimMgr._multiSchoolModifiers.end())
|
||||
{
|
||||
auto it = victimMgr._multiSchoolModifiers.find(mask);
|
||||
if (it != victimMgr._multiSchoolModifiers.end())
|
||||
{
|
||||
threat *= it->second;
|
||||
break;
|
||||
}
|
||||
float mod = victim->GetTotalAuraMultiplierByMiscMask(SPELL_AURA_MOD_THREAT, mask);
|
||||
victimMgr._multiSchoolModifiers[mask] = mod;
|
||||
threat *= mod;
|
||||
threat *= it->second;
|
||||
break;
|
||||
}
|
||||
float mod = victim->GetTotalAuraMultiplierByMiscMask(SPELL_AURA_MOD_THREAT, mask);
|
||||
victimMgr._multiSchoolModifiers[mask] = mod;
|
||||
threat *= mod;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return threat;
|
||||
}
|
||||
@@ -857,21 +863,21 @@ void ThreatManager::SendThreatListToClients(bool newHighest) const
|
||||
return;
|
||||
|
||||
auto fillSharedPacketDataAndSend = [&](auto& packet)
|
||||
{
|
||||
packet.UnitGUID = _owner->GetGUID();
|
||||
packet.ThreatList.reserve(_sortedThreatList->size());
|
||||
for (ThreatReference const* ref : *_sortedThreatList)
|
||||
{
|
||||
if (!ref->IsAvailable())
|
||||
continue;
|
||||
packet.UnitGUID = _owner->GetGUID();
|
||||
packet.ThreatList.reserve(_sortedThreatList->size());
|
||||
for (ThreatReference const* ref : *_sortedThreatList)
|
||||
{
|
||||
if (!ref->IsAvailable())
|
||||
continue;
|
||||
|
||||
WorldPackets::Combat::ThreatInfo threatInfo;
|
||||
threatInfo.UnitGUID = ref->GetVictim()->GetGUID();
|
||||
threatInfo.Threat = int64(ref->GetThreat() * 100);
|
||||
packet.ThreatList.push_back(threatInfo);
|
||||
}
|
||||
_owner->SendMessageToSet(packet.Write(), false);
|
||||
};
|
||||
WorldPackets::Combat::ThreatInfo threatInfo;
|
||||
threatInfo.UnitGUID = ref->GetVictim()->GetGUID();
|
||||
threatInfo.Threat = int64(ref->GetThreat() * 100);
|
||||
packet.ThreatList.push_back(threatInfo);
|
||||
}
|
||||
_owner->SendMessageToSet(packet.Write(), false);
|
||||
};
|
||||
|
||||
if (newHighest)
|
||||
{
|
||||
@@ -931,7 +937,7 @@ void ThreatManager::UpdateRedirectInfo()
|
||||
for (auto const& pair : _redirectRegistry) // (spellid, victim -> pct)
|
||||
for (auto const& victimPair : pair.second) // (victim,pct)
|
||||
{
|
||||
float thisPct = std::min(100.0f - totalPct, victimPair.second);
|
||||
float thisPct = std::min(100 - totalPct, victimPair.second);
|
||||
if (thisPct > 0)
|
||||
{
|
||||
_redirectInfo.push_back({ victimPair.first, thisPct });
|
||||
|
||||
@@ -163,6 +163,8 @@
|
||||
#include <G3D/g3dmath.h>
|
||||
#include <sstream>
|
||||
|
||||
#include "../../modules/Playerbot/Core/PlayerBotHooks.h"
|
||||
|
||||
// corpse reclaim times
|
||||
#define DEATH_EXPIRE_STEP (5*MINUTE)
|
||||
#define MAX_DEATH_COUNT 3
|
||||
@@ -1190,6 +1192,10 @@ void Player::setDeathState(DeathState s)
|
||||
|
||||
// reset all death criterias
|
||||
FailCriteria(CriteriaFailEvent::Death, 0);
|
||||
|
||||
// PLAYERBOT HOOK: Notify bots of death
|
||||
if (Playerbot::PlayerBotHooks::OnPlayerDeath)
|
||||
Playerbot::PlayerBotHooks::OnPlayerDeath(this);
|
||||
}
|
||||
|
||||
Unit::setDeathState(s);
|
||||
@@ -4646,6 +4652,10 @@ void Player::ResurrectPlayer(float restore_percent, bool applySickness)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PLAYERBOT HOOK: Notify bots of resurrection
|
||||
if (Playerbot::PlayerBotHooks::OnPlayerResurrected)
|
||||
Playerbot::PlayerBotHooks::OnPlayerResurrected(this);
|
||||
}
|
||||
|
||||
void Player::KillPlayer()
|
||||
@@ -25760,7 +25770,12 @@ void Player::SendInitialPacketsAfterAddToMap()
|
||||
|
||||
GetSession()->SendLoadCUFProfiles();
|
||||
|
||||
CastSpell(this, 836, true); // LOGINEFFECT
|
||||
// Skip LOGINEFFECT for bots - visual effect requires client rendering
|
||||
// Bots don't send CMSG_CAST_SPELL ACKs, causing m_spellModTakingSpell assertion failures (Spell.cpp:603)
|
||||
#ifdef BUILD_PLAYERBOT
|
||||
if (!GetSession()->IsBot())
|
||||
#endif
|
||||
CastSpell(this, 836, true); // LOGINEFFECT
|
||||
|
||||
// set some aura effects that send packet to player client after add player to map
|
||||
// SendMessageToSet not send it to player not it map, only for aura that not changed anything at re-apply
|
||||
@@ -26649,25 +26664,15 @@ Player* Player::GetTrader() const
|
||||
return m_trade ? m_trade->GetTrader() : nullptr;
|
||||
}
|
||||
|
||||
void Player::InitiateTrade(Player* target)
|
||||
void Player::InitiateTrade(Player* trader)
|
||||
{
|
||||
if (!target || target == this)
|
||||
// PlayerBot integration: minimal core hook for trade initiation
|
||||
// Pattern from TradeHandler.cpp:714-715
|
||||
if (!trader || m_trade || trader->m_trade)
|
||||
return;
|
||||
|
||||
if (m_trade)
|
||||
TradeCancel(true);
|
||||
|
||||
if (target->m_trade)
|
||||
target->TradeCancel(true);
|
||||
|
||||
m_trade = new TradeData(this, target);
|
||||
target->m_trade = new TradeData(target, this);
|
||||
|
||||
// Send trade status to both players
|
||||
// WorldPackets::Trade::TradeStatus info;
|
||||
// info.Status = TRADE_STATUS_BEGIN_TRADE;
|
||||
// GetSession()->SendTradeStatus(info);
|
||||
// target->GetSession()->SendTradeStatus(info);
|
||||
m_trade = new TradeData(this, trader);
|
||||
trader->m_trade = new TradeData(trader, this);
|
||||
}
|
||||
|
||||
bool Player::IsSpellFitByClassAndRace(uint32 spell_id) const
|
||||
|
||||
@@ -1660,7 +1660,7 @@ class TC_GAME_API Player final : public Unit, public GridObject<Player>
|
||||
Player* GetTrader() const;
|
||||
TradeData* GetTradeData() const { return m_trade; }
|
||||
void TradeCancel(bool sendback);
|
||||
void InitiateTrade(Player* target);
|
||||
void InitiateTrade(Player* trader); // PlayerBot integration: minimal core hook
|
||||
|
||||
CinematicMgr* GetCinematicMgr() const { return _cinematicMgr.get(); }
|
||||
|
||||
|
||||
@@ -1971,7 +1971,31 @@ void Map::SendObjectUpdates()
|
||||
while (!_updateObjects.empty())
|
||||
{
|
||||
BaseEntity* obj = *_updateObjects.begin();
|
||||
ASSERT(obj->IsInWorld());
|
||||
|
||||
// PLAYERBOT FIX: Multiple safety checks for race condition prevention
|
||||
//
|
||||
// Race condition scenarios:
|
||||
// 1. BaseEntity::RemoveFromWorld() sets m_inWorld=false before ClearUpdateMask removes from set
|
||||
// 2. Bot marked for removal (SetDestroyedObject) but still in _updateObjects due to re-add
|
||||
// 3. Object freed but memory not yet overwritten - partial corruption
|
||||
//
|
||||
// Check 1: Skip objects not in world
|
||||
if (!obj->IsInWorld())
|
||||
{
|
||||
TC_LOG_DEBUG("maps", "Map::SendObjectUpdates: Skipping object not in world");
|
||||
_updateObjects.erase(_updateObjects.begin());
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check 2: Skip objects marked for destruction (prevents use-after-free)
|
||||
// This catches objects that passed IsInWorld() but are being destroyed
|
||||
if (obj->IsDestroyedObject())
|
||||
{
|
||||
TC_LOG_DEBUG("maps", "Map::SendObjectUpdates: Skipping destroyed object");
|
||||
_updateObjects.erase(_updateObjects.begin());
|
||||
continue;
|
||||
}
|
||||
|
||||
_updateObjects.erase(_updateObjects.begin());
|
||||
obj->BuildUpdate(update_players);
|
||||
}
|
||||
|
||||
@@ -171,6 +171,12 @@ WorldSession::WorldSession(uint32 id, std::string&& name, uint32 battlenetAccoun
|
||||
ResetTimeOutTime(false);
|
||||
LoginDatabase.PExecute("UPDATE account SET online = 1 WHERE id = {};", GetAccountId()); // One-time query
|
||||
}
|
||||
#ifdef BUILD_PLAYERBOT
|
||||
else if (is_bot)
|
||||
{
|
||||
m_Address = "bot";
|
||||
}
|
||||
#endif
|
||||
|
||||
_instanceConnectKey.Raw = UI64LIT(0);
|
||||
}
|
||||
@@ -187,7 +193,10 @@ WorldSession::~WorldSession()
|
||||
{
|
||||
if (m_Socket[i])
|
||||
{
|
||||
m_Socket[i]->CloseSocket();
|
||||
#ifdef BUILD_PLAYERBOT
|
||||
if (!IsBot()) // Bot sessions have nullptr sockets
|
||||
#endif
|
||||
m_Socket[i]->CloseSocket();
|
||||
m_Socket[i].reset();
|
||||
}
|
||||
}
|
||||
@@ -268,7 +277,11 @@ void WorldSession::SendPacket(WorldPacket const* packet, bool forced /*= false*/
|
||||
|
||||
if (!m_Socket[conIdx])
|
||||
{
|
||||
TC_LOG_ERROR("network.opcode", "Prevented sending of {} to non existent socket {} to {}", GetOpcodeNameForLogging(static_cast<OpcodeServer>(packet->GetOpcode())), uint32(conIdx), GetPlayerInfo());
|
||||
#ifdef BUILD_PLAYERBOT
|
||||
// Bot sessions may not have sockets, silently skip packet sending for them
|
||||
if (!IsBot())
|
||||
#endif
|
||||
TC_LOG_ERROR("network.opcode", "Prevented sending of {} to non existent socket {} to {}", GetOpcodeNameForLogging(static_cast<OpcodeServer>(packet->GetOpcode())), uint32(conIdx), GetPlayerInfo());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -369,7 +382,12 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater)
|
||||
///- Before we process anything:
|
||||
/// If necessary, kick the player because the client didn't send anything for too long
|
||||
/// (or they've been idling in character select)
|
||||
/// Note: Bots don't have sockets, so skip idle check for them
|
||||
#ifdef BUILD_PLAYERBOT
|
||||
if (!IsBot() && IsConnectionIdle() && !HasPermission(rbac::RBAC_PERM_IGNORE_IDLE_CONNECTION) && m_Socket[CONNECTION_TYPE_REALM])
|
||||
#else
|
||||
if (IsConnectionIdle() && !HasPermission(rbac::RBAC_PERM_IGNORE_IDLE_CONNECTION))
|
||||
#endif
|
||||
m_Socket[CONNECTION_TYPE_REALM]->CloseSocket();
|
||||
|
||||
///- Retrieve packets from the receive queue and call the appropriate handlers
|
||||
@@ -383,7 +401,11 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater)
|
||||
|
||||
constexpr uint32 MAX_PROCESSED_PACKETS_IN_SAME_WORLDSESSION_UPDATE = 100;
|
||||
|
||||
#ifdef BUILD_PLAYERBOT
|
||||
while ((m_Socket[CONNECTION_TYPE_REALM] || IsBot()) && _recvQueue.next(packet, updater))
|
||||
#else
|
||||
while (m_Socket[CONNECTION_TYPE_REALM] && _recvQueue.next(packet, updater))
|
||||
#endif
|
||||
{
|
||||
OpcodeClient opcode = static_cast<OpcodeClient>(packet->GetOpcode());
|
||||
ClientOpcodeHandler const* opHandle = opcodeTable[opcode];
|
||||
@@ -577,7 +599,11 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef BUILD_PLAYERBOT
|
||||
if (!m_Socket[CONNECTION_TYPE_REALM] && !IsBot())
|
||||
#else
|
||||
if (!m_Socket[CONNECTION_TYPE_REALM])
|
||||
#endif
|
||||
return false; //Will remove this session from the world session map
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user