diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 49a2d9635f..560b95f389 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -210,12 +210,9 @@ Player::Player(WorldSession* session) : Unit(true), m_sceneMgr(this) m_atLoginFlags = AT_LOGIN_NONE; - mSemaphoreTeleport_Near = false; - mSemaphoreTeleport_Far = false; - m_DelayedOperations = 0; m_bCanDelayTeleport = false; - m_bHasDelayedTeleport = false; + m_teleport_state = TeleportState::NotTeleporting; m_teleport_options = TELE_TO_NONE; m_teleportSpellId = 0; m_newWorldCounter = 0; @@ -1121,8 +1118,11 @@ void Player::Update(uint32 p_time) //we should execute delayed teleports only for alive(!) players //because we don't want player's ghost teleported from graveyard - if (IsHasDelayedTeleport() && IsAlive()) + if ((GetTeleportState() == TeleportState::DelayedTeleport || GetTeleportState() == TeleportState::DelayedWorldPort) && IsAlive()) + { + SetTeleportState(TeleportState::NotTeleporting); // skip state check inside TeleportTo TeleportTo(m_teleport_dest, m_teleport_options, m_teleportSpellId); + } } void Player::Heartbeat() @@ -1249,6 +1249,9 @@ bool Player::TeleportTo(TeleportLocation const& teleportLocation, TeleportToOpti return false; } + if (GetTeleportState() != TeleportState::NotTeleporting) + return false; + // preparing unsummon pet if lost (we must get pet before teleportation or will not find it later) Pet* pet = GetPet(); @@ -1300,15 +1303,12 @@ bool Player::TeleportTo(TeleportLocation const& teleportLocation, TeleportToOpti if (GetMapId() == teleportLocation.Location.GetMapId() && (!teleportLocation.InstanceId || GetInstanceId() == teleportLocation.InstanceId)) { - //lets reset far teleport flag if it wasn't reset during chained teleport - SetSemaphoreTeleportFar(false); - //setup delayed teleport flag - SetDelayedTeleportFlag(IsCanDelayTeleport()); + SetTeleportState(TeleportState::Initiated); //if teleport spell is cast in Unit::Update() func //then we need to delay it until update process will be finished - if (IsHasDelayedTeleport()) + if (IsCanDelayTeleport()) { - SetSemaphoreTeleportNear(true); + SetTeleportState(TeleportState::DelayedTeleport); //lets save teleport destination for player m_teleport_dest = teleportLocation; m_teleport_options = options; @@ -1337,16 +1337,13 @@ bool Player::TeleportTo(TeleportLocation const& teleportLocation, TeleportToOpti // code for finish transfer called in WorldSession::HandleMovementOpcodes() // at client packet CMSG_MOVE_TELEPORT_ACK - SetSemaphoreTeleportNear(true); + SetTeleportState(TeleportState::WaitingForTeleportAck); // near teleport, triggering send CMSG_MOVE_TELEPORT_ACK from client at landing if (!GetSession()->PlayerLogout()) SendTeleportPacket(m_teleport_dest); } else { - if (IsBeingTeleportedFar()) - return false; - if (GetClass() == CLASS_DEATH_KNIGHT && GetMapId() == 609 && !IsGameMaster() && !HasSpell(50977)) { SendTransferAborted(teleportLocation.Location.GetMapId(), TRANSFER_ABORT_UNIQUE_MESSAGE, 1); @@ -1371,16 +1368,13 @@ bool Player::TeleportTo(TeleportLocation const& teleportLocation, TeleportToOpti !((oldmap->GetEntry()->CosmeticParentMapID != -1) ^ (oldmap->GetEntry()->CosmeticParentMapID != mEntry->CosmeticParentMapID)))) options &= ~TELE_TO_SEAMLESS; - //lets reset near teleport flag if it wasn't reset during chained teleports - SetSemaphoreTeleportNear(false); - //setup delayed teleport flag - SetDelayedTeleportFlag(IsCanDelayTeleport()); - SetSemaphoreTeleportFar(true); + SetTeleportState(TeleportState::Initiated); //if teleport spell is cast in Unit::Update() func //then we need to delay it until update process will be finished - if (IsHasDelayedTeleport()) + if (IsCanDelayTeleport()) { //lets save teleport destination for player + SetTeleportState(TeleportState::DelayedWorldPort); m_teleport_dest = teleportLocation; m_teleport_options = options; m_teleportSpellId = teleportSpellId; @@ -1466,6 +1460,8 @@ bool Player::TeleportTo(TeleportLocation const& teleportLocation, TeleportToOpti // if the player is saved before worldportack (at logout for example) // this will be used instead of the current location in SaveToDB + SetTeleportState(TeleportState::WaitingForSuspendTokenResponse); + if (!GetSession()->PlayerLogout()) { ++m_newWorldCounter; @@ -1475,10 +1471,6 @@ bool Player::TeleportTo(TeleportLocation const& teleportLocation, TeleportToOpti suspendToken.Reason = options & TELE_TO_SEAMLESS ? 2 : 1; SendDirectMessage(suspendToken.Write()); } - - // 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); } return true; } diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 96325bd34b..b2b938f6e8 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -904,6 +904,19 @@ enum ArenaTeamInfoType ARENA_TEAM_END = 7 }; +enum class TeleportState +{ + NotTeleporting, + Initiated, + // destination is on same map and instance + DelayedTeleport, + WaitingForTeleportAck, + // destination is on different map or different instance of the same map + DelayedWorldPort, + WaitingForSuspendTokenResponse, + WaitingForWorldPortAck +}; + enum TeleportToOptions { TELE_TO_NONE = 0x00, @@ -2381,14 +2394,16 @@ class TC_GAME_API Player final : public Unit, public GridObject void SetSkillPermBonus(uint32 pos, uint16 bonus) { SetUpdateFieldValue(m_values.ModifyValue(&Player::m_activePlayerData).ModifyValue(&UF::ActivePlayerData::Skill).ModifyValue(&UF::SkillInfo::SkillPermBonus, pos), bonus); } TeleportLocation& GetTeleportDest() { return m_teleport_dest; } - uint32 GetTeleportOptions() const { return m_teleport_options; } + TeleportState GetTeleportState() const { return m_teleport_state; } + void SetTeleportState(TeleportState state) { m_teleport_state = state; } + EnumFlag GetTeleportOptions() const { return m_teleport_options; } int32 GetNewWorldCounter() const { return m_newWorldCounter; } - bool IsBeingTeleported() const { return IsBeingTeleportedNear() || IsBeingTeleportedFar(); } - bool IsBeingTeleportedNear() const { return mSemaphoreTeleport_Near; } - bool IsBeingTeleportedFar() const { return mSemaphoreTeleport_Far; } - bool IsBeingTeleportedSeamlessly() const { return IsBeingTeleportedFar() && m_teleport_options & TELE_TO_SEAMLESS; } - void SetSemaphoreTeleportNear(bool semphsetting) { mSemaphoreTeleport_Near = semphsetting; } - void SetSemaphoreTeleportFar(bool semphsetting) { mSemaphoreTeleport_Far = semphsetting; } + bool IsBeingTeleported() const { return m_teleport_state != TeleportState::NotTeleporting; } + bool IsBeingTeleportedNear() const { return m_teleport_state == TeleportState::DelayedTeleport + || m_teleport_state == TeleportState::WaitingForTeleportAck; } + bool IsBeingTeleportedFar() const { return m_teleport_state == TeleportState::DelayedWorldPort + || m_teleport_state == TeleportState::WaitingForSuspendTokenResponse + || m_teleport_state == TeleportState::WaitingForWorldPortAck; } void ProcessDelayedOperations(); void CheckAreaExplore(); @@ -3346,8 +3361,6 @@ class TC_GAME_API Player final : public Unit, public GridObject bool IsCanDelayTeleport() const { return m_bCanDelayTeleport; } void SetCanDelayTeleport(bool setting) { m_bCanDelayTeleport = setting; } - bool IsHasDelayedTeleport() const { return m_bHasDelayedTeleport; } - void SetDelayedTeleportFlag(bool setting) { m_bHasDelayedTeleport = setting; } void ScheduleDelayedOperation(uint32 operation) { if (operation < DELAYED_END) m_DelayedOperations |= operation; } bool IsInstanceLoginGameMasterException() const; @@ -3363,15 +3376,13 @@ class TC_GAME_API Player final : public Unit, public GridObject // Current teleport data TeleportLocation m_teleport_dest; + TeleportState m_teleport_state; TeleportToOptions m_teleport_options; uint32 m_teleportSpellId; int32 m_newWorldCounter; - bool mSemaphoreTeleport_Near; - bool mSemaphoreTeleport_Far; uint32 m_DelayedOperations; bool m_bCanDelayTeleport; - bool m_bHasDelayedTeleport; std::unique_ptr m_petStable; diff --git a/src/server/game/Handlers/MovementHandler.cpp b/src/server/game/Handlers/MovementHandler.cpp index c897e588fb..bc8bd06e76 100644 --- a/src/server/game/Handlers/MovementHandler.cpp +++ b/src/server/game/Handlers/MovementHandler.cpp @@ -47,6 +47,9 @@ void WorldSession::HandleMoveWorldportAckOpcode(WorldPackets::Movement::WorldPortResponse& /*packet*/) { + if (_player->GetTeleportState() != TeleportState::WaitingForWorldPortAck) + return; + HandleMoveWorldportAck(); } @@ -54,11 +57,8 @@ void WorldSession::HandleMoveWorldportAck() { Player* player = GetPlayer(); // ignore unexpected far teleports - if (!player->IsBeingTeleportedFar()) - return; - - bool seamlessTeleport = player->IsBeingTeleportedSeamlessly(); - player->SetSemaphoreTeleportFar(false); + bool seamlessTeleport = player->GetTeleportOptions().HasFlag(TELE_TO_SEAMLESS); + player->SetTeleportState(TeleportState::NotTeleporting); // get the teleport destination TeleportLocation const& loc = player->GetTeleportDest(); @@ -188,7 +188,7 @@ void WorldSession::HandleMoveWorldportAck() player->FinishTaxiFlight(); } - if (!player->IsAlive() && player->GetTeleportOptions() & TELE_REVIVE_AT_TELEPORT) + if (!player->IsAlive() && player->GetTeleportOptions().HasFlag(TELE_REVIVE_AT_TELEPORT)) player->ResurrectPlayer(0.5f); // resurrect character at enter into instance where his corpse exist after add to map @@ -252,7 +252,7 @@ void WorldSession::HandleMoveWorldportAck() void WorldSession::HandleSuspendTokenResponse(WorldPackets::Movement::SuspendTokenResponse& /*suspendTokenResponse*/) { - if (!_player->IsBeingTeleportedFar()) + if (_player->GetTeleportState() != TeleportState::WaitingForSuspendTokenResponse) return; TeleportLocation const& loc = GetPlayer()->GetTeleportDest(); @@ -267,11 +267,13 @@ void WorldSession::HandleSuspendTokenResponse(WorldPackets::Movement::SuspendTok WorldPackets::Movement::NewWorld packet; packet.MapID = loc.Location.GetMapId(); packet.Loc.Pos = loc.Location; - packet.Reason = !_player->IsBeingTeleportedSeamlessly() ? NEW_WORLD_NORMAL : NEW_WORLD_SEAMLESS; + packet.Reason = !_player->GetTeleportOptions().HasFlag(TELE_TO_SEAMLESS) ? NEW_WORLD_NORMAL : NEW_WORLD_SEAMLESS; packet.Counter = _player->GetNewWorldCounter(); SendPacket(packet.Write()); - if (_player->IsBeingTeleportedSeamlessly()) + _player->SetTeleportState(TeleportState::WaitingForWorldPortAck); + + if (_player->GetTeleportOptions().HasFlag(TELE_TO_SEAMLESS)) HandleMoveWorldportAck(); } @@ -281,13 +283,13 @@ void WorldSession::HandleMoveTeleportAck(WorldPackets::Movement::MoveTeleportAck Player* plMover = _player->GetUnitBeingMoved()->ToPlayer(); - if (!plMover || !plMover->IsBeingTeleportedNear()) + if (!plMover || plMover->GetTeleportState() != TeleportState::WaitingForTeleportAck) return; if (packet.MoverGUID != plMover->GetGUID()) return; - plMover->SetSemaphoreTeleportNear(false); + plMover->SetTeleportState(TeleportState::NotTeleporting); uint32 old_zone = plMover->GetZoneId();