aboutsummaryrefslogtreecommitdiff
path: root/src/server/game/Server
diff options
context:
space:
mode:
authorChaouki Dhib <chaodhib@gmail.com>2019-04-15 23:31:25 +0200
committerGitHub <noreply@github.com>2019-04-15 23:31:25 +0200
commit975f1e364a6a68be2beca261a64ea8aecc16f6f6 (patch)
treeb1f27c7cd32542a7503fcb9f173fee9131d8ad7d /src/server/game/Server
parent9f32aaf4548b4bcb429d78a72f74521465efc8a7 (diff)
Core/Movement: Add time synchronisation (#18189)
Diffstat (limited to 'src/server/game/Server')
-rw-r--r--src/server/game/Server/Protocol/Opcodes.cpp2
-rw-r--r--src/server/game/Server/WorldPacket.h8
-rw-r--r--src/server/game/Server/WorldSession.cpp42
-rw-r--r--src/server/game/Server/WorldSession.h21
-rw-r--r--src/server/game/Server/WorldSocket.cpp53
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());