Core/Players: Improve setting ActivePlayerData::TransportServerTime by including TIME_SYNC and CMSG_QUEUED_MESSAGES_END values in its calculation

* This removes delay on nearby object visibility after login and teleport

(cherry picked from commit 821ecf8fa3)

# Conflicts:
#	src/server/game/Server/Protocol/Opcodes.cpp
This commit is contained in:
Shauren
2025-04-16 16:16:49 +02:00
committed by Ovahlord
parent c637ee12cc
commit cbb86c9e5b
10 changed files with 75 additions and 24 deletions

View File

@@ -1426,9 +1426,6 @@ bool Player::TeleportTo(TeleportLocation const& teleportLocation, TeleportToOpti
}
SendDirectMessage(transferPending.Write());
RemovePlayerLocalFlag(PLAYER_LOCAL_FLAG_OVERRIDE_TRANSPORT_SERVER_TIME);
SetTransportServerTime(0);
}
// remove from old map now

View File

@@ -64,6 +64,7 @@
#include "SystemPackets.h"
#include "Util.h"
#include "World.h"
#include <boost/circular_buffer.hpp>
#include <sstream>
class LoginQueryHolder : public CharacterDatabaseQueryHolder
@@ -964,6 +965,9 @@ void WorldSession::HandleContinuePlayerLogin()
SendPacket(WorldPackets::Auth::ResumeComms(CONNECTION_TYPE_INSTANCE).Write());
// client will respond to SMSG_RESUME_COMMS with CMSG_QUEUED_MESSAGES_END
RegisterTimeSync(SPECIAL_RESUME_COMMS_TIME_SYNC_COUNTER);
AddQueryHolderCallback(CharacterDatabase.DelayQueryHolder(holder)).AfterComplete([this](SQLQueryHolderBase const& holder)
{
HandlePlayerLogin(static_cast<LoginQueryHolder const&>(holder));
@@ -1005,6 +1009,12 @@ void WorldSession::HandlePlayerLogin(LoginQueryHolder const& holder)
return;
}
if (!_timeSyncClockDeltaQueue->empty())
{
pCurrChar->SetPlayerLocalFlag(PLAYER_LOCAL_FLAG_OVERRIDE_TRANSPORT_SERVER_TIME);
pCurrChar->SetTransportServerTime(_timeSyncClockDelta);
}
pCurrChar->SetVirtualPlayerRealm(GetVirtualRealmAddress());
SendAccountDataTimes(ObjectGuid::Empty, GLOBAL_CACHE_MASK);

View File

@@ -15,6 +15,7 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "AuthenticationPackets.h"
#include "WorldSession.h"
#include "Battleground.h"
#include "Corpse.h"
@@ -783,20 +784,18 @@ void WorldSession::HandleMoveTimeSkippedOpcode(WorldPackets::Movement::MoveTimeS
mover->SendMessageToSet(moveSkipTime.Write(), _player);
}
void WorldSession::HandleTimeSyncResponse(WorldPackets::Misc::TimeSyncResponse& timeSyncResponse)
void WorldSession::HandleTimeSync(uint32 counter, int64 clientTime, TimePoint responseReceiveTime)
{
if (_pendingTimeSyncRequests.count(timeSyncResponse.SequenceIndex) == 0)
auto serverTimeAtSent = _pendingTimeSyncRequests.extract(counter);
if (!serverTimeAtSent)
return;
uint32 serverTimeAtSent = _pendingTimeSyncRequests.at(timeSyncResponse.SequenceIndex);
_pendingTimeSyncRequests.erase(timeSyncResponse.SequenceIndex);
// time it took for the request to travel to the client, for the client to process it and reply and for response to travel back to the server.
// we are going to make 2 assumptions:
// 1) we assume that the request processing time equals 0.
// 2) we assume that the packet took as much time to travel from server to client than it took to travel from client to server.
uint32 roundTripDuration = getMSTimeDiff(serverTimeAtSent, timeSyncResponse.GetReceivedTime());
uint32 lagDelay = roundTripDuration / 2;
uint32 roundTripDuration = getMSTimeDiff(serverTimeAtSent.mapped(), responseReceiveTime);
int64 lagDelay = roundTripDuration / 2;
/*
clockDelta = serverTime - clientTime
@@ -808,11 +807,28 @@ void WorldSession::HandleTimeSyncResponse(WorldPackets::Misc::TimeSyncResponse&
using the following relation:
serverTime = clockDelta + clientTime
*/
int64 clockDelta = (int64)serverTimeAtSent + (int64)lagDelay - (int64)timeSyncResponse.ClientTime;
int64 clockDelta = serverTimeAtSent.mapped() + lagDelay - clientTime;
_timeSyncClockDeltaQueue->push_back(std::pair<int64, uint32>(clockDelta, roundTripDuration));
ComputeNewClockDelta();
}
void WorldSession::HandleTimeSyncResponse(WorldPackets::Misc::TimeSyncResponse const& timeSyncResponse)
{
HandleTimeSync(timeSyncResponse.SequenceIndex, timeSyncResponse.ClientTime, timeSyncResponse.GetReceivedTime());
}
void WorldSession::HandleQueuedMessagesEnd(WorldPackets::Auth::QueuedMessagesEnd const& queuedMessagesEnd)
{
HandleTimeSync(SPECIAL_RESUME_COMMS_TIME_SYNC_COUNTER, queuedMessagesEnd.Timestamp, queuedMessagesEnd.GetRawPacket()->GetReceivedTime());
}
void WorldSession::HandleMoveInitActiveMoverComplete(WorldPackets::Movement::MoveInitActiveMoverComplete const& moveInitActiveMoverComplete)
{
HandleTimeSync(SPECIAL_INIT_ACTIVE_MOVER_TIME_SYNC_COUNTER, moveInitActiveMoverComplete.Ticks, moveInitActiveMoverComplete.GetRawPacket()->GetReceivedTime());
_player->UpdateObjectVisibility(false);
}
void WorldSession::ComputeNewClockDelta()
{
// implementation of the technique described here: https://web.archive.org/web/20180430214420/http://www.mine-control.com/zack/timesync/timesync.html
@@ -846,12 +862,10 @@ void WorldSession::ComputeNewClockDelta()
}
else if (_timeSyncClockDelta == 0)
_timeSyncClockDelta = _timeSyncClockDeltaQueue->back().first;
}
void WorldSession::HandleMoveInitActiveMoverComplete(WorldPackets::Movement::MoveInitActiveMoverComplete& moveInitActiveMoverComplete)
{
_player->SetPlayerLocalFlag(PLAYER_LOCAL_FLAG_OVERRIDE_TRANSPORT_SERVER_TIME);
_player->SetTransportServerTime(GameTime::GetGameTimeMS() - moveInitActiveMoverComplete.Ticks);
_player->UpdateObjectVisibility(false);
if (_player)
{
_player->SetPlayerLocalFlag(PLAYER_LOCAL_FLAG_OVERRIDE_TRANSPORT_SERVER_TIME);
_player->SetTransportServerTime(int32(_timeSyncClockDelta));
}
}

View File

@@ -1853,6 +1853,9 @@ void Map::SendInitSelf(Player* player)
WorldPacket packet;
data.BuildPacket(&packet);
player->SendDirectMessage(&packet);
// client will respond to SMSG_UPDATE_OBJECT that contains ThisIsYou = true with CMSG_MOVE_INIT_ACTIVE_MOVER_COMPLETE
player->GetSession()->RegisterTimeSync(WorldSession::SPECIAL_INIT_ACTIVE_MOVER_TIME_SYNC_COUNTER);
}
void Map::SendInitTransports(Player* player)

View File

@@ -367,3 +367,8 @@ WorldPacket const* WorldPackets::Auth::EnterEncryptedMode::Write()
return &_worldPacket;
}
void WorldPackets::Auth::QueuedMessagesEnd::Read()
{
_worldPacket >> Timestamp;
}

View File

@@ -310,6 +310,16 @@ namespace WorldPackets
std::array<uint8, 32> const& EncryptionKey;
bool Enabled = false;
};
class QueuedMessagesEnd final : public ClientPacket
{
public:
QueuedMessagesEnd(WorldPacket&& packet) : ClientPacket(CMSG_QUEUED_MESSAGES_END, std::move(packet)) { }
void Read() override;
uint32 Timestamp = 0;
};
}
}

View File

@@ -652,7 +652,7 @@ void OpcodeTable::InitializeClientOpcodes()
DEFINE_HANDLER(CMSG_QUEST_LOG_REMOVE_QUEST, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleQuestLogRemoveQuest);
DEFINE_HANDLER(CMSG_QUEST_POI_QUERY, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleQuestPOIQuery);
DEFINE_HANDLER(CMSG_QUEST_PUSH_RESULT, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleQuestPushResult);
DEFINE_HANDLER(CMSG_QUEUED_MESSAGES_END, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL);
DEFINE_HANDLER(CMSG_QUEUED_MESSAGES_END, STATUS_AUTHED, PROCESS_INPLACE, &WorldSession::HandleQueuedMessagesEnd);
DEFINE_HANDLER(CMSG_QUICK_JOIN_AUTO_ACCEPT_REQUESTS, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::Handle_NULL);
DEFINE_HANDLER(CMSG_QUICK_JOIN_REQUEST_INVITE, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::Handle_NULL);
DEFINE_HANDLER(CMSG_QUICK_JOIN_RESPOND_TO_INVITE, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::Handle_NULL);

View File

@@ -1566,13 +1566,18 @@ void WorldSession::SendTimeSync()
timeSyncRequest.SequenceIndex = _timeSyncNextCounter;
SendPacket(timeSyncRequest.Write());
_pendingTimeSyncRequests[_timeSyncNextCounter] = getMSTime();
RegisterTimeSync(_timeSyncNextCounter);
// Schedule next sync in 10 sec (except for the 2 first packets, which are spaced by only 5s)
_timeSyncTimer = _timeSyncNextCounter == 0 ? 5000 : 10000;
_timeSyncNextCounter++;
}
void WorldSession::RegisterTimeSync(uint32 counter)
{
_pendingTimeSyncRequests[counter] = getMSTime();
}
uint32 WorldSession::AdjustClientMovementTime(uint32 time) const
{
int64 movementTime = int64(time) + _timeSyncClockDelta;

View File

@@ -141,6 +141,7 @@ namespace WorldPackets
namespace Auth
{
enum class ConnectToSerial : uint32;
class QueuedMessagesEnd;
}
namespace Bank
@@ -1105,8 +1106,12 @@ class TC_GAME_API WorldSession
// Time Synchronisation
void ResetTimeSync();
void SendTimeSync();
void RegisterTimeSync(uint32 counter);
uint32 AdjustClientMovementTime(uint32 time) const;
static constexpr uint32 SPECIAL_INIT_ACTIVE_MOVER_TIME_SYNC_COUNTER = 0xFFFFFFFF;
static constexpr uint32 SPECIAL_RESUME_COMMS_TIME_SYNC_COUNTER = 0xFFFFFFFE;
// Packets cooldown
time_t GetCalendarEventCreationCooldown() const { return _calendarEventCreationCooldown; }
void SetCalendarEventCreationCooldown(time_t cooldown) { _calendarEventCreationCooldown = cooldown; }
@@ -1265,7 +1270,7 @@ class TC_GAME_API WorldSession
void HandleMoveSetVehicleRecAck(WorldPackets::Vehicle::MoveSetVehicleRecIdAck& setVehicleRecIdAck);
void HandleMoveTimeSkippedOpcode(WorldPackets::Movement::MoveTimeSkipped& moveTimeSkipped);
void HandleMovementAckMessage(WorldPackets::Movement::MovementAckMessage& movementAck);
void HandleMoveInitActiveMoverComplete(WorldPackets::Movement::MoveInitActiveMoverComplete& moveInitActiveMoverComplete);
void HandleMoveInitActiveMoverComplete(WorldPackets::Movement::MoveInitActiveMoverComplete const& moveInitActiveMoverComplete);
void HandleRequestRaidInfoOpcode(WorldPackets::Party::RequestRaidInfo& packet);
@@ -1544,7 +1549,9 @@ class TC_GAME_API WorldSession
void HandleSetDungeonDifficultyOpcode(WorldPackets::Misc::SetDungeonDifficulty& setDungeonDifficulty);
void HandleSetRaidDifficultyOpcode(WorldPackets::Misc::SetRaidDifficulty& setRaidDifficulty);
void HandleSetTitleOpcode(WorldPackets::Character::SetTitle& packet);
void HandleTimeSyncResponse(WorldPackets::Misc::TimeSyncResponse& timeSyncResponse);
void HandleTimeSync(uint32 counter, int64 clientTime, TimePoint responseReceiveTime);
void HandleTimeSyncResponse(WorldPackets::Misc::TimeSyncResponse const& timeSyncResponse);
void HandleQueuedMessagesEnd(WorldPackets::Auth::QueuedMessagesEnd const& queuedMessagesEnd);
void HandleWhoIsOpcode(WorldPackets::Who::WhoIsRequest& packet);
void HandleResetInstancesOpcode(WorldPackets::Instance::ResetInstances& packet);
void HandleInstanceLockResponse(WorldPackets::Instance::InstanceLockResponse& packet);
@@ -1847,7 +1854,7 @@ class TC_GAME_API WorldSession
int64 _timeSyncClockDelta;
void ComputeNewClockDelta();
std::map<uint32, uint32> _pendingTimeSyncRequests; // key: counter. value: server time when packet with that counter was sent.
std::map<uint32, int64> _pendingTimeSyncRequests; // key: counter. value: server time when packet with that counter was sent.
uint32 _timeSyncNextCounter;
uint32 _timeSyncTimer;

View File

@@ -478,7 +478,7 @@ WorldSocket::ReadDataHandlerResult WorldSocket::ReadDataHandler()
[[fallthrough]];
default:
{
if (opcode == CMSG_TIME_SYNC_RESPONSE)
if (opcode == CMSG_TIME_SYNC_RESPONSE || opcode == CMSG_MOVE_INIT_ACTIVE_MOVER_COMPLETE || opcode == CMSG_QUEUED_MESSAGES_END)
packet.SetReceiveTime(std::chrono::steady_clock::now());
sessionGuard.lock();