]> git.xonotic.org Git - xonotic/darkplaces.git/commitdiff
cl_netfps: reduce pings by synchronising when possible, related fixes
authorbones_was_here <bones_was_here@xonotic.au>
Fri, 17 Nov 2023 01:30:14 +0000 (11:30 +1000)
committerbones_was_here <bones_was_here@xonotic.au>
Sat, 9 Dec 2023 10:13:32 +0000 (20:13 +1000)
Fixes possibility of input frequency to exceed cl_netfps in certain
configurations.

Fixes possibility of rate limiting to reduce input frequency more than
it should.

Fixes timescale affecting input frequency (now consistent with client
FPS and server HZ being unaffected by timescale).

History of this code (most recent first):
1b6ea396b616e359cfe98d1e85e8e7a9ac571e91
528b92ffab3a9ab09770310e2796adbe5f0f1e20
79e332ad14e7ec9dbd2702a2d28127f7600f0678
663279ba8feeb4c114df0950bbc820f3bb32dcdc
21beb5fe4fa2e1f1b7591a4a1f6013b82186912f

Signed-off-by: bones_was_here <bones_was_here@xonotic.au>
cl_input.c
cl_parse.c
client.h

index 6281e925b6accacc880e48ede1c0da4ee9fb1fb0..505bc1a29555fdbd134e935abd82d9e37afa35ce 100644 (file)
@@ -1538,8 +1538,8 @@ void CL_UpdateMoveVars(void)
        else
        {
                cl.moveflags = 0;
-               cl.movevars_ticrate = (cls.demoplayback ? 1.0f : host_timescale.value) / bound(10.0f, cl_netfps.value, 1000.0f);
-               cl.movevars_timescale = (cls.demoplayback ? 1.0f : host_timescale.value);
+               cl.movevars_ticrate = 0; // bones_was_here: no guessing, unavailable ticrate triggers better fallbacks
+               cl.movevars_timescale = (cls.demoplayback || host_timescale.value <= 0) ? 1.0f : host_timescale.value;
                cl.movevars_gravity = sv_gravity.value;
                cl.movevars_stopspeed = cl_movement_stopspeed.value;
                cl.movevars_maxspeed = cl_movement_maxspeed.value;
@@ -1777,7 +1777,8 @@ void CL_SendMove(void)
        usercmd_t *cmd;
        sizebuf_t buf;
        unsigned char data[1024];
-       float packettime;
+       float packettime, sv_frametime, lag, frames_per_tic;
+       qbool opportune_moment;
        qbool quemove;
        qbool important;
 
@@ -1893,8 +1894,14 @@ void CL_SendMove(void)
        if (quemove)
                cl.movecmd[0] = cl.cmd;
 
-       // don't predict more than 200fps
-       if (cl.timesincepacket >= 0.005)
+       /* Accumulating cl.realframetime to prevent low packet rates,
+        * previously with cl_maxfps == cl_netfps it did not send every frame because
+        * host.realtime - cl.lastpackettime was often well below (or above) cl_packetinterval.
+        */
+       cl.timesincepacket += cl.realframetime;
+
+       // don't predict more than 256fps
+       if (cl.timesincepacket >= 1/256)
                cl.movement_replay = true; // redo the prediction
 
        // now decide whether to actually send this move
@@ -1907,32 +1914,46 @@ void CL_SendMove(void)
        // don't send too often or else network connections can get clogged by a
        // high renderer framerate
        packettime = 1.0f / bound(10.0f, cl_netfps.value, 1000.0f);
-       if (cl.movevars_timescale && cl.movevars_ticrate)
+       if (cl.movevars_ticrate)
        {
-               // try to ensure at least 1 packet per server frame
-               // and apply soft limit of 2, hard limit < 4 (packettime reduced further below)
-               float maxtic = cl.movevars_ticrate / cl.movevars_timescale;
-               packettime = bound(maxtic * 0.5f, packettime, maxtic);
+               packettime = bound(cl.movevars_ticrate * 0.5f, packettime, cl.movevars_ticrate);
+               sv_frametime = cl.movevars_ticrate;
        }
-       // bones_was_here: reduce packettime to (largest multiple of realframetime) <= packettime
-       // prevents packet rates lower than cl_netfps or server frame rate
-       // eg: cl_netfps 60 and cl_maxfps 250 would otherwise send only 50 netfps
-       // with this line that config sends 62.5 netfps
-       // (this causes it to emit packets at a steady beat)
-       packettime = floor(packettime / (float)cl.realframetime) * (float)cl.realframetime;
+       else // fallback, may be affected by server->client network problems
+               sv_frametime = (cl.mtime[0] - cl.mtime[1]) / cl.movevars_timescale;
 
        // always send if buttons changed or an impulse is pending
        // even if it violates the rate limit!
        important = (cl.cmd.impulse || (cl_netimmediatebuttons.integer && cl.cmd.buttons != cl.movecmd[1].buttons));
 
-       // don't send too often (cl_netfps), allowing a small margin for float error
-       // bones_was_here: accumulate realframetime to prevent low packet rates
-       // previously with cl_maxfps == cl_netfps it did not send every frame as
-       // host.realtime - cl.lastpackettime was often well below (or above) packettime
-       if (!important && cl.timesincepacket < packettime * 0.99999f)
+       lag = cl.mtime[0] - cl.cmd.time;
+       frames_per_tic = sv_frametime / cl.realframetime;
+//     opportune_moment = lag <= cl.realframetime * 2; // FAIL: can miss the moment with uncapped fps
+//     opportune_moment = lag <= cl.realframetime * frames_per_tic * 0.5; // FAIL: too early at high fps, reducing multi causes misses at moderate fps
+       opportune_moment = lag <= cl.realframetime * (frames_per_tic <= 1 ? 1 : sqrt(frames_per_tic)); // perfect
+
+       // Two methods for deciding when to send
+       if (!important)
        {
-               cl.timesincepacket += cl.realframetime;
-               return;
+               // Traditional time interval, now used as fallback
+               if (sv_frametime <= 0 || lag > sv_frametime || lag < 0) // unknown ticrate || lag/PL || still connecting
+               {
+                       if (cl.timesincepacket < packettime * 0.99999f)
+                       {
+//                             Con_Printf("^6moveft %f realft %f lag %f tic %f inputsince %d\n", cl.cmd.frametime, cl.realframetime, lag, sv_frametime, cl.opt_inputs_since_update);
+                               return;
+                       }
+               }
+               // Server-synchronised, for better pings
+               // Sends at least once per server frame
+               else // cl.opt_inputs_since_update is usable
+               {
+                       if (!opportune_moment || cl.opt_inputs_since_update >= sv_frametime / packettime)
+                       {
+//                             Con_Printf("^1moveft %f realft %f lag %f tic %f inputsince %d\n", cl.cmd.frametime, cl.realframetime, lag, sv_frametime, cl.opt_inputs_since_update);
+                               return;
+                       }
+               }
        }
 
        // don't choke the connection with packets (obey rate limit)
@@ -1943,8 +1964,13 @@ void CL_SendMove(void)
        if (!NetConn_CanSend(cls.netcon) && !important)
                return;
 
+//     Con_Printf("%smoveft %f realft %f lag %f tic %f inputsince %d important %d\n", (lag < 0.0005 || !opportune_moment) ? "^3" : "^2", cl.cmd.frametime, cl.realframetime, lag, sv_frametime, cl.opt_inputs_since_update, important);
+
+       if (opportune_moment) // this check is needed for optimal timing especially with cl_netimmediatebuttons
+               ++cl.opt_inputs_since_update;
+
        // reset the packet timing accumulator
-       cl.timesincepacket = cl.realframetime;
+       cl.timesincepacket = 0;
 
        buf.maxsize = sizeof(data);
        buf.cursize = 0;
index 2ca989333a161801a1d283fad4cefce6d1384923..91f0f274559a2894167f47306eec1f988fa6c63c 100644 (file)
@@ -3281,6 +3281,7 @@ extern cvar_t host_timescale;
 extern cvar_t cl_lerpexcess;
 static void CL_NetworkTimeReceived(double newtime)
 {
+       cl.opt_inputs_since_update = 0;
        cl.mtime[1] = cl.mtime[0];
        cl.mtime[0] = newtime;
        if (cl_nolerp.integer || cls.timedemo || cl.mtime[1] == cl.mtime[0] || cls.signon < SIGNONS)
index 313ba97c2ded7716d4556dbd36ca4c8d5ab991de..60d921e6496bb864373636b50bcd8f23f828d8ff 100644 (file)
--- a/client.h
+++ b/client.h
@@ -1048,6 +1048,8 @@ typedef struct client_state_s
 
        // time accumulated since an input packet was sent
        float timesincepacket;
+       // how many optimally timed inputs we sent since we received an update from the server
+       uint8_t opt_inputs_since_update;
 
        // movement parameters for client prediction
        unsigned int moveflags;