mirror of
https://github.com/araxiaonline/TrinityCore.git
synced 2026-06-13 03:32:28 -04:00
Core/Players: Add more granular teleport state tracking, fixes delayed cross map teleports
Closes #31782 Closes #31788
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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<Player>
|
||||
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<TeleportToOptions> 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<Player>
|
||||
|
||||
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<Player>
|
||||
|
||||
// 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<PetStable> m_petStable;
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user