]> git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - sv_main.c
net_slist: reduce worst-case time until any given server first appears
[xonotic/darkplaces.git] / sv_main.c
index 1c3c6f816b3a91b0201e30b77f4f17638786fddc..f656085c67ca1d8231ad957cd66caef0f6f8e48a 100644 (file)
--- a/sv_main.c
+++ b/sv_main.c
@@ -72,6 +72,7 @@ cvar_t sv_allowdownloads_archive = {CF_SERVER, "sv_allowdownloads_archive", "0",
 cvar_t sv_allowdownloads_config = {CF_SERVER, "sv_allowdownloads_config", "0", "whether to allow downloads of config files (cfg)"};
 cvar_t sv_allowdownloads_dlcache = {CF_SERVER, "sv_allowdownloads_dlcache", "0", "whether to allow downloads of dlcache files (dlcache/)"};
 cvar_t sv_allowdownloads_inarchive = {CF_SERVER, "sv_allowdownloads_inarchive", "0", "whether to allow downloads from archives (pak/pk3)"};
+cvar_t sv_areagrid_link_SOLID_NOT = {CF_SERVER | CF_NOTIFY, "sv_areagrid_link_SOLID_NOT", "1", "set to 0 to prevent SOLID_NOT entities from being linked to the area grid, and unlink any that are already linked (in the code paths that would otherwise link them), for better performance"};
 cvar_t sv_areagrid_mingridsize = {CF_SERVER | CF_NOTIFY, "sv_areagrid_mingridsize", "128", "minimum areagrid cell size, smaller values work better for lots of small objects, higher values for large objects"};
 cvar_t sv_checkforpacketsduringsleep = {CF_SERVER, "sv_checkforpacketsduringsleep", "0", "uses select() function to wait between frames which can be interrupted by packets being received, instead of Sleep()/usleep()/SDL_Sleep() functions which do not check for packets"};
 cvar_t sv_clmovement_enable = {CF_SERVER, "sv_clmovement_enable", "1", "whether to allow clients to use cl_movement prediction, which can cause choppy movement on the server which may annoy other players"};
@@ -112,7 +113,7 @@ cvar_t sv_gameplayfix_grenadebouncedownslopes = {CF_SERVER, "sv_gameplayfix_gren
 cvar_t sv_gameplayfix_multiplethinksperframe = {CF_SERVER, "sv_gameplayfix_multiplethinksperframe", "1", "allows entities to think more often than the server framerate, primarily useful for very high fire rate weapons"};
 cvar_t sv_gameplayfix_noairborncorpse = {CF_SERVER, "sv_gameplayfix_noairborncorpse", "1", "causes entities (corpses, items, etc) sitting ontop of moving entities (players) to fall when the moving entity (player) is no longer supporting them"};
 cvar_t sv_gameplayfix_noairborncorpse_allowsuspendeditems = {CF_SERVER, "sv_gameplayfix_noairborncorpse_allowsuspendeditems", "1", "causes entities sitting ontop of objects that are instantaneously remove to float in midair (special hack to allow a common level design trick for floating items)"};
-cvar_t sv_gameplayfix_nudgeoutofsolid = {CF_SERVER, "sv_gameplayfix_nudgeoutofsolid", "0", "attempts to fix physics errors (where an object ended up in solid for some reason)"};
+cvar_t sv_gameplayfix_nudgeoutofsolid = {CF_SERVER, "sv_gameplayfix_nudgeoutofsolid", "1", "attempts to fix physics errors where an object ended up in solid for some reason, supersedes sv_gameplayfix_unstickentities"};
 cvar_t sv_gameplayfix_nudgeoutofsolid_separation = {CF_SERVER, "sv_gameplayfix_nudgeoutofsolid_separation", "0.03125", "keep objects this distance apart to prevent collision issues on seams"};
 cvar_t sv_gameplayfix_q2airaccelerate = {CF_SERVER, "sv_gameplayfix_q2airaccelerate", "0", "Quake2-style air acceleration"};
 cvar_t sv_gameplayfix_nogravityonground = {CF_SERVER, "sv_gameplayfix_nogravityonground", "0", "turn off gravity when on ground (to get rid of sliding)"};
@@ -125,15 +126,15 @@ cvar_t sv_gameplayfix_swiminbmodels = {CF_SERVER, "sv_gameplayfix_swiminbmodels"
 cvar_t sv_gameplayfix_upwardvelocityclearsongroundflag = {CF_SERVER, "sv_gameplayfix_upwardvelocityclearsongroundflag", "1", "prevents monsters, items, and most other objects from being stuck to the floor when pushed around by damage, and other situations in mods"};
 cvar_t sv_gameplayfix_downtracesupportsongroundflag = {CF_SERVER, "sv_gameplayfix_downtracesupportsongroundflag", "1", "prevents very short moves from clearing onground (which may make the player stick to the floor at high netfps), fixes groundentity not being set when walking onto a mover with sv_gameplayfix_nogravityonground"};
 cvar_t sv_gameplayfix_q1bsptracelinereportstexture = {CF_SERVER, "sv_gameplayfix_q1bsptracelinereportstexture", "1", "enables mods to get accurate trace_texture results on q1bsp by using a surface-hitting traceline implementation rather than the standard solidbsp method, q3bsp always reports texture accurately"};
-cvar_t sv_gameplayfix_unstickplayers = {CF_SERVER, "sv_gameplayfix_unstickplayers", "1", "big hack to try and fix the rare case of MOVETYPE_WALK entities getting stuck in the world clipping hull."};
-cvar_t sv_gameplayfix_unstickentities = {CF_SERVER, "sv_gameplayfix_unstickentities", "1", "hack to check if entities are crossing world collision hull and try to move them to the right position"};
+cvar_t sv_gameplayfix_unstickplayers = {CF_SERVER, "sv_gameplayfix_unstickplayers", "0", "big hack to try and fix the rare case of MOVETYPE_WALK entities getting stuck in the world clipping hull."};
+cvar_t sv_gameplayfix_unstickentities = {CF_SERVER, "sv_gameplayfix_unstickentities", "1", "hack to check if entities are crossing world collision hull and try to move them to the right position, superseded by sv_gameplayfix_nudgeoutofsolid"};
 cvar_t sv_gameplayfix_fixedcheckwatertransition = {CF_SERVER, "sv_gameplayfix_fixedcheckwatertransition", "1", "fix two very stupid bugs in SV_CheckWaterTransition when watertype is CONTENTS_EMPTY (the bugs causes waterlevel to be 1 on first frame, -1 on second frame - the fix makes it 0 on both frames)"};
-cvar_t sv_gameplayfix_customstats = {CF_SERVER, "sv_gameplayfix_customstats", "0", "Disable stats higher than 220, for use by certain games such as Xonotic"};
 cvar_t sv_gravity = {CF_SERVER | CF_NOTIFY, "sv_gravity","800", "how fast you fall (512 = roughly earth gravity)"};
 cvar_t sv_init_frame_count = {CF_SERVER, "sv_init_frame_count", "2", "number of frames to run to allow everything to settle before letting clients connect"};
 cvar_t sv_idealpitchscale = {CF_SERVER, "sv_idealpitchscale","0.8", "how much to look up/down slopes and stairs when not using freelook"};
 cvar_t sv_jumpstep = {CF_SERVER | CF_NOTIFY, "sv_jumpstep", "0", "whether you can step up while jumping"};
 cvar_t sv_jumpvelocity = {CF_SERVER, "sv_jumpvelocity", "270", "cvar that can be used by QuakeC code for jump velocity"};
+cvar_t sv_legacy_bbox_expand = {CF_SERVER, "sv_legacy_bbox_expand", "1", "before linking an entity to the area grid, decrease its mins and increase its maxs by '1 1 1', or '15 15 1' if it has flag FL_ITEM (this is the Quake/QuakeWorld behaviour); disable to make SVQC bboxes consistent with CSQC which never does this expansion"};
 cvar_t sv_maxairspeed = {CF_SERVER, "sv_maxairspeed", "30", "maximum speed a player can accelerate to when airborn (note that it is possible to completely stop by moving the opposite direction)"};
 cvar_t sv_maxrate = {CF_SERVER | CF_ARCHIVE | CF_NOTIFY, "sv_maxrate", "1000000", "upper limit on client rate cvar, should reflect your network connection quality"};
 cvar_t sv_maxspeed = {CF_SERVER | CF_NOTIFY, "sv_maxspeed", "320", "maximum speed a player can accelerate to when on ground (can be exceeded by tricks)"};
@@ -142,6 +143,7 @@ cvar_t sv_nostep = {CF_SERVER | CF_NOTIFY, "sv_nostep","0", "prevents MOVETYPE_S
 cvar_t sv_playerphysicsqc = {CF_SERVER | CF_NOTIFY, "sv_playerphysicsqc", "1", "enables QuakeC function to override player physics"};
 cvar_t sv_progs = {CF_SERVER, "sv_progs", "progs.dat", "selects which quakec progs.dat file to run" };
 cvar_t sv_protocolname = {CF_SERVER, "sv_protocolname", "DP7", "selects network protocol to host for (values include QUAKE, QUAKEDP, NEHAHRAMOVIE, DP1 and up)"};
+cvar_t sv_qcstats = {CF_SERVER, "sv_qcstats", "0", "Disables engine sending of stats 220 and above, for use by certain games such as Xonotic, NOTE: it's strongly recommended that SVQC send correct STAT_MOVEVARS_TICRATE and STAT_MOVEVARS_TIMESCALE"};
 cvar_t sv_random_seed = {CF_SERVER, "sv_random_seed", "", "random seed; when set, on every map start this random seed is used to initialize the random number generator. Don't touch it unless for benchmarking or debugging"};
 cvar_t host_limitlocal = {CF_SERVER, "host_limitlocal", "0", "whether to apply rate limiting to the local player in a listen server (only useful for testing)"};
 cvar_t sv_sound_land = {CF_SERVER, "sv_sound_land", "demon/dland2.wav", "sound to play when MOVETYPE_STEP entity hits the ground at high speed (empty cvar disables the sound)"};
@@ -158,10 +160,15 @@ cvar_t sv_warsowbunny_turnaccel = {CF_SERVER, "sv_warsowbunny_turnaccel", "0", "
 cvar_t sv_warsowbunny_backtosideratio = {CF_SERVER, "sv_warsowbunny_backtosideratio", "0.8", "lower values make it easier to change direction without losing speed; the drawback is \"understeering\" in sharp turns"};
 cvar_t sv_onlycsqcnetworking = {CF_SERVER, "sv_onlycsqcnetworking", "0", "disables legacy entity networking code for higher performance (except on clients, which can still be legacy)"};
 cvar_t sv_areadebug = {CF_SERVER, "sv_areadebug", "0", "disables physics culling for debugging purposes (only for development)"};
+
 cvar_t sys_ticrate = {CF_SERVER | CF_ARCHIVE, "sys_ticrate","0.0138889", "how long a server frame is in seconds, 0.05 is 20fps server rate, 0.1 is 10fps (can not be set higher than 0.1), 0 runs as many server frames as possible (makes games against bots a little smoother, overwhelms network players), 0.0138889 matches QuakeWorld physics"};
+cvar_t sv_maxphysicsframesperserverframe = {CF_SERVER, "sv_maxphysicsframesperserverframe","10", "maximum number of physics frames per server frame"};
+cvar_t sv_lagreporting_always = {CF_SERVER, "sv_lagreporting_always", "0", "report lag even in singleplayer, listen, or an empty dedicated server"};
+cvar_t sv_lagreporting_strict = {CF_SERVER, "sv_lagreporting_strict", "0", "log any extra frames run to catch up after a holdup (only applies when sv_maxphysicsframesperserverframe > 1)"};
+cvar_t sv_threaded = {CF_SERVER, "sv_threaded", "0", "enables a separate thread for server code, improving performance, especially when hosting a game while playing, EXPERIMENTAL, may be crashy"};
+
 cvar_t teamplay = {CF_SERVER | CF_NOTIFY, "teamplay","0", "teamplay mode, values depend on mod but typically 0 = no teams, 1 = no team damage no self damage, 2 = team damage and self damage, some mods support 3 = no team damage but can damage self"};
 cvar_t timelimit = {CF_SERVER | CF_NOTIFY, "timelimit","0", "ends level at this time (in minutes)"};
-cvar_t sv_threaded = {CF_SERVER, "sv_threaded", "0", "enables a separate thread for server code, improving performance, especially when hosting a game while playing, EXPERIMENTAL, may be crashy"};
 
 cvar_t sv_rollspeed = {CF_CLIENT, "sv_rollspeed", "200", "how much strafing is necessary to tilt the view"};
 cvar_t sv_rollangle = {CF_CLIENT, "sv_rollangle", "2.0", "how much to tilt the view when strafing"};
@@ -450,7 +457,6 @@ static void SV_ServerOptions (void)
        i = Sys_CheckParm ("-dedicated");
        if (i || !cl_available)
        {
-               cls.state = ca_dedicated;
                // check for -dedicated specifying how many players
                if (i && i + 1 < sys.argc && atoi (sys.argv[i+1]) >= 1)
                        svs.maxclients = atoi (sys.argv[i+1]);
@@ -550,6 +556,7 @@ void SV_Init (void)
        Cvar_RegisterVariable (&sv_allowdownloads_config);
        Cvar_RegisterVariable (&sv_allowdownloads_dlcache);
        Cvar_RegisterVariable (&sv_allowdownloads_inarchive);
+       Cvar_RegisterVariable (&sv_areagrid_link_SOLID_NOT);
        Cvar_RegisterVariable (&sv_areagrid_mingridsize);
        Cvar_RegisterVariable (&sv_checkforpacketsduringsleep);
        Cvar_RegisterVariable (&sv_clmovement_enable);
@@ -606,12 +613,13 @@ void SV_Init (void)
        Cvar_RegisterVariable (&sv_gameplayfix_unstickplayers);
        Cvar_RegisterVariable (&sv_gameplayfix_unstickentities);
        Cvar_RegisterVariable (&sv_gameplayfix_fixedcheckwatertransition);
-       Cvar_RegisterVariable (&sv_gameplayfix_customstats);
+       Cvar_RegisterVariable (&sv_qcstats);
        Cvar_RegisterVariable (&sv_gravity);
        Cvar_RegisterVariable (&sv_init_frame_count);
        Cvar_RegisterVariable (&sv_idealpitchscale);
        Cvar_RegisterVariable (&sv_jumpstep);
        Cvar_RegisterVariable (&sv_jumpvelocity);
+       Cvar_RegisterVariable (&sv_legacy_bbox_expand);
        Cvar_RegisterVariable (&sv_maxairspeed);
        Cvar_RegisterVariable (&sv_maxrate);
        Cvar_RegisterVariable (&sv_maxspeed);
@@ -637,10 +645,15 @@ void SV_Init (void)
        Cvar_RegisterVariable (&sv_warsowbunny_backtosideratio);
        Cvar_RegisterVariable (&sv_onlycsqcnetworking);
        Cvar_RegisterVariable (&sv_areadebug);
+
        Cvar_RegisterVariable (&sys_ticrate);
+       Cvar_RegisterVariable (&sv_maxphysicsframesperserverframe);
+       Cvar_RegisterVariable (&sv_lagreporting_always);
+       Cvar_RegisterVariable (&sv_lagreporting_strict);
+       Cvar_RegisterVariable (&sv_threaded);
+
        Cvar_RegisterVariable (&teamplay);
        Cvar_RegisterVariable (&timelimit);
-       Cvar_RegisterVariable (&sv_threaded);
 
        Cvar_RegisterVariable (&sv_rollangle);
        Cvar_RegisterVariable (&sv_rollspeed);
@@ -783,7 +796,7 @@ void SV_SendServerinfo (client_t *client)
 
        SZ_Clear (&client->netconnection->message);
        MSG_WriteByte (&client->netconnection->message, svc_print);
-       dpsnprintf (message, sizeof (message), "\nServer: %s build %s (progs %i crc)\n", gamename, buildstring, prog->filecrc);
+       dpsnprintf (message, sizeof (message), "\nServer: %s (progs %i crc)\n", engineversion, prog->filecrc);
        MSG_WriteString (&client->netconnection->message,message);
 
        SV_StopDemoRecording(client); // to split up demos into different files
@@ -1886,6 +1899,10 @@ void SV_SpawnServer (const char *map)
 // set up the new server
 //
        memset (&sv, 0, sizeof(sv));
+
+       // tell SV_Frame() to reset its timers
+       sv.spawnframe = host.framecount;
+
        // if running a local client, make sure it doesn't try to access the last
        // level's data which is no longer valiud
        cls.signon = 0;
@@ -2153,7 +2170,7 @@ static void SVVM_end_increase_edicts(prvm_prog_t *prog)
 
        // link every entity except world
        for (i = 1, ent = prog->edicts;i < prog->num_edicts;i++, ent++)
-               if (!ent->free && !VectorCompare(PRVM_serveredictvector(ent, absmin), PRVM_serveredictvector(ent, absmax)))
+               if (!ent->free)
                        SV_LinkEdict(ent);
 }
 
@@ -2499,55 +2516,61 @@ Returns a time report string, for example for
 */
 const char *SV_TimingReport(char *buf, size_t buflen)
 {
-       return va(buf, buflen, "%.1f%% CPU, %.2f%% lost, offset avg %.1fms, max %.1fms, sdev %.1fms", svs.perf_cpuload * 100, svs.perf_lost * 100, svs.perf_offset_avg * 1000, svs.perf_offset_max * 1000, svs.perf_offset_sdev * 1000);
+       return va(buf, buflen, "%.1f%% CPU, %.2f%% lost, offset avg %.1fms, max %.1fms, sdev %.1fms", sv.perf_cpuload * 100, sv.perf_lost * 100, sv.perf_offset_avg * 1000, sv.perf_offset_max * 1000, sv.perf_offset_sdev * 1000);
 }
 
-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;
        char vabuf[1024];
-       qbool playing = false;
+       qbool reporting = false;
+
+       // reset timer after level change
+       if (host.framecount == sv.spawnframe || host.framecount == sv.spawnframe + 1)
+               sv_timer = time = host.sleeptime = 0;
 
        if (!svs.threaded)
        {
-               svs.perf_acc_sleeptime = host.sleeptime;
-               svs.perf_acc_realtime += time;
+               sv.perf_acc_sleeptime += host.sleeptime;
+               sv.perf_acc_realtime += time;
 
-               // Look for clients who have spawned
-               for (i = 0, host_client = svs.clients; i < svs.maxclients; i++, host_client++)
-                       if(host_client->begun && host_client->netconnection)
-                               playing = true;
+               if (sv_lagreporting_always.integer)
+                       reporting = true;
+               else if (cls.state == ca_dedicated)
+               {
+                       // Report lag if there's players, so they know it wasn't the network or their machine
+                       for (i = 0; i < svs.maxclients; ++i)
+                       {
+                               if (svs.clients[i].begun && svs.clients[i].netconnection)
+                               {
+                                       reporting = true;
+                                       break;
+                               }
+                       }
+               }
 
-               if(svs.perf_acc_realtime > 5)
+               if(sv.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;
+                       sv.perf_cpuload = 1 - sv.perf_acc_sleeptime / sv.perf_acc_realtime;
+                       sv.perf_lost = sv.perf_acc_lost / sv.perf_acc_realtime;
 
-                       if(svs.perf_acc_offset_samples > 0)
+                       if(sv.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);
+                               sv.perf_offset_max = sv.perf_acc_offset_max;
+                               sv.perf_offset_avg = sv.perf_acc_offset / sv.perf_acc_offset_samples;
+                               sv.perf_offset_sdev = sv.perf_acc_offset_squared / sv.perf_acc_offset_samples - sv.perf_offset_avg * sv.perf_offset_avg;
+                               sv.perf_offset_sdev = sv.perf_offset_sdev > 0 ? sqrt(sv.perf_offset_sdev) : 0;
                        }
 
-                       if(svs.perf_lost > 0 && developer_extra.integer && playing) // only complain if anyone is looking
-                               Con_DPrintf("Server can't keep up: %s\n", SV_TimingReport(vabuf, sizeof(vabuf)));
-               }
+                       if (sv.perf_lost > 0 && reporting)
+                               SV_BroadcastPrintf("\003" CON_WARN "Server lag report: %s\n", SV_TimingReport(vabuf, sizeof(vabuf)));
 
-               if(svs.perf_acc_realtime > 5 || 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;
+                       sv.perf_acc_realtime = sv.perf_acc_sleeptime =
+                       sv.perf_acc_lost = sv.perf_acc_offset =
+                       sv.perf_acc_offset_squared = sv.perf_acc_offset_max =
+                       sv.perf_acc_offset_samples = 0;
                }
 
                /*
@@ -2573,7 +2596,7 @@ double SV_Frame(double time)
        if (sv_timer > 0.1)
        {
                if (!svs.threaded)
-                       svs.perf_acc_lost += (sv_timer - 0.1);
+                       sv.perf_acc_lost += (sv_timer - 0.1);
                sv_timer = 0.1;
        }
 
@@ -2600,7 +2623,8 @@ double SV_Frame(double time)
                {
                        advancetime = sys_ticrate.value;
                        // listen servers can run multiple server frames per client frame
-                       framelimit = cl_maxphysicsframesperserverframe.integer;
+                       if (sv_maxphysicsframesperserverframe.integer > 0)
+                               framelimit = sv_maxphysicsframesperserverframe.integer;
                        aborttime = Sys_DirtyTime() + 0.1;
                }
 
@@ -2616,12 +2640,12 @@ double SV_Frame(double time)
                                offset = 0;
 
                        offset += sv_timer;
-                       ++svs.perf_acc_offset_samples;
-                       svs.perf_acc_offset += offset;
-                       svs.perf_acc_offset_squared += offset * offset;
+                       ++sv.perf_acc_offset_samples;
+                       sv.perf_acc_offset += offset;
+                       sv.perf_acc_offset_squared += offset * offset;
                        
-                       if(svs.perf_acc_offset_max < offset)
-                               svs.perf_acc_offset_max = offset;
+                       if(sv.perf_acc_offset_max < offset)
+                               sv.perf_acc_offset_max = offset;
                }
 
                // only advance time if not paused
@@ -2645,6 +2669,9 @@ double SV_Frame(double time)
                                break;
                }
 
+               if (framecount > 1 && sv_lagreporting_strict.integer && reporting)
+                       SV_BroadcastPrintf(CON_WARN "Server lag report: caught up %.1fms by running %d extra frames\n", advancetime * (framecount - 1) * 1000, framecount - 1);
+
                R_TimeReport("serverphysics");
 
                // send all messages to the clients
@@ -2671,7 +2698,7 @@ double SV_Frame(double time)
        if (sv_timer >= 0)
        {
                if (!svs.threaded)
-                       svs.perf_acc_lost += sv_timer;
+                       sv.perf_acc_lost += sv_timer;
                sv_timer = 0;
        }
 
@@ -2684,7 +2711,6 @@ static int SV_ThreadFunc(void *voiddata)
        qbool playing = false;
        double sv_timer = 0;
        double sv_deltarealtime, sv_oldrealtime, sv_realtime;
-       double wait;
        int i;
        char vabuf[1024];
        sv_realtime = Sys_DirtyTime();
@@ -2701,7 +2727,7 @@ static int SV_ThreadFunc(void *voiddata)
 
                sv_timer += sv_deltarealtime;
 
-               svs.perf_acc_realtime += sv_deltarealtime;
+               sv.perf_acc_realtime += sv_deltarealtime;
 
                // at this point we start doing real server work, and must block on any client activity pertaining to the server (such as executing SV_SpawnServer)
                SV_LockThreadMutex();
@@ -2717,22 +2743,22 @@ static int SV_ThreadFunc(void *voiddata)
                {
                        // 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 = 0;
+                       sv.perf_acc_realtime = sv.perf_acc_sleeptime = sv.perf_acc_lost = sv.perf_acc_offset = sv.perf_acc_offset_squared = sv.perf_acc_offset_max = sv.perf_acc_offset_samples = 0;
                }
-               else if(svs.perf_acc_realtime > 5)
+               else if(sv.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)
+                       sv.perf_cpuload = 1 - sv.perf_acc_sleeptime / sv.perf_acc_realtime;
+                       sv.perf_lost = sv.perf_acc_lost / sv.perf_acc_realtime;
+                       if(sv.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);
+                               sv.perf_offset_max = sv.perf_acc_offset_max;
+                               sv.perf_offset_avg = sv.perf_acc_offset / sv.perf_acc_offset_samples;
+                               sv.perf_offset_sdev = sqrt(sv.perf_acc_offset_squared / sv.perf_acc_offset_samples - sv.perf_offset_avg * sv.perf_offset_avg);
                        }
-                       if(svs.perf_lost > 0 && developer_extra.integer)
+                       if(sv.perf_lost > 0 && developer_extra.integer)
                                if(playing)
                                        Con_DPrintf("Server can't keep up: %s\n", SV_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 = 0;
+                       sv.perf_acc_realtime = sv.perf_acc_sleeptime = sv.perf_acc_lost = sv.perf_acc_offset = sv.perf_acc_offset_squared = sv.perf_acc_offset_max = sv.perf_acc_offset_samples = 0;
                }
 
                // get new packets
@@ -2743,21 +2769,10 @@ static int SV_ThreadFunc(void *voiddata)
                }
 
                // if the accumulators haven't become positive yet, wait a while
-               wait = sv_timer * -1000000.0;
-               if (wait >= 1)
+               if (sv_timer < 0)
                {
-                       double time0, delta;
                        SV_UnlockThreadMutex(); // don't keep mutex locked while sleeping
-                       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();
-                       Sys_Sleep((int)wait);
-                       delta = Sys_DirtyTime() - time0;if (delta < 0 || delta >= 1800) delta = 0;
-                       svs.perf_acc_sleeptime += delta;
+                       sv.perf_acc_sleeptime += Sys_Sleep(-sv_timer);
                        continue;
                }
 
@@ -2775,11 +2790,11 @@ static int SV_ThreadFunc(void *voiddata)
                        if(advancetime > 0)
                        {
                                offset = sv_timer + (Sys_DirtyTime() - sv_realtime); // LadyHavoc: FIXME: I don't understand this line
-                               ++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;
+                               ++sv.perf_acc_offset_samples;
+                               sv.perf_acc_offset += offset;
+                               sv.perf_acc_offset_squared += offset * offset;
+                               if(sv.perf_acc_offset_max < offset)
+                                       sv.perf_acc_offset_max = offset;
                        }
 
                        // only advance time if not paused
@@ -2817,7 +2832,7 @@ static int SV_ThreadFunc(void *voiddata)
                // if there is some time remaining from this frame, reset the timers
                if (sv_timer >= 0)
                {
-                       svs.perf_acc_lost += sv_timer;
+                       sv.perf_acc_lost += sv_timer;
                        sv_timer = 0;
                }
        }