]> de.git.xonotic.org Git - xonotic/darkplaces.git/commitdiff
csqc packet log code (for entityframedatabase 5 only, i.e. DP5 and higher)
authordivverent <divverent@d7cf8633-e32d-0410-b094-e92efae38249>
Sat, 1 Nov 2008 17:44:32 +0000 (17:44 +0000)
committerdivverent <divverent@d7cf8633-e32d-0410-b094-e92efae38249>
Sat, 1 Nov 2008 17:44:32 +0000 (17:44 +0000)
git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@8544 d7cf8633-e32d-0410-b094-e92efae38249

protocol.c
protocol.h
server.h
sv_main.c
sv_user.c

index fa17b60eac79ebb88d166aaa6abda4c8784ef2df..d4931797b4aba6fedc29b87861bd0529af0e1972 100644 (file)
@@ -1,4 +1,3 @@
-
 #include "quakedef.h"
 
 #define ENTITYSIZEPROFILING_START(msg, num) \
@@ -264,12 +263,174 @@ void EntityFrameQuake_ISeeDeadEntities(void)
        }
 }
 
-// FIXME FIXME FIXME: at this time the CSQC entity writing does not store
-// packet logs and thus if an update is lost it is never repeated, this makes
-// csqc entities useless at the moment.
+// NOTE: this only works with DP5 protocol and upwards. For lower protocols
+// (including QUAKE), no packet loss handling for CSQC is done, which makes
+// CSQC basically useless.
+// Always use the DP5 protocol, or a higher one, when using CSQC entities.
+static void EntityFrameCSQC_LostAllFrames(client_t *client)
+{
+       // mark ALL csqc entities as requiring a FULL resend!
+       // I know this is a bad workaround, but better than nothing.
+       int i, n;
+       prvm_eval_t *val;
+       prvm_edict_t *ed;
+
+       if(prog->fieldoffsets.SendEntity < 0 || prog->fieldoffsets.Version < 0)
+               return;
+
+       n = client->csqcnumedicts;
+       for(i = 0; i < n; ++i)
+       {
+               if(client->csqcentityglobalhistory[i])
+               {
+                       ed = prog->edicts + i;
+                       val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.SendEntity);
+                       if (val->function)
+                               client->csqcentitysendflags[i] |= 0xFFFFFF; // FULL RESEND
+                       else // if it was ever sent to that client as a CSQC entity
+                       {
+                               client->csqcentityscope[i] = 1; // REMOVE
+                               client->csqcentitysendflags[i] |= 0xFFFFFF;
+                       }
+               }
+       }
+}
+void EntityFrameCSQC_LostFrame(client_t *client, int framenum)
+{
+       // marks a frame as lost
+       int i, j, n;
+       qboolean valid;
+       int ringfirst, ringlast;
+       int recoversendflags[MAX_EDICTS];
+       csqcentityframedb_t *d;
+
+       n = client->csqcnumedicts;
+
+       // is our frame out of history?
+       ringfirst = client->csqcentityframehistory_next; // oldest entry
+       ringlast = (ringfirst + NUM_CSQCENTITYDB_FRAMES - 1) % NUM_CSQCENTITYDB_FRAMES; // most recently added entry
+
+       valid = false;
+       
+       for(j = 0; j < NUM_CSQCENTITYDB_FRAMES; ++j)
+       {
+               d = &client->csqcentityframehistory[(ringfirst + j) % NUM_CSQCENTITYDB_FRAMES];
+               if(d->framenum < 0)
+                       continue;
+               if(d->framenum == framenum)
+                       break;
+               else if(d->framenum < framenum)
+                       valid = true;
+       }
+       if(j == NUM_CSQCENTITYDB_FRAMES)
+       {
+               if(valid) // got beaten, i.e. there is a frame < framenum
+               {
+                       // a non-csqc frame got lost... great
+                       return;
+               }
+               else
+               {
+                       // a too old frame got lost... sorry, cannot handle this
+                       Con_DPrintf("CSQC entity DB: lost a frame too early to do any handling (resending ALL)...\n");
+                       Con_DPrintf("Lost frame = %d\n", framenum);
+                       Con_DPrintf("Entity DB = %d to %d\n", client->csqcentityframehistory[ringfirst].framenum, client->csqcentityframehistory[ringlast].framenum);
+                       EntityFrameCSQC_LostAllFrames(client);
+               }
+               return;
+       }
+
+       // so j is the frame that got lost
+       // ringlast is the frame that we have to go to
+       ringfirst = (ringfirst + j) % NUM_CSQCENTITYDB_FRAMES;
+       if(ringlast < ringfirst)
+               ringlast += NUM_CSQCENTITYDB_FRAMES;
+       
+       memset(recoversendflags, 0, sizeof(recoversendflags));
+
+       for(j = ringfirst; j <= ringlast; ++j)
+       {
+               d = &client->csqcentityframehistory[j % NUM_CSQCENTITYDB_FRAMES];
+               if(d->framenum < 0)
+               {
+                       // deleted frame
+               }
+               else if(d->framenum < framenum)
+               {
+                       // a frame in the past... should never happen
+                       Con_Printf("CSQC entity DB encountered a frame from the past when recovering from PL...?\n");
+               }
+               else if(d->framenum == framenum)
+               {
+                       // handling the actually lost frame now
+                       for(i = 0; i < d->num; ++i)
+                       {
+                               int sf = d->sendflags[i];
+                               int ent = d->entno[i];
+                               if(sf < 0) // remove
+                                       recoversendflags[ent] |= -1; // all bits, including sign
+                               else if(sf > 0)
+                                       recoversendflags[ent] |= sf;
+                       }
+               }
+               else
+               {
+                       // handling the frames that followed it now
+                       for(i = 0; i < d->num; ++i)
+                       {
+                               int sf = d->sendflags[i];
+                               int ent = d->entno[i];
+                               if(sf < 0) // remove
+                               {
+                                       recoversendflags[ent] = 0; // no need to update, we got a more recent remove (and will fix it THEN)
+                                       break; // no flags left to remove...
+                               }
+                               else if(sf > 0)
+                                       recoversendflags[ent] &= ~sf; // no need to update these bits, we already got them later
+                       }
+               }
+       }
+
+       for(i = 0; i < client->csqcnumedicts; ++i)
+       {
+               if(recoversendflags[i] < 0)
+               {
+                       // a remove got lost, then either send a remove or - if it was
+                       // recreated later - a FULL update to make totally sure
+                       client->csqcentityscope[i] = 1;
+                       client->csqcentitysendflags[i] = 0xFFFFFF;
+               }
+               else
+                       client->csqcentitysendflags[i] |= recoversendflags[i];
+       }
+}
+static int EntityFrameCSQC_AllocFrame(client_t *client, int framenum)
+{
+       int ringfirst = client->csqcentityframehistory_next; // oldest entry
+       client->csqcentityframehistory_next += 1;
+       client->csqcentityframehistory_next %= NUM_CSQCENTITYDB_FRAMES;
+       client->csqcentityframehistory[ringfirst].framenum = framenum;
+       client->csqcentityframehistory[ringfirst].num = 0;
+       return ringfirst;
+}
+static void EntityFrameCSQC_DeallocFrame(client_t *client, int framenum)
+{
+       int ringfirst = client->csqcentityframehistory_next; // oldest entry
+       int ringlast = (ringfirst + NUM_CSQCENTITYDB_FRAMES - 1) % NUM_CSQCENTITYDB_FRAMES; // most recently added entry
+       if(framenum == client->csqcentityframehistory[ringlast].framenum)
+       {
+               client->csqcentityframehistory[ringlast].framenum = -1;
+               client->csqcentityframehistory[ringlast].num = 0;
+               client->csqcentityframehistory_next = ringlast;
+       }
+       else
+               Con_Printf("Trying to dealloc the wrong entity frame\n");
+}
 
 //[515]: we use only one array per-client for SendEntity feature
-void EntityFrameCSQC_WriteFrame (sizebuf_t *msg, int maxsize, int numstates, const entity_state_t *states)
+// TODO: add some handling for entity send priorities, to better deal with huge
+// amounts of csqc networked entities
+void EntityFrameCSQC_WriteFrame (sizebuf_t *msg, int maxsize, int numstates, const entity_state_t *states, int framenum)
 {
        int num, number, end, sendflags;
        qboolean sectionstarted = false;
@@ -277,6 +438,10 @@ void EntityFrameCSQC_WriteFrame (sizebuf_t *msg, int maxsize, int numstates, con
        prvm_edict_t *ed;
        prvm_eval_t *val;
        client_t *client = svs.clients + sv.writeentitiestoclient_clientnumber;
+       int dbframe = EntityFrameCSQC_AllocFrame(client, framenum);
+       csqcentityframedb_t *db = &client->csqcentityframehistory[dbframe];
+
+       maxsize -= 24; // always fit in an empty svc_entities message (for packet loss detection!)
 
        // if this server progs is not CSQC-aware, return early
        if(prog->fieldoffsets.SendEntity < 0 || prog->fieldoffsets.Version < 0)
@@ -349,6 +514,8 @@ void EntityFrameCSQC_WriteFrame (sizebuf_t *msg, int maxsize, int numstates, con
                sendflags = client->csqcentitysendflags[number];
                if (!sendflags)
                        continue;
+               if(db->num >= NUM_CSQCENTITIES_PER_FRAME)
+                       break;
                ed = prog->edicts + number;
                // entity scope is either update (2) or remove (1)
                if (client->csqcentityscope[number] == 1)
@@ -366,6 +533,10 @@ void EntityFrameCSQC_WriteFrame (sizebuf_t *msg, int maxsize, int numstates, con
                                MSG_WriteShort(msg, (unsigned short)number | 0x8000);
                                client->csqcentityscope[number] = 0;
                                client->csqcentitysendflags[number] = 0xFFFFFF; // resend completely if it becomes active again
+                               db->entno[db->num] = number;
+                               db->sendflags[db->num] = -1;
+                               db->num += 1;
+                               client->csqcentityglobalhistory[number] = 1;
                                ENTITYSIZEPROFILING_END(msg, number);
                        }
                        if (msg->cursize + 17 >= maxsize)
@@ -395,12 +566,16 @@ void EntityFrameCSQC_WriteFrame (sizebuf_t *msg, int maxsize, int numstates, con
                                        {
                                                // an update has been successfully written
                                                client->csqcentitysendflags[number] = 0;
+                                               db->entno[db->num] = number;
+                                               db->sendflags[db->num] = sendflags;
+                                               db->num += 1;
+                                               client->csqcentityglobalhistory[number] = 1;
                                                // and take note that we have begun the svc_csqcentities
                                                // section of the packet
                                                sectionstarted = 1;
+                                               ENTITYSIZEPROFILING_END(msg, number);
                                                if (msg->cursize + 17 >= maxsize)
                                                        break;
-                                               ENTITYSIZEPROFILING_END(msg, number);
                                                continue;
                                        }
                                }
@@ -417,6 +592,11 @@ void EntityFrameCSQC_WriteFrame (sizebuf_t *msg, int maxsize, int numstates, con
                // write index 0 to end the update (0 is never used by real entities)
                MSG_WriteShort(msg, 0);
        }
+
+       if(db->num == 0)
+               // if no single ent got added, remove the frame from the DB again, to allow
+               // for a larger history
+               EntityFrameCSQC_DeallocFrame(client, framenum);
 }
 
 void Protocol_UpdateClientStats(const int *stats)
@@ -1752,7 +1932,7 @@ void EntityFrame5_FreeDatabase(entityframe5_database_t *d)
        Mem_Free(d);
 }
 
-void EntityFrame5_ExpandEdicts(entityframe5_database_t *d, int newmax)
+static void EntityFrame5_ExpandEdicts(entityframe5_database_t *d, int newmax)
 {
        if (d->maxedicts < newmax)
        {
@@ -1783,7 +1963,7 @@ void EntityFrame5_ExpandEdicts(entityframe5_database_t *d, int newmax)
        }
 }
 
-int EntityState5_Priority(entityframe5_database_t *d, int stateindex)
+static int EntityState5_Priority(entityframe5_database_t *d, int stateindex)
 {
        int limit, priority;
        entity_state_t *s;
@@ -1972,7 +2152,7 @@ void EntityState5_WriteUpdate(int number, const entity_state_t *s, int changedbi
        ENTITYSIZEPROFILING_END(msg, s->number);
 }
 
-void EntityState5_ReadUpdate(entity_state_t *s, int number)
+static void EntityState5_ReadUpdate(entity_state_t *s, int number)
 {
        int bits;
        bits = MSG_ReadByte();
@@ -2138,7 +2318,7 @@ void EntityState5_ReadUpdate(entity_state_t *s, int number)
        }
 }
 
-int EntityState5_DeltaBits(const entity_state_t *o, const entity_state_t *n)
+static int EntityState5_DeltaBits(const entity_state_t *o, const entity_state_t *n)
 {
        unsigned int bits = 0;
        if (n->active)
index 8cb06002419da7f79652484bb2456dcb1d92b349..ec4a291a3a5272bbbe5c3d0c45cb16c939441822 100644 (file)
@@ -967,5 +967,9 @@ void EntityFrameQW_FreeDatabase(entityframeqw_database_t *d);
 void EntityStateQW_ReadPlayerUpdate(void);
 void EntityFrameQW_CL_ReadFrame(qboolean delta);
 
+struct client_s;
+void EntityFrameCSQC_LostFrame(struct client_s *client, int framenum);
+void EntityFrameCSQC_WriteFrame (sizebuf_t *msg, int maxsize, int numstates, const entity_state_t *states, int framenum);
+
 #endif
 
index 15b7c3d06763e34800b4e5f9ecb500757fe4b26b..fe11c2660fb8b3c6b841fea218cbf7034e7af058 100644 (file)
--- a/server.h
+++ b/server.h
@@ -152,6 +152,15 @@ typedef struct server_s
        unsigned char csqcentityversion[MAX_EDICTS]; // legacy
 } server_t;
 
+#define NUM_CSQCENTITIES_PER_FRAME 1024
+typedef struct csqcentityframedb_s
+{
+       int framenum;
+       int num;
+       unsigned short entno[NUM_CSQCENTITIES_PER_FRAME];
+       int sendflags[NUM_CSQCENTITIES_PER_FRAME];
+} csqcentityframedb_t;
+
 // if defined this does ping smoothing, otherwise it does not
 //#define NUM_PING_TIMES 16
 
@@ -225,6 +234,11 @@ typedef struct client_s
        unsigned char csqcentityscope[MAX_EDICTS];
        unsigned int csqcentitysendflags[MAX_EDICTS];
 
+#define NUM_CSQCENTITYDB_FRAMES 64
+       unsigned char csqcentityglobalhistory[MAX_EDICTS]; // set to 1 if the entity was ever csqc networked to the client, and never reset back to 0
+       csqcentityframedb_t csqcentityframehistory[NUM_CSQCENTITYDB_FRAMES];
+       int csqcentityframehistory_next;
+
        // prevent animated names
        float nametime;
 
index bc6de193f5d40dcfd781c806bb7eeb8d5d799f27..8caa1788c95bb406910a3c87ad978535e2fb3c91 100644 (file)
--- a/sv_main.c
+++ b/sv_main.c
@@ -31,7 +31,6 @@ static void SV_VM_Setup();
 
 void VM_CustomStats_Clear (void);
 void VM_SV_UpdateCustomStats (client_t *client, prvm_edict_t *ent, sizebuf_t *msg, int *stats);
-void EntityFrameCSQC_WriteFrame (sizebuf_t *msg, int maxsize, int numstates, const entity_state_t *states);
 
 cvar_t coop = {0, "coop","0", "coop mode, 0 = no coop, 1 = coop mode, multiple players playing through the singleplayer game (coop mode also shuts off deathmatch)"};
 cvar_t deathmatch = {0, "deathmatch","0", "deathmatch mode, values depend on mod but typically 0 = no deathmatch, 1 = normal deathmatch with respawning weapons, 2 = weapons stay (players can only pick up new weapons)"};
@@ -738,7 +737,15 @@ void SV_SendServerinfo (client_t *client)
        {
                client->csqcentityscope[i] = 0;
                client->csqcentitysendflags[i] = 0xFFFFFF;
+               client->csqcentityglobalhistory[i] = 0;
        }
+       for (i = 0;i < NUM_CSQCENTITYDB_FRAMES;i++)
+       {
+               client->csqcentityframehistory[i].num = 0;
+               client->csqcentityframehistory[i].framenum = -1;
+       }
+       client->csqcnumedicts = 0;
+       client->csqcentityframehistory_next = 0;
 
        SZ_Clear (&client->netconnection->message);
        MSG_WriteByte (&client->netconnection->message, svc_print);
@@ -1447,7 +1454,10 @@ void SV_WriteEntitiesToClient(client_t *client, prvm_edict_t *clent, sizebuf_t *
        if (sv_cullentities_stats.integer)
                Con_Printf("client \"%s\" entities: %d total, %d visible, %d culled by: %d pvs %d trace\n", client->name, sv.writeentitiestoclient_stats_totalentities, sv.writeentitiestoclient_stats_visibleentities, sv.writeentitiestoclient_stats_culled_pvs + sv.writeentitiestoclient_stats_culled_trace, sv.writeentitiestoclient_stats_culled_pvs, sv.writeentitiestoclient_stats_culled_trace);
 
-       EntityFrameCSQC_WriteFrame(msg, maxsize, numsendstates, sv.writeentitiestoclient_sendstates);
+       if(client->entitydatabase5)
+               EntityFrameCSQC_WriteFrame(msg, maxsize, numsendstates, sv.writeentitiestoclient_sendstates, client->entitydatabase5->latestframenum + 1);
+       else
+               EntityFrameCSQC_WriteFrame(msg, maxsize, numsendstates, sv.writeentitiestoclient_sendstates, 0);
 
        if (client->entitydatabase5)
                EntityFrame5_WriteFrame(msg, maxsize, client->entitydatabase5, numsendstates, sv.writeentitiestoclient_sendstates, client - svs.clients + 1, client->movesequence);
index 91f505c185c361d1294d6dbaec4512a4c41b9cec..78ff1f9fdb5af10e3f28fd112407302fcf6431e7 100644 (file)
--- a/sv_user.c
+++ b/sv_user.c
@@ -706,7 +706,10 @@ void SV_ApplyClientMove (void)
 void SV_FrameLost(int framenum)
 {
        if (host_client->entitydatabase5)
+       {
                EntityFrame5_LostFrame(host_client->entitydatabase5, framenum);
+               EntityFrameCSQC_LostFrame(host_client, framenum);
+       }
 }
 
 void SV_FrameAck(int framenum)