feat(Core/Movement): time synchronisation to better interpret client timestamps (#5300)

This commit is contained in:
Chaouki Dhib
2021-04-23 15:53:09 +02:00
committed by GitHub
parent 970d371442
commit 2d21bfc915
11 changed files with 367 additions and 96 deletions

View File

@@ -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 },

View File

@@ -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++;
}

View File

@@ -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 <utility>
#include <map>
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<time_t> 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<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;
};
#endif
/// @}

View File

@@ -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<std::mutex> 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<std::mutex> 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
{