Skip to content

Commit

Permalink
Unit/AI: Move CharmedPlayerAI logic from the SetCharmedBy/RemoveCharm…
Browse files Browse the repository at this point in the history
…edBy hooks to Player::Update -> Unit::UpdateCharmAI to guarantee thread safety and prevent race condition crashes.

(cherry picked from commit 5354b58)
  • Loading branch information
Treeston authored and Shauren committed Apr 8, 2016
1 parent 4726116 commit f4baf7c
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 39 deletions.
6 changes: 6 additions & 0 deletions src/server/game/Entities/Player/Player.cpp
Expand Up @@ -1066,6 +1066,12 @@ void Player::Update(uint32 p_time)

if (IsAIEnabled && GetAI())
GetAI()->UpdateAI(p_time);
else if (NeedChangeAI)
{
UpdateCharmAI();
NeedChangeAI = false;
IsAIEnabled = !!GetAI();
}

// Update items that have just a limited lifetime
if (now > m_Last_tick)
Expand Down
99 changes: 60 additions & 39 deletions src/server/game/Entities/Unit/Unit.cpp
Expand Up @@ -7552,7 +7552,7 @@ Unit* Unit::GetCharmer() const
if (!charmerGUID.IsEmpty())
return ObjectAccessor::GetUnit(*this, charmerGUID);

return NULL;
return nullptr;
}

Player* Unit::GetCharmerOrOwnerPlayerOrPlayerItself() const
Expand Down Expand Up @@ -11806,28 +11806,63 @@ void Unit::CleanupsBeforeDelete(bool finalCleanup)

void Unit::UpdateCharmAI()
{
if (GetTypeId() == TYPEID_PLAYER)
return;

if (i_disabledAI) // disabled AI must be primary AI
switch (GetTypeId())
{
if (!IsCharmed())
{
delete i_AI;
i_AI = i_disabledAI;
i_disabledAI = NULL;
}
}
else
{
if (IsCharmed())
case TYPEID_UNIT:
if (i_disabledAI) // disabled AI must be primary AI
{
if (!IsCharmed())
{
delete i_AI;
i_AI = i_disabledAI;
i_disabledAI = nullptr;
}
}
else
{
if (IsCharmed())
{
i_disabledAI = i_AI;
if (isPossessed() || IsVehicle())
i_AI = new PossessedAI(ToCreature());
else
i_AI = new PetAI(ToCreature());
}
}
break;
case TYPEID_PLAYER:
{
i_disabledAI = i_AI;
if (isPossessed() || IsVehicle())
i_AI = new PossessedAI(ToCreature());
if (Unit* charmer = GetCharmer()) // if we are currently being charmed, then we should apply charm AI
{
if (Creature* creatureCharmer = charmer->ToCreature()) // this should only ever happen for creature charmers
{
i_disabledAI = i_AI;
// first, we check if the creature's own AI specifies an override playerai for its owned players
if (PlayerAI* charmAI = creatureCharmer->IsAIEnabled ? creatureCharmer->AI()->GetAIForCharmedPlayer(ToPlayer()) : nullptr)
i_AI = charmAI;
else // otherwise, we default to the generic one
i_AI = new SimpleCharmedPlayerAI(ToPlayer());
}
else
{
TC_LOG_ERROR("misc", "Attempt to assign charm AI to player %s who is charmed by non-creature %s.", GetGUID().ToString().c_str(), charmer->GetGUID().ToString().c_str());
}
}
else
i_AI = new PetAI(ToCreature());
{
// we allow the charmed PlayerAI to clean up
i_AI->OnCharmed(false);
// then delete it
delete i_AI;
// and restore our previous PlayerAI (if we had one)
i_AI = i_disabledAI;
i_disabledAI = nullptr;
// IsAIEnabled gets handled in the caller
}
break;
}
default:
TC_LOG_ERROR("misc", "Attempt to update charm AI for unit %s, which is neither player nor creature.", GetGUID().ToString().c_str());
}
}

Expand Down Expand Up @@ -14106,15 +14141,11 @@ bool Unit::SetCharmedBy(Unit* charmer, CharmType type, AuraApplication const* au
if (player->isAFK())
player->ToggleAFK();

if (Creature* creatureCharmer = charmer->ToCreature()) // we are charmed by a creature
if (charmer->GetTypeId() == TYPEID_UNIT) // we are charmed by a creature
{
IsAIEnabled = true;
i_disabledAI = i_AI;
// set our AI to the charmer's custom charm AI if applicable
if (PlayerAI* charmAI = creatureCharmer->AI()->GetAIForCharmedPlayer(player))
i_AI = charmAI;
else // otherwise use the default charmed player AI
i_AI = new SimpleCharmedPlayerAI(player);
// change AI to charmed AI on next Update tick
NeedChangeAI = true;
IsAIEnabled = false;
}
player->SetClientControl(this, false);
}
Expand Down Expand Up @@ -14276,18 +14307,8 @@ void Unit::RemoveCharmedBy(Unit* charmer)
{
if (charmer->GetTypeId() == TYPEID_UNIT) // charmed by a creature, this means we had PlayerAI
{
if (i_AI)
{
// allow charmed player AI to clean up
i_AI->OnCharmed(false);
// then delete it
delete i_AI;
// and restore our previous playerAI (if we had one)
if ((i_AI = i_disabledAI))
i_disabledAI = nullptr;
else
IsAIEnabled = false;
}
NeedChangeAI = true;
IsAIEnabled = false;
}
player->SetClientControl(this, true);
}
Expand Down

0 comments on commit f4baf7c

Please sign in to comment.