From 2d21bfc915febc53a0d41d6f1de22ab8ed97957c Mon Sep 17 00:00:00 2001 From: Chaouki Dhib Date: Fri, 23 Apr 2021 15:53:09 +0200 Subject: [PATCH] feat(Core/Movement): time synchronisation to better interpret client timestamps (#5300) --- src/common/Packets/WorldPacket.h | 9 ++ src/common/Utilities/CircularBuffer.h | 100 +++++++++++++++++++ src/common/Utilities/MathUtil.h | 83 +++++++++++++++ src/server/game/Entities/Player/Player.cpp | 36 +------ src/server/game/Entities/Player/Player.h | 8 -- src/server/game/Handlers/MiscHandler.cpp | 8 -- src/server/game/Handlers/MovementHandler.cpp | 91 +++++++++++++++-- src/server/game/Server/Protocol/Opcodes.cpp | 2 +- src/server/game/Server/WorldSession.cpp | 50 +++++++++- src/server/game/Server/WorldSession.h | 19 +++- src/server/game/Server/WorldSocket.cpp | 57 ++++++----- 11 files changed, 367 insertions(+), 96 deletions(-) create mode 100644 src/common/Utilities/CircularBuffer.h create mode 100644 src/common/Utilities/MathUtil.h diff --git a/src/common/Packets/WorldPacket.h b/src/common/Packets/WorldPacket.h index 650fc14c2..0b35ca57e 100644 --- a/src/common/Packets/WorldPacket.h +++ b/src/common/Packets/WorldPacket.h @@ -9,6 +9,7 @@ #include "Common.h" #include "ByteBuffer.h" +#include "Duration.h" class WorldPacket : public ByteBuffer { @@ -18,6 +19,11 @@ public: { } explicit WorldPacket(uint16 opcode, size_t res = 200) : ByteBuffer(res), m_opcode(opcode) { } + + WorldPacket(WorldPacket&& packet, TimePoint receivedTime) : ByteBuffer(std::move(packet)), m_opcode(packet.m_opcode), m_receivedTime(receivedTime) + { + } + // copy constructor WorldPacket(const WorldPacket& packet) : ByteBuffer(packet), m_opcode(packet.m_opcode) { @@ -39,7 +45,10 @@ public: [[nodiscard]] uint16 GetOpcode() const { return m_opcode; } void SetOpcode(uint16 opcode) { m_opcode = opcode; } + [[nodiscard]] TimePoint GetReceivedTime() const { return m_receivedTime; } + protected: uint16 m_opcode{0}; + TimePoint m_receivedTime; // only set for a specific set of opcodes, for performance reasons. }; #endif diff --git a/src/common/Utilities/CircularBuffer.h b/src/common/Utilities/CircularBuffer.h new file mode 100644 index 000000000..dcb95940d --- /dev/null +++ b/src/common/Utilities/CircularBuffer.h @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3 + * + * This file was based on + * https://embeddedartistry.com/blog/2017/05/17/creating-a-circular-buffer-in-c-and-c/ + * https://github.com/embeddedartistry/embedded-resources/blob/master/examples/cpp/circular_buffer.cpp + */ + +#ifndef AZEROTHCORE_CIRCULAR_BUFFER_H +#define AZEROTHCORE_CIRCULAR_BUFFER_H + +#include +#include +#include + +template +class CircularBuffer { +public: + explicit CircularBuffer(size_t size) : + buf_(std::unique_ptr(new T[size])), + max_size_(size) + { + + } + + void put(T item) + { + std::lock_guard lock(mutex_); + + buf_[head_] = item; + + if (full_) + { + tail_ = (tail_ + 1) % max_size_; + } + + head_ = (head_ + 1) % max_size_; + + full_ = head_ == tail_; + } + + bool empty() const + { + //if head and tail are equal, we are empty + return (!full_ && (head_ == tail_)); + } + + bool full() const + { + //If tail is ahead the head by 1, we are full + return full_; + } + + size_t capacity() const + { + return max_size_; + } + + size_t size() const + { + size_t size = max_size_; + + if (!full_) + { + if (head_ >= tail_) + { + size = head_ - tail_; + } + else + { + size += head_ - tail_; + } + } + + return size; + } + + // the implementation of this function is simplified by the fact that head_ will never be lower than tail_ + // when compared to the original implementation of this class + std::vector content() { + std::lock_guard lock(mutex_); + + return std::vector(buf_.get(), buf_.get() + size()); + } + + T peak_back() { + std::lock_guard lock(mutex_); + + return empty() ? T() : buf_[tail_]; + } + +private: + std::mutex mutex_; + std::unique_ptr buf_; + size_t head_ = 0; + size_t tail_ = 0; + const size_t max_size_; + bool full_ = 0; +}; +#endif diff --git a/src/common/Utilities/MathUtil.h b/src/common/Utilities/MathUtil.h new file mode 100644 index 000000000..62943822a --- /dev/null +++ b/src/common/Utilities/MathUtil.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3 + */ + +#ifndef _MATH_UTIL_H +#define _MATH_UTIL_H + +#include +#include +#include +#include + +// based on https://stackoverflow.com/questions/7616511/calculate-mean-and-standard-deviation-from-a-vector-of-samples-in-c-using-boos/12405793#comment32490316_12405793 +template ()))>::type> +inline T standard_deviation(Container&& c) +{ + auto b = std::begin(c), e = std::end(c); + auto size = std::distance(b, e); + auto sum = std::accumulate(b, e, T()); + auto mean = sum / size; + + if (size == 1) + return (T) 0; + + T accum = T(); + for (const auto d : c) + accum += (d - mean) * (d - mean); + return std::sqrt(accum / (size - 1)); +} + + +template ()))>::type> +inline T mean(Container&& c) +{ + auto b = std::begin(c), e = std::end(c); + auto size = std::distance(b, e); + auto sum = std::accumulate(b, e, T()); + return sum / size; +} + +// based off https://www.geeksforgeeks.org/finding-median-of-unsorted-array-in-linear-time-using-c-stl/ +template +inline T median(std::vector a) +{ + size_t n = a.size(); + // If size of the arr[] is even + if (n % 2 == 0) { + + // Applying nth_element + // on n/2th index + std::nth_element(a.begin(), + a.begin() + n / 2, + a.end()); + + // Applying nth_element + // on (n-1)/2 th index + std::nth_element(a.begin(), + a.begin() + (n - 1) / 2, + a.end()); + + // Find the average of value at + // index N/2 and (N-1)/2 + return (T)(a[(n - 1) / 2] + + a[n / 2]) + / 2.0; + } + + // If size of the arr[] is odd + else { + + // Applying nth_element + // on n/2 + std::nth_element(a.begin(), + a.begin() + n / 2, + a.end()); + + // Value at index (N/2)th + // is the median + return (T)a[n / 2]; + } +} + +#endif diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index ca9eda249..22effef14 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -929,11 +929,6 @@ Player::Player(WorldSession* session): Unit(true), m_mover(this) m_ChampioningFaction = 0; - m_timeSyncCounter = 0; - m_timeSyncTimer = 0; - m_timeSyncClient = 0; - m_timeSyncServer = 0; - for (uint8 i = 0; i < MAX_POWERS; ++i) m_powerFraction[i] = 0; @@ -1833,14 +1828,6 @@ void Player::Update(uint32 p_time) m_zoneUpdateTimer -= p_time; } - if (m_timeSyncTimer > 0) - { - if (p_time >= m_timeSyncTimer) - SendTimeSync(); - else - m_timeSyncTimer -= p_time; - } - if (IsAlive()) { m_regenTimer += p_time; @@ -23804,8 +23791,8 @@ void Player::SendInitialPacketsAfterAddToMap() { UpdateVisibilityForPlayer(true); - ResetTimeSync(); - SendTimeSync(); + GetSession()->ResetTimeSync(); + GetSession()->SendTimeSync(); CastSpell(this, 836, true); // LOGINEFFECT @@ -27558,25 +27545,6 @@ uint8 Player::GetMostPointsTalentTree() const return maxIndex; } -void Player::ResetTimeSync() -{ - m_timeSyncCounter = 0; - m_timeSyncTimer = 0; - m_timeSyncClient = 0; - m_timeSyncServer = World::GetGameTimeMS(); -} - -void Player::SendTimeSync() -{ - WorldPacket data(SMSG_TIME_SYNC_REQ, 4); - data << uint32(m_timeSyncCounter++); - GetSession()->SendPacket(&data); - - // Schedule next sync in 10 sec - m_timeSyncTimer = 10000; - m_timeSyncServer = World::GetGameTimeMS(); -} - void Player::SetReputation(uint32 factionentry, uint32 value) { GetReputationMgr().SetReputation(sFactionStore.LookupEntry(factionentry), value); diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index c49df13f9..6cafa7990 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -2823,9 +2823,6 @@ protected: ItemDurationList m_itemSoulboundTradeable; std::mutex m_soulboundTradableLock; - void ResetTimeSync(); - void SendTimeSync(); - uint64 m_resurrectGUID; uint32 m_resurrectMap; float m_resurrectX, m_resurrectY, m_resurrectZ; @@ -2966,11 +2963,6 @@ private: uint32 m_ChampioningFaction; - uint32 m_timeSyncCounter; - uint32 m_timeSyncTimer; - uint32 m_timeSyncClient; - uint32 m_timeSyncServer; - InstanceTimeMap _instanceResetTimes; uint32 _pendingBindId; uint32 _pendingBindTimer; diff --git a/src/server/game/Handlers/MiscHandler.cpp b/src/server/game/Handlers/MiscHandler.cpp index 0a5909c1b..d55aa041b 100644 --- a/src/server/game/Handlers/MiscHandler.cpp +++ b/src/server/game/Handlers/MiscHandler.cpp @@ -1356,14 +1356,6 @@ void WorldSession::HandleSetTitleOpcode(WorldPacket& recv_data) GetPlayer()->SetUInt32Value(PLAYER_CHOSEN_TITLE, title); } -void WorldSession::HandleTimeSyncResp(WorldPacket& recv_data) -{ - uint32 counter, clientTicks; - recv_data >> counter >> clientTicks; - //uint32 ourTicks = clientTicks + (World::GetGameTimeMS() - _player->m_timeSyncServer); - _player->m_timeSyncClient = clientTicks; -} - void WorldSession::HandleResetInstancesOpcode(WorldPacket& /*recv_data*/) { #if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS) diff --git a/src/server/game/Handlers/MovementHandler.cpp b/src/server/game/Handlers/MovementHandler.cpp index ee19e1883..d56e1a9ff 100644 --- a/src/server/game/Handlers/MovementHandler.cpp +++ b/src/server/game/Handlers/MovementHandler.cpp @@ -14,6 +14,7 @@ #include "GameGraveyard.h" #include "InstanceSaveMgr.h" #include "Log.h" +#include "MathUtil.h" #include "MapManager.h" #include "ObjectMgr.h" #include "Opcodes.h" @@ -419,11 +420,6 @@ void WorldSession::HandleMovementOpcodes(WorldPacket& recvData) if (mover->GetGUID() != _player->GetGUID()) movementInfo.flags &= ~MOVEMENTFLAG_WALKING; - uint32 mstime = World::GetGameTimeMS(); - /*----------------------*/ - if(m_clientTimeDelay == 0) - m_clientTimeDelay = mstime > movementInfo.time ? std::min(mstime - movementInfo.time, (uint32)100) : 0; - // Xinef: do not allow to move with UNIT_FLAG_DISABLE_MOVE if (mover->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE)) { @@ -442,8 +438,16 @@ void WorldSession::HandleMovementOpcodes(WorldPacket& recvData) /* process position-change */ WorldPacket data(opcode, recvData.size()); - //movementInfo.time = movementInfo.time + m_clientTimeDelay + MOVEMENT_PACKET_TIME_DELAY; - movementInfo.time = mstime; // pussywizard: set to time of relocation (server time), constant addition may smoothen movement clientside, but client sees target on different position than the real serverside position + int64 movementTime = (int64)movementInfo.time + _timeSyncClockDelta; + if (_timeSyncClockDelta == 0 || movementTime < 0 || movementTime > 0xFFFFFFFF) + { + LOG_INFO("misc", "The computed movement time using clockDelta is erronous. Using fallback instead"); + movementInfo.time = getMSTime(); + } + else + { + movementInfo.time = (uint32) movementTime; + } movementInfo.guid = mover->GetGUID(); WriteMovementInfo(&data, &movementInfo); @@ -796,3 +800,76 @@ void WorldSession::HandleMoveTimeSkippedOpcode(WorldPacket& recvData) data << timeSkipped; GetPlayer()->SendMessageToSet(&data, false); } + +void WorldSession::HandleTimeSyncResp(WorldPacket& recvData) +{ +#if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS) + LOG_DEBUG("network", "CMSG_TIME_SYNC_RESP"); +#endif + + uint32 counter, clientTimestamp; + recvData >> counter >> clientTimestamp; + + if (_pendingTimeSyncRequests.count(counter) == 0) + return; + + uint32 serverTimeAtSent = _pendingTimeSyncRequests.at(counter); + _pendingTimeSyncRequests.erase(counter); + + // 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, recvData.GetReceivedTime()); + uint32 lagDelay = roundTripDuration / 2; + + /* + clockDelta = serverTime - clientTime + where + serverTime: time that was displayed on the clock of the SERVER at the moment when the client processed the SMSG_TIME_SYNC_REQUEST packet. + clientTime: time that was displayed on the clock of the CLIENT at the moment when the client processed the SMSG_TIME_SYNC_REQUEST packet. + + Once clockDelta has been computed, we can compute the time of an event on server clock when we know the time of that same event on the client clock, + using the following relation: + serverTime = clockDelta + clientTime + */ + int64 clockDelta = (int64)serverTimeAtSent + (int64)lagDelay - (int64)clientTimestamp; + _timeSyncClockDeltaQueue.put(std::pair(clockDelta, roundTripDuration)); + ComputeNewClockDelta(); +} + +void WorldSession::ComputeNewClockDelta() +{ + // implementation of the technique described here: https://web.archive.org/web/20180430214420/http://www.mine-control.com/zack/timesync/timesync.html + // to reduce the skew induced by dropped TCP packets that get resent. + + std::vector latencies; + std::vector clockDeltasAfterFiltering; + + for (auto pair : _timeSyncClockDeltaQueue.content()) + latencies.push_back(pair.second); + + uint32 latencyMedian = median(latencies); + uint32 latencyStandardDeviation = standard_deviation(latencies); + + uint32 sampleSizeAfterFiltering = 0; + for (auto pair : _timeSyncClockDeltaQueue.content()) + { + if (pair.second <= latencyMedian + latencyStandardDeviation) { + clockDeltasAfterFiltering.push_back(pair.first); + sampleSizeAfterFiltering++; + } + } + + if (sampleSizeAfterFiltering != 0) + { + int64 meanClockDelta = static_cast(mean(clockDeltasAfterFiltering)); + if (std::abs(meanClockDelta - _timeSyncClockDelta) > 25) + _timeSyncClockDelta = meanClockDelta; + } + else if (_timeSyncClockDelta == 0) + { + std::pair back = _timeSyncClockDeltaQueue.peak_back(); + _timeSyncClockDelta = back.first; + } +} diff --git a/src/server/game/Server/Protocol/Opcodes.cpp b/src/server/game/Server/Protocol/Opcodes.cpp index 470c9f343..6c32c8e0c 100644 --- a/src/server/game/Server/Protocol/Opcodes.cpp +++ b/src/server/game/Server/Protocol/Opcodes.cpp @@ -926,7 +926,7 @@ OpcodeHandler opcodeTable[NUM_MSG_TYPES] = /*0x38D*/ { "CMSG_MOVE_CHNG_TRANSPORT", STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleMovementOpcodes }, /*0x38E*/ { "MSG_PARTY_ASSIGNMENT", STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandlePartyAssignmentOpcode }, /*0x38F*/ { "SMSG_OFFER_PETITION_ERROR", STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide }, - /*0x390*/ { "SMSG_TIME_SYNC_REQ", STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide }, + /*0x390*/ { "SMSG_TIME_SYNC_REQ", STATUS_NEVER, PROCESS_THREADSAFE, &WorldSession::Handle_ServerSide }, /*0x391*/ { "CMSG_TIME_SYNC_RESP", STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleTimeSyncResp }, /*0x392*/ { "CMSG_SEND_LOCAL_EVENT", STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL }, /*0x393*/ { "CMSG_SEND_GENERAL_TRIGGER", STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL }, diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp index a9d58b5ad..1dd8817a0 100644 --- a/src/server/game/Server/WorldSession.cpp +++ b/src/server/game/Server/WorldSession.cpp @@ -110,14 +110,16 @@ WorldSession::WorldSession(uint32 id, WorldSocket* sock, AccountTypes sec, uint8 m_sessionDbcLocale(sWorld->GetDefaultDbcLocale()), m_sessionDbLocaleIndex(locale), m_latency(0), - m_clientTimeDelay(0), m_TutorialsChanged(false), recruiterId(recruiter), isRecruiter(isARecruiter), m_currentVendorEntry(0), m_currentBankerGUID(0), timeWhoCommandAllowed(0), - _calendarEventCreationCooldown(0) + _calendarEventCreationCooldown(0), + _timeSyncClockDeltaQueue(6), + _timeSyncClockDelta(0), + _pendingTimeSyncRequests() { memset(m_Tutorials, 0, sizeof(m_Tutorials)); @@ -126,6 +128,9 @@ WorldSession::WorldSession(uint32 id, WorldSocket* sock, AccountTypes sec, uint8 _kicked = false; _shouldSetOfflineInDB = true; + _timeSyncNextCounter = 0; + _timeSyncTimer = 0; + if (sock) { m_Address = sock->GetRemoteAddress(); @@ -253,7 +258,7 @@ void WorldSession::QueuePacket(WorldPacket* new_packet) /// Update the WorldSession (triggered by World update) bool WorldSession::Update(uint32 diff, PacketFilter& updater) { - if (updater.ProcessLogout()) + if (updater.ProcessUnsafe()) { UpdateTimeOutTime(diff); @@ -263,7 +268,7 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater) m_Socket->CloseSocket("Client didn't send anything for too long"); } - HandleTeleportTimeout(updater.ProcessLogout()); + HandleTeleportTimeout(updater.ProcessUnsafe()); uint32 _startMSTime = getMSTime(); WorldPacket* packet = nullptr; @@ -390,7 +395,7 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater) if (m_Socket && !m_Socket->IsClosed()) ProcessQueryCallbacks(); - if (updater.ProcessLogout()) + if (updater.ProcessUnsafe()) { if (m_Socket && !m_Socket->IsClosed() && _warden) { @@ -415,6 +420,22 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater) } } + if (!updater.ProcessUnsafe()) // <=> updater is of type MapSessionFilter + { + // Send time sync packet every 10s. + if (_timeSyncTimer > 0) + { + if (diff >= _timeSyncTimer) + { + SendTimeSync(); + } + else + { + _timeSyncTimer -= diff; + } + } + } + return true; } @@ -1648,3 +1669,22 @@ uint32 WorldSession::DosProtection::GetMaxPacketCounterAllowed(uint16 opcode) co return maxPacketCounterAllowed; } + +void WorldSession::ResetTimeSync() +{ + _timeSyncNextCounter = 0; + _pendingTimeSyncRequests.clear(); +} + +void WorldSession::SendTimeSync() +{ + WorldPacket data(SMSG_TIME_SYNC_REQ, 4); + data << uint32(_timeSyncNextCounter); + SendPacket(&data); + + _pendingTimeSyncRequests[_timeSyncNextCounter] = getMSTime(); + + // Schedule next sync in 10 sec (except for the 2 first packets, which are spaced by only 5s) + _timeSyncTimer = _timeSyncNextCounter == 0 ? 5000 : 10000; + _timeSyncNextCounter++; +} diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h index 7cf6bc061..f4ffb2fc6 100644 --- a/src/server/game/Server/WorldSession.h +++ b/src/server/game/Server/WorldSession.h @@ -15,6 +15,7 @@ #include "AuthDefines.h" #include "AddonMgr.h" #include "BanManager.h" +#include "CircularBuffer.h" #include "Common.h" #include "DatabaseEnv.h" #include "GossipDef.h" @@ -23,6 +24,7 @@ #include "World.h" #include "WorldPacket.h" #include +#include class Creature; class GameObject; @@ -124,7 +126,7 @@ public: virtual ~PacketFilter() = default; virtual bool Process(WorldPacket* /*packet*/) { return true; } - [[nodiscard]] virtual bool ProcessLogout() const { return true; } + [[nodiscard]] virtual bool ProcessUnsafe() const { return true; } protected: WorldSession* const m_pSession; @@ -138,7 +140,7 @@ public: bool Process(WorldPacket* packet) override; //in Map::Update() we do not process player logout! - [[nodiscard]] bool ProcessLogout() const override { return false; } + [[nodiscard]] bool ProcessUnsafe() const override { return false; } }; //class used to filer only thread-unsafe packets from queue @@ -357,7 +359,6 @@ public: uint32 GetLatency() const { return m_latency; } void SetLatency(uint32 latency) { m_latency = latency; } - void ResetClientTimeDelay() { m_clientTimeDelay = 0; } std::atomic m_timeOutTime; void UpdateTimeOutTime(uint32 diff) @@ -387,6 +388,9 @@ public: time_t GetCalendarEventCreationCooldown() const { return _calendarEventCreationCooldown; } void SetCalendarEventCreationCooldown(time_t cooldown) { _calendarEventCreationCooldown = cooldown; } + // Time Synchronisation + void ResetTimeSync(); + void SendTimeSync(); public: // opcodes handlers void Handle_NULL(WorldPacket& recvPacket); // not used void Handle_EarlyProccess(WorldPacket& recvPacket); // just mark packets processed in WorldSocket::OnRead @@ -1028,7 +1032,6 @@ private: LocaleConstant m_sessionDbcLocale; LocaleConstant m_sessionDbLocaleIndex; uint32 m_latency; - uint32 m_clientTimeDelay; AccountData m_accountData[NUM_ACCOUNT_DATA_TYPES]; uint32 m_Tutorials[MAX_ACCOUNT_TUTORIAL_VALUES]; bool m_TutorialsChanged; @@ -1044,6 +1047,14 @@ private: bool _shouldSetOfflineInDB; // Packets cooldown time_t _calendarEventCreationCooldown; + + CircularBuffer> _timeSyncClockDeltaQueue; // first member: clockDelta. Second member: latency of the packet exchange that was used to compute that clockDelta. + int64 _timeSyncClockDelta; + void ComputeNewClockDelta(); + + std::map _pendingTimeSyncRequests; // key: counter. value: server time when packet with that counter was sent. + uint32 _timeSyncNextCounter; + uint32 _timeSyncTimer; }; #endif /// @} diff --git a/src/server/game/Server/WorldSocket.cpp b/src/server/game/Server/WorldSocket.cpp index d3a0892d1..31b4539e0 100644 --- a/src/server/game/Server/WorldSocket.cpp +++ b/src/server/game/Server/WorldSocket.cpp @@ -669,15 +669,13 @@ int WorldSocket::ProcessIncoming(WorldPacket* new_pct) switch (opcode) { case CMSG_PING: + try { - try - { - return HandlePing(*new_pct); - } - catch (ByteBufferPositionException const&) {} - LOG_ERROR("server", "WorldSocket::ReadDataHandler(): client sent malformed CMSG_PING"); - return -1; + return HandlePing(*new_pct); } + catch (ByteBufferPositionException const&) { } + LOG_ERROR("server", "WorldSocket::ReadDataHandler(): client sent malformed CMSG_PING"); + return -1; case CMSG_AUTH_SESSION: if (m_Session) { @@ -689,27 +687,11 @@ int WorldSocket::ProcessIncoming(WorldPacket* new_pct) if (m_Session) m_Session->ResetTimeOutTime(true); return 0; + case CMSG_TIME_SYNC_RESP: + new_pct = new WorldPacket(std::move(*new_pct), std::chrono::steady_clock::now()); + break; default: - { - std::lock_guard guard(m_SessionLock); - - if (m_Session != nullptr) - { - // Our Idle timer will reset on any non PING opcodes. - // Catches people idling on the login screen and any lingering ingame connections. - m_Session->ResetTimeOutTime(false); - - // OK, give the packet to WorldSession - aptr.release(); - m_Session->QueuePacket (new_pct); - return 0; - } - else - { - LOG_ERROR("server", "WorldSocket::ProcessIncoming: Client not authed opcode = %u", uint32(opcode)); - return -1; - } - } + break; } } catch (ByteBufferException const&) @@ -724,7 +706,25 @@ int WorldSocket::ProcessIncoming(WorldPacket* new_pct) return -1; } - ACE_NOTREACHED (return 0); + std::lock_guard guard(m_SessionLock); + + if (m_Session != nullptr) + { + // Our Idle timer will reset on any non PING or TIME_SYNC opcodes. + // Catches people idling on the login screen and any lingering ingame connections. + if (opcode != CMSG_PING && opcode != CMSG_TIME_SYNC_RESP) + { + m_Session->ResetTimeOutTime(false); + } + + // OK, give the packet to WorldSession + aptr.release(); + m_Session->QueuePacket(new_pct); + return 0; + } + + LOG_ERROR("server", "WorldSocket::ProcessIncoming: Client not authed opcode = %u", uint32(opcode)); + return -1; } int WorldSocket::HandleAuthSession(WorldPacket& recvPacket) @@ -1074,7 +1074,6 @@ int WorldSocket::HandlePing(WorldPacket& recvPacket) if (m_Session) { m_Session->SetLatency (latency); - m_Session->ResetClientTimeDelay(); } else {