mirror of
https://github.com/TrinityCore/TrinityCore.git
synced 2026-01-15 23:20:36 +01:00
Core/Movement: Add time synchronisation (#18189)
(cherry picked from commit 975f1e364a)
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user