aboutsummaryrefslogtreecommitdiff
path: root/src/server/game/Handlers/MovementHandler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/server/game/Handlers/MovementHandler.cpp')
-rw-r--r--src/server/game/Handlers/MovementHandler.cpp94
1 files changed, 86 insertions, 8 deletions
diff --git a/src/server/game/Handlers/MovementHandler.cpp b/src/server/game/Handlers/MovementHandler.cpp
index c12841d7e0f..b2c5fe4edc2 100644
--- a/src/server/game/Handlers/MovementHandler.cpp
+++ b/src/server/game/Handlers/MovementHandler.cpp
@@ -32,8 +32,9 @@
#include "ObjectMgr.h"
#include "Vehicle.h"
#include "GameTime.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(WorldPacket & /*recvData*/)
{
@@ -362,14 +363,18 @@ void WorldSession::HandleMovementOpcodes(WorldPacket& recvData)
plrMover->SetInWater(!plrMover->IsInWater() || plrMover->GetBaseMap()->IsUnderWater(movementInfo.pos.GetPositionX(), movementInfo.pos.GetPositionY(), movementInfo.pos.GetPositionZ()));
}
- uint32 mstime = GameTime::GetGameTimeMS();
- /*----------------------*/
- if (m_clientTimeDelay == 0)
- m_clientTimeDelay = mstime - movementInfo.time;
-
/* process position-change */
WorldPacket data(opcode, recvData.size());
- movementInfo.time = movementInfo.time + m_clientTimeDelay + MOVEMENT_PACKET_TIME_DELAY;
+ int64 movementTime = (int64) movementInfo.time + _timeSyncClockDelta;
+ if (_timeSyncClockDelta == 0 || movementTime < 0 || movementTime > 0xFFFFFFFF)
+ {
+ TC_LOG_WARN("misc", "The computed movement time using clockDelta is erronous. Using fallback instead");
+ movementInfo.time = GameTime::GetGameTimeMS();
+ }
+ else
+ {
+ movementInfo.time = (uint32)movementTime;
+ }
movementInfo.guid = mover->GetGUID();
WriteMovementInfo(&data, &movementInfo);
@@ -640,3 +645,76 @@ void WorldSession::HandleMoveTimeSkippedOpcode(WorldPacket& recvData)
data << timeSkipped;
GetPlayer()->SendMessageToSet(&data, false);
}
+
+void WorldSession::HandleTimeSyncResp(WorldPacket& recvData)
+{
+ TC_LOG_DEBUG("network", "CMSG_TIME_SYNC_RESP");
+
+ 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 + lagDelay) - (int64)clientTimestamp;
+ _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;
+ }
+}