1 /*
2 
3 *************************************************************************
4 
5 ArmageTron -- Just another Tron Lightcycle Game in 3D.
6 Copyright (C) 2000  Manuel Moos (manuel@moosnet.de)
7 
8 **************************************************************************
9 
10 This program is free software; you can redistribute it and/or
11 modify it under the terms of the GNU General Public License
12 as published by the Free Software Foundation; either version 2
13 of the License, or (at your option) any later version.
14 
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 GNU General Public License for more details.
19 
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
23 
24 ***************************************************************************
25 
26 */
27 
28 #include "tMemManager.h"
29 #include "eTimer.h"
30 #include "eNetGameObject.h"
31 #include "nSimulatePing.h"
32 #include "tRecorder.h"
33 #include "tMath.h"
34 #include "tConfiguration.h"
35 #include "eLagCompensation.h"
36 
37 eTimer *se_mainGameTimer=NULL;
38 
39 // from nNetwork.C; used to sync time with the server
40 //extern REAL sn_ping[MAXCLIENTS+2];
41 
eTimer()42 eTimer::eTimer():nNetObject(), startTimeSmoothedOffset_(0){
43     //con << "Creating own eTimer.\n";
44 
45     speed = 1.0;
46 
47     Reset(0);
48 
49     if (se_mainGameTimer)
50         delete se_mainGameTimer;
51     se_mainGameTimer=this;
52     if (sn_GetNetState()==nSERVER)
53         RequestSync();
54 
55     averageSpf_.Reset();
56     averageSpf_.Add(1/60.0,5);
57 
58     creationSystemTime_ = tSysTimeFloat();
59 }
60 
eTimer(nMessage & m)61 eTimer::eTimer(nMessage &m):nNetObject(m), startTimeSmoothedOffset_(0){
62     //con << "Creating remote eTimer.\n";
63 
64     speed = 1.0;
65 
66     Reset(0);
67 
68     if (se_mainGameTimer)
69         delete se_mainGameTimer;
70     se_mainGameTimer=this;
71 
72     averageSpf_.Reset();
73     averageSpf_.Add(1/60.0,5);
74 
75     creationSystemTime_ = tSysTimeFloat();
76 
77     lastStartTime_ = lastRemoteTime_ = 0;
78 
79     // assume VERY strongly the clocks are in sync
80     startTimeDrift_.Add( 0, 10000 );
81 }
82 
~eTimer()83 eTimer::~eTimer(){
84     //con << "Deleting eTimer.\n";
85     se_mainGameTimer=NULL;
86 }
87 
88 // see if a client supports lag compensation
89 static nVersionFeature se_clientLagCompensation( 14 );
90 
91 // old clients need a default lag compensation
92 static REAL se_lagOffsetLegacy = 0.0f;
93 static tSettingItem< REAL > se_lagOffsetLegacyConf( "LAG_OFFSET_LEGACY", se_lagOffsetLegacy );
94 
WriteSync(nMessage & m)95 void eTimer::WriteSync(nMessage &m){
96     nNetObject::WriteSync(m);
97     REAL time = Time();
98 
99     if ( SyncedUser() > 0 && !se_clientLagCompensation.Supported( SyncedUser() ) )
100         time += se_lagOffsetLegacy;
101 
102     m << time;
103     m << speed;
104     //std::cerr << "syncing:" << currentTime << ":" << speed << '\n';
105 
106     // plan next sync
107     const REAL stopFast = 3;
108     const REAL maxFast  = 3;
109     if ( time < stopFast )
110     {
111         nextSync_ = smoothedSystemTime_ + maxFast/(stopFast+maxFast-time);
112     }
113     else
114     {
115         nextSync_ = smoothedSystemTime_ + 1;
116     }
117 }
118 
119 static REAL se_timerStartFudge = 0.0;
120 static tSettingItem<REAL> se_timerStartFudgeConf("TIMER_SYNC_START_FUDGE",se_timerStartFudge);
121 static REAL se_timerStartFudgeStop = 2.0;
122 static tSettingItem<REAL> se_timerStartFudgeStopConf("TIMER_SYNC_START_FUDGE_STOP",se_timerStartFudgeStop);
123 
ReadSync(nMessage & m)124 void eTimer::ReadSync(nMessage &m){
125     nNetObject::ReadSync(m);
126 
127     bool checkDrift = true;
128 
129     //std::cerr << "Got sync:" << remote_currentTime << ":" << speed << '\n';
130 
131     // determine the best estimate of the start time offset that reproduces the sent remote
132     // time and its expected quality.
133     REAL remoteStartTimeOffset = 0;
134     REAL remoteTimeNonQuality = 0;
135     {
136         //REAL oldTime=currentTime;
137         REAL remote_currentTime, remoteSpeed;
138         m >> remote_currentTime; // read in the remote time
139         m >> remoteSpeed;
140 
141         // forget about earlier syncs if the speed changed
142         if ( fabs( speed - remoteSpeed ) > 10 * EPS )
143         {
144             qualityTester_.Timestep( 100 );
145             startTimeOffset_.Reset();
146             checkDrift = false;
147         }
148 
149         speed = remoteSpeed;
150 
151         // determine ping
152         nPingAverager & averager = sn_Connections[m.SenderID()].ping;
153         // pingAverager_.Add( rawAverager.GetPing(), remote_currentTime > .01 ? remote_currentTime : .01 );
154         REAL ping = averager.GetPing();
155 
156         // add half our ping (see Einsteins SRT on clock syncronisation)
157         REAL real_remoteTime=remote_currentTime+ping*speed*.5;
158 
159         // and the normal time including ping charity
160         REAL min_remoteTime=remote_currentTime+ping*speed-
161                             sn_pingCharityServer*.001;
162 
163         if (real_remoteTime<min_remoteTime)
164             remote_currentTime=min_remoteTime;
165         else
166             remote_currentTime=real_remoteTime;
167 
168         // HACK: warp time into the future at the beginning of the round.
169         // I can't explain why this is required; without it, the timer is late.
170         if ( remote_currentTime < se_timerStartFudgeStop )
171         {
172             remote_currentTime += ping * ( se_timerStartFudgeStop - remote_currentTime ) * se_timerStartFudge;
173             checkDrift = false;
174         }
175 
176         // determine quality from ping variance
177         remoteTimeNonQuality = averager.GetFastAverager().GetAverageVariance();
178 
179         // determine start time
180         remoteStartTimeOffset = smoothedSystemTime_ - startTime_ - remote_currentTime;
181 
182         // calculate drift
183         if( checkDrift )
184         {
185             REAL timeDifference = remote_currentTime - lastRemoteTime_;
186             REAL drift = lastStartTime_ - remoteStartTimeOffset;
187 
188             if ( timeDifference > 0 )
189             {
190                 REAL driftAverage = startTimeDrift_.GetAverage();
191                 // con << "Drift: " << driftAverage << "\n";
192                 REAL driftDifference = drift/timeDifference;
193 
194                 timeDifference = timeDifference > 1 ? 1 : timeDifference;
195                 startTimeDrift_.Timestep( timeDifference * .1 );
196                 startTimeDrift_.Add( driftDifference + driftAverage, timeDifference );
197             }
198         }
199 
200         lastStartTime_ = remoteStartTimeOffset;
201         lastRemoteTime_ = remote_currentTime;
202 
203         // let the averagers decay faster at the beginning
204         if ( remote_currentTime < 0 )
205         {
206             qualityTester_.Timestep( .2 );
207             startTimeOffset_.Timestep( .2 );
208         }
209     }
210 
211     // try to get independend quality measure: get the variance of the received start times
212     qualityTester_.Add( remoteStartTimeOffset );
213 
214     // add the variance to the non-quality, along with an offset to avoid division by zero
215     remoteTimeNonQuality += 0.00001 + 4 * qualityTester_.GetAverageVariance();
216 
217     // add the offset to the statistics, weighted by the quality
218     startTimeOffset_.Add( remoteStartTimeOffset, 1/remoteTimeNonQuality );
219 }
220 
221 static nNOInitialisator<eTimer> eTimer_init(210,"eTimer");
222 
CreatorDescriptor() const223 nDescriptor &eTimer::CreatorDescriptor() const{
224     return eTimer_init;
225 }
226 
227 // determines whether time should be smoothed
se_SmoothTime()228 static bool se_SmoothTime()
229 {
230     bool smooth = 0;
231 
232     // try to get value from recording
233     char const * section = "SMOOTHTIME";
234     if ( !tRecorder::PlaybackStrict( section, smooth ) )
235     {
236         // get value by OS type
237         smooth = !tTimerIsAccurate();
238     }
239     // archive the decision
240     tRecorder::Record( section, smooth );
241 
242     return smooth;
243 }
244 
Time()245 REAL eTimer::Time()
246 {
247     return ( smoothedSystemTime_ - startTime_ ) - startTimeSmoothedOffset_ + eLag::Current();
248 }
249 
SyncTime()250 void eTimer::SyncTime(){
251     // get current system time
252     {
253         double newTime=tSysTimeFloat();
254 
255         static bool smooth = se_SmoothTime();
256 
257         // recheck if no recording/playback is running
258         if ( !tRecorder::IsRunning() )
259         {
260             smooth = !tTimerIsAccurate();
261         }
262 
263         if ( !smooth )
264         {
265             // take it
266             smoothedSystemTime_ = newTime;
267         }
268         else
269         {
270             // smooth it
271             REAL smoothDecay = .2f;
272             smoothedSystemTime_ = ( smoothedSystemTime_ + smoothDecay * newTime )/( 1 + smoothDecay );
273         }
274     }
275 
276     // update timers
277     REAL timeStep=smoothedSystemTime_ - lastTime_;
278     lastTime_ = smoothedSystemTime_;
279 
280 #ifdef DEBUG
281 #ifndef DEDICATED
282     // maximum effective timestep in SP debug mode: .1s
283     if (timeStep > .1f && sn_GetNetState() == nSTANDALONE && !tRecorder::IsRunning() )
284     {
285         startTime_ += timeStep - .1f;
286         timeStep = .1f;
287     }
288 #endif
289 #endif
290 
291     // update lag compensation
292     eLag::Timestep( timeStep );
293 
294     // store and average frame times
295     spf_ = timeStep;
296     if ( timeStep > 0 && speed > 0 )
297     {
298         averageSpf_.Add( timeStep );
299         averageSpf_.Timestep( timeStep );
300     }
301 
302     // let averagers decay
303     startTimeOffset_.Timestep( timeStep * .1 );
304     qualityTester_  .Timestep( timeStep * .3 );
305     // pingAverager_   .Timestep( timeStep * .05);
306 
307     // if we're not running at default speed, update the virtual start time
308     startTime_ += ( 1.0 - speed - startTimeDrift_.GetAverage() ) * timeStep;
309 
310     // smooth time offset
311     {
312         REAL startTimeOffset = startTimeOffset_.GetAverage();
313 
314         // correct huge deviations (compared to variance) faster
315         REAL deviation = startTimeSmoothedOffset_ - startTimeOffset;
316         REAL extraSmooth = deviation * deviation / ( startTimeOffset_.GetAverageVariance() + .0001 );
317 
318         // allow for faster smoothing at the beginning of the round
319         REAL time = Time();
320         if ( time < 0 )
321             extraSmooth -= 4 * time;
322 
323         REAL smooth = timeStep * ( .5 + extraSmooth );
324         startTimeSmoothedOffset_ = ( startTimeSmoothedOffset_ + startTimeOffset * smooth )/(1 + smooth);
325 
326         if ( !finite( startTimeSmoothedOffset_ ) )
327         {
328             // emergency, smoothing the timer produced infinite results
329             st_Breakpoint();
330             startTimeSmoothedOffset_ = startTimeOffset;
331         }
332     }
333 
334     // request syncs
335     if (sn_GetNetState()==nSERVER && smoothedSystemTime_ >= nextSync_ )
336     {
337 #ifdef nSIMULATE_PING
338         RequestSync(); // ack.
339 #else
340         RequestSync(false); // NO ack.
341 #endif
342     }
343 }
344 
Reset(REAL t)345 void eTimer::Reset(REAL t){
346     if (sn_GetNetState()!=nCLIENT)
347         speed=1;
348 
349     // reset start time
350     smoothedSystemTime_ = tSysTimeFloat();
351     startTime_ = smoothedSystemTime_ - t;
352 
353     // reset averagers
354     startTimeOffset_.Reset();
355     startTimeOffset_.Add(100,EPS);
356     startTimeOffset_.Add(-100,EPS);
357 
358     qualityTester_.Reset();
359     static const REAL qual = sqrt(1/EPS);
360     qualityTester_.Add(qual,EPS);
361     qualityTester_.Add(-qual,EPS);
362 
363     // reset times of actions
364     lastTime_ = nextSync_ = smoothedSystemTime_;
365     startTimeSmoothedOffset_ = startTimeOffset_.GetAverage();
366 }
367 
IsSynced() const368 bool eTimer::IsSynced() const
369 {
370     // allow non-synced status only during the first ten seconds of a connection
371     if ( smoothedSystemTime_ - creationSystemTime_ > 10 || sn_GetNetState() != nCLIENT )
372         return true;
373 
374     // the quality of the start time offset needs to be good enough (to .1 s, variance is the square of the expected)
375     bool synced = startTimeOffset_.GetAverageVariance() < .01 &&
376                   fabs( startTimeOffset_.GetAverage() - startTimeSmoothedOffset_ ) < .01;
377 
378     static char const * section = "TIMER_SYNCED";
379 
380     if ( tRecorder::IsPlayingBack() ? tRecorder::Playback( section ) : synced )
381     {
382         tRecorder::Record( section );
383 
384         // and make sure the next calls also return true.
385         creationSystemTime_ = smoothedSystemTime_ - 11;
386         return true;
387     }
388 
389     return false;
390 }
391 
pause(bool p)392 void eTimer::pause(bool p){
393     if (p){
394         if(speed!=0){
395             speed=0;
396             if (sn_GetNetState()==nSERVER)
397                 RequestSync();
398         }
399     }
400     else{
401         if (speed!=1){
402             speed=1;
403             //Reset(currentTime_);
404 
405             if (sn_GetNetState()==nSERVER)
406                 RequestSync();
407         }
408     }
409 }
410 
se_GameTime()411 REAL se_GameTime(){
412     if (se_mainGameTimer)
413         return se_mainGameTimer->Time();
414     else
415         return 0;
416 }
se_GameTimeNoSync()417 REAL se_GameTimeNoSync(){
418     if (se_mainGameTimer)
419         return se_mainGameTimer->TimeNoSync();
420     else
421         return 0;
422 }
423 
se_SyncGameTimer()424 void se_SyncGameTimer(){
425     if (se_mainGameTimer)
426         se_mainGameTimer->SyncTime();
427 }
428 
se_MakeGameTimer()429 void se_MakeGameTimer(){
430     tNEW(eTimer);
431 }
432 
se_KillGameTimer()433 void se_KillGameTimer(){
434     if (se_mainGameTimer)
435         delete se_mainGameTimer;
436     se_mainGameTimer=NULL; // to make sure
437 }
438 
439 
se_ResetGameTimer(REAL t)440 void se_ResetGameTimer(REAL t){
441     if (se_mainGameTimer)
442         se_mainGameTimer->Reset(t);
443 }
444 
se_PauseGameTimer(bool p)445 void se_PauseGameTimer(bool p){
446     if (se_mainGameTimer && sn_GetNetState()!=nCLIENT)
447         se_mainGameTimer->pause(p);
448 }
449 
se_AverageFrameTime()450 REAL se_AverageFrameTime(){
451     return 1/se_AverageFPS();
452 }
453 
se_AverageFPS()454 REAL se_AverageFPS(){
455     if (se_mainGameTimer)
456         return se_mainGameTimer->AverageFPS();
457     else
458         return (.2);
459 }
460 
se_PredictTime()461 REAL se_PredictTime(){
462     return se_AverageFrameTime()*.5;
463 }
464 
465 
466 
467 
468