diff options
| author | Chaouki Dhib <chaodhib@gmail.com> | 2019-04-15 23:31:25 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2019-04-15 23:31:25 +0200 |
| commit | 975f1e364a6a68be2beca261a64ea8aecc16f6f6 (patch) | |
| tree | b1f27c7cd32542a7503fcb9f173fee9131d8ad7d /src/server/game/Server | |
| parent | 9f32aaf4548b4bcb429d78a72f74521465efc8a7 (diff) | |
Core/Movement: Add time synchronisation (#18189)
Diffstat (limited to 'src/server/game/Server')
| -rw-r--r-- | src/server/game/Server/Protocol/Opcodes.cpp | 2 | ||||
| -rw-r--r-- | src/server/game/Server/WorldPacket.h | 8 | ||||
| -rw-r--r-- | src/server/game/Server/WorldSession.cpp | 42 | ||||
| -rw-r--r-- | src/server/game/Server/WorldSession.h | 21 | ||||
| -rw-r--r-- | src/server/game/Server/WorldSocket.cpp | 53 |
5 files changed, 94 insertions, 32 deletions
diff --git a/src/server/game/Server/Protocol/Opcodes.cpp b/src/server/game/Server/Protocol/Opcodes.cpp index 73c54a4dd29..ce5d8d24945 100644 --- a/src/server/game/Server/Protocol/Opcodes.cpp +++ b/src/server/game/Server/Protocol/Opcodes.cpp @@ -1042,7 +1042,7 @@ void OpcodeTable::Initialize() /*0x38E*/ DEFINE_HANDLER(MSG_PARTY_ASSIGNMENT, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandlePartyAssignmentOpcode ); /*0x38F*/ DEFINE_SERVER_OPCODE_HANDLER(SMSG_OFFER_PETITION_ERROR, STATUS_NEVER); /*0x390*/ DEFINE_SERVER_OPCODE_HANDLER(SMSG_TIME_SYNC_REQ, STATUS_NEVER); - /*0x391*/ DEFINE_HANDLER(CMSG_TIME_SYNC_RESP, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleTimeSyncResp ); + /*0x391*/ DEFINE_HANDLER(CMSG_TIME_SYNC_RESP, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleTimeSyncResp ); /*0x392*/ DEFINE_HANDLER(CMSG_SEND_LOCAL_EVENT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); /*0x393*/ DEFINE_HANDLER(CMSG_SEND_GENERAL_TRIGGER, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); /*0x394*/ DEFINE_HANDLER(CMSG_SEND_COMBAT_TRIGGER, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL ); diff --git a/src/server/game/Server/WorldPacket.h b/src/server/game/Server/WorldPacket.h index 40da8c65534..4cdb4c06a3f 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 <chrono> class WorldPacket : public ByteBuffer { @@ -38,6 +39,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) { } @@ -76,8 +81,11 @@ class WorldPacket : public ByteBuffer uint16 GetOpcode() const { return m_opcode; } void SetOpcode(uint16 opcode) { m_opcode = opcode; } + std::chrono::steady_clock::time_point GetReceivedTime() const { return m_receivedTime; } + protected: uint16 m_opcode; + 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 ca5fa691375..dafb08bd2ce 100644 --- a/src/server/game/Server/WorldSession.cpp +++ b/src/server/game/Server/WorldSession.cpp @@ -125,17 +125,22 @@ WorldSession::WorldSession(uint32 id, std::string&& name, std::shared_ptr<WorldS m_sessionDbcLocale(sWorld->GetAvailableDbcLocale(locale)), m_sessionDbLocaleIndex(locale), m_latency(0), - m_clientTimeDelay(0), m_TutorialsChanged(TUTORIALS_FLAG_NONE), recruiterId(recruiter), isRecruiter(isARecruiter), _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(); @@ -400,11 +405,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()) { ///- If necessary, log the player out if (ShouldLogOut(currentTime) && !m_playerLoading) @@ -1548,3 +1565,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 7dc9a1d11c7..27098fc60ce 100644 --- a/src/server/game/Server/WorldSession.h +++ b/src/server/game/Server/WorldSession.h @@ -31,7 +31,9 @@ #include "QueryCallbackProcessor.h" #include "SharedDefines.h" #include <string> +#include <map> #include <unordered_map> +#include <boost/circular_buffer.hpp> class BigNumber; class Creature; @@ -172,7 +174,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; @@ -190,7 +192,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 @@ -438,7 +440,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<time_t> m_timeOutTime; @@ -450,6 +451,10 @@ class TC_GAME_API WorldSession uint32 GetRecruiterId() const { return recruiterId; } bool IsARecruiter() const { return isRecruiter; } + // Time Synchronisation + void ResetTimeSync(); + void SendTimeSync(); + public: // opcodes handlers void Handle_NULL(WorldPacket& recvPacket); // not used @@ -1066,7 +1071,6 @@ class TC_GAME_API WorldSession LocaleConstant m_sessionDbcLocale; LocaleConstant m_sessionDbLocaleIndex; std::atomic<uint32> m_latency; - std::atomic<uint32> m_clientTimeDelay; AccountData m_accountData[NUM_ACCOUNT_DATA_TYPES]; uint32 m_Tutorials[MAX_ACCOUNT_TUTORIAL_VALUES]; uint8 m_TutorialsChanged; @@ -1079,6 +1083,15 @@ class TC_GAME_API WorldSession bool forceExit; ObjectGuid m_currentBankerGUID; + boost::circular_buffer<std::pair<int64, uint32>> _timeSyncClockDeltaQueue; // first member: clockDelta. Second member: latency of the packet exchange that was used to compute that clockDelta. + int64 _timeSyncClockDelta; + void ComputeNewClockDelta(); + + std::map<uint32, uint32> _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 d05ec4fdb80..7fc21b64053 100644 --- a/src/server/game/Server/WorldSocket.cpp +++ b/src/server/game/Server/WorldSocket.cpp @@ -311,6 +311,7 @@ WorldSocket::ReadDataHandlerResult WorldSocket::ReadDataHandler() OpcodeClient opcode = static_cast<OpcodeClient>(header->cmd); WorldPacket packet(opcode, std::move(_packetBuffer)); + WorldPacket* packetToQueue; if (sPacketLog->CanLogPacket()) sPacketLog->LogPacket(packet, CLIENT_TO_SERVER, GetRemoteIpAddress(), GetRemotePort()); @@ -360,34 +361,41 @@ WorldSocket::ReadDataHandlerResult WorldSocket::ReadDataHandler() if (_worldSession) _worldSession->ResetTimeOutTime(true); break; - default: - { - sessionGuard.lock(); - LogOpcodeText(opcode, sessionGuard); + // todo: handle this packet in the same way of CMSG_TIME_SYNC_RESP - if (!_worldSession) - { - TC_LOG_ERROR("network.opcode", "ProcessIncoming: Client not authed opcode = %u", uint32(opcode)); - return ReadDataHandlerResult::Error; - } + case CMSG_TIME_SYNC_RESP: + packetToQueue = new WorldPacket(std::move(packet), std::chrono::steady_clock::now()); + break; - OpcodeHandler const* handler = opcodeTable[opcode]; - if (!handler) - { - TC_LOG_ERROR("network.opcode", "No defined handler for opcode %s sent by %s", GetOpcodeNameForLogging(static_cast<OpcodeClient>(packet.GetOpcode())).c_str(), _worldSession->GetPlayerInfo().c_str()); - break; - } + default: + packetToQueue = new WorldPacket(std::move(packet)); + break; + } - // Our Idle timer will reset on any non PING opcodes on login screen, allowing us to catch people idling. - _worldSession->ResetTimeOutTime(false); + sessionGuard.lock(); - // Copy the packet to the heap before enqueuing - _worldSession->QueuePacket(new WorldPacket(std::move(packet))); - break; - } + LogOpcodeText(opcode, sessionGuard); + + if (!_worldSession) + { + TC_LOG_ERROR("network.opcode", "ProcessIncoming: Client not authed opcode = %u", uint32(opcode)); + 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(static_cast<OpcodeClient>(packet.GetOpcode())).c_str(), _worldSession->GetPlayerInfo().c_str()); + return ReadDataHandlerResult::Error; } + // Our Idle timer will reset on any non PING opcodes on login screen, allowing us to catch people idling. + _worldSession->ResetTimeOutTime(false); + + // Copy the packet to the heap before enqueuing + _worldSession->QueuePacket(packetToQueue); + return ReadDataHandlerResult::Ok; } @@ -674,10 +682,7 @@ bool WorldSocket::HandlePing(WorldPacket& recvPacket) std::lock_guard<std::mutex> 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()); |
