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

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