aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChaouki Dhib <chaodhib@gmail.com>2019-04-15 23:31:25 +0200
committerShauren <shauren.trinity@gmail.com>2021-11-30 22:11:36 +0100
commitc19a4db1c12b8864d6c486ee8e2f0e058fb4155a (patch)
tree1976d6734054eb05eb03322be9d76da9a0c1840c
parent2f6f1c708960bfb23a0d69ce967b67b38e72b3fa (diff)
Core/Movement: Add time synchronisation (#18189)
(cherry picked from commit 975f1e364a6a68be2beca261a64ea8aecc16f6f6)
-rw-r--r--src/common/Utilities/Timer.h19
-rw-r--r--src/server/game/Entities/Player/Player.cpp40
-rw-r--r--src/server/game/Entities/Player/Player.h8
-rw-r--r--src/server/game/Handlers/MiscHandler.cpp23
-rw-r--r--src/server/game/Handlers/MovementHandler.cpp88
-rw-r--r--src/server/game/Server/Packets/MiscPackets.h2
-rw-r--r--src/server/game/Server/Protocol/Opcodes.cpp2
-rw-r--r--src/server/game/Server/WorldPacket.h7
-rw-r--r--src/server/game/Server/WorldSession.cpp50
-rw-r--r--src/server/game/Server/WorldSession.h27
-rw-r--r--src/server/game/Server/WorldSocket.cpp6
11 files changed, 177 insertions, 95 deletions
diff --git a/src/common/Utilities/Timer.h b/src/common/Utilities/Timer.h
index 0996c7e9dae..4abd6cc8ca1 100644
--- a/src/common/Utilities/Timer.h
+++ b/src/common/Utilities/Timer.h
@@ -21,13 +21,20 @@
#include "Define.h"
#include <chrono>
-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<milliseconds>(steady_clock::now() - ApplicationStartTime).count());
+ return ApplicationStartTime;
+}
+
+inline uint32 getMSTime()
+{
+ using namespace std::chrono;
+
+ return uint32(duration_cast<milliseconds>(steady_clock::now() - GetApplicationStartTime()).count());
}
inline uint32 getMSTimeDiff(uint32 oldMSTime, uint32 newMSTime)
@@ -39,6 +46,14 @@ 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<milliseconds>(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 e633754a955..f24f12540e4 100644
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -322,10 +322,6 @@ Player::Player(WorldSession* session) : Unit(true), m_sceneMgr(this)
m_ChampioningFaction = 0;
- m_timeSyncTimer = 0;
- m_timeSyncClient = 0;
- m_timeSyncServer = GameTime::GetGameTimeMS();
-
for (uint8 i = 0; i < MAX_POWERS_PER_CLASS; ++i)
m_powerFraction[i] = 0;
@@ -1132,14 +1128,6 @@ void Player::Update(uint32 p_time)
m_zoneUpdateTimer -= p_time;
}
- if (m_timeSyncTimer > 0 && !IsBeingTeleportedFar())
- {
- if (p_time >= m_timeSyncTimer)
- SendTimeSync();
- else
- m_timeSyncTimer -= p_time;
- }
-
if (IsAlive())
{
m_regenTimer += p_time;
@@ -24295,10 +24283,10 @@ void Player::SendInitialPacketsBeforeAddToMap()
if (!(m_teleport_options & TELE_TO_SEAMLESS))
{
m_movementCounter = 0;
- ResetTimeSync();
+ GetSession()->ResetTimeSync();
}
- SendTimeSync();
+ GetSession()->SendTimeSync();
/// Pass 'this' as argument because we're not stored in ObjectAccessor yet
GetSocial()->SendSocialList(this, SOCIAL_FLAG_ALL);
@@ -27802,30 +27790,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++);
-
- WorldPackets::Misc::TimeSyncRequest packet;
- packet.SequenceIndex = m_timeSyncQueue.back();
- SendDirectMessage(packet.Write());
-
- // Schedule next sync in 10 sec
- m_timeSyncTimer = 10000;
- m_timeSyncServer = GameTime::GetGameTimeMS();
-
- if (m_timeSyncQueue.size() > 3)
- TC_LOG_ERROR("network", "Player::SendTimeSync: Did not receive CMSG_TIME_SYNC_RESP for over 30 seconds from '%s' (%s), possible cheater",
- GetName().c_str(), GetGUID().ToString().c_str());
-}
-
void Player::SetReputation(uint32 factionentry, int32 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 9774f6a9b69..53507fd4182 100644
--- a/src/server/game/Entities/Player/Player.h
+++ b/src/server/game/Entities/Player/Player.h
@@ -2933,9 +2933,6 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
ItemDurationList m_itemDuration;
GuidUnorderedSet m_itemSoulboundTradeable;
- void ResetTimeSync();
- void SendTimeSync();
-
std::unique_ptr<ResurrectionData> _resurrectionData;
WorldSession* m_session;
@@ -3063,11 +3060,6 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
uint32 m_ChampioningFaction;
- std::queue<uint32> 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 9c49cfa17bf..5ca99c36248 100644
--- a/src/server/game/Handlers/MiscHandler.cpp
+++ b/src/server/game/Handlers/MiscHandler.cpp
@@ -857,29 +857,6 @@ void WorldSession::HandleSetTitleOpcode(WorldPackets::Character::SetTitle& packe
GetPlayer()->SetChosenTitle(packet.TitleID);
}
-void WorldSession::HandleTimeSyncResponse(WorldPackets::Misc::TimeSyncResponse& packet)
-{
- // Prevent crashing server if queue is empty
- if (_player->m_timeSyncQueue.empty())
- {
- TC_LOG_ERROR("network", "Received CMSG_TIME_SYNC_RESPONSE from player %s without requesting it (hacker?)", _player->GetName().c_str());
- return;
- }
-
- if (packet.SequenceIndex != _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", packet.SequenceIndex, packet.ClientTime, packet.ClientTime - _player->m_timeSyncClient);
-
- uint32 ourTicks = packet.ClientTime + (GameTime::GetGameTimeMS() - _player->m_timeSyncServer);
-
- // diff should be small
- TC_LOG_DEBUG("network", "Our ticks: %u, diff %u, latency %u", ourTicks, ourTicks - packet.ClientTime, GetLatency());
-
- _player->m_timeSyncClient = packet.ClientTime;
- _player->m_timeSyncQueue.pop();
-}
-
void WorldSession::HandleResetInstancesOpcode(WorldPackets::Instance::ResetInstances& /*packet*/)
{
if (Group* group = _player->GetGroup())
diff --git a/src/server/game/Handlers/MovementHandler.cpp b/src/server/game/Handlers/MovementHandler.cpp
index 7273b896c97..4cc1b5b1977 100644
--- a/src/server/game/Handlers/MovementHandler.cpp
+++ b/src/server/game/Handlers/MovementHandler.cpp
@@ -27,6 +27,7 @@
#include "InstanceSaveMgr.h"
#include "Log.h"
#include "MapManager.h"
+#include "MiscPackets.h"
#include "MovementPackets.h"
#include "ObjectMgr.h"
#include "Opcodes.h"
@@ -37,8 +38,9 @@
#include "Transport.h"
#include "Vehicle.h"
#include "SpellMgr.h"
-
-#define MOVEMENT_PACKET_TIME_DELAY 0
+#include <boost/accumulators/statistics/variance.hpp>
+#include <boost/accumulators/accumulators.hpp>
+#include <boost/accumulators/statistics.hpp>
void WorldSession::HandleMoveWorldportAckOpcode(WorldPackets::Movement::WorldPortResponse& /*packet*/)
{
@@ -382,15 +384,9 @@ void WorldSession::HandleMovementOpcode(OpcodeClient opcode, MovementInfo& movem
if (opcode == CMSG_MOVE_FALL_LAND || opcode == CMSG_MOVE_START_SWIM || opcode == CMSG_MOVE_SET_FLY)
mover->RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags::LandingOrFlight); // Parachutes
- 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;
-
movementInfo.guid = mover->GetGUID();
+ movementInfo.time = AdjustClientMovementTime(movementInfo.time);
mover->m_movementInfo = movementInfo;
// Some vehicles allow the passenger to turn by himself
@@ -579,7 +575,7 @@ void WorldSession::HandleMoveApplyMovementForceAck(WorldPackets::Movement::MoveA
return;
}
- moveApplyMovementForceAck.Ack.Status.time += m_clientTimeDelay + MOVEMENT_PACKET_TIME_DELAY;
+ moveApplyMovementForceAck.Ack.Status.time = AdjustClientMovementTime(moveApplyMovementForceAck.Ack.Status.time);
WorldPackets::Movement::MoveUpdateApplyMovementForce updateApplyMovementForce;
updateApplyMovementForce.Status = &moveApplyMovementForceAck.Ack.Status;
@@ -601,7 +597,7 @@ void WorldSession::HandleMoveRemoveMovementForceAck(WorldPackets::Movement::Move
return;
}
- moveRemoveMovementForceAck.Ack.Status.time += m_clientTimeDelay + MOVEMENT_PACKET_TIME_DELAY;
+ moveRemoveMovementForceAck.Ack.Status.time = AdjustClientMovementTime(moveRemoveMovementForceAck.Ack.Status.time);
WorldPackets::Movement::MoveUpdateRemoveMovementForce updateRemoveMovementForce;
updateRemoveMovementForce.Status = &moveRemoveMovementForceAck.Ack.Status;
@@ -643,7 +639,7 @@ void WorldSession::HandleMoveSetModMovementForceMagnitudeAck(WorldPackets::Movem
}
}
- setModMovementForceMagnitudeAck.Ack.Status.time += m_clientTimeDelay + MOVEMENT_PACKET_TIME_DELAY;
+ setModMovementForceMagnitudeAck.Ack.Status.time = AdjustClientMovementTime(setModMovementForceMagnitudeAck.Ack.Status.time);
WorldPackets::Movement::MoveUpdateSpeed updateModMovementForceMagnitude(SMSG_MOVE_UPDATE_MOD_MOVEMENT_FORCE_MAGNITUDE);
updateModMovementForceMagnitude.Status = &setModMovementForceMagnitudeAck.Ack.Status;
@@ -716,3 +712,71 @@ void WorldSession::HandleMoveTimeSkippedOpcode(WorldPackets::Movement::MoveTimeS
moveSkipTime.TimeSkipped = moveTimeSkipped.TimeSkipped;
mover->SendMessageToSet(moveSkipTime.Write(), _player);
}
+
+void WorldSession::HandleTimeSyncResponse(WorldPackets::Misc::TimeSyncResponse& timeSyncResponse)
+{
+ if (_pendingTimeSyncRequests.count(timeSyncResponse.SequenceIndex) == 0)
+ return;
+
+ uint32 serverTimeAtSent = _pendingTimeSyncRequests.at(timeSyncResponse.SequenceIndex);
+ _pendingTimeSyncRequests.erase(timeSyncResponse.SequenceIndex);
+
+ // 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, timeSyncResponse.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)timeSyncResponse.ClientTime;
+ _timeSyncClockDeltaQueue.push_back(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.
+
+ using namespace boost::accumulators;
+
+ accumulator_set<uint32, features<tag::mean, tag::median, tag::variance(lazy)> > latencyAccumulator;
+
+ for (auto pair : _timeSyncClockDeltaQueue)
+ latencyAccumulator(pair.second);
+
+ uint32 latencyMedian = static_cast<uint32>(std::round(median(latencyAccumulator)));
+ uint32 latencyStandardDeviation = static_cast<uint32>(std::round(sqrt(variance(latencyAccumulator))));
+
+ accumulator_set<int64, features<tag::mean> > clockDeltasAfterFiltering;
+ uint32 sampleSizeAfterFiltering = 0;
+ for (auto pair : _timeSyncClockDeltaQueue)
+ {
+ if (pair.second < latencyStandardDeviation + latencyMedian) {
+ clockDeltasAfterFiltering(pair.first);
+ sampleSizeAfterFiltering++;
+ }
+ }
+
+ if (sampleSizeAfterFiltering != 0)
+ {
+ int64 meanClockDelta = static_cast<int64>(std::round(mean(clockDeltasAfterFiltering)));
+ if (std::abs(meanClockDelta - _timeSyncClockDelta) > 25)
+ _timeSyncClockDelta = meanClockDelta;
+ }
+ else if (_timeSyncClockDelta == 0)
+ {
+ std::pair<int64, uint32> back = _timeSyncClockDeltaQueue.back();
+ _timeSyncClockDelta = back.first;
+ }
+}
diff --git a/src/server/game/Server/Packets/MiscPackets.h b/src/server/game/Server/Packets/MiscPackets.h
index 75119e91e89..2e107094c2d 100644
--- a/src/server/game/Server/Packets/MiscPackets.h
+++ b/src/server/game/Server/Packets/MiscPackets.h
@@ -183,6 +183,8 @@ namespace WorldPackets
void Read() override;
+ std::chrono::steady_clock::time_point GetReceivedTime() const { return _worldPacket.GetReceivedTime(); }
+
uint32 ClientTime = 0; // Client ticks in ms
uint32 SequenceIndex = 0; // Same index as in request
};
diff --git a/src/server/game/Server/Protocol/Opcodes.cpp b/src/server/game/Server/Protocol/Opcodes.cpp
index 43fb5bbbf01..f0a7262d2e2 100644
--- a/src/server/game/Server/Protocol/Opcodes.cpp
+++ b/src/server/game/Server/Protocol/Opcodes.cpp
@@ -863,7 +863,7 @@ void OpcodeTable::Initialize()
DEFINE_HANDLER(CMSG_TAXI_QUERY_AVAILABLE_NODES, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleTaxiQueryAvailableNodesOpcode);
DEFINE_HANDLER(CMSG_TAXI_REQUEST_EARLY_LANDING, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleTaxiRequestEarlyLanding);
DEFINE_HANDLER(CMSG_TIME_ADJUSTMENT_RESPONSE, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL);
- DEFINE_HANDLER(CMSG_TIME_SYNC_RESPONSE, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleTimeSyncResponse);
+ DEFINE_HANDLER(CMSG_TIME_SYNC_RESPONSE, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleTimeSyncResponse);
DEFINE_HANDLER(CMSG_TIME_SYNC_RESPONSE_DROPPED, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL);
DEFINE_HANDLER(CMSG_TIME_SYNC_RESPONSE_FAILED, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL);
DEFINE_HANDLER(CMSG_TOGGLE_DIFFICULTY, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL);
diff --git a/src/server/game/Server/WorldPacket.h b/src/server/game/Server/WorldPacket.h
index 99e06b2b1cd..cb9565b1fd3 100644
--- a/src/server/game/Server/WorldPacket.h
+++ b/src/server/game/Server/WorldPacket.h
@@ -20,6 +20,7 @@
#include "ByteBuffer.h"
#include "Opcodes.h"
+#include <chrono>
class WorldPacket : public ByteBuffer
{
@@ -40,7 +41,7 @@ class WorldPacket : public ByteBuffer
WorldPacket(uint32 opcode, size_t res, ConnectionType connection = CONNECTION_TYPE_DEFAULT) : WorldPacket(opcode, res, Reserve{}, connection) { }
- WorldPacket(WorldPacket&& packet) noexcept : ByteBuffer(std::move(packet)), m_opcode(packet.m_opcode), _connection(packet._connection)
+ WorldPacket(WorldPacket&& packet) noexcept : ByteBuffer(std::move(packet)), m_opcode(packet.m_opcode), _connection(packet._connection), m_receivedTime(packet.m_receivedTime)
{
}
@@ -85,9 +86,13 @@ class WorldPacket : public ByteBuffer
ConnectionType GetConnection() const { return _connection; }
+ std::chrono::steady_clock::time_point GetReceivedTime() const { return m_receivedTime; }
+ void SetReceiveTime(std::chrono::steady_clock::time_point receivedTime) { m_receivedTime = receivedTime; }
+
protected:
uint32 m_opcode;
ConnectionType _connection;
+ 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 1f34512a9b6..a1416d18410 100644
--- a/src/server/game/Server/WorldSession.cpp
+++ b/src/server/game/Server/WorldSession.cpp
@@ -128,7 +128,6 @@ WorldSession::WorldSession(uint32 id, std::string&& name, uint32 battlenetAccoun
m_sessionDbcLocale(sWorld->GetAvailableDbcLocale(locale)),
m_sessionDbLocaleIndex(locale),
m_latency(0),
- m_clientTimeDelay(0),
_tutorialsChanged(TUTORIALS_FLAG_NONE),
_filterAddonMessages(false),
recruiterId(recruiter),
@@ -137,11 +136,17 @@ WorldSession::WorldSession(uint32 id, std::string&& name, uint32 battlenetAccoun
expireTime(60000), // 1 min after socket loss, session is deleted
forceExit(false),
m_currentBankerGUID(),
+ _timeSyncClockDeltaQueue(6),
+ _timeSyncClockDelta(0),
+ _pendingTimeSyncRequests(),
_battlePetMgr(std::make_unique<BattlePetMgr>(this)),
_collectionMgr(std::make_unique<CollectionMgr>(this))
{
memset(_tutorials, 0, sizeof(_tutorials));
+ _timeSyncNextCounter = 0;
+ _timeSyncTimer = 0;
+
if (sock)
{
m_Address = sock->GetRemoteIpAddress().to_string();
@@ -453,6 +458,18 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater)
if (m_Socket[CONNECTION_TYPE_REALM] && m_Socket[CONNECTION_TYPE_REALM]->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
@@ -1457,3 +1474,34 @@ 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()
+{
+ WorldPackets::Misc::TimeSyncRequest timeSyncRequest;
+ timeSyncRequest.SequenceIndex = _timeSyncNextCounter;
+ SendPacket(timeSyncRequest.Write());
+
+ _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++;
+}
+
+uint32 WorldSession::AdjustClientMovementTime(uint32 time) const
+{
+ int64 movementTime = int64(time) + _timeSyncClockDelta;
+ if (_timeSyncClockDelta == 0 || movementTime < 0 || movementTime > 0xFFFFFFFF)
+ {
+ TC_LOG_WARN("misc", "The computed movement time using clockDelta is erronous. Using fallback instead");
+ return GameTime::GetGameTimeMS();
+ }
+ else
+ return uint32(movementTime);
+}
diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h
index f2f4fb03159..24354eccd30 100644
--- a/src/server/game/Server/WorldSession.h
+++ b/src/server/game/Server/WorldSession.h
@@ -34,7 +34,9 @@
#include "Packet.h"
#include "RaceMask.h"
#include "SharedDefines.h"
+#include <boost/circular_buffer.hpp>
#include <array>
+#include <map>
#include <unordered_map>
#include <unordered_set>
@@ -894,8 +896,8 @@ public:
explicit PacketFilter(WorldSession* pSession) : m_pSession(pSession) { }
virtual ~PacketFilter() { }
- virtual bool Process(WorldPacket* /*packet*/) = 0;
- virtual bool ProcessUnsafe() const { return false; }
+ virtual bool Process(WorldPacket* /*packet*/) { return true; }
+ virtual bool ProcessUnsafe() const { return true; }
protected:
WorldSession* const m_pSession;
@@ -912,7 +914,9 @@ public:
explicit MapSessionFilter(WorldSession* pSession) : PacketFilter(pSession) { }
~MapSessionFilter() { }
- bool Process(WorldPacket* packet) override;
+ virtual bool Process(WorldPacket* packet) override;
+ //in Map::Update() we do not process player logout!
+ virtual bool ProcessUnsafe() const override { return false; }
};
//class used to filer only thread-unsafe packets from queue
@@ -1122,7 +1126,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;
@@ -1134,6 +1137,11 @@ class TC_GAME_API WorldSession
uint32 GetRecruiterId() const { return recruiterId; }
bool IsARecruiter() const { return isRecruiter; }
+ // Time Synchronisation
+ void ResetTimeSync();
+ void SendTimeSync();
+ uint32 AdjustClientMovementTime(uint32 time) const;
+
// Battle Pets
BattlePetMgr* GetBattlePetMgr() const { return _battlePetMgr.get(); }
@@ -1587,7 +1595,7 @@ class TC_GAME_API WorldSession
void HandleSetDungeonDifficultyOpcode(WorldPackets::Misc::SetDungeonDifficulty& setDungeonDifficulty);
void HandleSetRaidDifficultyOpcode(WorldPackets::Misc::SetRaidDifficulty& setRaidDifficulty);
void HandleSetTitleOpcode(WorldPackets::Character::SetTitle& packet);
- void HandleTimeSyncResponse(WorldPackets::Misc::TimeSyncResponse& packet);
+ void HandleTimeSyncResponse(WorldPackets::Misc::TimeSyncResponse& timeSyncResponse);
void HandleWhoIsOpcode(WorldPackets::Who::WhoIsRequest& packet);
void HandleResetInstancesOpcode(WorldPackets::Instance::ResetInstances& packet);
void HandleInstanceLockResponse(WorldPackets::Instance::InstanceLockResponse& packet);
@@ -1900,7 +1908,6 @@ class TC_GAME_API WorldSession
LocaleConstant m_sessionDbcLocale;
LocaleConstant m_sessionDbLocaleIndex;
std::atomic<uint32> m_latency;
- std::atomic<uint32> m_clientTimeDelay;
AccountData _accountData[NUM_ACCOUNT_DATA_TYPES];
uint32 _tutorials[MAX_ACCOUNT_TUTORIAL_VALUES];
uint8 _tutorialsChanged;
@@ -1914,6 +1921,14 @@ 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;
+
std::unique_ptr<BattlePetMgr> _battlePetMgr;
std::unique_ptr<CollectionMgr> _collectionMgr;
diff --git a/src/server/game/Server/WorldSocket.cpp b/src/server/game/Server/WorldSocket.cpp
index 72519fbd15f..5df8e8853cb 100644
--- a/src/server/game/Server/WorldSocket.cpp
+++ b/src/server/game/Server/WorldSocket.cpp
@@ -468,6 +468,9 @@ WorldSocket::ReadDataHandlerResult WorldSocket::ReadDataHandler()
/* fallthrough */
default:
{
+ if (opcode == CMSG_TIME_SYNC_RESPONSE)
+ packet.SetReceiveTime(std::chrono::steady_clock::now());
+
sessionGuard.lock();
LogOpcodeText(opcode, sessionGuard);
@@ -1035,10 +1038,7 @@ bool WorldSocket::HandlePing(WorldPackets::Auth::Ping& ping)
std::lock_guard<std::mutex> sessionGuard(_worldSessionLock);
if (_worldSession)
- {
_worldSession->SetLatency(ping.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());