src/engine/eTimer.cpp

Go to the documentation of this file.
00001 /*
00002 
00003 *************************************************************************
00004 
00005 ArmageTron -- Just another Tron Lightcycle Game in 3D.
00006 Copyright (C) 2000  Manuel Moos (manuel@moosnet.de)
00007 
00008 **************************************************************************
00009 
00010 This program is free software; you can redistribute it and/or
00011 modify it under the terms of the GNU General Public License
00012 as published by the Free Software Foundation; either version 2
00013 of the License, or (at your option) any later version.
00014 
00015 This program is distributed in the hope that it will be useful,
00016 but WITHOUT ANY WARRANTY; without even the implied warranty of
00017 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018 GNU General Public License for more details.
00019 
00020 You should have received a copy of the GNU General Public License
00021 along with this program; if not, write to the Free Software
00022 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
00023 
00024 ***************************************************************************
00025 
00026 */
00027 
00028 #include "tMemManager.h"
00029 #include "eTimer.h"
00030 #include "eNetGameObject.h"
00031 #include "nSimulatePing.h"
00032 #include "tRecorder.h"
00033 #include "tMath.h"
00034 #include "tConfiguration.h"
00035 #include "eLagCompensation.h"
00036 
00037 eTimer *se_mainGameTimer=NULL;
00038 
00039 // from nNetwork.C; used to sync time with the server
00040 //extern REAL sn_ping[MAXCLIENTS+2];
00041 
00042 eTimer::eTimer():nNetObject(), startTimeSmoothedOffset_(0){
00043     //con << "Creating own eTimer.\n";
00044 
00045     speed = 1.0;
00046 
00047     Reset(0);
00048 
00049     if (se_mainGameTimer)
00050         delete se_mainGameTimer;
00051     se_mainGameTimer=this;
00052     if (sn_GetNetState()==nSERVER)
00053         RequestSync();
00054 
00055     averageSpf_.Reset();
00056     averageSpf_.Add(1/60.0,5);
00057 
00058     creationSystemTime_ = tSysTimeFloat();
00059 }
00060 
00061 eTimer::eTimer(nMessage &m):nNetObject(m), startTimeSmoothedOffset_(0){
00062     //con << "Creating remote eTimer.\n";
00063 
00064     speed = 1.0;
00065 
00066     Reset(0);
00067 
00068     if (se_mainGameTimer)
00069         delete se_mainGameTimer;
00070     se_mainGameTimer=this;
00071 
00072     averageSpf_.Reset();
00073     averageSpf_.Add(1/60.0,5);
00074 
00075     creationSystemTime_ = tSysTimeFloat();
00076 }
00077 
00078 eTimer::~eTimer(){
00079     //con << "Deleting eTimer.\n";
00080     se_mainGameTimer=NULL;
00081 }
00082 
00083 // see if a client supports lag compensation
00084 static nVersionFeature se_clientLagCompensation( 14 );
00085 
00086 // old clients need a default lag compensation
00087 static REAL se_lagOffsetLegacy = 0.0f;
00088 static tSettingItem< REAL > se_lagOffsetLegacyConf( "LAG_OFFSET_LEGACY", se_lagOffsetLegacy );
00089 
00090 void eTimer::WriteSync(nMessage &m){
00091     nNetObject::WriteSync(m);
00092     REAL time = Time();
00093 
00094     if ( SyncedUser() > 0 && !se_clientLagCompensation.Supported( SyncedUser() ) )
00095         time += se_lagOffsetLegacy;
00096 
00097     m << time;
00098     m << speed;
00099     //std::cerr << "syncing:" << currentTime << ":" << speed << '\n';
00100 
00101     // plan next sync
00102     const REAL stopFast = 3;
00103     const REAL maxFast  = 3;
00104     if ( time < stopFast )
00105     {
00106         nextSync_ = smoothedSystemTime_ + maxFast/(stopFast+maxFast-time);
00107     }
00108     else
00109     {
00110         nextSync_ = smoothedSystemTime_ + 1;
00111     }
00112 }
00113 
00114 static REAL se_timerStartFudge = 0.0;
00115 static tSettingItem<REAL> se_timerStartFudgeConf("TIMER_SYNC_START_FUDGE",se_timerStartFudge);
00116 static REAL se_timerStartFudgeStop = 2.0;
00117 static tSettingItem<REAL> se_timerStartFudgeStopConf("TIMER_SYNC_START_FUDGE_STOP",se_timerStartFudgeStop);
00118 
00119 void eTimer::ReadSync(nMessage &m){
00120     nNetObject::ReadSync(m);
00121 
00122     //std::cerr << "Got sync:" << remote_currentTime << ":" << speed << '\n';
00123 
00124     // determine the best estimate of the start time offset that reproduces the sent remote
00125     // time and its expected quality.
00126     REAL remoteStartTimeOffset = 0;
00127     REAL remoteTimeNonQuality = 0;
00128     {
00129         //REAL oldTime=currentTime;
00130         REAL remote_currentTime, remoteSpeed;
00131         m >> remote_currentTime; // read in the remote time
00132         m >> remoteSpeed;
00133 
00134         // forget about earlier syncs if the speed changed
00135         if ( fabs( speed - remoteSpeed ) > 10 * EPS )
00136         {
00137             qualityTester_.Timestep( 100 );
00138             startTimeOffset_.Reset();
00139         }
00140 
00141         speed = remoteSpeed;
00142 
00143         // determine ping
00144         nPingAverager & averager = sn_Connections[m.SenderID()].ping;
00145         // pingAverager_.Add( rawAverager.GetPing(), remote_currentTime > .01 ? remote_currentTime : .01 );
00146         REAL ping = averager.GetPing();
00147 
00148         // add half our ping (see Einsteins SRT on clock syncronisation)
00149         REAL real_remoteTime=remote_currentTime+ping*speed*.5;
00150 
00151         // and the normal time including ping charity
00152         REAL min_remoteTime=remote_currentTime+ping*speed-
00153                             sn_pingCharityServer*.001;
00154 
00155         if (real_remoteTime<min_remoteTime)
00156             remote_currentTime=min_remoteTime;
00157         else
00158             remote_currentTime=real_remoteTime;
00159 
00160         // HACK: warp time into the future at the beginning of the round.
00161         // I can't explain why this is required; without it, the timer is late.
00162         if ( remote_currentTime < se_timerStartFudgeStop )
00163         {
00164             remote_currentTime += ping * ( se_timerStartFudgeStop - remote_currentTime ) * se_timerStartFudge;
00165         }
00166 
00167         // determine quality from ping variance
00168         remoteTimeNonQuality = averager.GetFastAverager().GetAverageVariance();
00169 
00170         // determine start time
00171         remoteStartTimeOffset = smoothedSystemTime_ - startTime_ - remote_currentTime;
00172 
00173         // let the averagers decay faster at the beginning
00174         if ( remote_currentTime < 0 )
00175         {
00176             qualityTester_.Timestep( .2 );
00177             startTimeOffset_.Timestep( .2 );
00178         }
00179     }
00180 
00181     // try to get independend quality measure: get the variance of the received start times
00182     qualityTester_.Add( remoteStartTimeOffset );
00183 
00184     // add the variance to the non-quality, along with an offset to avoid division by zero
00185     remoteTimeNonQuality += 0.00001 + 4 * qualityTester_.GetAverageVariance();
00186 
00187     // add the offset to the statistics, weighted by the quality
00188     startTimeOffset_.Add( remoteStartTimeOffset, 1/remoteTimeNonQuality );
00189 }
00190 
00191 static nNOInitialisator<eTimer> eTimer_init(210,"eTimer");
00192 
00193 nDescriptor &eTimer::CreatorDescriptor() const{
00194     return eTimer_init;
00195 }
00196 
00197 // determines whether time should be smoothed
00198 static bool se_SmoothTime()
00199 {
00200     bool smooth = 0;
00201 
00202     // try to get value from recording
00203     char const * section = "SMOOTHTIME";
00204     if ( !tRecorder::PlaybackStrict( section, smooth ) )
00205     {
00206         // get value by OS type
00207         smooth = !tTimerIsAccurate();
00208     }
00209     // archive the decision
00210     tRecorder::Record( section, smooth );
00211 
00212     return smooth;
00213 }
00214 
00215 REAL eTimer::Time()
00216 {
00217     return ( smoothedSystemTime_ - startTime_ ) - startTimeSmoothedOffset_ + eLag::Current();
00218 }
00219 
00220 void eTimer::SyncTime(){
00221     // get current system time
00222     {
00223         double newTime=tSysTimeFloat();
00224 
00225         static bool smooth = se_SmoothTime();
00226 
00227         // recheck if no recording/playback is running
00228         if ( !tRecorder::IsRunning() )
00229         {
00230             smooth = !tTimerIsAccurate();
00231         }
00232 
00233         if ( !smooth )
00234         {
00235             // take it
00236             smoothedSystemTime_ = newTime;
00237         }
00238         else
00239         {
00240             // smooth it
00241             REAL smoothDecay = .2f;
00242             smoothedSystemTime_ = ( smoothedSystemTime_ + smoothDecay * newTime )/( 1 + smoothDecay );
00243         }
00244     }
00245 
00246     // update timers
00247     REAL timeStep=smoothedSystemTime_ - lastTime_;
00248     lastTime_ = smoothedSystemTime_;
00249 
00250 #ifdef DEBUG
00251 #ifndef DEDICATED
00252     // maximum effective timestep in SP debug mode: .1s
00253     if (timeStep > .1f && sn_GetNetState() == nSTANDALONE && !tRecorder::IsRunning() )
00254     {
00255         startTime_ += timeStep - .1f;
00256         timeStep = .1f;
00257     }
00258 #endif
00259 #endif
00260 
00261     // update lag compensation
00262     eLag::Timestep( timeStep );
00263 
00264     // store and average frame times
00265     spf_ = timeStep;
00266     if ( timeStep > 0 && speed > 0 )
00267     {
00268         averageSpf_.Add( timeStep );
00269         averageSpf_.Timestep( timeStep );
00270     }
00271 
00272     // let averagers decay
00273     startTimeOffset_.Timestep( timeStep * .1 );
00274     qualityTester_  .Timestep( timeStep * .3 );
00275     // pingAverager_   .Timestep( timeStep * .05);
00276 
00277     // if we're not running at default speed, update the virtual start time
00278     startTime_ += ( 1.0 - speed ) * timeStep;
00279 
00280     // smooth time offset
00281     {
00282         REAL startTimeOffset = startTimeOffset_.GetAverage();
00283 
00284         // correct huge deviations (compared to variance) faster
00285         REAL deviation = startTimeSmoothedOffset_ - startTimeOffset;
00286         REAL extraSmooth = deviation * deviation / ( startTimeOffset_.GetAverageVariance() + .0001 );
00287 
00288         // allow for faster smoothing at the beginning of the round
00289         REAL time = Time();
00290         if ( time < 0 )
00291             extraSmooth -= 4 * time;
00292 
00293         REAL smooth = timeStep * ( .5 + extraSmooth );
00294         startTimeSmoothedOffset_ = ( startTimeSmoothedOffset_ + startTimeOffset * smooth )/(1 + smooth);
00295 
00296         if ( !finite( startTimeSmoothedOffset_ ) )
00297         {
00298             // emergency, smoothing the timer produced infinite results
00299             st_Breakpoint();
00300             startTimeSmoothedOffset_ = startTimeOffset;
00301         }
00302     }
00303 
00304     // request syncs
00305     if (sn_GetNetState()==nSERVER && smoothedSystemTime_ >= nextSync_ )
00306     {
00307 #ifdef nSIMULATE_PING
00308         RequestSync(); // ack.
00309 #else
00310         RequestSync(false); // NO ack.
00311 #endif
00312     }
00313 }
00314 
00315 void eTimer::Reset(REAL t){
00316     if (sn_GetNetState()!=nCLIENT)
00317         speed=1;
00318 
00319     // reset start time
00320     smoothedSystemTime_ = tSysTimeFloat();
00321     startTime_ = smoothedSystemTime_ - t;
00322 
00323     // reset averagers
00324     startTimeOffset_.Reset();
00325     startTimeOffset_.Add(100,EPS);
00326     startTimeOffset_.Add(-100,EPS);
00327 
00328     qualityTester_.Reset();
00329     static const REAL qual = sqrt(1/EPS);
00330     qualityTester_.Add(qual,EPS);
00331     qualityTester_.Add(-qual,EPS);
00332 
00333     // reset times of actions
00334     lastTime_ = nextSync_ = smoothedSystemTime_;
00335     startTimeSmoothedOffset_ = startTimeOffset_.GetAverage();
00336 }
00337 
00338 bool eTimer::IsSynced() const
00339 {
00340     // allow non-synced status only during the first ten seconds of a connection
00341     if ( smoothedSystemTime_ - creationSystemTime_ > 10 || sn_GetNetState() != nCLIENT )
00342         return true;
00343 
00344     // the quality of the start time offset needs to be good enough (to .1 s, variance is the square of the expected)
00345     bool synced = startTimeOffset_.GetAverageVariance() < .01 &&
00346                   fabs( startTimeOffset_.GetAverage() - startTimeSmoothedOffset_ ) < .01;
00347 
00348     static char const * section = "TIMER_SYNCED";
00349 
00350     if ( tRecorder::IsPlayingBack() ? tRecorder::Playback( section ) : synced )
00351     {
00352         tRecorder::Record( section );
00353 
00354         // and make sure the next calls also return true.
00355         creationSystemTime_ = smoothedSystemTime_ - 11;
00356         return true;
00357     }
00358 
00359     return false;
00360 }
00361 
00362 void eTimer::pause(bool p){
00363     if (p){
00364         if(speed!=0){
00365             speed=0;
00366             if (sn_GetNetState()==nSERVER)
00367                 RequestSync();
00368         }
00369     }
00370     else{
00371         if (speed!=1){
00372             speed=1;
00373             //Reset(currentTime_);
00374 
00375             if (sn_GetNetState()==nSERVER)
00376                 RequestSync();
00377         }
00378     }
00379 }
00380 
00381 REAL se_GameTime(){
00382     if (se_mainGameTimer)
00383         return se_mainGameTimer->Time();
00384     else
00385         return 0;
00386 }
00387 REAL se_GameTimeNoSync(){
00388     if (se_mainGameTimer)
00389         return se_mainGameTimer->TimeNoSync();
00390     else
00391         return 0;
00392 }
00393 
00394 void se_SyncGameTimer(){
00395     if (se_mainGameTimer)
00396         se_mainGameTimer->SyncTime();
00397 }
00398 
00399 void se_MakeGameTimer(){
00400     tNEW(eTimer);
00401 }
00402 
00403 void se_KillGameTimer(){
00404     if (se_mainGameTimer)
00405         delete se_mainGameTimer;
00406     se_mainGameTimer=NULL; // to make sure
00407 }
00408 
00409 
00410 void se_ResetGameTimer(REAL t){
00411     if (se_mainGameTimer)
00412         se_mainGameTimer->Reset(t);
00413 }
00414 
00415 void se_PauseGameTimer(bool p){
00416     if (se_mainGameTimer && sn_GetNetState()!=nCLIENT)
00417         se_mainGameTimer->pause(p);
00418 }
00419 
00420 REAL se_AverageFrameTime(){
00421     return 1/se_AverageFPS();
00422 }
00423 
00424 REAL se_AverageFPS(){
00425     if (se_mainGameTimer)
00426         return se_mainGameTimer->AverageFPS();
00427     else
00428         return (.2);
00429 }
00430 
00431 REAL se_PredictTime(){
00432     return se_AverageFrameTime()*.5;
00433 }
00434 
00435 
00436 
00437 

Generated on Sat Mar 15 22:55:47 2008 for Armagetron Advanced by  doxygen 1.5.4