]> git.xonotic.org Git - xonotic/darkplaces.git/commitdiff
test alpha bones_was_here/net_s12
authorbones_was_here <bones_was_here@xa.org.au>
Thu, 12 Aug 2021 09:58:58 +0000 (19:58 +1000)
committerbones_was_here <bones_was_here@xa.org.au>
Sat, 4 Sep 2021 18:29:28 +0000 (04:29 +1000)
.kdev4/darkplaces.kdev4 [new file with mode: 0644]
cl_parse.c
darkplaces.h
darkplaces.kdev4 [new file with mode: 0644]
server.h
sv_main.c
sv_phys.c
sv_send.c
sv_user.c

diff --git a/.kdev4/darkplaces.kdev4 b/.kdev4/darkplaces.kdev4
new file mode 100644 (file)
index 0000000..c727f06
--- /dev/null
@@ -0,0 +1,5 @@
+[Buildset]
+BuildItems=@Variant(\x00\x00\x00\t\x00\x00\x00\x00\x01\x00\x00\x00\x0b\x00\x00\x00\x00\x01\x00\x00\x00\x14\x00d\x00a\x00r\x00k\x00p\x00l\x00a\x00c\x00e\x00s)
+
+[Project]
+VersionControlSupport=kdevgit
index 7e228eea772df580d9f4e8adf77802635a05b3f5..a3982fcb58f2bc93e2a32d548d2fd2fa585ae0b1 100644 (file)
@@ -29,7 +29,6 @@ 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] =
 {
index 599d711c65df2e14a2f9f912442c572a71b59d44..72d2f025ce8249369042e8ed0ef6cbca82a41012 100644 (file)
@@ -39,6 +39,7 @@ extern char engineversion[128];
 #include <stdio.h>
 #include <stdlib.h>
 #include <limits.h>
+#include <float.h>
 
 #include "sys.h"
 #include "qtypes.h"
diff --git a/darkplaces.kdev4 b/darkplaces.kdev4
new file mode 100644 (file)
index 0000000..535306d
--- /dev/null
@@ -0,0 +1,4 @@
+[Project]
+CreatedFrom=makefile
+Manager=KDevCustomMakeManager
+Name=darkplaces
index 07639e4b5f52c530fa71cc50e974fe12201fc95d..02b1568de6c067920911465e6b42303a2ba5f73a 100644 (file)
--- a/server.h
+++ b/server.h
@@ -211,11 +211,16 @@ typedef struct client_s
        /// communications handle
        netconn_t *netconnection;
 
+       /// movement
        unsigned int movesequence;
        signed char movement_count[NETGRAPH_PACKETS];
        unsigned int movement_highestsequence_seen; // not the same as movesequence if prediction is off
-       /// movement
-       usercmd_t cmd;
+#define MVBUF_SIZE 8 // must be a power of 2!
+#define MVBUF_MASK(index) ((index) & (MVBUF_SIZE - 1))
+       usercmd_t cmd, mvbuf[MVBUF_SIZE]; // sv_clmovement_buffer
+       unsigned char mvbuf_r;            // ring buffer read index
+       unsigned char mvbuf_w;            // ring buffer write index
+       double frametime_accum;
        /// intended motion calced from cmd
        vec3_t wishdir;
 
@@ -229,6 +234,8 @@ typedef struct client_s
        double clmovement_disabletimeout;
        /// this is used by sv_clmovement_inputtimeout code
        float clmovement_inputtimeout;
+       /// counts how much synchronous physics moveframetime occurred during an inputtimeout
+       float clmovement_inputtimeout_accum;
 
 /// spawn parms are carried from level to level
        prvm_vec_t spawn_parms[NUM_SPAWN_PARMS];
@@ -421,7 +428,10 @@ extern cvar_t sv_clmovement_enable;
 extern cvar_t sv_clmovement_minping;
 extern cvar_t sv_clmovement_minping_disabletime;
 extern cvar_t sv_clmovement_inputtimeout;
+extern cvar_t sv_clmovement_inputtimeout_correct;
 extern cvar_t sv_clmovement_maxnetfps;
+extern cvar_t sv_clmovement_buffer;
+extern cvar_t sv_clmovement_noisy;
 extern cvar_t sv_cullentities_nevercullbmodels;
 extern cvar_t sv_cullentities_pvs;
 extern cvar_t sv_cullentities_stats;
@@ -523,6 +533,8 @@ void SV_SendClientMessages(void);
 
 void SV_ReadClientMessage(void);
 
+void SV_ExecuteBufferedAsyncMoves(void);
+
 // precachemode values:
 // 0 = fail if not precached,
 // 1 = warn if not found and precache if possible
index dcb91a7f9147ca33912d3ab6551c0a3cad8622d3..da2ac4600bafa2b57a6f2578ae8804f0de7d09ee 100644 (file)
--- a/sv_main.c
+++ b/sv_main.c
@@ -77,6 +77,9 @@ cvar_t sv_clmovement_enable = {CF_SERVER, "sv_clmovement_enable", "1", "whether
 cvar_t sv_clmovement_minping = {CF_SERVER, "sv_clmovement_minping", "0", "if client ping is below this time in milliseconds, then their ability to use cl_movement prediction is disabled for a while (as they don't need it)"};
 cvar_t sv_clmovement_minping_disabletime = {CF_SERVER, "sv_clmovement_minping_disabletime", "1000", "when client falls below minping, disable their prediction for this many milliseconds (should be at least 1000 or else their prediction may turn on/off frequently)"};
 cvar_t sv_clmovement_inputtimeout = {CF_SERVER, "sv_clmovement_inputtimeout", "0.1", "when a client does not send input for this many seconds (max 0.1), force them to move anyway (unlike QuakeWorld)"};
+cvar_t sv_clmovement_inputtimeout_correct = {CF_SERVER, "sv_clmovement_inputtimeout_correct", "0", "FOO"};
+cvar_t sv_clmovement_buffer = {CF_SERVER, "sv_clmovement_buffer", "0", "bar"};
+cvar_t sv_clmovement_noisy = {CF_SERVER, "sv_clmovement_noisy", "1", "TEST"};
 cvar_t sv_cullentities_nevercullbmodels = {CF_SERVER, "sv_cullentities_nevercullbmodels", "0", "if enabled the clients are always notified of moving doors and lifts and other submodels of world (warning: eats a lot of network bandwidth on some levels!)"};
 cvar_t sv_cullentities_pvs = {CF_SERVER, "sv_cullentities_pvs", "1", "fast but loose culling of hidden entities"};
 cvar_t sv_cullentities_stats = {CF_SERVER, "sv_cullentities_stats", "0", "displays stats on network entities culled by various methods for each client"};
@@ -553,6 +556,9 @@ void SV_Init (void)
        Cvar_RegisterVariable (&sv_clmovement_minping);
        Cvar_RegisterVariable (&sv_clmovement_minping_disabletime);
        Cvar_RegisterVariable (&sv_clmovement_inputtimeout);
+       Cvar_RegisterVariable (&sv_clmovement_inputtimeout_correct);
+       Cvar_RegisterVariable (&sv_clmovement_buffer);
+       Cvar_RegisterVariable (&sv_clmovement_noisy);
        Cvar_RegisterVariable (&sv_cullentities_nevercullbmodels);
        Cvar_RegisterVariable (&sv_cullentities_pvs);
        Cvar_RegisterVariable (&sv_cullentities_stats);
@@ -888,6 +894,9 @@ void SV_SendServerinfo (client_t *client)
 
        // clear movement info until client enters the new level properly
        memset(&client->cmd, 0, sizeof(client->cmd));
+       memset(&client->mvbuf, 0, sizeof(client->mvbuf));
+       client->mvbuf_r = client->mvbuf_w = 0;
+       client->clmovement_inputtimeout_accum = 0.0f;
        client->movesequence = 0;
        client->movement_highestsequence_seen = 0;
        memset(&client->movement_count, 0, sizeof(client->movement_count));
@@ -2525,7 +2534,6 @@ double SV_Frame(double time)
                if (sv.active)
                {
                        NetConn_ServerFrame();
-                       SV_CheckTimeouts();
                }
        }
 
@@ -2558,6 +2566,8 @@ double SV_Frame(double time)
                float offset;
                prvm_prog_t *prog = SVVM_prog;
 
+               SV_CheckTimeouts();
+
                // run the world state
                // don't allow simulation to run too fast or too slow or logic glitches can occur
 
@@ -2618,6 +2628,11 @@ double SV_Frame(double time)
                // send all messages to the clients
                SV_SendClientMessages();
 
+               // execute moves that were deferred to the "next" frame
+               // sv.time advanced and client updates were sent, since these moves were saved
+               if (sv_clmovement_buffer.integer)
+                       SV_ExecuteBufferedAsyncMoves();
+
                if (sv.paused == 1 && host.realtime > sv.pausedstart && sv.pausedstart > 0) {
                        prog->globals.fp[OFS_PARM0] = host.realtime - sv.pausedstart;
                        PRVM_serverglobalfloat(time) = sv.time;
index a72a7ca6acf5b8a0142c5df48bc506834fb4635c..a0dd12776a15009a4155c1323640a65bb19332e7 100644 (file)
--- a/sv_phys.c
+++ b/sv_phys.c
@@ -3015,6 +3015,16 @@ static void SV_Physics_ClientEntity_PreThink(prvm_edict_t *ent)
        {
                SV_PlayerPhysics();
                //host_client->cmd.time = max(host_client->cmd.time, sv.time);
+
+               if (host_client->movesequence)
+               {
+                       // client was in async mode but timed out
+                       if (sv_clmovement_inputtimeout_correct.integer)
+                               host_client->clmovement_inputtimeout_accum += sv.frametime;
+
+                       // TODO: move this to SV_CheckTimeouts() ?
+                       host_client->ping += sv.frametime;
+               }
        }
 
        // make sure the velocity is still sane (not a NaN)
index 984f9635ac4494afe78410e2d937510be2cccde2..de4d9e05c89280d6edb6a3a8a3013d928549ab7e 100644 (file)
--- a/sv_send.c
+++ b/sv_send.c
@@ -1716,6 +1716,9 @@ void SV_SendClientMessages(void)
                        continue;
                }
 
+               if (sv_clmovement_buffer.integer && host_client->movesequence && host_client->mvbuf_w != host_client->mvbuf_r && host_client->movesequence == host_client->mvbuf[MVBUF_MASK(host_client->mvbuf_r)].sequence)
+                       continue;
+
                if (!prepared)
                {
                        prepared = true;
index 1cb5a3f057c305be869a94fdd163658e2098427b..9ea6aed6ffe4338c4490e0c0c8510e6b39287d4a 100644 (file)
--- a/sv_user.c
+++ b/sv_user.c
@@ -772,13 +772,81 @@ static void SV_ReadClientMove (void)
        }
 }
 
+static void SV_ExecuteAsyncMove(usercmd_t *move)
+{
+       prvm_prog_t *prog = SVVM_prog;
+
+       double qcframetime = PRVM_serverglobalfloat(frametime);
+       double svframetime = sv.frametime;
+
+       host_client->cmd = *move;
+       host_client->movesequence = move->sequence;
+
+       if (sv_clmovement_buffer.integer)
+       {
+               // TODO can this break when paused? should we use sys_ticrate.value instead or just check sv.frametime?
+               // FIXME if the client has exactly half the server's framerate and never fluctuates at allow
+               // the amount of buffered moves never decreases
+               // happens when enabling buffer during a match with a client already running exactly half hz
+               // hack: could do a FLT_MIN (or bigger) offset so its doing slightly more frametime on the first one ?
+               // or just do enough extra on the first one that subsequent ones are == sv.frametime ?
+               if (move->frametime >= 2 * sv.frametime && sv.frametime > 0.0)
+               {
+//                     Con_Printf("Splitting a move in %d! ", (int)floor(move->frametime / sv.frametime));
+                       host_client->cmd.frametime /= floor(move->frametime / sv.frametime);
+                       move->frametime -= host_client->cmd.frametime;
+//                     Con_Printf("shortened frametime: %f stored remainder: %f\n", host_client->cmd.frametime, move->frametime);
+               }
+               else
+                       host_client->mvbuf_r++;
+
+               host_client->frametime_accum += host_client->cmd.frametime;
+       }
+
+       // if using prediction, we need to perform moves when packets are
+       // received, even if multiple occur in one frame
+       // (they can't go beyond the current time so there is no cheat issue
+       //  with this approach, and if they don't send input for a while they
+       //  start moving anyway, so the longest 'lagaport' possible is
+       //  determined by the sv_clmovement_inputtimeout cvar)
+
+       // update ping time for qc to see while executing this move
+       host_client->ping = host_client->cmd.receivetime - host_client->cmd.time;
+       // the server and qc frametime values must be changed temporarily
+       PRVM_serverglobalfloat(frametime) = sv.frametime = host_client->cmd.frametime;
+       // if move is more than 50ms, split it into two moves (this matches QWSV behavior and the client prediction)
+       if (sv.frametime > 0.05)
+       {
+               PRVM_serverglobalfloat(frametime) = sv.frametime = host_client->cmd.frametime * 0.5;
+               SV_Physics_ClientMove();
+       }
+       SV_Physics_ClientMove();
+       sv.frametime = svframetime;
+       PRVM_serverglobalfloat(frametime) = qcframetime;
+       host_client->clmovement_inputtimeout = min(0.1, sv_clmovement_inputtimeout.value);
+}
+
+inline void SV_ExecuteBufferedAsyncMoves(void)
+{
+       unsigned char i = 0;
+       for (host_client = &svs.clients[0]; i < svs.maxclients; i++, host_client++)
+       {
+//             if (host_client->begun)
+//                     Con_Printf("Buffered moves: %d\n", (unsigned char)(host_client->mvbuf_w - host_client->mvbuf_r));
+
+               host_client->frametime_accum = 0.0;
+               while (host_client->frametime_accum < sv.frametime && host_client->mvbuf_w != host_client->mvbuf_r)
+                       SV_ExecuteAsyncMove(&host_client->mvbuf[MVBUF_MASK(host_client->mvbuf_r)]);
+       }
+}
+
 static void SV_ExecuteClientMoves(void)
 {
        prvm_prog_t *prog = SVVM_prog;
        int moveindex;
-       double moveframetime;
-       double oldframetime;
-       double oldframetime2;
+//     double moveframetime;
+//     double oldframetime;
+//     double oldframetime2;
 
        if (sv_numreadmoves < 1)
                return;
@@ -788,8 +856,12 @@ static void SV_ExecuteClientMoves(void)
 #if DEBUGMOVES
        Con_Printf("SV_ExecuteClientMoves: read %i moves at sv.time %f\n", sv_numreadmoves, (float)sv.time);
 #endif
+
+       // update ping time
+       host_client->ping = max(sv_readmoves[sv_numreadmoves-1].receivetime - sv_readmoves[sv_numreadmoves-1].time, 0);
+
        // disable clientside movement prediction in some cases
-       if (ceil(max(sv_readmoves[sv_numreadmoves-1].receivetime - sv_readmoves[sv_numreadmoves-1].time, 0) * 1000.0) < sv_clmovement_minping.integer)
+       if (host_client->ping * 1000.0 < sv_clmovement_minping.value || host_client->ping > 0.3)
                host_client->clmovement_disabletimeout = host.realtime + sv_clmovement_minping_disabletime.value / 1000.0;
        // several conditions govern whether clientside movement prediction is allowed
        if (sv_readmoves[sv_numreadmoves-1].sequence && sv_clmovement_enable.integer && sv_clmovement_inputtimeout.value > 0 && host_client->clmovement_disabletimeout <= host.realtime && (PRVM_serveredictfloat(host_client->edict, disableclientprediction) == -1 || (PRVM_serveredictfloat(host_client->edict, movetype) == MOVETYPE_WALK && (!PRVM_serveredictfloat(host_client->edict, disableclientprediction)))))
@@ -804,15 +876,53 @@ static void SV_ExecuteClientMoves(void)
                        usercmd_t *move = sv_readmoves + moveindex;
                        if (host_client->movesequence < move->sequence || moveindex == sv_numreadmoves - 1)
                        {
+                               // this is a new move
+
+/*                             usercmd_t *prevmove;
+                               // TODO is it correct to check host_client->movesequence here ?!?
+                               // movesequence set means prev executed move was async
+                               // but not necessarily that buffered mode was enabled
+                               // could compare movesequence to most recently read buffered move's sequence ?
+                               if (sv_clmovement_buffer.integer && host_client->movesequence)
+                                       prevmove = &host_client->mvbuf[MVBUF_MASK(host_client->mvbuf_w - 1)];
+                               else
+                                       prevmove = &host_client->cmd; */
 #if DEBUGMOVES
                                Con_Printf("%smove #%u %ims (%ims) %i %i '%i %i %i' '%i %i %i'\n", (move->time - host_client->cmd.time) > sv.frametime * 1.01 ? "^1" : "^2", move->sequence, (int)floor((move->time - host_client->cmd.time) * 1000.0 + 0.5), (int)floor(move->time * 1000.0 + 0.5), move->impulse, move->buttons, (int)move->viewangles[0], (int)move->viewangles[1], (int)move->viewangles[2], (int)move->forwardmove, (int)move->sidemove, (int)move->upmove);
 #endif
-                               // this is a new move
-                               move->time = bound(sv.time - 1, move->time, sv.time); // prevent slowhack/speedhack combos
-                               move->time = max(move->time, host_client->cmd.time); // prevent backstepping of time
-                               // bones_was_here: limit moveframetime to a multiple of sv.frametime to match inputtimeout behaviour
-                               moveframetime = min(move->time - host_client->cmd.time, min(0.1, sys_ticrate.value > 0.0 && sv.frametime > 0.0 ? sv.frametime * ceil(sv_clmovement_inputtimeout.value / sv.frametime) : sv_clmovement_inputtimeout.value));
+//                             if (move->time > sv.time)
+//                                     Con_Printf("move->time exceeded sv.time by %f\n", move->time - sv.time);
+//                             if (move->time < oldmove->time)
+//                                     Con_Printf("time stepped back by %f\n", oldmove->time - move->time);
+
+                               // not required as we limit to sv.time + sv.frametime in SV_ReadClientMove and fall back to sync phys if too old
+//                             move->time = bound(sv.time - 1, move->time, sv.time); // prevent slowhack/speedhack combos
+                               // also not required: if move->time was older than oldmove->time
+                               // move->frametime will be < 0 and the move will be discarded
+//                             move->time = max(move->time, oldmove->time); // prevent backstepping of time
+
+                               // FIXME frametime of next move to arrive will be different to client's pred if we have prevented backstepping
+
+                               move->frametime = move->time - max(host_client->cmd.time, host_client->mvbuf[MVBUF_MASK(host_client->mvbuf_w - 1)].time);
+
+//                             if (move->frametime >= 0.05)
+//                                     Con_Printf("Long move frametime: %f\n", move->frametime);
 
+                               if (sv_clmovement_inputtimeout_correct.integer && host_client->clmovement_inputtimeout_accum)
+                               {
+                                       if (move->frametime > host_client->clmovement_inputtimeout_accum)
+                                       {
+//                                             Con_Printf("Shortening move frametime after %f inputtimeout, was %f, now %f\n", host_client->clmovement_inputtimeout_accum, move->frametime, move->frametime - host_client->clmovement_inputtimeout_accum);
+                                               move->frametime -= host_client->clmovement_inputtimeout_accum;
+                                               host_client->clmovement_inputtimeout_accum = 0.0f;
+                                       }
+                                       else
+                                       {
+//                                             Con_Printf("Shortening move frametime after %f inputtimeout, was %f, now 0.0\n", host_client->clmovement_inputtimeout_accum, move->frametime);
+                                               host_client->clmovement_inputtimeout_accum -= move->frametime;
+                                               move->frametime = 0.0;
+                                       }
+                               }
 
                                // discard (treat like lost) moves with too low distance from
                                // the previous one to prevent hacks using float inaccuracy
@@ -820,18 +930,47 @@ static void SV_ExecuteClientMoves(void)
                                // this should also apply if a move cannot get
                                // executed because it came too late and
                                // already was performed serverside
-                               if(moveframetime < 0.0005)
+                               if(move->frametime < 0.0005)
                                {
                                        // count the move as LOST if we don't
                                        // execute it but it has higher
                                        // sequence count
                                        if(host_client->movesequence)
-                                               if(move->sequence > host_client->movesequence)
+                                               if(move->sequence > host_client->movesequence && sv_clmovement_noisy.integer)
                                                        host_client->movement_count[(move->sequence) % NETGRAPH_PACKETS] = -1;
+//                                     Con_Printf("Discarding move with frametime %f\n", move->frametime);
                                        continue;
+                                       // TODO can we somehow apply only the impulses and buttons?
+                                       // there's code for accumulating them into the next move to arrive
+                                       // currently this only happens for explicit sync physics as it relies on the move being copied to host_client->cmd
                                }
 
+                               // bones_was_here: limit moveframetime to a multiple of sv.frametime to match inputtimeout behaviour
+                               if (!sv_clmovement_inputtimeout_correct.integer)
+                                       move->frametime = min(move->frametime, min(0.1, sys_ticrate.value > 0.0 && sv.frametime > 0.0 ? sv.frametime * ceil(sv_clmovement_inputtimeout.value / sv.frametime) : sv_clmovement_inputtimeout.value));
+                               else
+                                       move->frametime = min(move->frametime, 0.1);
+
                                //Con_Printf("movesequence = %i (%i lost), moveframetime = %f\n", move->sequence, move->sequence ? move->sequence - host_client->movesequence - 1 : 0, moveframetime);
+
+                               if (sv_clmovement_buffer.integer)
+                               {
+                                       // if the buffer is full we are about to overwrite the oldest move
+                                       // log it as lost and increment the read index
+                                       if ((unsigned char)(host_client->mvbuf_w - host_client->mvbuf_r) >= MVBUF_SIZE)
+                                               host_client->movement_count[(host_client->mvbuf[MVBUF_MASK(host_client->mvbuf_r++)].sequence) % NETGRAPH_PACKETS] = -1;
+
+                                       // store the move and increment the write index
+                                       host_client->mvbuf[MVBUF_MASK(host_client->mvbuf_w++)] = *move;
+
+                                       // if possible, execute the move now
+                                       if (host_client->frametime_accum < sv.frametime)
+                                               SV_ExecuteAsyncMove(&host_client->mvbuf[MVBUF_MASK(host_client->mvbuf_r)]);
+                               }
+                               else
+                                       SV_ExecuteAsyncMove(move);
+
+/*
                                host_client->cmd = *move;
                                host_client->movesequence = move->sequence;
 
@@ -857,6 +996,7 @@ static void SV_ExecuteClientMoves(void)
                                sv.frametime = oldframetime2;
                                PRVM_serverglobalfloat(frametime) = oldframetime;
                                host_client->clmovement_inputtimeout = min(0.1, sv_clmovement_inputtimeout.value);
+*/
                        }
                }
        }
@@ -876,7 +1016,7 @@ static void SV_ExecuteClientMoves(void)
                }
                // now copy the new move
                host_client->cmd = sv_readmoves[sv_numreadmoves-1];
-               host_client->cmd.time = max(host_client->cmd.time, sv.time);
+//             host_client->cmd.time = max(host_client->cmd.time, sv.time);
                        // physics will run up to sv.time, so allow no predicted moves
                        // before that otherwise, there is a speedhack by turning
                        // prediction on and off repeatedly on client side because the
@@ -885,8 +1025,10 @@ static void SV_ExecuteClientMoves(void)
                host_client->movesequence = 0;
                // make sure that normal physics takes over immediately
                host_client->clmovement_inputtimeout = 0;
+               // clear the async move buffer
+               host_client->mvbuf_r = host_client->mvbuf_w = 0;
                // update ping time
-               host_client->ping = host_client->cmd.receivetime - sv_readmoves[sv_numreadmoves-1].time;
+//             host_client->ping = host_client->cmd.receivetime - sv_readmoves[sv_numreadmoves-1].time;
        }
 }