From a44a8e4cff906212f7ec4aa15a2addbeaa8dfe7a Mon Sep 17 00:00:00 2001 From: cloudwalk Date: Mon, 20 Jul 2020 14:27:33 +0000 Subject: [PATCH] host: Major refactor * Split up Host_Main. Implemented Host_Frame (which is now most of what Host_Main was), and moved timekeeping and sleep code to the new Host_Main. * Moved all server frame code to the new SV_Frame. * Fixed sleep behavior. A dedicated server should sleep again. * General cleanup. New functions to split things up and keep things readable. git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@12844 d7cf8633-e32d-0410-b094-e92efae38249 --- cl_main.c | 39 ++++--- cmd.h | 2 +- host.c | 336 +++++++++++++++++------------------------------------ quakedef.h | 2 + sv_main.c | 148 +++++++++++++++++++++++ zone.h | 6 +- 6 files changed, 281 insertions(+), 252 deletions(-) diff --git a/cl_main.c b/cl_main.c index 0e73b66d..ef670d16 100644 --- a/cl_main.c +++ b/cl_main.c @@ -2716,21 +2716,27 @@ extern cvar_t host_speeds; double CL_Frame (double time) { static double clframetime; + static double cl_timer = 0; static double time1 = 0, time2 = 0, time3 = 0; - static double wait; + static double wait; int pass1, pass2, pass3; - // limit the frametime steps to no more than 100ms each - if (time > 0.1) - time = 0.1; + CL_VM_PreventInformationLeaks(); + + if((cl_timer += time) < 0) + return cl_timer; - // get new key events - Key_EventQueue_Unblock(); - SndSys_SendKeyEvents(); - Sys_SendKeyEvents(); + // limit the frametime steps to no more than 100ms each + if (cl_timer > 0.1) + cl_timer = 0.1; - if (cls.state != ca_dedicated && (time > 0 || cls.timedemo || ((vid_activewindow ? cl_maxfps : cl_maxidlefps).value < 1))) + if (cls.state != ca_dedicated && (cl_timer > 0 || cls.timedemo || ((vid_activewindow ? cl_maxfps : cl_maxidlefps).value < 1))) { + // get new key events + Key_EventQueue_Unblock(); + SndSys_SendKeyEvents(); + Sys_SendKeyEvents(); + R_TimeReport("---"); Collision_Cache_NewFrame(); R_TimeReport("photoncache"); @@ -2753,16 +2759,16 @@ double CL_Frame (double time) if (vid_activewindow && cl_maxfps.value >= 1 && !cls.timedemo) #endif { - clframetime = cl.realframetime = max(time, 1.0 / cl_maxfps.value); + clframetime = cl.realframetime = max(cl_timer, 1.0 / cl_maxfps.value); // when running slow, we need to sleep to keep input responsive wait = bound(0, cl_maxfps_alwayssleep.value * 1000, 100000); if (wait > 0) Sys_Sleep((int)wait); } else if (!vid_activewindow && cl_maxidlefps.value >= 1 && !cls.timedemo) - clframetime = cl.realframetime = max(time, 1.0 / cl_maxidlefps.value); + clframetime = cl.realframetime = max(cl_timer, 1.0 / cl_maxidlefps.value); else - clframetime = cl.realframetime = time; + clframetime = cl.realframetime = cl_timer; // apply slowmo scaling clframetime *= cl.movevars_timescale; @@ -2785,10 +2791,10 @@ double CL_Frame (double time) } if (cls.timedemo) - clframetime = cl.realframetime = time; + clframetime = cl.realframetime = cl_timer; // deduct the frame time from the accumulator - time -= cl.realframetime; + cl_timer -= cl.realframetime; cl.oldtime = cl.time; cl.time += clframetime; @@ -2853,11 +2859,8 @@ double CL_Frame (double time) pass1+pass2+pass3, pass1, pass2, pass3); } } - // if there is some time remaining from this frame, reset the timer - if (time >= 0) - time = 0; - return time; + return cl_timer >= 0 ? 0 : cl_timer; } /* diff --git a/cmd.h b/cmd.h index 9016c27c..2fe4a956 100644 --- a/cmd.h +++ b/cmd.h @@ -168,7 +168,7 @@ void Cbuf_InsertText (cmd_state_t *cmd, const char *text); * \note Do not call inside a command function! */ void Cbuf_Execute (cmd_state_t *cmd); -/*! Performs deferred commands and runs Cbuf_Execute, called by Host_Main */ +/*! Performs deferred commands and runs Cbuf_Execute, called by Host_Frame */ void Cbuf_Frame (cmd_state_t *cmd); //=========================================================================== diff --git a/host.c b/host.c index aedb1f7f..67434773 100644 --- a/host.c +++ b/host.c @@ -373,270 +373,142 @@ Runs all active servers ================== */ static void Host_Init(void); -void Host_Main(void) +double Host_Frame(double time) { - double cl_timer = 0, sv_timer = 0; - double time, oldtime, newtime; - double wait; - int i; - char vabuf[1024]; - qboolean playing; + double cl_timer = 0; + double sv_timer = 0; + static double wait; - host.restless = false; + if (host_framerate.value < 0.00001 && host_framerate.value != 0) + Cvar_SetValueQuick(&host_framerate, 0); - Host_Init(); + TaskQueue_Frame(false); - host.realtime = 0; - host.sleeptime = 0; - host.dirtytime = oldtime = Sys_DirtyTime(); + // keep the random time dependent, but not when playing demos/benchmarking + if(!*sv_random_seed.string && !cls.demoplayback) + rand(); - while(host.state != host_shutdown) - { - if (setjmp(host.abortframe)) - { - SCR_ClearLoadingScreen(false); - continue; // something bad happened, or the server disconnected - } + NetConn_UpdateSockets(); - newtime = host.dirtytime = Sys_DirtyTime(); - time = newtime - oldtime; - if (time < 0) - { - // warn if it's significant - if (time < -0.01) - Con_Printf(CON_WARN "Host_Mingled: time stepped backwards (went from %f to %f, difference %f)\n", oldtime, newtime, time); - time = 0; - } - else if (time >= 1800) - { - Con_Printf(CON_WARN "Host_Mingled: time stepped forward (went from %f to %f, difference %f)\n", oldtime, newtime, time); - time = 0; - } - host.realtime += time; + Log_DestBuffer_Flush(); - if (host_framerate.value < 0.00001 && host_framerate.value != 0) - Cvar_SetValueQuick(&host_framerate, 0); + Curl_Run(); - TaskQueue_Frame(false); + // check for commands typed to the host + Host_GetConsoleCommands(); - // keep the random time dependent, but not when playing demos/benchmarking - if(!*sv_random_seed.string && !cls.demoplayback) - rand(); + // process console commands +// R_TimeReport("preconsole"); + Cbuf_Frame(&cmd_client); + Cbuf_Frame(&cmd_server); - NetConn_UpdateSockets(); + if(sv.active) + Cbuf_Frame(&cmd_serverfromclient); - Log_DestBuffer_Flush(); +// R_TimeReport("console"); - Curl_Run(); + //Con_Printf("%6.0f %6.0f\n", cl_timer * 1000000.0, sv_timer * 1000000.0); - // check for commands typed to the host - Host_GetConsoleCommands(); + R_TimeReport("---"); - // process console commands -// R_TimeReport("preconsole"); - CL_VM_PreventInformationLeaks(); - Cbuf_Frame(&cmd_client); - Cbuf_Frame(&cmd_server); + sv_timer = SV_Frame(time); + cl_timer = CL_Frame(time); - if(sv.active) - Cbuf_Frame(&cmd_serverfromclient); + Mem_CheckSentinelsGlobal(); -// R_TimeReport("console"); + // if the accumulators haven't become positive yet, wait a while + if (cls.state == ca_dedicated) + wait = sv_timer * -1000000.0; // dedicated + else if (!sv.active || svs.threaded) + wait = cl_timer * -1000000.0; // connected to server, main menu, or server is on different thread + else + wait = max(cl_timer, sv_timer) * -1000000.0; // listen server or singleplayer - //Con_Printf("%6.0f %6.0f\n", cl_timer * 1000000.0, sv_timer * 1000000.0); + if (!host.restless && wait >= 1) + return wait; + else + return 0; +} - R_TimeReport("---"); +static inline void Host_Sleep(double time) +{ + double time0, delta; - //------------------- - // - // server operations - // - //------------------- + if(host_maxwait.value <= 0) + time = min(time, 1000000.0); + else + time = min(time, host_maxwait.value * 1000.0); + if(time < 1) + time = 1; // because we cast to int + + time0 = Sys_DirtyTime(); + if (sv_checkforpacketsduringsleep.integer && !sys_usenoclockbutbenchmark.integer && !svs.threaded) { + NetConn_SleepMicroseconds((int)time); + if (cls.state != ca_dedicated) + NetConn_ClientFrame(); // helps server browser get good ping values + // TODO can we do the same for ServerFrame? Probably not. + } + else + Sys_Sleep((int)time); + delta = Sys_DirtyTime() - time0; + if (delta < 0 || delta >= 1800) + delta = 0; + host.sleeptime += delta; +// R_TimeReport("sleep"); + return; +} - // limit the frametime steps to no more than 100ms each - if (sv_timer > 0.1) - { - if (!svs.threaded) - svs.perf_acc_lost += (sv_timer - 0.1); - sv_timer = 0.1; - } +// Cloudwalk: Most overpowered function declaration... +static inline double Host_UpdateTime (double newtime, double oldtime) +{ + double time = newtime - oldtime; - if (!svs.threaded) - { - svs.perf_acc_sleeptime = host.sleeptime; - svs.perf_acc_realtime += time; - - // Look for clients who have spawned - playing = false; - for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++) - if(host_client->begun) - if(host_client->netconnection) - playing = true; - if(sv.time < 10) - { - // don't accumulate time for the first 10 seconds of a match - // so things can settle - svs.perf_acc_realtime = svs.perf_acc_sleeptime = svs.perf_acc_lost = svs.perf_acc_offset = svs.perf_acc_offset_squared = svs.perf_acc_offset_max = svs.perf_acc_offset_samples = host.sleeptime = 0; - } - else if(svs.perf_acc_realtime > 5) - { - svs.perf_cpuload = 1 - svs.perf_acc_sleeptime / svs.perf_acc_realtime; - svs.perf_lost = svs.perf_acc_lost / svs.perf_acc_realtime; - if(svs.perf_acc_offset_samples > 0) - { - svs.perf_offset_max = svs.perf_acc_offset_max; - svs.perf_offset_avg = svs.perf_acc_offset / svs.perf_acc_offset_samples; - svs.perf_offset_sdev = sqrt(svs.perf_acc_offset_squared / svs.perf_acc_offset_samples - svs.perf_offset_avg * svs.perf_offset_avg); - } - if(svs.perf_lost > 0 && developer_extra.integer) - if(playing) // only complain if anyone is looking - Con_DPrintf("Server can't keep up: %s\n", Host_TimingReport(vabuf, sizeof(vabuf))); - svs.perf_acc_realtime = svs.perf_acc_sleeptime = svs.perf_acc_lost = svs.perf_acc_offset = svs.perf_acc_offset_squared = svs.perf_acc_offset_max = svs.perf_acc_offset_samples = host.sleeptime = 0; - } + if (time < 0) + { + // warn if it's significant + if (time < -0.01) + Con_Printf(CON_WARN "Host_GetTime: time stepped backwards (went from %f to %f, difference %f)\n", oldtime, newtime, time); + time = 0; + } + else if (time >= 1800) + { + Con_Printf(CON_WARN "Host_GetTime: time stepped forward (went from %f to %f, difference %f)\n", oldtime, newtime, time); + time = 0; + } - if (sv.active && sv_timer > 0) - { - // execute one or more server frames, with an upper limit on how much - // execution time to spend on server frames to avoid freezing the game if - // the server is overloaded, this execution time limit means the game will - // slow down if the server is taking too long. - int framecount, framelimit = 1; - double advancetime, aborttime = 0; - float offset; - prvm_prog_t *prog = SVVM_prog; - // receive packets on each main loop iteration, as the main loop may - // be undersleeping due to select() detecting a new packet - if (sv.active && !svs.threaded) - NetConn_ServerFrame(); - // run the world state - // don't allow simulation to run too fast or too slow or logic glitches can occur - - // stop running server frames if the wall time reaches this value - if (sys_ticrate.value <= 0) - advancetime = sv_timer; - else - { - advancetime = sys_ticrate.value; - // listen servers can run multiple server frames per client frame - framelimit = cl_maxphysicsframesperserverframe.integer; - aborttime = Sys_DirtyTime() + 0.1; - } - if(host_timescale.value > 0 && host_timescale.value < 1) - advancetime = min(advancetime, 0.1 / host_timescale.value); - else - advancetime = min(advancetime, 0.1); - - if(advancetime > 0) - { - offset = Sys_DirtyTime() - newtime;if (offset < 0 || offset >= 1800) offset = 0; - offset += sv_timer; - ++svs.perf_acc_offset_samples; - svs.perf_acc_offset += offset; - svs.perf_acc_offset_squared += offset * offset; - if(svs.perf_acc_offset_max < offset) - svs.perf_acc_offset_max = offset; - } - - // only advance time if not paused - // the game also pauses in singleplayer when menu or console is used - sv.frametime = advancetime * host_timescale.value; - if (host_framerate.value) - sv.frametime = host_framerate.value; - if (sv.paused || host.paused) - sv.frametime = 0; - - for (framecount = 0;framecount < framelimit && sv_timer > 0;framecount++) - { - sv_timer -= advancetime; - - // move things around and think unless paused - if (sv.frametime) - SV_Physics(); - - // if this server frame took too long, break out of the loop - if (framelimit > 1 && Sys_DirtyTime() >= aborttime) - break; - } - R_TimeReport("serverphysics"); - - // send all messages to the clients - SV_SendClientMessages(); - - if (sv.paused == 1 && host.realtime > sv.pausedstart && sv.pausedstart > 0) { - prog->globals.fp[OFS_PARM0] = host.realtime - sv.pausedstart; - PRVM_serverglobalfloat(time) = sv.time; - prog->ExecuteProgram(prog, PRVM_serverfunction(SV_PausedTic), "QC function SV_PausedTic is missing"); - } - - // send an heartbeat if enough time has passed since the last one - NetConn_Heartbeat(0); - R_TimeReport("servernetwork"); - } - else - { - // don't let r_speeds display jump around - R_TimeReport("serverphysics"); - R_TimeReport("servernetwork"); - } - } - // if there is some time remaining from this frame, reset the timer - if (sv_timer >= 0) - { - if (!svs.threaded) - svs.perf_acc_lost += sv_timer; - sv_timer = 0; - } + return time; +} - sv_timer += time; +void Host_Main(void) +{ + double time, newtime, oldtime, sleeptime; - //------------------- - // - // client operations - // - //------------------- + Host_Init(); // Start! - cl_timer = CL_Frame(cl_timer); - cl_timer += time; + host.realtime = 0; + oldtime = Sys_DirtyTime(); -#if MEMPARANOIA - Mem_CheckSentinelsGlobal(); -#else - if (developer_memorydebug.integer) - Mem_CheckSentinelsGlobal(); -#endif + while(host.state != host_shutdown) + { + if (setjmp(host.abortframe)) + { + host.state = host_active; // In case we were loading + continue; // something bad happened, or the server disconnected + } - // if the accumulators haven't become positive yet, wait a while - wait = max(cl_timer, sv_timer) * -1000000.0; + newtime = host.dirtytime = Sys_DirtyTime(); + host.realtime += time = Host_UpdateTime(newtime, oldtime); - if (!host.restless && wait >= 1) - { - double time0, delta; + sleeptime = Host_Frame(time); + oldtime = newtime; - if(host_maxwait.value <= 0) - wait = min(wait, 1000000.0); - else - wait = min(wait, host_maxwait.value * 1000.0); - if(wait < 1) - wait = 1; // because we cast to int - - time0 = Sys_DirtyTime(); - if (sv_checkforpacketsduringsleep.integer && !sys_usenoclockbutbenchmark.integer && !svs.threaded) { - NetConn_SleepMicroseconds((int)wait); - if (cls.state != ca_dedicated) - NetConn_ClientFrame(); // helps server browser get good ping values - // TODO can we do the same for ServerFrame? Probably not. - } - else - Sys_Sleep((int)wait); - delta = Sys_DirtyTime() - time0; - if (delta < 0 || delta >= 1800) - delta = 0; - host.sleeptime += delta; -// R_TimeReport("sleep"); + if (sleeptime) + { + Host_Sleep(sleeptime); + continue; } host.framecount++; - oldtime = newtime; } Sys_Quit(0); diff --git a/quakedef.h b/quakedef.h index a40a3ba6..1596f94e 100644 --- a/quakedef.h +++ b/quakedef.h @@ -543,11 +543,13 @@ extern host_t host; void Host_InitCommands(void); void Host_Main(void); +double Host_Frame(double time); void Host_Shutdown(void); void Host_StartVideo(void); void Host_Error(const char *error, ...) DP_FUNC_PRINTF(1) DP_FUNC_NORETURN; void Host_Quit_f(cmd_state_t *cmd); void SV_ClientCommands(const char *fmt, ...) DP_FUNC_PRINTF(1); +double SV_Frame(double time); void SV_Shutdown(void); void CL_Reconnect_f(cmd_state_t *cmd); void Host_NoOperation_f(cmd_state_t *cmd); diff --git a/sv_main.c b/sv_main.c index 23440b4d..1144daf6 100644 --- a/sv_main.c +++ b/sv_main.c @@ -4103,6 +4103,154 @@ static void SV_VM_Setup(void) extern cvar_t host_maxwait; extern cvar_t host_framerate; +extern cvar_t cl_maxphysicsframesperserverframe; +double SV_Frame(double time) +{ + static double sv_timer; + int i; + qboolean playing; + char vabuf[1024]; + + if((sv_timer += time) < 0) + return sv_timer; + // limit the frametime steps to no more than 100ms each + if (sv_timer > 0.1) + { + if (!svs.threaded) + svs.perf_acc_lost += (sv_timer - 0.1); + sv_timer = 0.1; + } + + if (!svs.threaded) + { + svs.perf_acc_sleeptime = host.sleeptime; + svs.perf_acc_realtime += time; + + // Look for clients who have spawned + playing = false; + for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++) + if(host_client->begun) + if(host_client->netconnection) + playing = true; + if(sv.time < 10) + { + // don't accumulate time for the first 10 seconds of a match + // so things can settle + svs.perf_acc_realtime = svs.perf_acc_sleeptime = svs.perf_acc_lost = svs.perf_acc_offset = svs.perf_acc_offset_squared = svs.perf_acc_offset_max = svs.perf_acc_offset_samples = host.sleeptime = 0; + } + else if(svs.perf_acc_realtime > 5) + { + svs.perf_cpuload = 1 - svs.perf_acc_sleeptime / svs.perf_acc_realtime; + svs.perf_lost = svs.perf_acc_lost / svs.perf_acc_realtime; + if(svs.perf_acc_offset_samples > 0) + { + svs.perf_offset_max = svs.perf_acc_offset_max; + svs.perf_offset_avg = svs.perf_acc_offset / svs.perf_acc_offset_samples; + svs.perf_offset_sdev = sqrt(svs.perf_acc_offset_squared / svs.perf_acc_offset_samples - svs.perf_offset_avg * svs.perf_offset_avg); + } + if(svs.perf_lost > 0 && developer_extra.integer) + if(playing) // only complain if anyone is looking + Con_DPrintf("Server can't keep up: %s\n", Host_TimingReport(vabuf, sizeof(vabuf))); + svs.perf_acc_realtime = svs.perf_acc_sleeptime = svs.perf_acc_lost = svs.perf_acc_offset = svs.perf_acc_offset_squared = svs.perf_acc_offset_max = svs.perf_acc_offset_samples = host.sleeptime = 0; + } + + if (sv.active && sv_timer > 0) + { + // execute one or more server frames, with an upper limit on how much + // execution time to spend on server frames to avoid freezing the game if + // the server is overloaded, this execution time limit means the game will + // slow down if the server is taking too long. + int framecount, framelimit = 1; + double advancetime, aborttime = 0; + float offset; + prvm_prog_t *prog = SVVM_prog; + // receive packets on each main loop iteration, as the main loop may + // be undersleeping due to select() detecting a new packet + if (sv.active && !svs.threaded) + NetConn_ServerFrame(); + // run the world state + // don't allow simulation to run too fast or too slow or logic glitches can occur + + // stop running server frames if the wall time reaches this value + if (sys_ticrate.value <= 0) + advancetime = sv_timer; + else + { + advancetime = sys_ticrate.value; + // listen servers can run multiple server frames per client frame + framelimit = cl_maxphysicsframesperserverframe.integer; + aborttime = Sys_DirtyTime() + 0.1; + } + if(host_timescale.value > 0 && host_timescale.value < 1) + advancetime = min(advancetime, 0.1 / host_timescale.value); + else + advancetime = min(advancetime, 0.1); + + if(advancetime > 0) + { + offset = Sys_DirtyTime() - host.dirtytime; + if (offset < 0 || offset >= 1800) + offset = 0; + offset += sv_timer; + ++svs.perf_acc_offset_samples; + svs.perf_acc_offset += offset; + svs.perf_acc_offset_squared += offset * offset; + if(svs.perf_acc_offset_max < offset) + svs.perf_acc_offset_max = offset; + } + + // only advance time if not paused + // the game also pauses in singleplayer when menu or console is used + sv.frametime = advancetime * host_timescale.value; + if (host_framerate.value) + sv.frametime = host_framerate.value; + if (sv.paused || host.paused) + sv.frametime = 0; + + for (framecount = 0;framecount < framelimit && sv_timer > 0;framecount++) + { + sv_timer -= advancetime; + + // move things around and think unless paused + if (sv.frametime) + SV_Physics(); + + // if this server frame took too long, break out of the loop + if (framelimit > 1 && Sys_DirtyTime() >= aborttime) + break; + } + R_TimeReport("serverphysics"); + + // send all messages to the clients + SV_SendClientMessages(); + + if (sv.paused == 1 && host.realtime > sv.pausedstart && sv.pausedstart > 0) { + prog->globals.fp[OFS_PARM0] = host.realtime - sv.pausedstart; + PRVM_serverglobalfloat(time) = sv.time; + prog->ExecuteProgram(prog, PRVM_serverfunction(SV_PausedTic), "QC function SV_PausedTic is missing"); + } + + // send an heartbeat if enough time has passed since the last one + NetConn_Heartbeat(0); + R_TimeReport("servernetwork"); + } + else + { + // don't let r_speeds display jump around + R_TimeReport("serverphysics"); + R_TimeReport("servernetwork"); + } + } + // if there is some time remaining from this frame, reset the timer + if (sv_timer >= 0) + { + if (!svs.threaded) + svs.perf_acc_lost += sv_timer; + sv_timer = 0; + } + return sv_timer; +} + static int SV_ThreadFunc(void *voiddata) { prvm_prog_t *prog = SVVM_prog; diff --git a/zone.h b/zone.h index a157baca..5112acf0 100644 --- a/zone.h +++ b/zone.h @@ -83,7 +83,11 @@ mempool_t; #define Mem_Realloc(pool,data,size) _Mem_Alloc(pool, data, size, 16, __FILE__, __LINE__) #define Mem_Free(mem) _Mem_Free(mem, __FILE__, __LINE__) #define Mem_CheckSentinels(data) _Mem_CheckSentinels(data, __FILE__, __LINE__) -#define Mem_CheckSentinelsGlobal() _Mem_CheckSentinelsGlobal(__FILE__, __LINE__) +#if MEMPARANOIA +#define Mem_CheckSentinelsGlobal() _Mem_CheckSentinelsGlobal(__FILE__, __LINE__) +#else +#define Mem_CheckSentinelsGlobal() if(developer_memorydebug.integer) { _Mem_CheckSentinelsGlobal(__FILE__, __LINE__); } +#endif #define Mem_AllocPool(name, flags, parent) _Mem_AllocPool(name, flags, parent, __FILE__, __LINE__) #define Mem_FreePool(pool) _Mem_FreePool(pool, __FILE__, __LINE__) #define Mem_EmptyPool(pool) _Mem_EmptyPool(pool, __FILE__, __LINE__) -- 2.39.2