src/engine/eLagCompensation.cpp

Go to the documentation of this file.
00001 /*
00002 
00003 *************************************************************************
00004 
00005 ArmageTron -- Just another Tron Lightcycle Game in 3D.
00006 Copyright (C) 2005  by 
00007 and the AA DevTeam (see the file AUTHORS(.txt) in the main source directory)
00008 
00009 **************************************************************************
00010 
00011 This program is free software; you can redistribute it and/or
00012 modify it under the terms of the GNU General Public License
00013 as published by the Free Software Foundation; either version 2
00014 of the License, or (at your option) any later version.
00015 
00016 This program is distributed in the hope that it will be useful,
00017 but WITHOUT ANY WARRANTY; without even the implied warranty of
00018 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019 GNU General Public License for more details.
00020 
00021 You should have received a copy of the GNU General Public License
00022 along with this program; if not, write to the Free Software
00023 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
00024   
00025 ***************************************************************************
00026 
00027 */
00028 
00029 #include "eLagCompensation.h"
00030 
00031 #include "tSysTime.h"
00032 
00033 #include "nNetwork.h"
00034 #include "nConfig.h"
00035 
00036 #ifdef DEBUG
00037 #define DEBUG_LAG
00038 #endif
00039 
00040 // client side settings
00041 static REAL se_maxLagSpeedup=.2;        // maximal speed increase of timer while lag is compensated for
00042 static REAL se_lagSlowDecayTime=30.0;   // timescale the slow lag measurement decays on
00043 static REAL se_lagFastDecayTime=5.0;    // timescale the fast lag measurement decays on
00044 static REAL se_lagSlowWeight=.2f;       // extra weight lag reports from the server influence the slow lag compensation with
00045 static REAL se_lagFastWeight=1.0f;      // extra weight lag reports from the server influence the fast lag compensation with
00046 
00047 static tSettingItem< REAL > se_maxLagSpeedupConf( "LAG_MAX_SPEEDUP_TIMER", se_maxLagSpeedup );
00048 static tSettingItem< REAL > se_lagSlowDecayTimeConf( "LAG_SLOW_TIME", se_lagSlowDecayTime );
00049 static tSettingItem< REAL > se_lagFastDecayTimeConf( "LAG_FAST_TIME", se_lagFastDecayTime );
00050 static tSettingItem< REAL > se_lagSlowWeightConf( "LAG_SLOW_WEIGHT", se_lagSlowWeight );
00051 static tSettingItem< REAL > se_lagFastWeightConf( "LAG_FAST_WEIGHT", se_lagFastWeight );
00052 
00054 class nClientLag
00055 {public:
00056     nClientLag():lagLast_(0), lagSlow_(0), lagFast_(0), smoothLag_(0) {}
00057 
00058     REAL SmoothLag(){ return smoothLag_; }
00059 
00060     void ReportLag( REAL lag, REAL weight )
00061     {
00062 #ifdef DEBUG
00063         con << "Received message of " << lag << " seconds of lag, weight " << weight << "\n";
00064 #endif
00065 
00066         // memorize the time of serious reports
00067         if ( weight > 1 )
00068             lagLast_ = tSysTimeFloat();
00069 
00070         REAL slowWeight = weight * se_lagSlowWeight;
00071         slowWeight = slowWeight > 1 ? 1 : slowWeight;
00072         REAL fastWeight = weight * se_lagFastWeight;
00073         fastWeight = fastWeight > 1 ? 1 : fastWeight;
00074 
00075         lagFast_ = smoothLag_ + lag * fastWeight;
00076         lagSlow_ = ( smoothLag_ > lagSlow_ ? lagSlow_ : smoothLag_ ) + lag * slowWeight;
00077     }
00078 
00079     void Timestep(REAL dt)
00080     {
00081         if ( dt > .5 )
00082             dt = .5;
00083 
00084         // increase smooth lag
00085         REAL speedup = se_maxLagSpeedup * dt;
00086         smoothLag_ += speedup;
00087 
00088         // clamp fast lag with slow lag
00089         if ( lagFast_ < lagSlow_ )
00090             lagFast_ = lagSlow_;
00091 
00092         // clam smooth lag with fast lag
00093         if (  smoothLag_ > lagFast_ )
00094             smoothLag_ = lagFast_;
00095 
00096         // the last serious lag report came from this many seconds ago
00097         REAL lastLag = tSysTimeFloat() - lagLast_;
00098 
00099         // let regular lag decay
00100         if ( lastLag > se_lagSlowDecayTime )
00101             lagSlow_ *= se_lagSlowDecayTime/( se_lagSlowDecayTime + dt );
00102         if ( lastLag > se_lagFastDecayTime )
00103             lagFast_ *= se_lagFastDecayTime/( se_lagFastDecayTime + dt );
00104     }
00105 private:
00106     REAL lagLast_;   
00107     REAL lagSlow_;   
00108     REAL lagFast_;   
00109     REAL smoothLag_; 
00110 };
00111 
00112 static nClientLag se_clientLag;
00113 
00114 static void se_receiveLagMessage( nMessage & m )
00115 {
00116     if ( sn_GetNetState() != nCLIENT )
00117         return;
00118 
00119     REAL lag;
00120     m >> lag;
00121 
00122     REAL weight;
00123     m >> weight;
00124 
00125     se_clientLag.ReportLag( lag, weight );
00126 }
00127 
00128 static nDescriptor se_receiveLagMessageDescriptor( 240, se_receiveLagMessage,"lag message" );
00129 
00130 // maximal seconds of lag credit
00131 static REAL se_lagCredit = .5f;
00132 
00133 // maximal seconds of lag credit given in a single event
00134 static REAL se_lagCreditSingle = .1f;
00135 
00136 // sweet spot, the fill ratio of lag credit the server tries to keep the client at
00137 static REAL se_lagCreditSweetSpot = .5f;
00138 
00139 // timescale lag credit is restored on
00140 static REAL se_lagCreditTime = 600.0f;
00141 
00142 static tSettingItem< REAL > se_lagCreditConf( "LAG_CREDIT", se_lagCredit );
00143 static tSettingItem< REAL > se_lagCreditSingleConf( "LAG_CREDIT_SINGLE", se_lagCreditSingle );
00144 static tSettingItem< REAL > se_lagCreditSweetSpotConf( "LAG_SWEET_SPOT", se_lagCreditSweetSpot );
00145 static tSettingItem< REAL > se_lagCreditTimeConf( "LAG_CREDIT_TIME", se_lagCreditTime );
00146 
00147 // threshold
00148 static REAL se_lagThreshold = 0.0f;
00149 static tSettingItem< REAL > se_lagThresholdConf( "LAG_THRESHOLD", se_lagThreshold );
00150 
00151 // see if a client supports lag compensation
00152 static nVersionFeature se_clientLagCompensation( 14 );
00153 
00155 class nServerLag
00156 {
00157 public:
00158     nServerLag()
00159     {
00160         Reset();
00161     }
00162 
00163     REAL Ping()
00164     {
00165         return sn_Connections[client_].ping.GetPing();
00166     }
00167 
00168     REAL Credit()
00169     {
00170         return se_lagCredit;
00171     }
00172 
00173     void Reset()
00174     {
00175         creditUsed_ = se_lagCreditSweetSpot * Credit();
00176         lastTime_ = lastLag_ = tSysTimeFloat();
00177         client_ = 0;
00178     }
00179 
00180     void SetClient( int client )
00181     {
00182         tASSERT( 1 <= client && client <= MAXCLIENTS );
00183 
00184         client_ = client;
00185     }
00186 
00187     void Report( REAL lag )
00188     {
00189         // see if it is useful to report
00190         if ( ! se_clientLagCompensation.Supported( client_ ) )
00191             return;
00192 
00193         // clamp
00194         lag = lag > se_lagCreditSingle ? se_lagCreditSingle : lag;
00195 
00196         // get info
00197         REAL credit = Credit();
00198         if ( credit < EPS )
00199             credit = EPS;
00200         REAL ping = Ping();
00201 
00202         // don't report too often
00203         double time = tSysTimeFloat();
00204         if ( time - lastLag_ < 4 * ping )
00205             return;
00206         lastLag_ = time;
00207 
00208         // send a simple message to the client
00209         nMessage * mess = tNEW( nMessage )( se_receiveLagMessageDescriptor );
00210         *mess << lag;
00211 
00212         // propose a weight to the client, it will determine how much impact the lag report has
00213         REAL weight = 1;
00214         if ( se_lagCreditSweetSpot > 0 )
00215         {
00216             weight = ( (creditUsed_+2*lag)/credit )/se_lagCreditSweetSpot;
00217         }
00218         *mess << weight;
00219 
00220         mess->Send( client_ );
00221     }
00222 
00223     REAL CreditLeft()
00224     {
00225         return Credit() - creditUsed_;
00226     }
00227 
00228     REAL TakeCredit( REAL lag )
00229     {
00230         lag -= se_lagThreshold;
00231         if ( lag > 0 )
00232         {
00233 #ifdef DEBUG_LAG
00234             REAL lagBefore = lag;
00235 #endif
00236 
00237             // if everyone is lagging, delete the used credit
00238             Balance();
00239 
00240             // clamp
00241             lag = lag > se_lagCreditSingle ? se_lagCreditSingle : lag;
00242 
00243             // get values
00244             REAL credit = Credit();
00245 
00246             // sanity check
00247             if ( se_lagCreditTime < EPS )
00248                 se_lagCreditTime = EPS;
00249 
00250             // timestep credit
00251             double time = tSysTimeFloat();
00252             REAL dt = time - lastTime_;
00253             lastTime_ = time;
00254             creditUsed_ -= dt * credit/se_lagCreditTime;
00255             if ( creditUsed_ < 0 )
00256                 creditUsed_ = 0;
00257 
00258             // see how much credit is left to be used
00259             REAL creditLeft = credit - creditUsed_;
00260             if ( lag > creditLeft )
00261                 lag = creditLeft;
00262             if ( lag < 0 )
00263                 lag = 0;
00264 
00265             // use it and return it
00266             creditUsed_ += lag;
00267 
00268 #ifdef DEBUG_LAG
00269             {
00270                 if ( lag > lagBefore )
00271                     con << "Requesting " << lagBefore << " seconds of lag credit, granting " << lag << ".\n";
00272                 else
00273                     con << "Granting " << lag << " seconds of lag credit.\n";
00274             }
00275 #endif
00276 
00277             lag += se_lagThreshold;
00278 
00279         }
00280 
00281         return lag;
00282     }
00283 
00284     static void Balance();
00285 private:
00286     REAL creditUsed_; 
00287     double lastTime_; 
00288     double lastLag_;  
00289     int client_;      
00290 };
00291 
00292 nServerLag se_serverLag[MAXCLIENTS+1];
00293 
00294 // credit amnesty: if everyone is lagging, it must be the server's fault. Delete the used credit.
00295 void nServerLag::Balance()
00296 {
00297     int i;
00298 
00299     // only if many users are online
00300     if ( sn_NumUsers() <= 1 )
00301         return;
00302 
00303     // find the minimum used credit
00304     REAL minCredit = se_lagCredit;
00305     for ( i = MAXCLIENTS; i>0; --i )
00306     {
00307         if ( sn_Connections[i].socket )
00308         {
00309             REAL credit = se_serverLag[i].creditUsed_;
00310             if ( credit < minCredit )
00311                 minCredit = credit;
00312         }
00313     }
00314 
00315     // find out how much you can take away
00316     REAL amnesty = minCredit - se_lagCredit * se_lagCreditSweetSpot;
00317 
00318     // and take it away from everyone
00319     if ( amnesty > 0 )
00320         for ( i = MAXCLIENTS; i>0; --i )
00321             se_serverLag[i].creditUsed_ -= amnesty;
00322 }
00323 
00324 // callback resetting the lag credit on login/logout
00325 static void login_callback(){
00326     int user = nCallbackLoginLogout::User();
00327     if ( sn_GetNetState() != nSERVER || user == 0 || user > MAXCLIENTS )
00328         return;
00329 
00330     se_serverLag[user].Reset();
00331     se_serverLag[user].SetClient(user);
00332 }
00333 static nCallbackLoginLogout nlc(&login_callback);
00334 
00335 // offsets added to the lag measurements so server admins who know their connection is crappy
00336 // can force the clients to add a bit of (predictable and therefore good) lag
00337 
00338 static REAL se_lagOffsetClient = 0.0f;
00339 static REAL se_lagOffsetServer = 0.0f;
00340 
00341 static tSettingItem< REAL > se_lagOffsetClientConf( "LAG_OFFSET_CLIENT", se_lagOffsetClient );
00342 static nSettingItem< REAL > se_lagOffsetServerConf( "LAG_OFFSET_SERVER", se_lagOffsetServer );
00343 
00344 // *******************************************************************************
00345 // *
00346 // *    Report
00347 // *
00348 // *******************************************************************************
00354 // *******************************************************************************
00355 
00356 void eLag::Report( int client, REAL lag )
00357 {
00358     tVERIFY( 1 <= client && client <= MAXCLIENTS );
00359 
00360     se_serverLag[client].Report( lag );
00361 }
00362 
00363 // *******************************************************************************
00364 // *
00365 // *    TakeCredit
00366 // *
00367 // *******************************************************************************
00373 // *******************************************************************************
00374 
00375 REAL eLag::TakeCredit( int client, REAL lag )
00376 {
00377     tVERIFY( 1 <= client && client <= MAXCLIENTS );
00378 
00379     return se_serverLag[client].TakeCredit( lag );
00380 }
00381 
00382 
00383 // *******************************************************************************
00384 // *
00385 // *    Credit
00386 // *
00387 // *******************************************************************************
00392 // *******************************************************************************
00393 
00394 REAL eLag::Credit( int client )
00395 {
00396     // don't give credit on the client
00397     if ( sn_GetNetState() != nSERVER )
00398         return 0;
00399 
00400     tVERIFY( 1 <= client && client <= MAXCLIENTS );
00401 
00402     // see how much total credit is left
00403     REAL credit = se_serverLag[client].CreditLeft();
00404 
00405     // but clamp it with the maximum single credit
00406     return credit > se_lagCreditSingle ? se_lagCreditSingle : credit;
00407 }
00408 
00409 // *******************************************************************************
00410 // *
00411 // *    Threshold
00412 // *
00413 // *******************************************************************************
00417 // *******************************************************************************
00418 
00419 REAL eLag::Threshold( void )
00420 {
00421     return se_lagThreshold;
00422 }
00423 
00424 // *******************************************************************************
00425 // *
00426 // *    Current
00427 // *
00428 // *******************************************************************************
00432 // *******************************************************************************
00433 
00434 REAL eLag::Current( void )
00435 {
00436     return se_clientLag.SmoothLag() + se_lagOffsetClient + se_lagOffsetServer;
00437 }
00438 
00439 // *******************************************************************************
00440 // *
00441 // *    Timestep
00442 // *
00443 // *******************************************************************************
00447 // *******************************************************************************
00448 
00449 void eLag::Timestep( REAL dt )
00450 {
00451     se_clientLag.Timestep( dt );
00452 }

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