mirror of
https://github.com/mod-playerbots/azerothcore-wotlk.git
synced 2025-11-29 17:38:24 +08:00
feat(Core/Movement): time synchronisation to better interpret client timestamps (#5300)
This commit is contained in:
@@ -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
|
||||
|
||||
100
src/common/Utilities/CircularBuffer.h
Normal file
100
src/common/Utilities/CircularBuffer.h
Normal file
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, 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 <memory>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
template <typename T>
|
||||
class CircularBuffer {
|
||||
public:
|
||||
explicit CircularBuffer(size_t size) :
|
||||
buf_(std::unique_ptr<T[]>(new T[size])),
|
||||
max_size_(size)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void put(T item)
|
||||
{
|
||||
std::lock_guard<std::mutex> 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<T> content() {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
return std::vector<T>(buf_.get(), buf_.get() + size());
|
||||
}
|
||||
|
||||
T peak_back() {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
return empty() ? T() : buf_[tail_];
|
||||
}
|
||||
|
||||
private:
|
||||
std::mutex mutex_;
|
||||
std::unique_ptr<T[]> buf_;
|
||||
size_t head_ = 0;
|
||||
size_t tail_ = 0;
|
||||
const size_t max_size_;
|
||||
bool full_ = 0;
|
||||
};
|
||||
#endif
|
||||
83
src/common/Utilities/MathUtil.h
Normal file
83
src/common/Utilities/MathUtil.h
Normal file
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, 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 <vector>
|
||||
#include <iterator>
|
||||
#include <algorithm>
|
||||
#include <numeric>
|
||||
|
||||
// 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 <typename Container, typename T = typename std::decay<decltype(*std::begin(std::declval<Container>()))>::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 <typename Container, typename T = typename std::decay<decltype(*std::begin(std::declval<Container>()))>::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 <typename T>
|
||||
inline T median(std::vector<T> 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
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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<int64, uint32>(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<uint32> latencies;
|
||||
std::vector<int64> 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<int64>(mean(clockDeltasAfterFiltering));
|
||||
if (std::abs(meanClockDelta - _timeSyncClockDelta) > 25)
|
||||
_timeSyncClockDelta = meanClockDelta;
|
||||
}
|
||||
else if (_timeSyncClockDelta == 0)
|
||||
{
|
||||
std::pair<int64, uint32> back = _timeSyncClockDeltaQueue.peak_back();
|
||||
_timeSyncClockDelta = back.first;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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++;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
/// @}
|
||||
|
||||
@@ -669,15 +669,13 @@ int WorldSocket::ProcessIncoming(WorldPacket* new_pct)
|
||||
switch (opcode)
|
||||
{
|
||||
case CMSG_PING:
|
||||
{
|
||||
try
|
||||
{
|
||||
return HandlePing(*new_pct);
|
||||
}
|
||||
catch (ByteBufferPositionException const&) {}
|
||||
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
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user