[8103] More wide use IsInWorld checks and delayed at teleport operations.

* IsInWorld used to prevent return unexpected not in world objects.
* Delayed operations need to process its in world state.

Signed-off-by: VladimirMangos <vladimir@getmangos.com>
This commit is contained in:
Ambal 2009-07-01 12:04:58 +04:00 committed by tomrus88
parent a8288b311e
commit 2baccbebc3
17 changed files with 220 additions and 40 deletions

View file

@ -338,6 +338,11 @@ Player::Player (WorldSession *session): Unit(), m_achievementMgr(this), m_reputa
mSemaphoreTeleport_Near = false;
mSemaphoreTeleport_Far = false;
m_DelayedOperations = 0;
m_bCanDelayTeleport = false;
m_bHasDelayedTeleport = false;
m_teleport_options = 0;
pTrader = 0;
ClearTrade();
@ -1065,7 +1070,10 @@ void Player::Update( uint32 p_time )
m_nextMailDelivereTime = 0;
}
//used to implement delayed far teleports
SetCanDelayTeleport(true);
Unit::Update( p_time );
SetCanDelayTeleport(false);
// update player only attacks
if(uint32 ranged_att = getAttackTimer(RANGED_ATTACK))
@ -1327,8 +1335,12 @@ void Player::Update( uint32 p_time )
if(pet && !IsWithinDistInMap(pet, OWNER_MAX_DISTANCE) && (GetCharmGUID() && (pet->GetGUID() != GetCharmGUID())))
{
RemovePet(pet, PET_SAVE_NOT_IN_SLOT, true);
return;
}
//we should execute delayed teleports only for alive(!) players
//because we don't want player's ghost teleported from graveyard
if(IsHasDelayedTeleport() && isAlive())
TeleportTo(m_teleport_dest, m_teleport_options);
}
void Player::setDeathState(DeathState s)
@ -1613,6 +1625,21 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati
if ((GetMapId() == mapid) && (!m_transport))
{
//lets reset far teleport flag if it wasn't reset during chained teleports
SetSemaphoreTeleportFar(false);
//setup delayed teleport flag
SetDelayedTeleportFlag(IsCanDelayTeleport());
//if teleport spell is casted in Unit::Update() func
//then we need to delay it until update process will be finished
if(IsHasDelayedTeleport())
{
SetSemaphoreTeleportNear(true);
//lets save teleport destination for player
m_teleport_dest = WorldLocation(mapid, x, y, z, orientation);
m_teleport_options = options;
return true;
}
if (!(options & TELE_TO_NOT_UNSUMMON_PET))
{
//same map, only remove pet if out of range for new position
@ -1654,6 +1681,21 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati
Map *map = MapManager::Instance().FindMap(mapid);
if (!map || map->CanEnter(this))
{
//lets reset near teleport flag if it wasn't reset during chained teleports
SetSemaphoreTeleportNear(false);
//setup delayed teleport flag
SetDelayedTeleportFlag(IsCanDelayTeleport());
//if teleport spell is casted in Unit::Update() func
//then we need to delay it until update process will be finished
if(IsHasDelayedTeleport())
{
SetSemaphoreTeleportFar(true);
//lets save teleport destination for player
m_teleport_dest = WorldLocation(mapid, x, y, z, orientation);
m_teleport_options = options;
return true;
}
SetSelection(0);
CombatStop();
@ -1683,6 +1725,9 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati
if(IsNonMeleeSpellCasted(true))
InterruptNonMeleeSpells(true);
//remove auras before removing from map...
RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_CHANGE_MAP | AURA_INTERRUPT_FLAG_MOVE | AURA_INTERRUPT_FLAG_TURNING);
if(!GetSession()->PlayerLogout())
{
// send transfer packets
@ -1729,8 +1774,6 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati
// if the player is saved before worldportack (at logout for example)
// this will be used instead of the current location in SaveToDB
RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_CHANGE_MAP | AURA_INTERRUPT_FLAG_MOVE | AURA_INTERRUPT_FLAG_TURNING);
// move packet sent by client always after far teleport
// code for finish transfer to new map called in WorldSession::HandleMoveWorldportAckOpcode at client packet
SetSemaphoreTeleportFar(true);
@ -1741,6 +1784,53 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati
return true;
}
void Player::ProcessDelayedOperations()
{
if(m_DelayedOperations == 0)
return;
if(m_DelayedOperations & DELAYED_RESURRECT_PLAYER)
{
ResurrectPlayer(0.0f, false);
if(GetMaxHealth() > m_resurrectHealth)
SetHealth( m_resurrectHealth );
else
SetHealth( GetMaxHealth() );
if(GetMaxPower(POWER_MANA) > m_resurrectMana)
SetPower(POWER_MANA, m_resurrectMana );
else
SetPower(POWER_MANA, GetMaxPower(POWER_MANA) );
SetPower(POWER_RAGE, 0 );
SetPower(POWER_ENERGY, GetMaxPower(POWER_ENERGY) );
SpawnCorpseBones();
}
if(m_DelayedOperations & DELAYED_SAVE_PLAYER)
{
SaveToDB();
}
if(m_DelayedOperations & DELAYED_SPELL_CAST_DESERTER)
{
CastSpell(this, 26013, true); // Deserter
}
//we have executed ALL delayed ops, so clear the flag
m_DelayedOperations = 0;
}
void Player::ScheduleDelayedOperation(uint32 operation)
{
if(operation >= DELAYED_END)
return;
m_DelayedOperations |= operation;
}
void Player::AddToWorld()
{
///- Do not add/remove the player from the object storage
@ -12083,7 +12173,11 @@ Quest const * Player::GetNextQuest( uint64 guid, Quest const *pQuest )
}
else
{
GameObject *pGameObject = GetMap()->GetGameObject(guid);
//we should obtain map pointer from GetMap() in 99% of cases. Special case
//only for quests which cast teleport spells on player
Map * _map = IsInWorld() ? GetMap() : MapManager::Instance().FindMap(GetMapId(), GetInstanceId());
ASSERT(_map);
GameObject *pGameObject = _map->GetGameObject(guid);
if( pGameObject )
{
pObject = (Object*)pGameObject;
@ -12417,6 +12511,10 @@ void Player::IncompleteQuest( uint32 quest_id )
void Player::RewardQuest( Quest const *pQuest, uint32 reward, Object* questGiver, bool announce )
{
//this THING should be here to protect code from quest, which cast on player far teleport as a reward
//should work fine, cause far teleport will be executed in Player::Update()
SetCanDelayTeleport(true);
uint32 quest_id = pQuest->GetQuestId();
for (int i = 0; i < QUEST_OBJECTIVES_COUNT; ++i )
@ -12609,6 +12707,9 @@ void Player::RewardQuest( Quest const *pQuest, uint32 reward, Object* questGiver
if( !HasAura(itr->second->spellId,0) )
CastSpell(this,itr->second->spellId,true);
}
//lets remove flag for delayed teleports
SetCanDelayTeleport(false);
}
void Player::FailQuest( uint32 quest_id )
@ -15379,6 +15480,13 @@ void Player::SaveToDB()
// delay auto save at any saves (manual, in code, or autosave)
m_nextSave = sWorld.getConfig(CONFIG_INTERVAL_SAVE);
//lets allow only players in world to be saved
if(IsBeingTeleportedFar())
{
ScheduleDelayedOperation(DELAYED_SAVE_PLAYER);
return;
}
// first save/honor gain after midnight will also update the player's honor fields
UpdateHonorFields();
@ -17653,7 +17761,16 @@ void Player::LeaveBattleground(bool teleportToEntryPoint)
if( bg->isBattleGround() && !isGameMaster() && sWorld.getConfig(CONFIG_BATTLEGROUND_CAST_DESERTER) )
{
if( bg->GetStatus() == STATUS_IN_PROGRESS || bg->GetStatus() == STATUS_WAIT_JOIN )
{
//lets check if player was teleported from BG and schedule delayed Deserter spell cast
if(IsBeingTeleportedFar())
{
ScheduleDelayedOperation(DELAYED_SPELL_CAST_DESERTER);
return;
}
CastSpell(this, 26013, true); // Deserter
}
}
}
}
@ -18880,6 +18997,14 @@ void Player::ResurectUsingRequestData()
if(IS_PLAYER_GUID(m_resurrectGUID))
TeleportTo(m_resurrectMap, m_resurrectX, m_resurrectY, m_resurrectZ, GetOrientation());
//we cannot resurrect player when we triggered far teleport
//player will be resurrected upon teleportation
if(IsBeingTeleportedFar())
{
ScheduleDelayedOperation(DELAYED_RESURRECT_PLAYER);
return;
}
ResurrectPlayer(0.0f,false);
if(GetMaxHealth() > m_resurrectHealth)