]> git.xonotic.org Git - xonotic/darkplaces.git/commitdiff
(Round 1) Break up protocol.c
authorcloudwalk <cloudwalk@d7cf8633-e32d-0410-b094-e92efae38249>
Wed, 1 Jul 2020 19:39:50 +0000 (19:39 +0000)
committercloudwalk <cloudwalk@d7cf8633-e32d-0410-b094-e92efae38249>
Wed, 1 Jul 2020 19:39:50 +0000 (19:39 +0000)
Different entframe functions for different protocols warrant separate
files for maintainability and readability purposes.

git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@12772 d7cf8633-e32d-0410-b094-e92efae38249

cl_entframe.c [new file with mode: 0644]
cl_entframe_quake.c [new file with mode: 0644]
darkplaces-sdl2-vs2017.vcxproj
darkplaces-sdl2-vs2019.vcxproj
makefile.inc
protocol.c
protocol.h
sv_entframe_csqc.c [new file with mode: 0644]
sv_entframe_quake.c [new file with mode: 0644]

diff --git a/cl_entframe.c b/cl_entframe.c
new file mode 100644 (file)
index 0000000..18ea48a
--- /dev/null
@@ -0,0 +1,191 @@
+#include "quakedef.h"
+#include "protocol.h"
+
+// (client) adds a entity_frame to the database, for future reference
+void EntityFrame_AddFrame_Client(entityframe_database_t *d, vec3_t eye, int framenum, int numentities, const entity_state_t *entitydata)
+{
+       int n, e;
+       entity_frameinfo_t *info;
+
+       VectorCopy(eye, d->eye);
+
+       // figure out how many entity slots are used already
+       if (d->numframes)
+       {
+               n = d->frames[d->numframes - 1].endentity - d->frames[0].firstentity;
+               if (n + numentities > MAX_ENTITY_DATABASE || d->numframes >= MAX_ENTITY_HISTORY)
+               {
+                       // ran out of room, dump database
+                       EntityFrame_ClearDatabase(d);
+               }
+       }
+
+       info = &d->frames[d->numframes];
+       info->framenum = framenum;
+       e = -1000;
+       // make sure we check the newly added frame as well, but we haven't incremented numframes yet
+       for (n = 0;n <= d->numframes;n++)
+       {
+               if (e >= d->frames[n].framenum)
+               {
+                       if (e == framenum)
+                               Con_Print("EntityFrame_AddFrame: tried to add out of sequence frame to database\n");
+                       else
+                               Con_Print("EntityFrame_AddFrame: out of sequence frames in database\n");
+                       return;
+               }
+               e = d->frames[n].framenum;
+       }
+       // if database still has frames after that...
+       if (d->numframes)
+               info->firstentity = d->frames[d->numframes - 1].endentity;
+       else
+               info->firstentity = 0;
+       info->endentity = info->firstentity + numentities;
+       d->numframes++;
+
+       n = info->firstentity % MAX_ENTITY_DATABASE;
+       e = MAX_ENTITY_DATABASE - n;
+       if (e > numentities)
+               e = numentities;
+       memcpy(d->entitydata + n, entitydata, sizeof(entity_state_t) * e);
+       if (numentities > e)
+               memcpy(d->entitydata, entitydata + e, sizeof(entity_state_t) * (numentities - e));
+}
+
+// (client) reads a frame from network stream
+void EntityFrame_CL_ReadFrame(void)
+{
+       int i, number, removed;
+       entity_frame_t *f, *delta;
+       entity_state_t *e, *old, *oldend;
+       entity_t *ent;
+       entityframe_database_t *d;
+       if (!cl.entitydatabase)
+               cl.entitydatabase = EntityFrame_AllocDatabase(cls.levelmempool);
+       d = cl.entitydatabase;
+       f = &d->framedata;
+       delta = &d->deltaframe;
+
+       EntityFrame_Clear(f, NULL, -1);
+
+       // read the frame header info
+       f->time = cl.mtime[0];
+       number = MSG_ReadLong(&cl_message);
+       f->framenum = MSG_ReadLong(&cl_message);
+       CL_NewFrameReceived(f->framenum);
+       f->eye[0] = MSG_ReadFloat(&cl_message);
+       f->eye[1] = MSG_ReadFloat(&cl_message);
+       f->eye[2] = MSG_ReadFloat(&cl_message);
+       EntityFrame_AckFrame(d, number);
+       EntityFrame_FetchFrame(d, number, delta);
+       old = delta->entitydata;
+       oldend = old + delta->numentities;
+       // read entities until we hit the magic 0xFFFF end tag
+       while ((number = (unsigned short) MSG_ReadShort(&cl_message)) != 0xFFFF && !cl_message.badread)
+       {
+               if (cl_message.badread)
+                       Host_Error("EntityFrame_Read: read error");
+               removed = number & 0x8000;
+               number &= 0x7FFF;
+               if (number >= MAX_EDICTS)
+                       Host_Error("EntityFrame_Read: number (%i) >= MAX_EDICTS (%i)", number, MAX_EDICTS);
+
+               // seek to entity, while copying any skipped entities (assume unchanged)
+               while (old < oldend && old->number < number)
+               {
+                       if (f->numentities >= MAX_ENTITY_DATABASE)
+                               Host_Error("EntityFrame_Read: entity list too big");
+                       f->entitydata[f->numentities] = *old++;
+                       f->entitydata[f->numentities++].time = cl.mtime[0];
+               }
+               if (removed)
+               {
+                       if (old < oldend && old->number == number)
+                               old++;
+                       else
+                               Con_Printf("EntityFrame_Read: REMOVE on unused entity %i\n", number);
+               }
+               else
+               {
+                       if (f->numentities >= MAX_ENTITY_DATABASE)
+                               Host_Error("EntityFrame_Read: entity list too big");
+
+                       // reserve this slot
+                       e = f->entitydata + f->numentities++;
+
+                       if (old < oldend && old->number == number)
+                       {
+                               // delta from old entity
+                               *e = *old++;
+                       }
+                       else
+                       {
+                               // delta from defaults
+                               *e = defaultstate;
+                       }
+
+                       if (cl.num_entities <= number)
+                       {
+                               cl.num_entities = number + 1;
+                               if (number >= cl.max_entities)
+                                       CL_ExpandEntities(number);
+                       }
+                       cl.entities_active[number] = true;
+                       e->active = ACTIVE_NETWORK;
+                       e->time = cl.mtime[0];
+                       e->number = number;
+                       EntityState_ReadFields(e, EntityState_ReadExtendBits());
+               }
+       }
+       while (old < oldend)
+       {
+               if (f->numentities >= MAX_ENTITY_DATABASE)
+                       Host_Error("EntityFrame_Read: entity list too big");
+               f->entitydata[f->numentities] = *old++;
+               f->entitydata[f->numentities++].time = cl.mtime[0];
+       }
+       EntityFrame_AddFrame_Client(d, f->eye, f->framenum, f->numentities, f->entitydata);
+
+       memset(cl.entities_active, 0, cl.num_entities * sizeof(unsigned char));
+       number = 1;
+       for (i = 0;i < f->numentities;i++)
+       {
+               for (;number < f->entitydata[i].number && number < cl.num_entities;number++)
+               {
+                       if (cl.entities_active[number])
+                       {
+                               cl.entities_active[number] = false;
+                               cl.entities[number].state_current.active = ACTIVE_NOT;
+                       }
+               }
+               if (number >= cl.num_entities)
+                       break;
+               // update the entity
+               ent = &cl.entities[number];
+               ent->state_previous = ent->state_current;
+               ent->state_current = f->entitydata[i];
+               CL_MoveLerpEntityStates(ent);
+               // the entity lives again...
+               cl.entities_active[number] = true;
+               number++;
+       }
+       for (;number < cl.num_entities;number++)
+       {
+               if (cl.entities_active[number])
+               {
+                       cl.entities_active[number] = false;
+                       cl.entities[number].state_current.active = ACTIVE_NOT;
+               }
+       }
+}
+
+
+// (client) returns the frame number of the most recent frame recieved
+int EntityFrame_MostRecentlyRecievedFrameNum(entityframe_database_t *d)
+{
+       if (d->numframes)
+               return d->frames[d->numframes - 1].framenum;
+       else
+               return -1;
+}
\ No newline at end of file
diff --git a/cl_entframe_quake.c b/cl_entframe_quake.c
new file mode 100644 (file)
index 0000000..0edd34e
--- /dev/null
@@ -0,0 +1,142 @@
+#include "quakedef.h"
+#include "protocol.h"
+
+void EntityFrameQuake_ReadEntity(int bits)
+{
+       int num;
+       entity_t *ent;
+       entity_state_t s;
+
+       if (bits & U_MOREBITS)
+               bits |= (MSG_ReadByte(&cl_message)<<8);
+       if ((bits & U_EXTEND1) && cls.protocol != PROTOCOL_NEHAHRAMOVIE)
+       {
+               bits |= MSG_ReadByte(&cl_message) << 16;
+               if (bits & U_EXTEND2)
+                       bits |= MSG_ReadByte(&cl_message) << 24;
+       }
+
+       if (bits & U_LONGENTITY)
+               num = (unsigned short) MSG_ReadShort(&cl_message);
+       else
+               num = MSG_ReadByte(&cl_message);
+
+       if (num >= MAX_EDICTS)
+               Host_Error("EntityFrameQuake_ReadEntity: entity number (%i) >= MAX_EDICTS (%i)", num, MAX_EDICTS);
+       if (num < 1)
+               Host_Error("EntityFrameQuake_ReadEntity: invalid entity number (%i)", num);
+
+       if (cl.num_entities <= num)
+       {
+               cl.num_entities = num + 1;
+               if (num >= cl.max_entities)
+                       CL_ExpandEntities(num);
+       }
+
+       ent = cl.entities + num;
+
+       // note: this inherits the 'active' state of the baseline chosen
+       // (state_baseline is always active, state_current may not be active if
+       // the entity was missing in the last frame)
+       if (bits & U_DELTA)
+               s = ent->state_current;
+       else
+       {
+               s = ent->state_baseline;
+               s.active = ACTIVE_NETWORK;
+       }
+
+       cl.isquakeentity[num] = true;
+       if (cl.lastquakeentity < num)
+               cl.lastquakeentity = num;
+       s.number = num;
+       s.time = cl.mtime[0];
+       s.flags = 0;
+       if (bits & U_MODEL)
+       {
+               if (cls.protocol == PROTOCOL_NEHAHRABJP || cls.protocol == PROTOCOL_NEHAHRABJP2 || cls.protocol == PROTOCOL_NEHAHRABJP3)
+                                                       s.modelindex = (unsigned short) MSG_ReadShort(&cl_message);
+               else
+                                                       s.modelindex = (s.modelindex & 0xFF00) | MSG_ReadByte(&cl_message);
+       }
+       if (bits & U_FRAME)             s.frame = (s.frame & 0xFF00) | MSG_ReadByte(&cl_message);
+       if (bits & U_COLORMAP)  s.colormap = MSG_ReadByte(&cl_message);
+       if (bits & U_SKIN)              s.skin = MSG_ReadByte(&cl_message);
+       if (bits & U_EFFECTS)   s.effects = (s.effects & 0xFF00) | MSG_ReadByte(&cl_message);
+       if (bits & U_ORIGIN1)   s.origin[0] = MSG_ReadCoord(&cl_message, cls.protocol);
+       if (bits & U_ANGLE1)    s.angles[0] = MSG_ReadAngle(&cl_message, cls.protocol);
+       if (bits & U_ORIGIN2)   s.origin[1] = MSG_ReadCoord(&cl_message, cls.protocol);
+       if (bits & U_ANGLE2)    s.angles[1] = MSG_ReadAngle(&cl_message, cls.protocol);
+       if (bits & U_ORIGIN3)   s.origin[2] = MSG_ReadCoord(&cl_message, cls.protocol);
+       if (bits & U_ANGLE3)    s.angles[2] = MSG_ReadAngle(&cl_message, cls.protocol);
+       if (bits & U_STEP)              s.flags |= RENDER_STEP;
+       if (bits & U_ALPHA)             s.alpha = MSG_ReadByte(&cl_message);
+       if (bits & U_SCALE)             s.scale = MSG_ReadByte(&cl_message);
+       if (bits & U_EFFECTS2)  s.effects = (s.effects & 0x00FF) | (MSG_ReadByte(&cl_message) << 8);
+       if (bits & U_GLOWSIZE)  s.glowsize = MSG_ReadByte(&cl_message);
+       if (bits & U_GLOWCOLOR) s.glowcolor = MSG_ReadByte(&cl_message);
+       if (bits & U_COLORMOD)  {int c = MSG_ReadByte(&cl_message);s.colormod[0] = (unsigned char)(((c >> 5) & 7) * (32.0f / 7.0f));s.colormod[1] = (unsigned char)(((c >> 2) & 7) * (32.0f / 7.0f));s.colormod[2] = (unsigned char)((c & 3) * (32.0f / 3.0f));}
+       if (bits & U_GLOWTRAIL) s.flags |= RENDER_GLOWTRAIL;
+       if (bits & U_FRAME2)    s.frame = (s.frame & 0x00FF) | (MSG_ReadByte(&cl_message) << 8);
+       if (bits & U_MODEL2)    s.modelindex = (s.modelindex & 0x00FF) | (MSG_ReadByte(&cl_message) << 8);
+       if (bits & U_VIEWMODEL) s.flags |= RENDER_VIEWMODEL;
+       if (bits & U_EXTERIORMODEL)     s.flags |= RENDER_EXTERIORMODEL;
+
+       // LadyHavoc: to allow playback of the Nehahra movie
+       if (cls.protocol == PROTOCOL_NEHAHRAMOVIE && (bits & U_EXTEND1))
+       {
+               // LadyHavoc: evil format
+               int i = (int)MSG_ReadFloat(&cl_message);
+               int j = (int)(MSG_ReadFloat(&cl_message) * 255.0f);
+               if (i == 2)
+               {
+                       i = (int)MSG_ReadFloat(&cl_message);
+                       if (i)
+                               s.effects |= EF_FULLBRIGHT;
+               }
+               if (j < 0)
+                       s.alpha = 0;
+               else if (j == 0 || j >= 255)
+                       s.alpha = 255;
+               else
+                       s.alpha = j;
+       }
+
+       ent->state_previous = ent->state_current;
+       ent->state_current = s;
+       if (ent->state_current.active == ACTIVE_NETWORK)
+       {
+               CL_MoveLerpEntityStates(ent);
+               cl.entities_active[ent->state_current.number] = true;
+       }
+
+       if (cl_message.badread)
+               Host_Error("EntityFrameQuake_ReadEntity: read error");
+}
+
+void EntityFrameQuake_ISeeDeadEntities(void)
+{
+       int num, lastentity;
+       if (cl.lastquakeentity == 0)
+               return;
+       lastentity = cl.lastquakeentity;
+       cl.lastquakeentity = 0;
+       for (num = 0;num <= lastentity;num++)
+       {
+               if (cl.isquakeentity[num])
+               {
+                       if (cl.entities_active[num] && cl.entities[num].state_current.time == cl.mtime[0])
+                       {
+                               cl.isquakeentity[num] = true;
+                               cl.lastquakeentity = num;
+                       }
+                       else
+                       {
+                               cl.isquakeentity[num] = false;
+                               cl.entities_active[num] = ACTIVE_NOT;
+                               cl.entities[num].state_current = defaultstate;
+                               cl.entities[num].state_current.number = num;
+                       }
+               }
+       }
+}
\ No newline at end of file
index c292a78270733c627c040d4effef82377e741635..680594339583acaa42d098db7a4f9cee1b36088e 100644 (file)
        <ClCompile Include="cl_cmd.c" />        \r
     <ClCompile Include="cl_collision.c" />\r
     <ClCompile Include="cl_demo.c" />\r
+       <ClCompile Include="cl_entframe.c" />\r
+       <ClCompile Include="cl_entframe_quake.c" />\r
     <ClCompile Include="cl_input.c" />\r
     <ClCompile Include="cl_main.c" />\r
     <ClCompile Include="cl_parse.c" />\r
        <ClInclude Include="snd_xmp.c" />\r
     <ClCompile Include="sv_ccmds.c" />\r
     <ClCompile Include="sv_demo.c" />\r
+       <ClCompile Include="sv_entframe_quake.c" />\r
+       <ClCompile Include="sv_entframe_csqc.c" />\r
     <ClCompile Include="sv_main.c" />\r
     <ClCompile Include="sv_move.c" />\r
     <ClCompile Include="sv_phys.c" />\r
index 3b4ea935fe298e57a81296fa0e40419afe6b940d..9f74d90ac21f15aaebab2483ca8fbefd4230bb18 100644 (file)
        <ClCompile Include="cl_cmd.c" />\r
     <ClCompile Include="cl_collision.c" />\r
     <ClCompile Include="cl_demo.c" />\r
+       <ClCompile Include="cl_entframe.c" />\r
+       <ClCompile Include="cl_entframe_quake.c" />     \r
     <ClCompile Include="cl_input.c" />\r
     <ClCompile Include="cl_main.c" />\r
     <ClCompile Include="cl_parse.c" />\r
        <ClCompile Include="snd_xmp.c" />\r
     <ClCompile Include="sv_ccmds.c" />\r
     <ClCompile Include="sv_demo.c" />\r
+       <ClCompile Include="sv_entframe_quake.c" />\r
+       <ClCompile Include="sv_entframe_csqc.c" />\r
     <ClCompile Include="sv_main.c" />\r
     <ClCompile Include="sv_move.c" />\r
     <ClCompile Include="sv_phys.c" />\r
index a69e8fa493407630b77ba49782d90cfd30d47f01..36aad5f7fd2d5d69bb27e97d9f4310c9a52b9f43 100644 (file)
@@ -82,6 +82,8 @@ OBJ_COMMON= \
        cl_cmd.o \
        cl_collision.o \
        cl_demo.o \
+       cl_entframe.o \
+       cl_entframe_quake.o \
        cl_input.o \
        cl_main.o \
        cl_parse.o \
@@ -144,6 +146,8 @@ OBJ_COMMON= \
        sbar.o \
        sv_ccmds.o \
        sv_demo.o \
+       sv_entframe_csqc.o \
+       sv_entframe_quake.o \
        sv_main.o \
        sv_move.o \
        sv_phys.o \
index 073828ee21f9768383d75f99e12e66d733fafa19..6db5495849d0ac95c51fb08b5d28b2da23e1dab6 100644 (file)
@@ -1,22 +1,5 @@
 #include "quakedef.h"
 
-#define ENTITYSIZEPROFILING_START(msg, num, flags) \
-       int entityprofiling_startsize = msg->cursize
-
-#define ENTITYSIZEPROFILING_END(msg, num, flags) \
-       if(developer_networkentities.integer >= 2) \
-       { \
-               prvm_edict_t *edict = prog->edicts + num; \
-               Con_Printf("sent entity update of size %u for %d classname %s flags %d\n", (msg->cursize - entityprofiling_startsize), num, PRVM_serveredictstring(edict, classname) ? PRVM_GetString(prog, PRVM_serveredictstring(edict, classname)) : "(no classname)", flags); \
-       }
-
-// CSQC entity scope values. Bitflags!
-#define SCOPE_WANTREMOVE 1        // Set if a remove has been scheduled. Never set together with WANTUPDATE.
-#define SCOPE_WANTUPDATE 2        // Set if an update has been scheduled.
-#define SCOPE_WANTSEND (SCOPE_WANTREMOVE | SCOPE_WANTUPDATE)
-#define SCOPE_EXISTED_ONCE 4      // Set if the entity once existed. All these get resent on a full loss.
-#define SCOPE_ASSUMED_EXISTING 8  // Set if the entity is currently assumed existing and therefore needs removes.
-
 // this is 88 bytes (must match entity_state_t in protocol.h)
 entity_state_t defaultstate =
 {
@@ -131,494 +114,6 @@ void Protocol_Names(char *buffer, size_t buffersize)
        }
 }
 
-void EntityFrameQuake_ReadEntity(int bits)
-{
-       int num;
-       entity_t *ent;
-       entity_state_t s;
-
-       if (bits & U_MOREBITS)
-               bits |= (MSG_ReadByte(&cl_message)<<8);
-       if ((bits & U_EXTEND1) && cls.protocol != PROTOCOL_NEHAHRAMOVIE)
-       {
-               bits |= MSG_ReadByte(&cl_message) << 16;
-               if (bits & U_EXTEND2)
-                       bits |= MSG_ReadByte(&cl_message) << 24;
-       }
-
-       if (bits & U_LONGENTITY)
-               num = (unsigned short) MSG_ReadShort(&cl_message);
-       else
-               num = MSG_ReadByte(&cl_message);
-
-       if (num >= MAX_EDICTS)
-               Host_Error("EntityFrameQuake_ReadEntity: entity number (%i) >= MAX_EDICTS (%i)", num, MAX_EDICTS);
-       if (num < 1)
-               Host_Error("EntityFrameQuake_ReadEntity: invalid entity number (%i)", num);
-
-       if (cl.num_entities <= num)
-       {
-               cl.num_entities = num + 1;
-               if (num >= cl.max_entities)
-                       CL_ExpandEntities(num);
-       }
-
-       ent = cl.entities + num;
-
-       // note: this inherits the 'active' state of the baseline chosen
-       // (state_baseline is always active, state_current may not be active if
-       // the entity was missing in the last frame)
-       if (bits & U_DELTA)
-               s = ent->state_current;
-       else
-       {
-               s = ent->state_baseline;
-               s.active = ACTIVE_NETWORK;
-       }
-
-       cl.isquakeentity[num] = true;
-       if (cl.lastquakeentity < num)
-               cl.lastquakeentity = num;
-       s.number = num;
-       s.time = cl.mtime[0];
-       s.flags = 0;
-       if (bits & U_MODEL)
-       {
-               if (cls.protocol == PROTOCOL_NEHAHRABJP || cls.protocol == PROTOCOL_NEHAHRABJP2 || cls.protocol == PROTOCOL_NEHAHRABJP3)
-                                                       s.modelindex = (unsigned short) MSG_ReadShort(&cl_message);
-               else
-                                                       s.modelindex = (s.modelindex & 0xFF00) | MSG_ReadByte(&cl_message);
-       }
-       if (bits & U_FRAME)             s.frame = (s.frame & 0xFF00) | MSG_ReadByte(&cl_message);
-       if (bits & U_COLORMAP)  s.colormap = MSG_ReadByte(&cl_message);
-       if (bits & U_SKIN)              s.skin = MSG_ReadByte(&cl_message);
-       if (bits & U_EFFECTS)   s.effects = (s.effects & 0xFF00) | MSG_ReadByte(&cl_message);
-       if (bits & U_ORIGIN1)   s.origin[0] = MSG_ReadCoord(&cl_message, cls.protocol);
-       if (bits & U_ANGLE1)    s.angles[0] = MSG_ReadAngle(&cl_message, cls.protocol);
-       if (bits & U_ORIGIN2)   s.origin[1] = MSG_ReadCoord(&cl_message, cls.protocol);
-       if (bits & U_ANGLE2)    s.angles[1] = MSG_ReadAngle(&cl_message, cls.protocol);
-       if (bits & U_ORIGIN3)   s.origin[2] = MSG_ReadCoord(&cl_message, cls.protocol);
-       if (bits & U_ANGLE3)    s.angles[2] = MSG_ReadAngle(&cl_message, cls.protocol);
-       if (bits & U_STEP)              s.flags |= RENDER_STEP;
-       if (bits & U_ALPHA)             s.alpha = MSG_ReadByte(&cl_message);
-       if (bits & U_SCALE)             s.scale = MSG_ReadByte(&cl_message);
-       if (bits & U_EFFECTS2)  s.effects = (s.effects & 0x00FF) | (MSG_ReadByte(&cl_message) << 8);
-       if (bits & U_GLOWSIZE)  s.glowsize = MSG_ReadByte(&cl_message);
-       if (bits & U_GLOWCOLOR) s.glowcolor = MSG_ReadByte(&cl_message);
-       if (bits & U_COLORMOD)  {int c = MSG_ReadByte(&cl_message);s.colormod[0] = (unsigned char)(((c >> 5) & 7) * (32.0f / 7.0f));s.colormod[1] = (unsigned char)(((c >> 2) & 7) * (32.0f / 7.0f));s.colormod[2] = (unsigned char)((c & 3) * (32.0f / 3.0f));}
-       if (bits & U_GLOWTRAIL) s.flags |= RENDER_GLOWTRAIL;
-       if (bits & U_FRAME2)    s.frame = (s.frame & 0x00FF) | (MSG_ReadByte(&cl_message) << 8);
-       if (bits & U_MODEL2)    s.modelindex = (s.modelindex & 0x00FF) | (MSG_ReadByte(&cl_message) << 8);
-       if (bits & U_VIEWMODEL) s.flags |= RENDER_VIEWMODEL;
-       if (bits & U_EXTERIORMODEL)     s.flags |= RENDER_EXTERIORMODEL;
-
-       // LadyHavoc: to allow playback of the Nehahra movie
-       if (cls.protocol == PROTOCOL_NEHAHRAMOVIE && (bits & U_EXTEND1))
-       {
-               // LadyHavoc: evil format
-               int i = (int)MSG_ReadFloat(&cl_message);
-               int j = (int)(MSG_ReadFloat(&cl_message) * 255.0f);
-               if (i == 2)
-               {
-                       i = (int)MSG_ReadFloat(&cl_message);
-                       if (i)
-                               s.effects |= EF_FULLBRIGHT;
-               }
-               if (j < 0)
-                       s.alpha = 0;
-               else if (j == 0 || j >= 255)
-                       s.alpha = 255;
-               else
-                       s.alpha = j;
-       }
-
-       ent->state_previous = ent->state_current;
-       ent->state_current = s;
-       if (ent->state_current.active == ACTIVE_NETWORK)
-       {
-               CL_MoveLerpEntityStates(ent);
-               cl.entities_active[ent->state_current.number] = true;
-       }
-
-       if (cl_message.badread)
-               Host_Error("EntityFrameQuake_ReadEntity: read error");
-}
-
-void EntityFrameQuake_ISeeDeadEntities(void)
-{
-       int num, lastentity;
-       if (cl.lastquakeentity == 0)
-               return;
-       lastentity = cl.lastquakeentity;
-       cl.lastquakeentity = 0;
-       for (num = 0;num <= lastentity;num++)
-       {
-               if (cl.isquakeentity[num])
-               {
-                       if (cl.entities_active[num] && cl.entities[num].state_current.time == cl.mtime[0])
-                       {
-                               cl.isquakeentity[num] = true;
-                               cl.lastquakeentity = num;
-                       }
-                       else
-                       {
-                               cl.isquakeentity[num] = false;
-                               cl.entities_active[num] = ACTIVE_NOT;
-                               cl.entities[num].state_current = defaultstate;
-                               cl.entities[num].state_current.number = num;
-                       }
-               }
-       }
-}
-
-// 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)
-{
-       prvm_prog_t *prog = SVVM_prog;
-       // mark ALL csqc entities as requiring a FULL resend!
-       // I know this is a bad workaround, but better than nothing.
-       int i, n;
-       prvm_edict_t *ed;
-
-       n = client->csqcnumedicts;
-       for(i = 0; i < n; ++i)
-       {
-               if(client->csqcentityscope[i] & SCOPE_EXISTED_ONCE)
-               {
-                       ed = prog->edicts + i;
-                       client->csqcentitysendflags[i] |= 0xFFFFFF;  // FULL RESEND. We can't clear SCOPE_ASSUMED_EXISTING yet as this would cancel removes on a rejected send attempt.
-                       if (!PRVM_serveredictfunction(ed, SendEntity))  // If it was ever sent to that client as a CSQC entity...
-                               client->csqcentityscope[i] |= SCOPE_ASSUMED_EXISTING;  // FORCE REMOVE.
-               }
-       }
-}
-void EntityFrameCSQC_LostFrame(client_t *client, int framenum)
-{
-       // marks a frame as lost
-       int i, j;
-       qboolean valid;
-       int ringfirst, ringlast;
-       static int recoversendflags[MAX_EDICTS]; // client only
-       csqcentityframedb_t *d;
-
-       if(client->csqcentityframe_lastreset < 0)
-               return;
-       if(framenum < client->csqcentityframe_lastreset)
-               return; // no action required, as we resent that data anyway
-
-       // 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);
-                       client->csqcentityframe_lastreset = -1;
-               }
-               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)
-                       client->csqcentityscope[i] |= SCOPE_ASSUMED_EXISTING;  // FORCE REMOVE.
-               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
-// TODO: add some handling for entity send priorities, to better deal with huge
-// amounts of csqc networked entities
-qboolean EntityFrameCSQC_WriteFrame (sizebuf_t *msg, int maxsize, int numnumbers, const unsigned short *numbers, int framenum)
-{
-       prvm_prog_t *prog = SVVM_prog;
-       int num, number, end, sendflags;
-       qboolean sectionstarted = false;
-       const unsigned short *n;
-       prvm_edict_t *ed;
-       client_t *client = svs.clients + sv.writeentitiestoclient_clientnumber;
-       int dbframe = EntityFrameCSQC_AllocFrame(client, framenum);
-       csqcentityframedb_t *db = &client->csqcentityframehistory[dbframe];
-
-       if(client->csqcentityframe_lastreset < 0)
-               client->csqcentityframe_lastreset = framenum;
-
-       maxsize -= 24; // always fit in an empty svc_entities message (for packet loss detection!)
-
-       // make sure there is enough room to store the svc_csqcentities byte,
-       // the terminator (0x0000) and at least one entity update
-       if (msg->cursize + 32 >= maxsize)
-               return false;
-
-       if (client->csqcnumedicts < prog->num_edicts)
-               client->csqcnumedicts = prog->num_edicts;
-
-       number = 1;
-       for (num = 0, n = numbers;num < numnumbers;num++, n++)
-       {
-               end = *n;
-               for (;number < end;number++)
-               {
-                       client->csqcentityscope[number] &= ~SCOPE_WANTSEND;
-                       if (client->csqcentityscope[number] & SCOPE_ASSUMED_EXISTING)
-                               client->csqcentityscope[number] |= SCOPE_WANTREMOVE;
-                       client->csqcentitysendflags[number] = 0xFFFFFF;
-               }
-               ed = prog->edicts + number;
-               client->csqcentityscope[number] &= ~SCOPE_WANTSEND;
-               if (PRVM_serveredictfunction(ed, SendEntity))
-                       client->csqcentityscope[number] |= SCOPE_WANTUPDATE;
-               else
-               {
-                       if (client->csqcentityscope[number] & SCOPE_ASSUMED_EXISTING)
-                               client->csqcentityscope[number] |= SCOPE_WANTREMOVE;
-                       client->csqcentitysendflags[number] = 0xFFFFFF;
-               }
-               number++;
-       }
-       end = client->csqcnumedicts;
-       for (;number < end;number++)
-       {
-               client->csqcentityscope[number] &= ~SCOPE_WANTSEND;
-               if (client->csqcentityscope[number] & SCOPE_ASSUMED_EXISTING)
-                       client->csqcentityscope[number] |= SCOPE_WANTREMOVE;
-               client->csqcentitysendflags[number] = 0xFFFFFF;
-       }
-
-       // now try to emit the entity updates
-       // (FIXME: prioritize by distance?)
-       end = client->csqcnumedicts;
-       for (number = 1;number < end;number++)
-       {
-               if (!(client->csqcentityscope[number] & SCOPE_WANTSEND))
-                       continue;
-               if(db->num >= NUM_CSQCENTITIES_PER_FRAME)
-                       break;
-               ed = prog->edicts + number;
-               if (client->csqcentityscope[number] & SCOPE_WANTREMOVE)  // Also implies ASSUMED_EXISTING.
-               {
-                       // A removal. SendFlags have no power here.
-                       // write a remove message
-                       // first write the message identifier if needed
-                       if(!sectionstarted)
-                       {
-                               sectionstarted = 1;
-                               MSG_WriteByte(msg, svc_csqcentities);
-                       }
-                       // write the remove message
-                       {
-                               ENTITYSIZEPROFILING_START(msg, number, 0);
-                               MSG_WriteShort(msg, (unsigned short)number | 0x8000);
-                               client->csqcentityscope[number] &= ~(SCOPE_WANTSEND | SCOPE_ASSUMED_EXISTING);
-                               client->csqcentitysendflags[number] = 0xFFFFFF; // resend completely if it becomes active again
-                               db->entno[db->num] = number;
-                               db->sendflags[db->num] = -1;
-                               db->num += 1;
-                               ENTITYSIZEPROFILING_END(msg, number, 0);
-                       }
-                       if (msg->cursize + 17 >= maxsize)
-                               break;
-               }
-               else
-               {
-                       // save the cursize value in case we overflow and have to rollback
-                       int oldcursize = msg->cursize;
-
-                       // An update.
-                       sendflags = client->csqcentitysendflags[number];
-                       // Nothing to send? FINE.
-                       if (!sendflags)
-                               continue;
-                       // If it's a new entity, always assume sendflags 0xFFFFFF.
-                       if (!(client->csqcentityscope[number] & SCOPE_ASSUMED_EXISTING))
-                               sendflags = 0xFFFFFF;
-
-                       // write an update
-                       if (PRVM_serveredictfunction(ed, SendEntity))
-                       {
-                               if(!sectionstarted)
-                                       MSG_WriteByte(msg, svc_csqcentities);
-                               {
-                                       int oldcursize2 = msg->cursize;
-                                       ENTITYSIZEPROFILING_START(msg, number, sendflags);
-                                       MSG_WriteShort(msg, number);
-                                       msg->allowoverflow = true;
-                                       PRVM_G_INT(OFS_PARM0) = sv.writeentitiestoclient_cliententitynumber;
-                                       PRVM_G_FLOAT(OFS_PARM1) = sendflags;
-                                       PRVM_serverglobaledict(self) = number;
-                                       prog->ExecuteProgram(prog, PRVM_serveredictfunction(ed, SendEntity), "Null SendEntity\n");
-                                       msg->allowoverflow = false;
-                                       if(!PRVM_G_FLOAT(OFS_RETURN))
-                                       {
-                                               // Send rejected by CSQC. This means we want to remove it.
-                                               // CSQC requests we remove this one.
-                                               if (client->csqcentityscope[number] & SCOPE_ASSUMED_EXISTING)
-                                               {
-                                                       msg->cursize = oldcursize2;
-                                                       msg->overflowed = false;
-                                                       MSG_WriteShort(msg, (unsigned short)number | 0x8000);
-                                                       client->csqcentityscope[number] &= ~(SCOPE_WANTSEND | SCOPE_ASSUMED_EXISTING);
-                                                       client->csqcentitysendflags[number] = 0;
-                                                       db->entno[db->num] = number;
-                                                       db->sendflags[db->num] = -1;
-                                                       db->num += 1;
-                                                       // and take note that we have begun the svc_csqcentities
-                                                       // section of the packet
-                                                       sectionstarted = 1;
-                                                       ENTITYSIZEPROFILING_END(msg, number, 0);
-                                                       if (msg->cursize + 17 >= maxsize)
-                                                               break;
-                                               }
-                                               else
-                                               {
-                                                       // Nothing to do. Just don't do it again.
-                                                       msg->cursize = oldcursize;
-                                                       msg->overflowed = false;
-                                                       client->csqcentityscope[number] &= ~SCOPE_WANTSEND;
-                                                       client->csqcentitysendflags[number] = 0;
-                                               }
-                                               continue;
-                                       }
-                                       else if(PRVM_G_FLOAT(OFS_RETURN) && msg->cursize + 2 <= maxsize)
-                                       {
-                                               // an update has been successfully written
-                                               client->csqcentitysendflags[number] = 0;
-                                               db->entno[db->num] = number;
-                                               db->sendflags[db->num] = sendflags;
-                                               db->num += 1;
-                                               client->csqcentityscope[number] &= ~SCOPE_WANTSEND;
-                                               client->csqcentityscope[number] |= SCOPE_EXISTED_ONCE | SCOPE_ASSUMED_EXISTING;
-                                               // and take note that we have begun the svc_csqcentities
-                                               // section of the packet
-                                               sectionstarted = 1;
-                                               ENTITYSIZEPROFILING_END(msg, number, sendflags);
-                                               if (msg->cursize + 17 >= maxsize)
-                                                       break;
-                                               continue;
-                                       }
-                               }
-                       }
-                       // self.SendEntity returned false (or does not exist) or the
-                       // update was too big for this packet - rollback the buffer to its
-                       // state before the writes occurred, we'll try again next frame
-                       msg->cursize = oldcursize;
-                       msg->overflowed = false;
-               }
-       }
-       if (sectionstarted)
-       {
-               // 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);
-       
-       return sectionstarted;
-}
-
 void Protocol_UpdateClientStats(const int *stats)
 {
        int i;
@@ -697,177 +192,6 @@ void Protocol_WriteStatsReliable(void)
        }
 }
 
-
-qboolean EntityFrameQuake_WriteFrame(sizebuf_t *msg, int maxsize, int numstates, const entity_state_t **states)
-{
-       prvm_prog_t *prog = SVVM_prog;
-       const entity_state_t *s;
-       entity_state_t baseline;
-       int i, bits;
-       sizebuf_t buf;
-       unsigned char data[128];
-       qboolean success = false;
-
-       // prepare the buffer
-       memset(&buf, 0, sizeof(buf));
-       buf.data = data;
-       buf.maxsize = sizeof(data);
-
-       for (i = 0;i < numstates;i++)
-       {
-               s = states[i];
-               if(PRVM_serveredictfunction((&prog->edicts[s->number]), SendEntity))
-                       continue;
-
-               // prepare the buffer
-               SZ_Clear(&buf);
-
-// send an update
-               bits = 0;
-               if (s->number >= 256)
-                       bits |= U_LONGENTITY;
-               if (s->flags & RENDER_STEP)
-                       bits |= U_STEP;
-               if (s->flags & RENDER_VIEWMODEL)
-                       bits |= U_VIEWMODEL;
-               if (s->flags & RENDER_GLOWTRAIL)
-                       bits |= U_GLOWTRAIL;
-               if (s->flags & RENDER_EXTERIORMODEL)
-                       bits |= U_EXTERIORMODEL;
-
-               // LadyHavoc: old stuff, but rewritten to have more exact tolerances
-               baseline = prog->edicts[s->number].priv.server->baseline;
-               if (baseline.origin[0] != s->origin[0])
-                       bits |= U_ORIGIN1;
-               if (baseline.origin[1] != s->origin[1])
-                       bits |= U_ORIGIN2;
-               if (baseline.origin[2] != s->origin[2])
-                       bits |= U_ORIGIN3;
-               if (baseline.angles[0] != s->angles[0])
-                       bits |= U_ANGLE1;
-               if (baseline.angles[1] != s->angles[1])
-                       bits |= U_ANGLE2;
-               if (baseline.angles[2] != s->angles[2])
-                       bits |= U_ANGLE3;
-               if (baseline.colormap != s->colormap)
-                       bits |= U_COLORMAP;
-               if (baseline.skin != s->skin)
-                       bits |= U_SKIN;
-               if (baseline.frame != s->frame)
-               {
-                       bits |= U_FRAME;
-                       if (s->frame & 0xFF00)
-                               bits |= U_FRAME2;
-               }
-               if (baseline.effects != s->effects)
-               {
-                       bits |= U_EFFECTS;
-                       if (s->effects & 0xFF00)
-                               bits |= U_EFFECTS2;
-               }
-               if (baseline.modelindex != s->modelindex)
-               {
-                       bits |= U_MODEL;
-                       if ((s->modelindex & 0xFF00) && sv.protocol != PROTOCOL_NEHAHRABJP && sv.protocol != PROTOCOL_NEHAHRABJP2 && sv.protocol != PROTOCOL_NEHAHRABJP3)
-                               bits |= U_MODEL2;
-               }
-               if (baseline.alpha != s->alpha)
-                       bits |= U_ALPHA;
-               if (baseline.scale != s->scale)
-                       bits |= U_SCALE;
-               if (baseline.glowsize != s->glowsize)
-                       bits |= U_GLOWSIZE;
-               if (baseline.glowcolor != s->glowcolor)
-                       bits |= U_GLOWCOLOR;
-               if (!VectorCompare(baseline.colormod, s->colormod))
-                       bits |= U_COLORMOD;
-
-               // if extensions are disabled, clear the relevant update flags
-               if (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_NEHAHRAMOVIE)
-                       bits &= 0x7FFF;
-               if (sv.protocol == PROTOCOL_NEHAHRAMOVIE)
-                       if (s->alpha != 255 || s->effects & EF_FULLBRIGHT)
-                               bits |= U_EXTEND1;
-
-               // write the message
-               if (bits >= 16777216)
-                       bits |= U_EXTEND2;
-               if (bits >= 65536)
-                       bits |= U_EXTEND1;
-               if (bits >= 256)
-                       bits |= U_MOREBITS;
-               bits |= U_SIGNAL;
-
-               {
-                       ENTITYSIZEPROFILING_START(msg, states[i]->number, bits);
-
-                       MSG_WriteByte (&buf, bits);
-                       if (bits & U_MOREBITS)          MSG_WriteByte(&buf, bits>>8);
-                       if (sv.protocol != PROTOCOL_NEHAHRAMOVIE)
-                       {
-                               if (bits & U_EXTEND1)   MSG_WriteByte(&buf, bits>>16);
-                               if (bits & U_EXTEND2)   MSG_WriteByte(&buf, bits>>24);
-                       }
-                       if (bits & U_LONGENTITY)        MSG_WriteShort(&buf, s->number);
-                       else                                            MSG_WriteByte(&buf, s->number);
-
-                       if (bits & U_MODEL)
-                       {
-                               if (sv.protocol == PROTOCOL_NEHAHRABJP || sv.protocol == PROTOCOL_NEHAHRABJP2 || sv.protocol == PROTOCOL_NEHAHRABJP3)
-                                       MSG_WriteShort(&buf, s->modelindex);
-                               else
-                                       MSG_WriteByte(&buf, s->modelindex);
-                       }
-                       if (bits & U_FRAME)                     MSG_WriteByte(&buf, s->frame);
-                       if (bits & U_COLORMAP)          MSG_WriteByte(&buf, s->colormap);
-                       if (bits & U_SKIN)                      MSG_WriteByte(&buf, s->skin);
-                       if (bits & U_EFFECTS)           MSG_WriteByte(&buf, s->effects);
-                       if (bits & U_ORIGIN1)           MSG_WriteCoord(&buf, s->origin[0], sv.protocol);
-                       if (bits & U_ANGLE1)            MSG_WriteAngle(&buf, s->angles[0], sv.protocol);
-                       if (bits & U_ORIGIN2)           MSG_WriteCoord(&buf, s->origin[1], sv.protocol);
-                       if (bits & U_ANGLE2)            MSG_WriteAngle(&buf, s->angles[1], sv.protocol);
-                       if (bits & U_ORIGIN3)           MSG_WriteCoord(&buf, s->origin[2], sv.protocol);
-                       if (bits & U_ANGLE3)            MSG_WriteAngle(&buf, s->angles[2], sv.protocol);
-                       if (bits & U_ALPHA)                     MSG_WriteByte(&buf, s->alpha);
-                       if (bits & U_SCALE)                     MSG_WriteByte(&buf, s->scale);
-                       if (bits & U_EFFECTS2)          MSG_WriteByte(&buf, s->effects >> 8);
-                       if (bits & U_GLOWSIZE)          MSG_WriteByte(&buf, s->glowsize);
-                       if (bits & U_GLOWCOLOR)         MSG_WriteByte(&buf, s->glowcolor);
-                       if (bits & U_COLORMOD)          {int c = ((int)bound(0, s->colormod[0] * (7.0f / 32.0f), 7) << 5) | ((int)bound(0, s->colormod[1] * (7.0f / 32.0f), 7) << 2) | ((int)bound(0, s->colormod[2] * (3.0f / 32.0f), 3) << 0);MSG_WriteByte(&buf, c);}
-                       if (bits & U_FRAME2)            MSG_WriteByte(&buf, s->frame >> 8);
-                       if (bits & U_MODEL2)            MSG_WriteByte(&buf, s->modelindex >> 8);
-
-                       // the nasty protocol
-                       if ((bits & U_EXTEND1) && sv.protocol == PROTOCOL_NEHAHRAMOVIE)
-                       {
-                               if (s->effects & EF_FULLBRIGHT)
-                               {
-                                       MSG_WriteFloat(&buf, 2); // QSG protocol version
-                                       MSG_WriteFloat(&buf, s->alpha <= 0 ? 0 : (s->alpha >= 255 ? 1 : s->alpha * (1.0f / 255.0f))); // alpha
-                                       MSG_WriteFloat(&buf, 1); // fullbright
-                               }
-                               else
-                               {
-                                       MSG_WriteFloat(&buf, 1); // QSG protocol version
-                                       MSG_WriteFloat(&buf, s->alpha <= 0 ? 0 : (s->alpha >= 255 ? 1 : s->alpha * (1.0f / 255.0f))); // alpha
-                               }
-                       }
-
-                       // if the commit is full, we're done this frame
-                       if (msg->cursize + buf.cursize > maxsize)
-                       {
-                               // next frame we will continue where we left off
-                               break;
-                       }
-                       // write the message to the packet
-                       SZ_Write(msg, buf.data, buf.cursize);
-                       success = true;
-                       ENTITYSIZEPROFILING_END(msg, s->number, bits);
-               }
-       }
-       return success;
-}
-
 int EntityState_DeltaBits(const entity_state_t *o, const entity_state_t *n)
 {
        unsigned int bits;
@@ -1305,58 +629,6 @@ void EntityFrame_FetchFrame(entityframe_database_t *d, int framenum, entity_fram
        }
 }
 
-// (client) adds a entity_frame to the database, for future reference
-void EntityFrame_AddFrame_Client(entityframe_database_t *d, vec3_t eye, int framenum, int numentities, const entity_state_t *entitydata)
-{
-       int n, e;
-       entity_frameinfo_t *info;
-
-       VectorCopy(eye, d->eye);
-
-       // figure out how many entity slots are used already
-       if (d->numframes)
-       {
-               n = d->frames[d->numframes - 1].endentity - d->frames[0].firstentity;
-               if (n + numentities > MAX_ENTITY_DATABASE || d->numframes >= MAX_ENTITY_HISTORY)
-               {
-                       // ran out of room, dump database
-                       EntityFrame_ClearDatabase(d);
-               }
-       }
-
-       info = &d->frames[d->numframes];
-       info->framenum = framenum;
-       e = -1000;
-       // make sure we check the newly added frame as well, but we haven't incremented numframes yet
-       for (n = 0;n <= d->numframes;n++)
-       {
-               if (e >= d->frames[n].framenum)
-               {
-                       if (e == framenum)
-                               Con_Print("EntityFrame_AddFrame: tried to add out of sequence frame to database\n");
-                       else
-                               Con_Print("EntityFrame_AddFrame: out of sequence frames in database\n");
-                       return;
-               }
-               e = d->frames[n].framenum;
-       }
-       // if database still has frames after that...
-       if (d->numframes)
-               info->firstentity = d->frames[d->numframes - 1].endentity;
-       else
-               info->firstentity = 0;
-       info->endentity = info->firstentity + numentities;
-       d->numframes++;
-
-       n = info->firstentity % MAX_ENTITY_DATABASE;
-       e = MAX_ENTITY_DATABASE - n;
-       if (e > numentities)
-               e = numentities;
-       memcpy(d->entitydata + n, entitydata, sizeof(entity_state_t) * e);
-       if (numentities > e)
-               memcpy(d->entitydata, entitydata + e, sizeof(entity_state_t) * (numentities - e));
-}
-
 // (server) adds a entity_frame to the database, for future reference
 void EntityFrame_AddFrame_Server(entityframe_database_t *d, vec3_t eye, int framenum, int numentities, const entity_state_t **entitydata)
 {
@@ -1479,148 +751,6 @@ qboolean EntityFrame_WriteFrame(sizebuf_t *msg, int maxsize, entityframe_databas
        return true;
 }
 
-// (client) reads a frame from network stream
-void EntityFrame_CL_ReadFrame(void)
-{
-       int i, number, removed;
-       entity_frame_t *f, *delta;
-       entity_state_t *e, *old, *oldend;
-       entity_t *ent;
-       entityframe_database_t *d;
-       if (!cl.entitydatabase)
-               cl.entitydatabase = EntityFrame_AllocDatabase(cls.levelmempool);
-       d = cl.entitydatabase;
-       f = &d->framedata;
-       delta = &d->deltaframe;
-
-       EntityFrame_Clear(f, NULL, -1);
-
-       // read the frame header info
-       f->time = cl.mtime[0];
-       number = MSG_ReadLong(&cl_message);
-       f->framenum = MSG_ReadLong(&cl_message);
-       CL_NewFrameReceived(f->framenum);
-       f->eye[0] = MSG_ReadFloat(&cl_message);
-       f->eye[1] = MSG_ReadFloat(&cl_message);
-       f->eye[2] = MSG_ReadFloat(&cl_message);
-       EntityFrame_AckFrame(d, number);
-       EntityFrame_FetchFrame(d, number, delta);
-       old = delta->entitydata;
-       oldend = old + delta->numentities;
-       // read entities until we hit the magic 0xFFFF end tag
-       while ((number = (unsigned short) MSG_ReadShort(&cl_message)) != 0xFFFF && !cl_message.badread)
-       {
-               if (cl_message.badread)
-                       Host_Error("EntityFrame_Read: read error");
-               removed = number & 0x8000;
-               number &= 0x7FFF;
-               if (number >= MAX_EDICTS)
-                       Host_Error("EntityFrame_Read: number (%i) >= MAX_EDICTS (%i)", number, MAX_EDICTS);
-
-               // seek to entity, while copying any skipped entities (assume unchanged)
-               while (old < oldend && old->number < number)
-               {
-                       if (f->numentities >= MAX_ENTITY_DATABASE)
-                               Host_Error("EntityFrame_Read: entity list too big");
-                       f->entitydata[f->numentities] = *old++;
-                       f->entitydata[f->numentities++].time = cl.mtime[0];
-               }
-               if (removed)
-               {
-                       if (old < oldend && old->number == number)
-                               old++;
-                       else
-                               Con_Printf("EntityFrame_Read: REMOVE on unused entity %i\n", number);
-               }
-               else
-               {
-                       if (f->numentities >= MAX_ENTITY_DATABASE)
-                               Host_Error("EntityFrame_Read: entity list too big");
-
-                       // reserve this slot
-                       e = f->entitydata + f->numentities++;
-
-                       if (old < oldend && old->number == number)
-                       {
-                               // delta from old entity
-                               *e = *old++;
-                       }
-                       else
-                       {
-                               // delta from defaults
-                               *e = defaultstate;
-                       }
-
-                       if (cl.num_entities <= number)
-                       {
-                               cl.num_entities = number + 1;
-                               if (number >= cl.max_entities)
-                                       CL_ExpandEntities(number);
-                       }
-                       cl.entities_active[number] = true;
-                       e->active = ACTIVE_NETWORK;
-                       e->time = cl.mtime[0];
-                       e->number = number;
-                       EntityState_ReadFields(e, EntityState_ReadExtendBits());
-               }
-       }
-       while (old < oldend)
-       {
-               if (f->numentities >= MAX_ENTITY_DATABASE)
-                       Host_Error("EntityFrame_Read: entity list too big");
-               f->entitydata[f->numentities] = *old++;
-               f->entitydata[f->numentities++].time = cl.mtime[0];
-       }
-       EntityFrame_AddFrame_Client(d, f->eye, f->framenum, f->numentities, f->entitydata);
-
-       memset(cl.entities_active, 0, cl.num_entities * sizeof(unsigned char));
-       number = 1;
-       for (i = 0;i < f->numentities;i++)
-       {
-               for (;number < f->entitydata[i].number && number < cl.num_entities;number++)
-               {
-                       if (cl.entities_active[number])
-                       {
-                               cl.entities_active[number] = false;
-                               cl.entities[number].state_current.active = ACTIVE_NOT;
-                       }
-               }
-               if (number >= cl.num_entities)
-                       break;
-               // update the entity
-               ent = &cl.entities[number];
-               ent->state_previous = ent->state_current;
-               ent->state_current = f->entitydata[i];
-               CL_MoveLerpEntityStates(ent);
-               // the entity lives again...
-               cl.entities_active[number] = true;
-               number++;
-       }
-       for (;number < cl.num_entities;number++)
-       {
-               if (cl.entities_active[number])
-               {
-                       cl.entities_active[number] = false;
-                       cl.entities[number].state_current.active = ACTIVE_NOT;
-               }
-       }
-}
-
-
-// (client) returns the frame number of the most recent frame recieved
-int EntityFrame_MostRecentlyRecievedFrameNum(entityframe_database_t *d)
-{
-       if (d->numframes)
-               return d->frames[d->numframes - 1].framenum;
-       else
-               return -1;
-}
-
-
-
-
-
-
 entity_state_t *EntityFrame4_GetReferenceEntity(entityframe4_database_t *d, int number)
 {
        if (d->maxreferenceentities <= number)
index 8770d82be5ce3bc11d94f590cb084696329750a9..1799ca11f655038fd830b8a2939eec5d1498654f 100644 (file)
@@ -30,6 +30,24 @@ protocolversion_t Protocol_EnumForNumber(int n);
 int Protocol_NumberForEnum(protocolversion_t p);
 void Protocol_Names(char *buffer, size_t buffersize);
 
+#define ENTITYSIZEPROFILING_START(msg, num, flags) \
+       int entityprofiling_startsize = msg->cursize
+
+#define ENTITYSIZEPROFILING_END(msg, num, flags) \
+       if(developer_networkentities.integer >= 2) \
+       { \
+               prvm_edict_t *edict = prog->edicts + num; \
+               Con_Printf("sent entity update of size %u for %d classname %s flags %d\n", (msg->cursize - entityprofiling_startsize), num, PRVM_serveredictstring(edict, classname) ? PRVM_GetString(prog, PRVM_serveredictstring(edict, classname)) : "(no classname)", flags); \
+       }
+
+// CSQC entity scope values. Bitflags!
+#define SCOPE_WANTREMOVE 1        // Set if a remove has been scheduled. Never set together with WANTUPDATE.
+#define SCOPE_WANTUPDATE 2        // Set if an update has been scheduled.
+#define SCOPE_WANTSEND (SCOPE_WANTREMOVE | SCOPE_WANTUPDATE)
+#define SCOPE_EXISTED_ONCE 4      // Set if the entity once existed. All these get resent on a full loss.
+#define SCOPE_ASSUMED_EXISTING 8  // Set if the entity is currently assumed existing and therefore needs removes.
+
+
 // model effects
 #define        MF_ROCKET       1                       // leave a trail
 #define        MF_GRENADE      2                       // leave a trail
diff --git a/sv_entframe_csqc.c b/sv_entframe_csqc.c
new file mode 100644 (file)
index 0000000..9144f96
--- /dev/null
@@ -0,0 +1,350 @@
+#include "quakedef.h"
+#include "protocol.h"
+
+// 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)
+{
+       prvm_prog_t *prog = SVVM_prog;
+       // mark ALL csqc entities as requiring a FULL resend!
+       // I know this is a bad workaround, but better than nothing.
+       int i, n;
+       prvm_edict_t *ed;
+
+       n = client->csqcnumedicts;
+       for(i = 0; i < n; ++i)
+       {
+               if(client->csqcentityscope[i] & SCOPE_EXISTED_ONCE)
+               {
+                       ed = prog->edicts + i;
+                       client->csqcentitysendflags[i] |= 0xFFFFFF;  // FULL RESEND. We can't clear SCOPE_ASSUMED_EXISTING yet as this would cancel removes on a rejected send attempt.
+                       if (!PRVM_serveredictfunction(ed, SendEntity))  // If it was ever sent to that client as a CSQC entity...
+                               client->csqcentityscope[i] |= SCOPE_ASSUMED_EXISTING;  // FORCE REMOVE.
+               }
+       }
+}
+void EntityFrameCSQC_LostFrame(client_t *client, int framenum)
+{
+       // marks a frame as lost
+       int i, j;
+       qboolean valid;
+       int ringfirst, ringlast;
+       static int recoversendflags[MAX_EDICTS]; // client only
+       csqcentityframedb_t *d;
+
+       if(client->csqcentityframe_lastreset < 0)
+               return;
+       if(framenum < client->csqcentityframe_lastreset)
+               return; // no action required, as we resent that data anyway
+
+       // 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);
+                       client->csqcentityframe_lastreset = -1;
+               }
+               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)
+                       client->csqcentityscope[i] |= SCOPE_ASSUMED_EXISTING;  // FORCE REMOVE.
+               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
+// TODO: add some handling for entity send priorities, to better deal with huge
+// amounts of csqc networked entities
+qboolean EntityFrameCSQC_WriteFrame (sizebuf_t *msg, int maxsize, int numnumbers, const unsigned short *numbers, int framenum)
+{
+       prvm_prog_t *prog = SVVM_prog;
+       int num, number, end, sendflags;
+       qboolean sectionstarted = false;
+       const unsigned short *n;
+       prvm_edict_t *ed;
+       client_t *client = svs.clients + sv.writeentitiestoclient_clientnumber;
+       int dbframe = EntityFrameCSQC_AllocFrame(client, framenum);
+       csqcentityframedb_t *db = &client->csqcentityframehistory[dbframe];
+
+       if(client->csqcentityframe_lastreset < 0)
+               client->csqcentityframe_lastreset = framenum;
+
+       maxsize -= 24; // always fit in an empty svc_entities message (for packet loss detection!)
+
+       // make sure there is enough room to store the svc_csqcentities byte,
+       // the terminator (0x0000) and at least one entity update
+       if (msg->cursize + 32 >= maxsize)
+               return false;
+
+       if (client->csqcnumedicts < prog->num_edicts)
+               client->csqcnumedicts = prog->num_edicts;
+
+       number = 1;
+       for (num = 0, n = numbers;num < numnumbers;num++, n++)
+       {
+               end = *n;
+               for (;number < end;number++)
+               {
+                       client->csqcentityscope[number] &= ~SCOPE_WANTSEND;
+                       if (client->csqcentityscope[number] & SCOPE_ASSUMED_EXISTING)
+                               client->csqcentityscope[number] |= SCOPE_WANTREMOVE;
+                       client->csqcentitysendflags[number] = 0xFFFFFF;
+               }
+               ed = prog->edicts + number;
+               client->csqcentityscope[number] &= ~SCOPE_WANTSEND;
+               if (PRVM_serveredictfunction(ed, SendEntity))
+                       client->csqcentityscope[number] |= SCOPE_WANTUPDATE;
+               else
+               {
+                       if (client->csqcentityscope[number] & SCOPE_ASSUMED_EXISTING)
+                               client->csqcentityscope[number] |= SCOPE_WANTREMOVE;
+                       client->csqcentitysendflags[number] = 0xFFFFFF;
+               }
+               number++;
+       }
+       end = client->csqcnumedicts;
+       for (;number < end;number++)
+       {
+               client->csqcentityscope[number] &= ~SCOPE_WANTSEND;
+               if (client->csqcentityscope[number] & SCOPE_ASSUMED_EXISTING)
+                       client->csqcentityscope[number] |= SCOPE_WANTREMOVE;
+               client->csqcentitysendflags[number] = 0xFFFFFF;
+       }
+
+       // now try to emit the entity updates
+       // (FIXME: prioritize by distance?)
+       end = client->csqcnumedicts;
+       for (number = 1;number < end;number++)
+       {
+               if (!(client->csqcentityscope[number] & SCOPE_WANTSEND))
+                       continue;
+               if(db->num >= NUM_CSQCENTITIES_PER_FRAME)
+                       break;
+               ed = prog->edicts + number;
+               if (client->csqcentityscope[number] & SCOPE_WANTREMOVE)  // Also implies ASSUMED_EXISTING.
+               {
+                       // A removal. SendFlags have no power here.
+                       // write a remove message
+                       // first write the message identifier if needed
+                       if(!sectionstarted)
+                       {
+                               sectionstarted = 1;
+                               MSG_WriteByte(msg, svc_csqcentities);
+                       }
+                       // write the remove message
+                       {
+                               ENTITYSIZEPROFILING_START(msg, number, 0);
+                               MSG_WriteShort(msg, (unsigned short)number | 0x8000);
+                               client->csqcentityscope[number] &= ~(SCOPE_WANTSEND | SCOPE_ASSUMED_EXISTING);
+                               client->csqcentitysendflags[number] = 0xFFFFFF; // resend completely if it becomes active again
+                               db->entno[db->num] = number;
+                               db->sendflags[db->num] = -1;
+                               db->num += 1;
+                               ENTITYSIZEPROFILING_END(msg, number, 0);
+                       }
+                       if (msg->cursize + 17 >= maxsize)
+                               break;
+               }
+               else
+               {
+                       // save the cursize value in case we overflow and have to rollback
+                       int oldcursize = msg->cursize;
+
+                       // An update.
+                       sendflags = client->csqcentitysendflags[number];
+                       // Nothing to send? FINE.
+                       if (!sendflags)
+                               continue;
+                       // If it's a new entity, always assume sendflags 0xFFFFFF.
+                       if (!(client->csqcentityscope[number] & SCOPE_ASSUMED_EXISTING))
+                               sendflags = 0xFFFFFF;
+
+                       // write an update
+                       if (PRVM_serveredictfunction(ed, SendEntity))
+                       {
+                               if(!sectionstarted)
+                                       MSG_WriteByte(msg, svc_csqcentities);
+                               {
+                                       int oldcursize2 = msg->cursize;
+                                       ENTITYSIZEPROFILING_START(msg, number, sendflags);
+                                       MSG_WriteShort(msg, number);
+                                       msg->allowoverflow = true;
+                                       PRVM_G_INT(OFS_PARM0) = sv.writeentitiestoclient_cliententitynumber;
+                                       PRVM_G_FLOAT(OFS_PARM1) = sendflags;
+                                       PRVM_serverglobaledict(self) = number;
+                                       prog->ExecuteProgram(prog, PRVM_serveredictfunction(ed, SendEntity), "Null SendEntity\n");
+                                       msg->allowoverflow = false;
+                                       if(!PRVM_G_FLOAT(OFS_RETURN))
+                                       {
+                                               // Send rejected by CSQC. This means we want to remove it.
+                                               // CSQC requests we remove this one.
+                                               if (client->csqcentityscope[number] & SCOPE_ASSUMED_EXISTING)
+                                               {
+                                                       msg->cursize = oldcursize2;
+                                                       msg->overflowed = false;
+                                                       MSG_WriteShort(msg, (unsigned short)number | 0x8000);
+                                                       client->csqcentityscope[number] &= ~(SCOPE_WANTSEND | SCOPE_ASSUMED_EXISTING);
+                                                       client->csqcentitysendflags[number] = 0;
+                                                       db->entno[db->num] = number;
+                                                       db->sendflags[db->num] = -1;
+                                                       db->num += 1;
+                                                       // and take note that we have begun the svc_csqcentities
+                                                       // section of the packet
+                                                       sectionstarted = 1;
+                                                       ENTITYSIZEPROFILING_END(msg, number, 0);
+                                                       if (msg->cursize + 17 >= maxsize)
+                                                               break;
+                                               }
+                                               else
+                                               {
+                                                       // Nothing to do. Just don't do it again.
+                                                       msg->cursize = oldcursize;
+                                                       msg->overflowed = false;
+                                                       client->csqcentityscope[number] &= ~SCOPE_WANTSEND;
+                                                       client->csqcentitysendflags[number] = 0;
+                                               }
+                                               continue;
+                                       }
+                                       else if(PRVM_G_FLOAT(OFS_RETURN) && msg->cursize + 2 <= maxsize)
+                                       {
+                                               // an update has been successfully written
+                                               client->csqcentitysendflags[number] = 0;
+                                               db->entno[db->num] = number;
+                                               db->sendflags[db->num] = sendflags;
+                                               db->num += 1;
+                                               client->csqcentityscope[number] &= ~SCOPE_WANTSEND;
+                                               client->csqcentityscope[number] |= SCOPE_EXISTED_ONCE | SCOPE_ASSUMED_EXISTING;
+                                               // and take note that we have begun the svc_csqcentities
+                                               // section of the packet
+                                               sectionstarted = 1;
+                                               ENTITYSIZEPROFILING_END(msg, number, sendflags);
+                                               if (msg->cursize + 17 >= maxsize)
+                                                       break;
+                                               continue;
+                                       }
+                               }
+                       }
+                       // self.SendEntity returned false (or does not exist) or the
+                       // update was too big for this packet - rollback the buffer to its
+                       // state before the writes occurred, we'll try again next frame
+                       msg->cursize = oldcursize;
+                       msg->overflowed = false;
+               }
+       }
+       if (sectionstarted)
+       {
+               // 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);
+       
+       return sectionstarted;
+}
\ No newline at end of file
diff --git a/sv_entframe_quake.c b/sv_entframe_quake.c
new file mode 100644 (file)
index 0000000..05d82d9
--- /dev/null
@@ -0,0 +1,172 @@
+#include "quakedef.h"
+#include "protocol.h"
+
+qboolean EntityFrameQuake_WriteFrame(sizebuf_t *msg, int maxsize, int numstates, const entity_state_t **states)
+{
+       prvm_prog_t *prog = SVVM_prog;
+       const entity_state_t *s;
+       entity_state_t baseline;
+       int i, bits;
+       sizebuf_t buf;
+       unsigned char data[128];
+       qboolean success = false;
+
+       // prepare the buffer
+       memset(&buf, 0, sizeof(buf));
+       buf.data = data;
+       buf.maxsize = sizeof(data);
+
+       for (i = 0;i < numstates;i++)
+       {
+               s = states[i];
+               if(PRVM_serveredictfunction((&prog->edicts[s->number]), SendEntity))
+                       continue;
+
+               // prepare the buffer
+               SZ_Clear(&buf);
+
+// send an update
+               bits = 0;
+               if (s->number >= 256)
+                       bits |= U_LONGENTITY;
+               if (s->flags & RENDER_STEP)
+                       bits |= U_STEP;
+               if (s->flags & RENDER_VIEWMODEL)
+                       bits |= U_VIEWMODEL;
+               if (s->flags & RENDER_GLOWTRAIL)
+                       bits |= U_GLOWTRAIL;
+               if (s->flags & RENDER_EXTERIORMODEL)
+                       bits |= U_EXTERIORMODEL;
+
+               // LadyHavoc: old stuff, but rewritten to have more exact tolerances
+               baseline = prog->edicts[s->number].priv.server->baseline;
+               if (baseline.origin[0] != s->origin[0])
+                       bits |= U_ORIGIN1;
+               if (baseline.origin[1] != s->origin[1])
+                       bits |= U_ORIGIN2;
+               if (baseline.origin[2] != s->origin[2])
+                       bits |= U_ORIGIN3;
+               if (baseline.angles[0] != s->angles[0])
+                       bits |= U_ANGLE1;
+               if (baseline.angles[1] != s->angles[1])
+                       bits |= U_ANGLE2;
+               if (baseline.angles[2] != s->angles[2])
+                       bits |= U_ANGLE3;
+               if (baseline.colormap != s->colormap)
+                       bits |= U_COLORMAP;
+               if (baseline.skin != s->skin)
+                       bits |= U_SKIN;
+               if (baseline.frame != s->frame)
+               {
+                       bits |= U_FRAME;
+                       if (s->frame & 0xFF00)
+                               bits |= U_FRAME2;
+               }
+               if (baseline.effects != s->effects)
+               {
+                       bits |= U_EFFECTS;
+                       if (s->effects & 0xFF00)
+                               bits |= U_EFFECTS2;
+               }
+               if (baseline.modelindex != s->modelindex)
+               {
+                       bits |= U_MODEL;
+                       if ((s->modelindex & 0xFF00) && sv.protocol != PROTOCOL_NEHAHRABJP && sv.protocol != PROTOCOL_NEHAHRABJP2 && sv.protocol != PROTOCOL_NEHAHRABJP3)
+                               bits |= U_MODEL2;
+               }
+               if (baseline.alpha != s->alpha)
+                       bits |= U_ALPHA;
+               if (baseline.scale != s->scale)
+                       bits |= U_SCALE;
+               if (baseline.glowsize != s->glowsize)
+                       bits |= U_GLOWSIZE;
+               if (baseline.glowcolor != s->glowcolor)
+                       bits |= U_GLOWCOLOR;
+               if (!VectorCompare(baseline.colormod, s->colormod))
+                       bits |= U_COLORMOD;
+
+               // if extensions are disabled, clear the relevant update flags
+               if (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_NEHAHRAMOVIE)
+                       bits &= 0x7FFF;
+               if (sv.protocol == PROTOCOL_NEHAHRAMOVIE)
+                       if (s->alpha != 255 || s->effects & EF_FULLBRIGHT)
+                               bits |= U_EXTEND1;
+
+               // write the message
+               if (bits >= 16777216)
+                       bits |= U_EXTEND2;
+               if (bits >= 65536)
+                       bits |= U_EXTEND1;
+               if (bits >= 256)
+                       bits |= U_MOREBITS;
+               bits |= U_SIGNAL;
+
+               {
+                       ENTITYSIZEPROFILING_START(msg, states[i]->number, bits);
+
+                       MSG_WriteByte (&buf, bits);
+                       if (bits & U_MOREBITS)          MSG_WriteByte(&buf, bits>>8);
+                       if (sv.protocol != PROTOCOL_NEHAHRAMOVIE)
+                       {
+                               if (bits & U_EXTEND1)   MSG_WriteByte(&buf, bits>>16);
+                               if (bits & U_EXTEND2)   MSG_WriteByte(&buf, bits>>24);
+                       }
+                       if (bits & U_LONGENTITY)        MSG_WriteShort(&buf, s->number);
+                       else                                            MSG_WriteByte(&buf, s->number);
+
+                       if (bits & U_MODEL)
+                       {
+                               if (sv.protocol == PROTOCOL_NEHAHRABJP || sv.protocol == PROTOCOL_NEHAHRABJP2 || sv.protocol == PROTOCOL_NEHAHRABJP3)
+                                       MSG_WriteShort(&buf, s->modelindex);
+                               else
+                                       MSG_WriteByte(&buf, s->modelindex);
+                       }
+                       if (bits & U_FRAME)                     MSG_WriteByte(&buf, s->frame);
+                       if (bits & U_COLORMAP)          MSG_WriteByte(&buf, s->colormap);
+                       if (bits & U_SKIN)                      MSG_WriteByte(&buf, s->skin);
+                       if (bits & U_EFFECTS)           MSG_WriteByte(&buf, s->effects);
+                       if (bits & U_ORIGIN1)           MSG_WriteCoord(&buf, s->origin[0], sv.protocol);
+                       if (bits & U_ANGLE1)            MSG_WriteAngle(&buf, s->angles[0], sv.protocol);
+                       if (bits & U_ORIGIN2)           MSG_WriteCoord(&buf, s->origin[1], sv.protocol);
+                       if (bits & U_ANGLE2)            MSG_WriteAngle(&buf, s->angles[1], sv.protocol);
+                       if (bits & U_ORIGIN3)           MSG_WriteCoord(&buf, s->origin[2], sv.protocol);
+                       if (bits & U_ANGLE3)            MSG_WriteAngle(&buf, s->angles[2], sv.protocol);
+                       if (bits & U_ALPHA)                     MSG_WriteByte(&buf, s->alpha);
+                       if (bits & U_SCALE)                     MSG_WriteByte(&buf, s->scale);
+                       if (bits & U_EFFECTS2)          MSG_WriteByte(&buf, s->effects >> 8);
+                       if (bits & U_GLOWSIZE)          MSG_WriteByte(&buf, s->glowsize);
+                       if (bits & U_GLOWCOLOR)         MSG_WriteByte(&buf, s->glowcolor);
+                       if (bits & U_COLORMOD)          {int c = ((int)bound(0, s->colormod[0] * (7.0f / 32.0f), 7) << 5) | ((int)bound(0, s->colormod[1] * (7.0f / 32.0f), 7) << 2) | ((int)bound(0, s->colormod[2] * (3.0f / 32.0f), 3) << 0);MSG_WriteByte(&buf, c);}
+                       if (bits & U_FRAME2)            MSG_WriteByte(&buf, s->frame >> 8);
+                       if (bits & U_MODEL2)            MSG_WriteByte(&buf, s->modelindex >> 8);
+
+                       // the nasty protocol
+                       if ((bits & U_EXTEND1) && sv.protocol == PROTOCOL_NEHAHRAMOVIE)
+                       {
+                               if (s->effects & EF_FULLBRIGHT)
+                               {
+                                       MSG_WriteFloat(&buf, 2); // QSG protocol version
+                                       MSG_WriteFloat(&buf, s->alpha <= 0 ? 0 : (s->alpha >= 255 ? 1 : s->alpha * (1.0f / 255.0f))); // alpha
+                                       MSG_WriteFloat(&buf, 1); // fullbright
+                               }
+                               else
+                               {
+                                       MSG_WriteFloat(&buf, 1); // QSG protocol version
+                                       MSG_WriteFloat(&buf, s->alpha <= 0 ? 0 : (s->alpha >= 255 ? 1 : s->alpha * (1.0f / 255.0f))); // alpha
+                               }
+                       }
+
+                       // if the commit is full, we're done this frame
+                       if (msg->cursize + buf.cursize > maxsize)
+                       {
+                               // next frame we will continue where we left off
+                               break;
+                       }
+                       // write the message to the packet
+                       SZ_Write(msg, buf.data, buf.cursize);
+                       success = true;
+                       ENTITYSIZEPROFILING_END(msg, s->number, bits);
+               }
+       }
+       return success;
+}
\ No newline at end of file