From: cloudwalk Date: Sun, 11 Apr 2021 16:46:15 +0000 (+0000) Subject: cl_parse: Implement 7th mode for cl_nettimesyncboundmode; jitter compensated dynamic... X-Git-Url: http://git.xonotic.org/?a=commitdiff_plain;h=d192fabc74fe10ad9f173418e765b189278a1075;p=xonotic%2Fdarkplaces.git cl_parse: Implement 7th mode for cl_nettimesyncboundmode; jitter compensated dynamic adjustment rate This mode aims to prevent network jitter or other disturbances from significantly affecting the client's timekeeping, by correcting gradually (max 10% of mean error per tic). The rolling harmonic mean gives large time error outliers low significance. Correction rate is dynamic, determined by mean error size. Time is correct within a few tics of connect/map start despite no hard bounding. The adjustment approach is from mode 5 and can achieve microsecond accuracy if client frametime is a multiple of server frametime. Prevents 0ms move frame times with uncapped fps. Smoothest mode esp for vsynced clients on servers with aggressive inputtimeout. Authored by bones_was_here https://gitlab.com/xonotic/darkplaces/-/merge_requests/112 git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@13115 d7cf8633-e32d-0410-b094-e92efae38249 --- diff --git a/cl_parse.c b/cl_parse.c index cabcefe1..98c415ab 100644 --- a/cl_parse.c +++ b/cl_parse.c @@ -29,6 +29,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "menu.h" #endif #include "cl_video.h" +#include "float.h" const char *svc_strings[128] = { @@ -186,7 +187,7 @@ cvar_t cl_sound_r_exp3 = {CF_CLIENT, "cl_sound_r_exp3", "weapons/r_exp3.wav", "s cvar_t cl_serverextension_download = {CF_CLIENT, "cl_serverextension_download", "0", "indicates whether the server supports the download command"}; cvar_t cl_joinbeforedownloadsfinish = {CF_CLIENT | CF_ARCHIVE, "cl_joinbeforedownloadsfinish", "1", "if non-zero the game will begin after the map is loaded before other downloads finish"}; cvar_t cl_nettimesyncfactor = {CF_CLIENT | CF_ARCHIVE, "cl_nettimesyncfactor", "0", "rate at which client time adapts to match server time, 1 = instantly, 0.125 = slowly, 0 = not at all (only applied in bound modes 0, 1, 2, 3)"}; -cvar_t cl_nettimesyncboundmode = {CF_CLIENT | CF_ARCHIVE, "cl_nettimesyncboundmode", "6", "method of restricting client time to valid values, 0 = no correction, 1 = tight bounding (jerky with packet loss), 2 = loose bounding (corrects it if out of bounds), 3 = leniant bounding (ignores temporary errors due to varying framerate), 4 = slow adjustment method from Quake3, 5 = slightly nicer version of Quake3 method, 6 = tight bounding + mode 5"}; +cvar_t cl_nettimesyncboundmode = {CF_CLIENT | CF_ARCHIVE, "cl_nettimesyncboundmode", "6", "method of restricting client time to valid values, 0 = no correction, 1 = tight bounding (jerky with packet loss), 2 = loose bounding (corrects it if out of bounds), 3 = leniant bounding (ignores temporary errors due to varying framerate), 4 = slow adjustment method from Quake3, 5 = slightly nicer version of Quake3 method, 6 = tight bounding + mode 5, 7 = jitter compensated dynamic adjustment rate"}; cvar_t cl_nettimesyncboundtolerance = {CF_CLIENT | CF_ARCHIVE, "cl_nettimesyncboundtolerance", "0.25", "how much error is tolerated by bounding check, as a fraction of frametime, 0.25 = up to 25% margin of error tolerated, 1 = use only new time, 0 = use only old time (same effect as setting cl_nettimesyncfactor to 1) (only affects bound modes 2 and 3)"}; cvar_t cl_iplog_name = {CF_CLIENT | CF_ARCHIVE, "cl_iplog_name", "darkplaces_iplog.txt", "name of iplog file containing player addresses for iplog_list command and automatic ip logging when parsing status command"}; @@ -3351,6 +3352,28 @@ static void CL_NetworkTimeReceived(double newtime) cl.time = bound(cl.time - 0.002 * cl.movevars_timescale, cl.mtime[1], cl.time + 0.001 * cl.movevars_timescale); break; + case 7: + /* bones_was_here: this aims to prevent disturbances in the force from affecting cl.time + * the rolling harmonic mean gives large time error outliers low significance + * correction rate is dynamic and gradual (max 10% of mean error per tic) + * time is correct within a few server frames of connect/map start + * can achieve microsecond accuracy when cl.realframetime is a multiple of sv.frametime + * prevents 0ms move frame times with uncapped fps + * smoothest mode esp. for vsynced clients on servers with aggressive inputtimeout + */ + { + unsigned char i; + float error; + // in event of packet loss, cl.mtime[1] could be very old, so avoid if possible + double target = cl.movevars_ticrate ? cl.mtime[0] - cl.movevars_ticrate : cl.mtime[1]; + cl.ts_error_stor[cl.ts_error_num] = 1.0f / max(fabs(cl.time - target), FLT_MIN); + cl.ts_error_num = (cl.ts_error_num + 1) % NUM_TS_ERRORS; + for (i = 0, error = 0.0f; i < NUM_TS_ERRORS; i++) + error += cl.ts_error_stor[i]; + error = 0.1f / (error / NUM_TS_ERRORS); + cl.time = bound(cl.time - error, target, cl.time + error); + } + break; } } // this packet probably contains a player entity update, so we will need diff --git a/client.h b/client.h index 67262d7c..5335bb0a 100644 --- a/client.h +++ b/client.h @@ -870,7 +870,12 @@ typedef struct client_state_s // how long it has been since the previous client frame in real time // (not game time, for that use cl.time - cl.oldtime) double realframetime; - + + // used by cl_nettimesyncboundmode 7 +#define NUM_TS_ERRORS 32 // max 256 + unsigned char ts_error_num; + float ts_error_stor[NUM_TS_ERRORS]; + // fade var for fading while dead float deathfade;