From f44bba4d0245519b71fbea8970ae7823260862ce Mon Sep 17 00:00:00 2001 From: divverent Date: Fri, 7 Aug 2009 18:39:53 +0000 Subject: [PATCH] Stupid hack: force an entity frame once 5 frames were skipped. Fixes issues with cl_movement replay taking longer and longer when standing still and nothing on the map moves, while not causing so many empty entity frames that replay of lost frames can cause problems (well, in theory it now can, if an entity takes 5+3 server frames to be processed on the client, that is 0.1sec at a sys_ticrate matching 72fps - so try to avoid doing overlong processing in CSQC entity receive functions). git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@9104 d7cf8633-e32d-0410-b094-e92efae38249 --- host.c | 9 +++++---- protocol.c | 27 ++++++++++++++++++--------- protocol.h | 8 ++++---- server.h | 4 ++++ sv_main.c | 17 +++++++++++++---- 5 files changed, 44 insertions(+), 21 deletions(-) diff --git a/host.c b/host.c index e16d75d3..8d1eba31 100644 --- a/host.c +++ b/host.c @@ -828,6 +828,7 @@ void Host_Main(void) // decide the simulation time if (cls.capturevideo.active) { + //*** if (cls.capturevideo.realtime) clframetime = cl.realframetime = max(cl_timer, 1.0 / cls.capturevideo.framerate); else @@ -870,6 +871,10 @@ void Host_Main(void) cl.oldtime = cl.time; cl.time += clframetime; + // update video + if (host_speeds.integer) + time1 = Sys_DoubleTime(); + // Collect input into cmd CL_Input(); @@ -885,10 +890,6 @@ void Host_Main(void) // update client world (interpolate entities, create trails, etc) CL_UpdateWorld(); - // update video - if (host_speeds.integer) - time1 = Sys_DoubleTime(); - CL_Video_Frame(); CL_Gecko_Frame(); diff --git a/protocol.c b/protocol.c index 748e23b3..f6fce7a0 100644 --- a/protocol.c +++ b/protocol.c @@ -680,7 +680,7 @@ void Protocol_WriteStatsReliable(void) } -void EntityFrameQuake_WriteFrame(sizebuf_t *msg, int maxsize, int numstates, const entity_state_t *states) +qboolean EntityFrameQuake_WriteFrame(sizebuf_t *msg, int maxsize, int numstates, const entity_state_t *states) { const entity_state_t *s; entity_state_t baseline; @@ -688,6 +688,7 @@ void EntityFrameQuake_WriteFrame(sizebuf_t *msg, int maxsize, int numstates, con sizebuf_t buf; unsigned char data[128]; prvm_eval_t *val; + qboolean success = false; // prepare the buffer memset(&buf, 0, sizeof(buf)); @@ -840,8 +841,10 @@ void EntityFrameQuake_WriteFrame(sizebuf_t *msg, int maxsize, int numstates, con } // write the message to the packet SZ_Write(msg, buf.data, buf.cursize); + success = true; ENTITYSIZEPROFILING_END(msg, s->number); } + return success; } int EntityState_DeltaBits(const entity_state_t *o, const entity_state_t *n) @@ -1331,7 +1334,7 @@ void EntityFrame_AddFrame(entityframe_database_t *d, vec3_t eye, int framenum, i } // (server) writes a frame to network stream -void EntityFrame_WriteFrame(sizebuf_t *msg, int maxsize, entityframe_database_t *d, int numstates, const entity_state_t *states, int viewentnum) +qboolean EntityFrame_WriteFrame(sizebuf_t *msg, int maxsize, entityframe_database_t *d, int numstates, const entity_state_t *states, int viewentnum) { int i, onum, number; entity_frame_t *o = &d->deltaframe; @@ -1370,7 +1373,7 @@ void EntityFrame_WriteFrame(sizebuf_t *msg, int maxsize, entityframe_database_t val = PRVM_EDICTFIELDVALUE((&prog->edicts[number]), prog->fieldoffsets.SendEntity); if(val && val->function) - continue; + continue; for (;onum < o->numentities && o->entitydata[onum].number < number;onum++) { // write remove message @@ -1396,6 +1399,8 @@ void EntityFrame_WriteFrame(sizebuf_t *msg, int maxsize, entityframe_database_t MSG_WriteShort(msg, o->entitydata[onum].number | 0x8000); } MSG_WriteShort(msg, 0xFFFF); + + return true; } // (client) reads a frame from network stream @@ -1807,7 +1812,7 @@ void EntityFrame4_CL_ReadFrame(void) EntityFrame4_ResetDatabase(d); } -void EntityFrame4_WriteFrame(sizebuf_t *msg, int maxsize, entityframe4_database_t *d, int numstates, const entity_state_t *states) +qboolean EntityFrame4_WriteFrame(sizebuf_t *msg, int maxsize, entityframe4_database_t *d, int numstates, const entity_state_t *states) { const entity_state_t *e, *s; entity_state_t inactiveentitystate; @@ -1818,7 +1823,7 @@ void EntityFrame4_WriteFrame(sizebuf_t *msg, int maxsize, entityframe4_database_ // if there isn't enough space to accomplish anything, skip it if (msg->cursize + 24 > maxsize) - return; + return false; // prepare the buffer memset(&buf, 0, sizeof(buf)); @@ -1830,7 +1835,7 @@ void EntityFrame4_WriteFrame(sizebuf_t *msg, int maxsize, entityframe4_database_ break; // if commit buffer full, just don't bother writing an update this frame if (i == MAX_ENTITY_HISTORY) - return; + return false; d->currentcommit = d->commit + i; // this state's number gets played around with later @@ -1909,6 +1914,8 @@ void EntityFrame4_WriteFrame(sizebuf_t *msg, int maxsize, entityframe4_database_ MSG_WriteShort(msg, d->currententitynumber); // just to be sure d->currentcommit = NULL; + + return true; } @@ -2499,7 +2506,7 @@ void EntityFrame5_AckFrame(entityframe5_database_t *d, int framenum) d->packetlog[i].packetnumber = 0; } -void EntityFrame5_WriteFrame(sizebuf_t *msg, int maxsize, entityframe5_database_t *d, int numstates, const entity_state_t *states, int viewentnum, int movesequence, qboolean need_empty) +qboolean EntityFrame5_WriteFrame(sizebuf_t *msg, int maxsize, entityframe5_database_t *d, int numstates, const entity_state_t *states, int viewentnum, int movesequence, qboolean need_empty) { const entity_state_t *n; int i, num, l, framenum, packetlognumber, priority; @@ -2581,7 +2588,7 @@ void EntityFrame5_WriteFrame(sizebuf_t *msg, int maxsize, entityframe5_database_ // if there isn't at least enough room for an empty svc_entities, // don't bother trying... if (buf.cursize + 11 > buf.maxsize) - return; + return false; // build lists of entities by priority level memset(d->prioritychaincounts, 0, sizeof(d->prioritychaincounts)); @@ -2641,7 +2648,7 @@ void EntityFrame5_WriteFrame(sizebuf_t *msg, int maxsize, entityframe5_database_ // only send empty svc_entities frame if needed if(!l && !need_empty) - return; + return false; // add packetlog entry now that we have something for it if (!packetlog) @@ -2686,6 +2693,8 @@ void EntityFrame5_WriteFrame(sizebuf_t *msg, int maxsize, entityframe5_database_ } } MSG_WriteShort(msg, 0x8000); + + return true; } diff --git a/protocol.h b/protocol.h index c101e05f..7e01d764 100644 --- a/protocol.h +++ b/protocol.h @@ -388,7 +388,7 @@ void Protocol_UpdateClientStats(const int *stats); void Protocol_WriteStatsReliable(void); // writes a list of quake entities to the network stream // (or as many will fit) -void EntityFrameQuake_WriteFrame(sizebuf_t *msg, int maxsize, int numstates, const entity_state_t *states); +qboolean EntityFrameQuake_WriteFrame(sizebuf_t *msg, int maxsize, int numstates, const entity_state_t *states); // cleans up dead entities each frame after ReadEntity (which doesn't clear unused entities) void EntityFrameQuake_ISeeDeadEntities(void); @@ -577,7 +577,7 @@ void EntityFrame_FetchFrame(entityframe_database_t *d, int framenum, entity_fram // reference void EntityFrame_AddFrame(entityframe_database_t *d, vec3_t eye, int framenum, int numentities, const entity_state_t *entitydata); // (server) writes a frame to network stream -void EntityFrame_WriteFrame(sizebuf_t *msg, int maxsize, entityframe_database_t *d, int numstates, const entity_state_t *states, int viewentnum); +qboolean EntityFrame_WriteFrame(sizebuf_t *msg, int maxsize, entityframe_database_t *d, int numstates, const entity_state_t *states, int viewentnum); // (client) reads a frame from network stream void EntityFrame_CL_ReadFrame(void); // (client) returns the frame number of the most recent frame recieved @@ -631,7 +631,7 @@ void EntityFrame4_ResetDatabase(entityframe4_database_t *d); // updates database to account for a frame-received acknowledgment int EntityFrame4_AckFrame(entityframe4_database_t *d, int framenum, int servermode); // writes a frame to the network stream -void EntityFrame4_WriteFrame(sizebuf_t *msg, int maxsize, entityframe4_database_t *d, int numstates, const entity_state_t *states); +qboolean EntityFrame4_WriteFrame(sizebuf_t *msg, int maxsize, entityframe4_database_t *d, int numstates, const entity_state_t *states); // reads a frame from the network stream void EntityFrame4_CL_ReadFrame(void); @@ -788,7 +788,7 @@ int EntityState5_DeltaBitsForState(entity_state_t *o, entity_state_t *n); void EntityFrame5_CL_ReadFrame(void); void EntityFrame5_LostFrame(entityframe5_database_t *d, int framenum); void EntityFrame5_AckFrame(entityframe5_database_t *d, int framenum); -void EntityFrame5_WriteFrame(sizebuf_t *msg, int maxsize, entityframe5_database_t *d, int numstates, const entity_state_t *states, int viewentnum, int movesequence, qboolean need_empty); +qboolean EntityFrame5_WriteFrame(sizebuf_t *msg, int maxsize, entityframe5_database_t *d, int numstates, const entity_state_t *states, int viewentnum, int movesequence, qboolean need_empty); extern cvar_t developer_networkentities; diff --git a/server.h b/server.h index 0b21d37d..d05778d6 100644 --- a/server.h +++ b/server.h @@ -285,6 +285,10 @@ typedef struct client_s /// demo recording qfile_t *sv_demo_file; + + // number of skipped entity frames + // if it exceeds a limit, an empty entity frame is sent + int num_skippedentityframes; } client_t; diff --git a/sv_main.c b/sv_main.c index 0453ec3a..f18dd31e 100644 --- a/sv_main.c +++ b/sv_main.c @@ -1443,6 +1443,7 @@ void SV_WriteEntitiesToClient(client_t *client, prvm_edict_t *clent, sizebuf_t * int i, numsendstates; entity_state_t *s; prvm_edict_t *camera; + qboolean success; // if there isn't enough space to accomplish anything, skip it if (msg->cursize + 25 > maxsize) @@ -1491,23 +1492,31 @@ void SV_WriteEntitiesToClient(client_t *client, prvm_edict_t *clent, sizebuf_t * else EntityFrameCSQC_WriteFrame(msg, maxsize, numsendstates, sv.writeentitiestoclient_sendstates, 0); + if(client->num_skippedentityframes >= 5) + need_empty = true; // force every 5th frame to be not empty (or cl_movement replay takes too long) + if (client->entitydatabase5) - EntityFrame5_WriteFrame(msg, maxsize, client->entitydatabase5, numsendstates, sv.writeentitiestoclient_sendstates, client - svs.clients + 1, client->movesequence, need_empty); + success = EntityFrame5_WriteFrame(msg, maxsize, client->entitydatabase5, numsendstates, sv.writeentitiestoclient_sendstates, client - svs.clients + 1, client->movesequence, need_empty); else if (client->entitydatabase4) { - EntityFrame4_WriteFrame(msg, maxsize, client->entitydatabase4, numsendstates, sv.writeentitiestoclient_sendstates); + success = EntityFrame4_WriteFrame(msg, maxsize, client->entitydatabase4, numsendstates, sv.writeentitiestoclient_sendstates); Protocol_WriteStatsReliable(); } else if (client->entitydatabase) { - EntityFrame_WriteFrame(msg, maxsize, client->entitydatabase, numsendstates, sv.writeentitiestoclient_sendstates, client - svs.clients + 1); + success = EntityFrame_WriteFrame(msg, maxsize, client->entitydatabase, numsendstates, sv.writeentitiestoclient_sendstates, client - svs.clients + 1); Protocol_WriteStatsReliable(); } else { - EntityFrameQuake_WriteFrame(msg, maxsize, numsendstates, sv.writeentitiestoclient_sendstates); + success = EntityFrameQuake_WriteFrame(msg, maxsize, numsendstates, sv.writeentitiestoclient_sendstates); Protocol_WriteStatsReliable(); } + + if(success) + client->num_skippedentityframes = 0; + else + ++client->num_skippedentityframes; } /* -- 2.39.2