/* ************************************************************************* ArmageTron -- Just another Tron Lightcycle Game in 3D. Copyright (C) 2000 Manuel Moos (manuel@moosnet.de) ************************************************************************** This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *************************************************************************** */ #include "tMemManager.h" #include "eTimer.h" #include "eNetGameObject.h" #include "nSimulatePing.h" /* #define TICKSPS 1000 #define MYTICK 10 static const REAL step=MYTICK/REAL(TICKSPS); static List timers; eTimer ArmageTronTimer; unsigned int tick(unsigned int interval){ for(int i=timers.Len()-1;i>=0;i--){ timers(i)->tick(interval); //con << "tickin' " << i << '\n'; } return interval; } unsigned int timer::tick(unsigned int interval){ if (!paused) ticks+=interval; return interval; } eTimer::eTimer():id(-1),ticks(0),paused(0){ eTimers.Add(this,id); Reset(); avg_spf=1/60.0; } eTimer::~eTimer(){ eTimers.Remove(this,id); } // call this function after every buffer swap. It will try to // syncronize the clock to the screen refresh. (currently does nothing // like that) static const REAL middle=2; void eTimer::sync(){ #define fps_middle 30.0 avg_spf/=(1+1/fps_middle); avg_spf+=(REAL(ticks-lastSync))/(TICKSPS*fps_middle); lastSync=ticks; if (speedup) current+=1/6.0; // assume 6 FPS else current=REAL((unsigned int)(lastSync-start))/TICKSPS; //current+=1/60.0; // assume 60 FPS median*=(middle-1)/middle; //median+=(current)/middle + (middle-1)/(middle*avg_spf); median+=(current)/middle + avg_spf*(middle-.8)/middle; if (median < current){ median=current; sync_overflow=-1; } else if (median > current+step){ median=current+step; sync_overflow=1; } else sync_overflow=0; // con << "Time=" << current << '\n'; } void eTimer::pause(bool is_pause){ paused=is_pause; } // resets the eTimer; void eTimer::Reset(){ start=lastSync=ticks; current=0; median=0; } // returns the time of the last sync(). eTimer::operator REAL(){ return median; } REAL eTimer::operator()(int t){ return REAL((unsigned int)(lastSync-start))/TICKSPS; } void eTimer::Render(){ tConsole c(.8,.9); switch (sync_overflow){ case 0: glColor3f(1,1,1); break; case 1: glColor3f(1,0,0); break; case -1: glColor3f(0,0,1); break; } c << (int)fps() << " fps"; } static void eTimers_init(){ if (SDL_Init(SDL_INIT_TIMER)<0){ std::cerr << "Couldn't set timer.\n"; exit(1); } SDL_SetTimer(MYTICK, tick); } static tInitExit eTimer_ie(&eTimers_init); */ eTimer *se_mainGameTimer=NULL; // from nNetwork.C; used to sync time with the server //extern REAL sn_ping[MAXCLIENTS+2]; eTimer::eTimer():nNetObject(){ //con << "Creating own eTimer.\n"; startTime=tSysTimeFloat(); lastTime=startTime; currentTime=0; correctTimeSmooth=0; nextsync=.5; if (se_mainGameTimer) delete se_mainGameTimer; se_mainGameTimer=this; if (sn_GetNetState()==nSERVER) RequestSync(); median=currentTime; speed=1; avg_spf=.2; } eTimer::eTimer(nMessage &m):nNetObject(m){ //con << "Creating remote eTimer.\n"; startTime=tSysTimeFloat(); lastTime=startTime; currentTime=-.1; correctTimeSmooth=0; nextsync=.5; if (se_mainGameTimer) delete se_mainGameTimer; se_mainGameTimer=this; median=currentTime; speed=1; avg_spf=.2; } eTimer::~eTimer(){ //con << "Deleting eTimer.\n"; se_mainGameTimer=NULL; } void eTimer::WriteSync(nMessage &m){ nNetObject::WriteSync(m); m << currentTime; m << speed; //std::cerr << "syncing:" << currentTime << ":" << speed << '\n'; } void eTimer::ReadSync(nMessage &m){ nNetObject::ReadSync(m); //REAL oldTime=currentTime; REAL remote_currentTime; m >> remote_currentTime; // read in the remote time m >> speed; //std::cerr << "Got sync:" << remote_currentTime << ":" << speed << '\n'; // add half our ping (see Einsteins SRT on clock syncronisation) REAL real_remoteTime=remote_currentTime+sn_Connections[m.SenderID()].ping*speed*.5; // and the normal time including ping charity REAL min_remoteTime=remote_currentTime+sn_Connections[m.SenderID()].ping*speed- sn_pingCharityServer*.001; if (real_remoteTimeremote_currentTime+2 || speed==0){ // this is the first sync, or we are badly wrong //con << "Timer emergency: "; median=currentTime=remote_currentTime+.1; } else // divide the corrections for the next few frames correctTimeSmooth+=(remote_currentTime-currentTime-correctTimeSmooth)*.1; //con << "Timer: " << currentTime << '\t'; //con << "remote: " << remote_currentTime << '\n'; } static nNOInitialisator eTimer_init(210,"eTimer"); nDescriptor &eTimer::CreatorDescriptor() const{ return eTimer_init; } REAL eTimer::Time(){ #ifdef WIN32 return median; #else //return median; return currentTime; #endif } void eTimer::SyncTime(){ double newtime=tSysTimeFloat(); REAL timestep=newtime-lastTime; lastTime=newtime; REAL ts=timestep; timestep*=speed; #ifdef DEBUG #ifndef DEDICATED if (timestep > .1f && sn_GetNetState() == nSTANDALONE) timestep = .1f; #endif #endif if (sn_GetNetState() == nSTANDALONE && timestep>1) // do not make to big timesteps timestep=1; currentTime+=timestep; // smooth time syncing with server if (sn_GetNetState()==nCLIENT){ #define TIMESMOOTH 1 currentTime+=ts*TIMESMOOTH*correctTimeSmooth; correctTimeSmooth/=(1+ts*TIMESMOOTH); } else if (sn_GetNetState()==nSERVER && currentTime>nextsync){ #ifdef nSIMULATE_PING RequestSync(); // ack. #else RequestSync(false); // NO ack. #endif //con << "syncing timer..\n"; nextsync=currentTime+1/(speed+.1); } #define fps_middle 20.0 if ( speed > 0 ) { avg_spf/=(1+1/fps_middle); avg_spf+=timestep/fps_middle; } #define middle 20.0 median*=(middle-1)/middle; //median+=(current)/middle + (middle-1)/(middle*avg_spf); median+=(currentTime)/middle + avg_spf*(middle-.8)/middle; #define step (1/18.0) if (median < currentTime-step) median=currentTime-step; else if (median > currentTime+2*step) median=currentTime+2*step; } void eTimer::Reset(REAL t){ if (sn_GetNetState()!=nCLIENT) speed=1; lastTime=startTime=tSysTimeFloat(); median=currentTime=nextsync=t; } void eTimer::pause(bool p){ if (p){ if(speed!=0){ speed=0; if (sn_GetNetState()==nSERVER) RequestSync(); } } else{ if (speed!=1){ speed=1; Reset(currentTime); if (sn_GetNetState()==nSERVER) RequestSync(); // continue from where we started } } } REAL se_GameTime(){ if (se_mainGameTimer) return se_mainGameTimer->Time(); else return 0; } REAL se_GameTimeNoSync(){ if (se_mainGameTimer) return se_mainGameTimer->TimeNoSync(); else return 0; } void se_SyncGameTimer(){ if (se_mainGameTimer) se_mainGameTimer->SyncTime(); } void se_MakeGameTimer(){ tNEW(eTimer); } void se_KillGameTimer(){ if (se_mainGameTimer) delete se_mainGameTimer; se_mainGameTimer=NULL; // to make sure } void se_ResetGameTimer(REAL t){ if (se_mainGameTimer) se_mainGameTimer->Reset(t); } void se_PauseGameTimer(bool p){ if (se_mainGameTimer && sn_GetNetState()!=nCLIENT) se_mainGameTimer->pause(p); } REAL se_AverageFrameTime(){ if (se_mainGameTimer) return se_mainGameTimer->AverageFrameTime(); else return (.2); } REAL se_AverageFPS(){ return 1/se_AverageFrameTime(); } REAL se_PredictTime(){ return se_AverageFrameTime()*.5; }