Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- class ClockSyncronizer
- {
- public:
- void Reset();
- bool AddDelta(float delta_seconds, float reported_error);
- float Delta() const { return delta; }
- float Error() const { return error; }
- float Confidence() const { return confidence; }
- void FixDeltaError(float e) { delta += e; }
- private:
- rde::vector<float> deltas;
- rde::vector<float> errors;
- float delta = 0;
- float confidence = 0;
- float error = 0;
- };
- class ClockSyncClient
- {
- public:
- ClockSyncClient() { Reset(); }
- const ClockSyncronizer& Results() const { return m_sync; }
- float Ping() const { return m_ping; }
- float ConfidenceGoal() const { return m_threshold; }
- void FixDeltaError(float);
- bool Complete() const;
- void Receive( double time, const TimeRequestMessage& );
- bool Send( double time, NetClient& );
- void Reset();
- private:
- ClockSyncronizer m_sync;
- float m_threshold = 1;
- double m_lastSendTime = -1;
- float m_ping = 0;
- };
- class ClockSyncServer
- {
- public:
- static void Respond( uint clientIndex, double time, NetServer&, const TimeRequestMessage& );
- };
- //------------------------------------------------------------------------------
- //------------------------------------------------------------------------------
- void ClockSyncClient::Reset()
- {
- m_threshold = 0.98f;
- m_lastSendTime = -1;
- m_ping = 0;
- m_sync.Reset();
- }
- void ClockSyncClient::Receive( double time, const TimeRequestMessage& msg )
- {
- u64 micros_now = (u64)round(time * 1000 * 1000);
- const TimeRequestMessage::Data& data = msg.GetData();
- eiASSERT( data.your_microseconds < micros_now );
- u64 rtt = micros_now - data.your_microseconds;
- m_ping = (float)(rtt / (1000.0*1000.0));
- u64 server_now = data.my_microseconds + rtt/2;
- s64 delta = (s64)server_now - (s64)micros_now;
- float delta_seconds = delta / (1000.0f*1000.0f);
- float reported_error = ((s64)data.your_guess_error) / (1000.0f*1000.0f);
- if( !m_sync.AddDelta(delta_seconds, reported_error) )
- m_threshold = max( 0.1f, m_threshold - 0.05f );//if it's struggling, drop our confidence goal by 5%, to a minimum of 10% confidence...
- }
- bool ClockSyncClient::Complete() const
- {
- return m_sync.Confidence() >= m_threshold;
- }
- void ClockSyncClient::FixDeltaError(float e)
- {
- eiASSERT( e > 0 );
- m_sync.FixDeltaError(e);
- }
- bool ClockSyncClient::Send( double time, NetClient& client )
- {
- const static float sendInterval = 0.35f;
- if( Complete() || time <= m_lastSendTime + sendInterval )
- return false;
- TimeRequestMessage msg;
- if( client.AllocateMessage( msg ) )
- {
- u64 micros_now = (u64)round(time * 1000 * 1000);
- TimeRequestMessage::Data& data = msg.GetData();
- data.my_microseconds = micros_now;
- data.your_microseconds = micros_now + (u64)(m_sync.Delta()*1000.0*1000.0) + (u64)((m_ping/2)*1000.0*1000.0);
- if( client.SendMessage(msg, NetChannel::UnreliableUnordered) )
- m_lastSendTime = time;
- else
- client.ReleaseMessage(msg);
- }
- return true;
- }
- //------------------------------------------------------------------------------
- void ClockSyncServer::Respond( uint clientIndex, double time, NetServer& server, const TimeRequestMessage& msg )
- {
- const TimeRequestMessage::Data& in = msg.GetData();
- TimeRequestMessage response;
- if( server.AllocateMessage(clientIndex, response) )
- {
- TimeRequestMessage::Data& out = response.GetData();
- u64 micros_now = (u64)round(time * 1000 * 1000);
- out.my_microseconds = micros_now;
- out.your_microseconds = in.my_microseconds;
- out.your_guess_error = (s64)micros_now - (s64)in.your_microseconds;
- if( !server.SendMessage(clientIndex, response, NetChannel::UnreliableUnordered) )
- server.ReleaseMessage(clientIndex, response);
- }
- }
- //------------------------------------------------------------------------------
- //------------------------------------------------------------------------------
- void ClockSyncronizer::Reset()
- {
- deltas.clear();
- errors.clear();
- delta = 0;
- confidence = 0;
- error = 0;
- }
- bool ClockSyncronizer::AddDelta(float delta_seconds, float reported_error)
- {
- deltas.push_back(delta_seconds);
- if( Abs(reported_error) < 1 )
- errors.push_back(reported_error);
- if( !errors.empty() )
- error = FilteredMean(errors.begin(), errors.end());
- std::sort(deltas.begin(), deltas.end());
- float median = Median(deltas.begin(), deltas.end());
- float stdDev = StandardDeviation(deltas.begin(), deltas.end());
- double filteredLogSum = 0;
- double filteredSum = 0;
- uint numValidSamples = 0;
- double filteredProduct = 1;
- for( auto& n : deltas )
- {
- if( Abs(n - median) > stdDev )// filter outliers
- continue;
- ++numValidSamples;
- filteredProduct *= abs(n);
- filteredLogSum += log(abs(n));
- filteredSum += n;
- }
- double geometricMean = Exp(filteredLogSum / numValidSamples) * SignBit(filteredSum);
- double geometricMean2 = Pow(filteredProduct, 1.0/numValidSamples) * SignBit(filteredSum);
- double linearMean = filteredSum / numValidSamples;
- float delta1 = (float)linearMean;
- float delta2 = (float)geometricMean;
- float delta3 = (float)geometricMean2;
- if( isinf(delta1) ) delta1 = 0;
- if( isinf(delta2) ) delta2 = delta1;
- if( isinf(delta3) ) delta3 = delta2;
- delta = (delta1 + delta2 + delta3)/3.0f;
- //magic numbers, ahoy!
- confidence = Saturate(numValidSamples / 10.0f)*0.35f //use a decent number of samples to reach a conclusion
- + Saturate(numValidSamples / (float)deltas.size())*0.35f //the less outliers the better
- + (1-Saturate(fabs(error)*100))*0.3f; //and below 10ms reported error would be nice
- if( errors.empty() ) //and at least one reported error value recieved
- confidence = 0;
- bool onTrack = true;
- if( deltas.size() > 20 ) // if still trying after too many samples, clear the arrays and signal that we're struggling
- {
- deltas.clear();
- errors.clear();
- errors.push_back(error);
- deltas.push_back(delta);
- onTrack = false;
- }
- return onTrack;
- }
- //------------------------------------------------------------------------------
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement