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)"};
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_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"};
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);
// 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;
*/
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;
}
/*
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;
}
{
advancetime = sys_ticrate.value;
// listen servers can run multiple server frames per client frame
- if (cl_maxphysicsframesperserverframe.integer > 0)
- framelimit = cl_maxphysicsframesperserverframe.integer;
+ if (sv_maxphysicsframesperserverframe.integer > 0)
+ framelimit = sv_maxphysicsframesperserverframe.integer;
aborttime = Sys_DirtyTime() + 0.1;
}
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
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
if (sv_timer >= 0)
{
if (!svs.threaded)
- svs.perf_acc_lost += sv_timer;
+ sv.perf_acc_lost += sv_timer;
sv_timer = 0;
}
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();
{
// 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
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 += delta;
continue;
}
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
// 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;
}
}