X-Git-Url: https://git.xonotic.org/?a=blobdiff_plain;f=sv_main.c;h=f656085c67ca1d8231ad957cd66caef0f6f8e48a;hb=a2bd5617e8017d15aad71e6e27060b7624601124;hp=35b6123361e0e06516982a1c71a10ec68ae06faf;hpb=6b6279b585b0c84cbb109a26121027f5e6b723b0;p=xonotic%2Fdarkplaces.git diff --git a/sv_main.c b/sv_main.c index 35b61233..f656085c 100644 --- a/sv_main.c +++ b/sv_main.c @@ -113,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)"}; @@ -126,10 +126,9 @@ 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"}; @@ -144,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)"}; @@ -160,11 +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"}; @@ -609,7 +613,7 @@ 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); @@ -641,11 +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); @@ -788,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 @@ -1891,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; @@ -2504,54 +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; 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; } /* @@ -2577,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; } @@ -2621,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 @@ -2650,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 @@ -2676,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; } @@ -2689,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(); @@ -2706,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(); @@ -2722,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 @@ -2748,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; } @@ -2780,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 @@ -2822,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; } }