From ee69943717028f73f183ed9d2edef486e37f66a6 Mon Sep 17 00:00:00 2001 From: Ovahlord Date: Fri, 26 Apr 2019 16:48:09 +0200 Subject: [PATCH] Core/Movement: ported time synchronization (commits: 975f1e364a6a68be2beca261a64ea8aecc16f6f6 and 50d32fe49386598c7cd876e51cf02478669cc252) --- src/common/Utilities/Timer.h | 20 ++++- src/server/game/Entities/Player/Player.cpp | 39 +------- src/server/game/Entities/Player/Player.h | 8 -- src/server/game/Handlers/MiscHandler.cpp | 21 ----- src/server/game/Handlers/MovementHandler.cpp | 94 ++++++++++++++++++-- src/server/game/Server/Protocol/Opcodes.cpp | 2 +- src/server/game/Server/WorldPacket.h | 8 ++ src/server/game/Server/WorldSession.cpp | 42 ++++++++- src/server/game/Server/WorldSession.h | 20 ++++- src/server/game/Server/WorldSocket.cpp | 85 +++++++++--------- 10 files changed, 212 insertions(+), 127 deletions(-) diff --git a/src/common/Utilities/Timer.h b/src/common/Utilities/Timer.h index 0c99e7afe0d..d0ae9271b63 100644 --- a/src/common/Utilities/Timer.h +++ b/src/common/Utilities/Timer.h @@ -22,13 +22,20 @@ #include "Define.h" #include -inline uint32 getMSTime() +inline std::chrono::steady_clock::time_point GetApplicationStartTime() { using namespace std::chrono; static const steady_clock::time_point ApplicationStartTime = steady_clock::now(); - return uint32(duration_cast(steady_clock::now() - ApplicationStartTime).count()); + return ApplicationStartTime; +} + +inline uint32 getMSTime() +{ + using namespace std::chrono; + + return uint32(duration_cast(steady_clock::now() - GetApplicationStartTime()).count()); } inline uint32 getMSTimeDiff(uint32 oldMSTime, uint32 newMSTime) @@ -40,6 +47,15 @@ inline uint32 getMSTimeDiff(uint32 oldMSTime, uint32 newMSTime) return newMSTime - oldMSTime; } +inline uint32 getMSTimeDiff(uint32 oldMSTime, std::chrono::steady_clock::time_point newTime) +{ + using namespace std::chrono; + + uint32 newMSTime = uint32(duration_cast(newTime - GetApplicationStartTime()).count()); + return getMSTimeDiff(oldMSTime, newMSTime); +} + + inline uint32 GetMSTimeDiffToNow(uint32 oldMSTime) { return getMSTimeDiff(oldMSTime, getMSTime()); diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index b2c7232862e..282a8d16c35 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -381,10 +381,6 @@ Player::Player(WorldSession* session): Unit(true) m_ChampioningFaction = 0; - m_timeSyncTimer = 0; - m_timeSyncClient = 0; - m_timeSyncServer = 0; - for (uint8 i = 0; i < MAX_POWERS_PER_CLASS; ++i) m_powerFraction[i] = 0; @@ -1260,14 +1256,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; @@ -23850,8 +23838,8 @@ void Player::SendInitialPacketsAfterAddToMap() GetZoneAndAreaId(newzone, newarea); UpdateZone(newzone, newarea); // also call SendInitWorldStates(); - ResetTimeSync(); - SendTimeSync(); + GetSession()->ResetTimeSync(); + GetSession()->SendTimeSync(); GetSession()->SendLoadCUFProfiles(); @@ -27417,29 +27405,6 @@ void Player::LoadActions(PreparedQueryResult result) SendActionButtons(1); } -void Player::ResetTimeSync() -{ - m_timeSyncTimer = 0; - m_timeSyncClient = 0; - m_timeSyncServer = GameTime::GetGameTimeMS(); -} - -void Player::SendTimeSync() -{ - m_timeSyncQueue.push(m_movementCounter++); - - WorldPacket data(SMSG_TIME_SYNC_REQ, 4); - data << uint32(m_timeSyncQueue.back()); - SendDirectMessage(&data); - - // Schedule next sync in 10 sec - m_timeSyncTimer = 10000; - m_timeSyncServer = GameTime::GetGameTimeMS(); - - if (m_timeSyncQueue.size() > 3) - TC_LOG_ERROR("network", "Not received CMSG_TIME_SYNC_RESP for over 30 seconds from player %u (%s), possible cheater", GetGUID().GetCounter(), GetName().c_str()); -} - 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 4da1497ec45..b7da8b54c38 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -2597,9 +2597,6 @@ class TC_GAME_API Player : public Unit, public GridObject ItemDurationList m_itemDuration; GuidUnorderedSet m_itemSoulboundTradeable; - void ResetTimeSync(); - void SendTimeSync(); - ResurrectionData* _resurrectionData; WorldSession* m_session; @@ -2743,11 +2740,6 @@ class TC_GAME_API Player : public Unit, public GridObject uint32 m_ChampioningFaction; - std::queue m_timeSyncQueue; - 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 c49115ead52..a16ee3186d8 100644 --- a/src/server/game/Handlers/MiscHandler.cpp +++ b/src/server/game/Handlers/MiscHandler.cpp @@ -1322,27 +1322,6 @@ void WorldSession::HandleSetTitleOpcode(WorldPacket& recvData) GetPlayer()->SetUInt32Value(PLAYER_CHOSEN_TITLE, title); } -void WorldSession::HandleTimeSyncResp(WorldPacket& recvData) -{ - TC_LOG_DEBUG("network", "CMSG_TIME_SYNC_RESP"); - - uint32 counter, clientTicks; - recvData >> counter >> clientTicks; - - if (counter != _player->m_timeSyncQueue.front()) - TC_LOG_ERROR("network", "Wrong time sync counter from player %s (cheater?)", _player->GetName().c_str()); - - TC_LOG_DEBUG("network", "Time sync received: counter %u, client ticks %u, time since last sync %u", counter, clientTicks, clientTicks - _player->m_timeSyncClient); - - uint32 ourTicks = clientTicks + (GameTime::GetGameTimeMS() - _player->m_timeSyncServer); - - // diff should be small - TC_LOG_DEBUG("network", "Our ticks: %u, diff %u, latency %u", ourTicks, ourTicks - clientTicks, GetLatency()); - - _player->m_timeSyncClient = clientTicks; - _player->m_timeSyncQueue.pop(); -} - void WorldSession::HandleResetInstancesOpcode(WorldPacket& /*recvData*/) { TC_LOG_DEBUG("network", "WORLD: CMSG_RESET_INSTANCES"); diff --git a/src/server/game/Handlers/MovementHandler.cpp b/src/server/game/Handlers/MovementHandler.cpp index 4b58e2d5ade..fe0944d31c8 100644 --- a/src/server/game/Handlers/MovementHandler.cpp +++ b/src/server/game/Handlers/MovementHandler.cpp @@ -34,8 +34,9 @@ #include "Vehicle.h" #include "GameTime.h" #include "SpellAuraEffects.h" - -#define MOVEMENT_PACKET_TIME_DELAY 0 +#include +#include +#include void WorldSession::HandleMoveWorldportAckOpcode(WorldPacket & /*recvData*/) { @@ -387,13 +388,17 @@ void WorldSession::HandleMovementOpcodes(WorldPacket& recvPacket) plrMover->SetInWater(!plrMover->IsInWater() || plrMover->GetMap()->IsUnderWater(plrMover->GetPhaseShift(), movementInfo.pos.GetPositionX(), movementInfo.pos.GetPositionY(), movementInfo.pos.GetPositionZ())); } - uint32 mstime = GameTime::GetGameTimeMS(); - /*----------------------*/ - if (m_clientTimeDelay == 0) - m_clientTimeDelay = mstime - movementInfo.time; - /* process position-change */ - movementInfo.time = movementInfo.time + m_clientTimeDelay + MOVEMENT_PACKET_TIME_DELAY; + int64 movementTime = (int64)movementInfo.time + _timeSyncClockDelta; + if (_timeSyncClockDelta == 0 || movementTime < 0 || movementTime > 0xFFFFFFFF) + { + TC_LOG_WARN("misc", "The computed movement time using clockDelta is erronous. Using fallback instead"); + movementInfo.time = GameTime::GetGameTimeMS(); + } + else + { + movementInfo.time = (uint32)movementTime; + } movementInfo.guid = mover->GetGUID(); mover->m_movementInfo = movementInfo; @@ -701,3 +706,76 @@ void WorldSession::HandleMoveTimeSkippedOpcode(WorldPacket& recvData) data << time; GetPlayer()->SendMessageToSet(&data, false); } + +void WorldSession::HandleTimeSyncResp(WorldPacket& recvData) +{ + TC_LOG_DEBUG("network", "CMSG_TIME_SYNC_RESP"); + + 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 + lagDelay) - (int64)clientTimestamp; + _timeSyncClockDeltaQueue.push_back(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. + + using namespace boost::accumulators; + + accumulator_set > latencyAccumulator; + + for (auto pair : _timeSyncClockDeltaQueue) + latencyAccumulator(pair.second); + + uint32 latencyMedian = static_cast(std::round(median(latencyAccumulator))); + uint32 latencyStandardDeviation = static_cast(std::round(sqrt(variance(latencyAccumulator)))); + + accumulator_set > clockDeltasAfterFiltering; + uint32 sampleSizeAfterFiltering = 0; + for (auto pair : _timeSyncClockDeltaQueue) + { + if (pair.second < latencyStandardDeviation + latencyMedian) { + clockDeltasAfterFiltering(pair.first); + sampleSizeAfterFiltering++; + } + } + + if (sampleSizeAfterFiltering != 0) + { + int64 meanClockDelta = static_cast(std::round(mean(clockDeltasAfterFiltering))); + if (std::abs(meanClockDelta - _timeSyncClockDelta) > 25) + _timeSyncClockDelta = meanClockDelta; + } + else if (_timeSyncClockDelta == 0) + { + std::pair back = _timeSyncClockDeltaQueue.back(); + _timeSyncClockDelta = back.first; + } +} diff --git a/src/server/game/Server/Protocol/Opcodes.cpp b/src/server/game/Server/Protocol/Opcodes.cpp index 72bb0ca0131..09cb32f9045 100644 --- a/src/server/game/Server/Protocol/Opcodes.cpp +++ b/src/server/game/Server/Protocol/Opcodes.cpp @@ -539,7 +539,7 @@ void OpcodeTable::Initialize() DEFINE_OPCODE_HANDLER(CMSG_TELEPORT_TO_UNIT, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL ); DEFINE_OPCODE_HANDLER(CMSG_TEXT_EMOTE, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleTextEmoteOpcode ); DEFINE_OPCODE_HANDLER(CMSG_TIME_ADJUSTMENT_RESPONSE, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL ); - DEFINE_OPCODE_HANDLER(CMSG_TIME_SYNC_RESP, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleTimeSyncResp ); + DEFINE_OPCODE_HANDLER(CMSG_TIME_SYNC_RESP, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleTimeSyncResp ); DEFINE_OPCODE_HANDLER(CMSG_TIME_SYNC_RESP_FAILED, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL ); DEFINE_OPCODE_HANDLER(CMSG_TOGGLE_PVP, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleTogglePvP ); DEFINE_OPCODE_HANDLER(CMSG_TOTEM_DESTROYED, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleTotemDestroyed ); diff --git a/src/server/game/Server/WorldPacket.h b/src/server/game/Server/WorldPacket.h index 19c8e4dd2a5..fc897383380 100644 --- a/src/server/game/Server/WorldPacket.h +++ b/src/server/game/Server/WorldPacket.h @@ -22,6 +22,7 @@ #include "Common.h" #include "Opcodes.h" #include "ByteBuffer.h" +#include struct z_stream_s; @@ -39,6 +40,10 @@ class WorldPacket : public ByteBuffer { } + WorldPacket(WorldPacket&& packet, std::chrono::steady_clock::time_point receivedTime) : ByteBuffer(std::move(packet)), m_opcode(packet.m_opcode), m_receivedTime(receivedTime) + { + } + WorldPacket(WorldPacket const& right) : ByteBuffer(right), m_opcode(right.m_opcode) { } @@ -69,10 +74,13 @@ class WorldPacket : public ByteBuffer void Compress(z_stream_s* compressionStream); void Compress(z_stream_s* compressionStream, WorldPacket const* source); + std::chrono::steady_clock::time_point GetReceivedTime() const { return m_receivedTime; } + protected: Opcodes m_opcode; void Compress(void* dst, uint32 *dst_size, const void* src, int src_size); z_stream_s* _compressionStream; + std::chrono::steady_clock::time_point m_receivedTime; // only set for a specific set of opcodes, for performance reasons. }; #endif diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp index d0a021d92b5..80b34a692dc 100644 --- a/src/server/game/Server/WorldSession.cpp +++ b/src/server/game/Server/WorldSession.cpp @@ -124,7 +124,6 @@ WorldSession::WorldSession(uint32 id, std::string&& name, uint32 battlenetAccoun m_sessionDbcLocale(sWorld->GetAvailableDbcLocale(locale)), m_sessionDbLocaleIndex(locale), m_latency(0), - m_clientTimeDelay(0), m_TutorialsChanged(TUTORIALS_FLAG_NONE), _filterAddonMessages(false), recruiterId(recruiter), @@ -132,10 +131,16 @@ WorldSession::WorldSession(uint32 id, std::string&& name, uint32 battlenetAccoun _RBACData(nullptr), expireTime(60000), // 1 min after socket loss, session is deleted forceExit(false), - m_currentBankerGUID() + m_currentBankerGUID(), + _timeSyncClockDeltaQueue(6), + _timeSyncClockDelta(0), + _pendingTimeSyncRequests() { memset(m_Tutorials, 0, sizeof(m_Tutorials)); + _timeSyncNextCounter = 0; + _timeSyncTimer = 0; + if (sock) { m_Address = sock->GetRemoteIpAddress().to_string(); @@ -431,11 +436,23 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater) if (m_Socket && m_Socket->IsOpen() && _warden) _warden->Update(); + if (!updater.ProcessUnsafe()) // <=> updater is of type MapSessionFilter + { + // Send time sync packet every 10s. + if (_timeSyncTimer > 0) + { + if (diff >= _timeSyncTimer) + SendTimeSync(); + else + _timeSyncTimer -= diff; + } + } + ProcessQueryCallbacks(); //check if we are safe to proceed with logout //logout procedure should happen only in World::UpdateSessions() method!!! - if (updater.ProcessLogout()) + if (updater.ProcessUnsafe()) { time_t currTime = time(nullptr); ///- If necessary, log the player out @@ -1481,3 +1498,22 @@ uint32 WorldSession::DosProtection::GetMaxPacketCounterAllowed(uint16 opcode) co WorldSession::DosProtection::DosProtection(WorldSession* s) : Session(s), _policy((Policy)sWorld->getIntConfig(CONFIG_PACKET_SPOOF_POLICY)) { } + +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 1f775bc56dd..d3a1fe13a91 100644 --- a/src/server/game/Server/WorldSession.h +++ b/src/server/game/Server/WorldSession.h @@ -29,7 +29,9 @@ #include "ObjectGuid.h" #include "QueryCallbackProcessor.h" #include "SharedDefines.h" +#include #include +#include class BigNumber; class Creature; @@ -180,7 +182,7 @@ public: virtual ~PacketFilter() { } virtual bool Process(WorldPacket* /*packet*/) { return true; } - virtual bool ProcessLogout() const { return true; } + virtual bool ProcessUnsafe() const { return true; } protected: WorldSession* const m_pSession; @@ -198,7 +200,7 @@ public: virtual bool Process(WorldPacket* packet) override; //in Map::Update() we do not process player logout! - virtual bool ProcessLogout() const override { return false; } + virtual bool ProcessUnsafe() const override { return false; } }; //class used to filer only thread-unsafe packets from queue @@ -444,7 +446,6 @@ class TC_GAME_API WorldSession uint32 GetLatency() const { return m_latency; } void SetLatency(uint32 latency) { m_latency = latency; } - void ResetClientTimeDelay() { m_clientTimeDelay = 0; } std::atomic m_timeOutTime; @@ -466,6 +467,10 @@ class TC_GAME_API WorldSession z_stream_s* GetCompressionStream() { return _compressionStream; } + // Time Synchronisation + void ResetTimeSync(); + void SendTimeSync(); + public: // opcodes handlers void Handle_NULL(WorldPacket& recvPacket); // not used @@ -1163,7 +1168,6 @@ class TC_GAME_API WorldSession LocaleConstant m_sessionDbcLocale; LocaleConstant m_sessionDbLocaleIndex; std::atomic m_latency; - std::atomic m_clientTimeDelay; AccountData m_accountData[NUM_ACCOUNT_DATA_TYPES]; uint32 m_Tutorials[MAX_ACCOUNT_TUTORIAL_VALUES]; uint8 m_TutorialsChanged; @@ -1179,6 +1183,14 @@ class TC_GAME_API WorldSession bool forceExit; ObjectGuid m_currentBankerGUID; + boost::circular_buffer> _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; + WorldSession(WorldSession const& right) = delete; WorldSession& operator=(WorldSession const& right) = delete; }; diff --git a/src/server/game/Server/WorldSocket.cpp b/src/server/game/Server/WorldSocket.cpp index b8ee9e42581..cf88690d44e 100644 --- a/src/server/game/Server/WorldSocket.cpp +++ b/src/server/game/Server/WorldSocket.cpp @@ -339,6 +339,7 @@ WorldSocket::ReadDataHandlerResult WorldSocket::ReadDataHandler() Opcodes opcode = Opcodes(header->cmd); WorldPacket packet(opcode, std::move(_packetBuffer)); + WorldPacket* packetToQueue; if (sPacketLog->CanLogPacket()) sPacketLog->LogPacket(packet, CLIENT_TO_SERVER, GetRemoteIpAddress(), GetRemotePort()); @@ -382,58 +383,59 @@ WorldSocket::ReadDataHandlerResult WorldSocket::ReadDataHandler() TC_LOG_ERROR("network", "WorldSocket::ReadDataHandler(): client %s sent malformed CMSG_AUTH_SESSION", GetRemoteIpAddress().to_string().c_str()); return ReadDataHandlerResult::Error; } - case CMSG_KEEP_ALIVE: - LogOpcodeText(opcode, sessionGuard); - sScriptMgr->OnPacketReceive(_worldSession, packet); - break; case CMSG_LOG_DISCONNECT: packet.rfinish(); // contains uint32 disconnectReason; TC_LOG_DEBUG("network", "%s", GetOpcodeNameForLogging(opcode).c_str()); sScriptMgr->OnPacketReceive(_worldSession, packet); - break; + return ReadDataHandlerResult::Ok; case CMSG_ENABLE_NAGLE: - { TC_LOG_DEBUG("network", "%s", GetOpcodeNameForLogging(opcode).c_str()); sScriptMgr->OnPacketReceive(_worldSession, packet); if (_worldSession) _worldSession->HandleEnableNagleAlgorithm(); - break; - } - default: - { - sessionGuard.lock(); - + return ReadDataHandlerResult::Ok; + case CMSG_KEEP_ALIVE: // todo: handle this packet in the same way of CMSG_TIME_SYNC_RESP LogOpcodeText(opcode, sessionGuard); - - if (!_worldSession) - { - TC_LOG_ERROR("network.opcode", "ProcessIncoming: Client not authed opcode = %u", uint32(opcode)); - return ReadDataHandlerResult::Error; - } - - // prevent invalid memory access/crash with custom opcodes - if (opcode >= NUM_OPCODE_HANDLERS) - { - CloseSocket(); - return ReadDataHandlerResult::Error; - } - - OpcodeHandler const* handler = opcodeTable[opcode]; - if (!handler) - { - TC_LOG_ERROR("network.opcode", "No defined handler for opcode %s sent by %s", GetOpcodeNameForLogging(packet.GetOpcode()).c_str(), _worldSession->GetPlayerInfo().c_str()); - break; - } - - // Our Idle timer will reset on any non PING opcodes. - // Catches people idling on the login screen and any lingering ingame connections. - _worldSession->ResetTimeOutTime(); - - // Copy the packet to the heap before enqueuing - _worldSession->QueuePacket(new WorldPacket(std::move(packet))); + sScriptMgr->OnPacketReceive(_worldSession, packet); + return ReadDataHandlerResult::Ok; + case CMSG_TIME_SYNC_RESP: + packetToQueue = new WorldPacket(std::move(packet), std::chrono::steady_clock::now()); + break; + default: + packetToQueue = new WorldPacket(std::move(packet)); break; - } } + + sessionGuard.lock(); + + LogOpcodeText(opcode, sessionGuard); + + if (!_worldSession) + { + TC_LOG_ERROR("network.opcode", "ProcessIncoming: Client not authed opcode = %u", uint32(opcode)); + return ReadDataHandlerResult::Error; + } + + // prevent invalid memory access/crash with custom opcodes + if (opcode >= NUM_OPCODE_HANDLERS) + { + CloseSocket(); + return ReadDataHandlerResult::Error; + } + + OpcodeHandler const* handler = opcodeTable[opcode]; + if (!handler) + { + TC_LOG_ERROR("network.opcode", "No defined handler for opcode %s sent by %s", GetOpcodeNameForLogging(packet.GetOpcode()).c_str(), _worldSession->GetPlayerInfo().c_str()); + return ReadDataHandlerResult::Error; + } + + // Our Idle timer will reset on any non PING opcodes. + // Catches people idling on the login screen and any lingering ingame connections. + _worldSession->ResetTimeOutTime(); + + // Copy the packet to the heap before enqueuing + _worldSession->QueuePacket(packetToQueue); } else { @@ -764,10 +766,7 @@ bool WorldSocket::HandlePing(WorldPacket& recvPacket) std::lock_guard sessionGuard(_worldSessionLock); if (_worldSession) - { _worldSession->SetLatency(latency); - _worldSession->ResetClientTimeDelay(); - } else { TC_LOG_ERROR("network", "WorldSocket::HandlePing: peer sent CMSG_PING, but is not authenticated or got recently kicked, address = %s", GetRemoteIpAddress().to_string().c_str());