]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - protocol.c
Fix engine not starting on Windows if linked against SDL > 2.0.5
[xonotic/darkplaces.git] / protocol.c
index 4f0139b502453f07d3407d205059e4ed8d531d3d..e9d9489fa65699ff6ceb66bb474a9fd3aafae519 100644 (file)
@@ -1,15 +1,22 @@
 #include "quakedef.h"
 
-#define ENTITYSIZEPROFILING_START(msg, num) \
+#define ENTITYSIZEPROFILING_START(msg, num, flags) \
        int entityprofiling_startsize = msg->cursize
 
-#define ENTITYSIZEPROFILING_END(msg, num) \
+#define ENTITYSIZEPROFILING_END(msg, num, flags) \
        if(developer_networkentities.integer >= 2) \
        { \
-               prvm_edict_t *ed = prog->edicts + num; \
-               Con_Printf("sent entity update of size %d for a %s\n", (msg->cursize - entityprofiling_startsize), PRVM_serveredictstring(ed, classname) ? PRVM_GetString(prog, PRVM_serveredictstring(ed, classname)) : "(no classname)"); \
+               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 =
 {
@@ -279,16 +286,12 @@ static void EntityFrameCSQC_LostAllFrames(client_t *client)
        n = client->csqcnumedicts;
        for(i = 0; i < n; ++i)
        {
-               if(client->csqcentityglobalhistory[i])
+               if(client->csqcentityscope[i] & SCOPE_EXISTED_ONCE)
                {
                        ed = prog->edicts + i;
-                       if (PRVM_serveredictfunction(ed, SendEntity))
-                               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;
-                       }
+                       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.
                }
        }
 }
@@ -395,12 +398,7 @@ void EntityFrameCSQC_LostFrame(client_t *client, int framenum)
        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;
-               }
+                       client->csqcentityscope[i] |= SCOPE_ASSUMED_EXISTING;  // FORCE REMOVE.
                else
                        client->csqcentitysendflags[i] |= recoversendflags[i];
        }
@@ -461,18 +459,19 @@ qboolean EntityFrameCSQC_WriteFrame (sizebuf_t *msg, int maxsize, int numnumbers
                end = *n;
                for (;number < end;number++)
                {
-                       if (client->csqcentityscope[number])
-                       {
-                               client->csqcentityscope[number] = 1;
-                               client->csqcentitysendflags[number] = 0xFFFFFF;
-                       }
+                       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] = 2;
-               else if (client->csqcentityscope[number])
+                       client->csqcentityscope[number] |= SCOPE_WANTUPDATE;
+               else
                {
-                       client->csqcentityscope[number] = 1;
+                       if (client->csqcentityscope[number] & SCOPE_ASSUMED_EXISTING)
+                               client->csqcentityscope[number] |= SCOPE_WANTREMOVE;
                        client->csqcentitysendflags[number] = 0xFFFFFF;
                }
                number++;
@@ -480,44 +479,25 @@ qboolean EntityFrameCSQC_WriteFrame (sizebuf_t *msg, int maxsize, int numnumbers
        end = client->csqcnumedicts;
        for (;number < end;number++)
        {
-               if (client->csqcentityscope[number])
-               {
-                       client->csqcentityscope[number] = 1;
-                       client->csqcentitysendflags[number] = 0xFFFFFF;
-               }
-       }
-
-       /*
-       // mark all scope entities as remove
-       for (number = 1;number < client->csqcnumedicts;number++)
-               if (client->csqcentityscope[number])
-                       client->csqcentityscope[number] = 1;
-       // keep visible entities
-       for (i = 0, n = numbers;i < numnumbers;i++, n++)
-       {
-               number = *n;
-               ed = prog->edicts + number;
-               if (PRVM_serveredictfunction(ed, SendEntity))
-                       client->csqcentityscope[number] = 2;
+               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])
-                       continue;
-               sendflags = client->csqcentitysendflags[number];
-               if (!sendflags)
+               if (!(client->csqcentityscope[number] & SCOPE_WANTSEND))
                        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)
+               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)
@@ -527,31 +507,40 @@ qboolean EntityFrameCSQC_WriteFrame (sizebuf_t *msg, int maxsize, int numnumbers
                        }
                        // write the remove message
                        {
-                               ENTITYSIZEPROFILING_START(msg, number);
+                               ENTITYSIZEPROFILING_START(msg, number, 0);
                                MSG_WriteShort(msg, (unsigned short)number | 0x8000);
-                               client->csqcentityscope[number] = 0;
+                               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;
-                               client->csqcentityglobalhistory[number] = 1;
-                               ENTITYSIZEPROFILING_END(msg, number);
+                               ENTITYSIZEPROFILING_END(msg, number, 0);
                        }
                        if (msg->cursize + 17 >= maxsize)
                                break;
                }
                else
                {
-                       // write an update
                        // save the cursize value in case we overflow and have to rollback
                        int oldcursize = msg->cursize;
-                       client->csqcentityscope[number] = 1;
+
+                       // 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);
                                {
-                                       ENTITYSIZEPROFILING_START(msg, number);
+                                       int oldcursize2 = msg->cursize;
+                                       ENTITYSIZEPROFILING_START(msg, number, sendflags);
                                        MSG_WriteShort(msg, number);
                                        msg->allowoverflow = true;
                                        PRVM_G_INT(OFS_PARM0) = sv.writeentitiestoclient_cliententitynumber;
@@ -559,18 +548,50 @@ qboolean EntityFrameCSQC_WriteFrame (sizebuf_t *msg, int maxsize, int numnumbers
                                        PRVM_serverglobaledict(self) = number;
                                        prog->ExecuteProgram(prog, PRVM_serveredictfunction(ed, SendEntity), "Null SendEntity\n");
                                        msg->allowoverflow = false;
-                                       if(PRVM_G_FLOAT(OFS_RETURN) && msg->cursize + 2 <= maxsize)
+                                       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->csqcentityglobalhistory[number] = 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);
+                                               ENTITYSIZEPROFILING_END(msg, number, sendflags);
                                                if (msg->cursize + 17 >= maxsize)
                                                        break;
                                                continue;
@@ -694,7 +715,6 @@ qboolean EntityFrameQuake_WriteFrame(sizebuf_t *msg, int maxsize, int numstates,
 
        for (i = 0;i < numstates;i++)
        {
-               ENTITYSIZEPROFILING_START(msg, states[i]->number);
                s = states[i];
                if(PRVM_serveredictfunction((&prog->edicts[s->number]), SendEntity))
                        continue;
@@ -778,68 +798,72 @@ qboolean EntityFrameQuake_WriteFrame(sizebuf_t *msg, int maxsize, int numstates,
                        bits |= U_MOREBITS;
                bits |= U_SIGNAL;
 
-               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);
+                       ENTITYSIZEPROFILING_START(msg, states[i]->number, bits);
 
-               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_WriteByte (&buf, bits);
+                       if (bits & U_MOREBITS)          MSG_WriteByte(&buf, bits>>8);
+                       if (sv.protocol != PROTOCOL_NEHAHRAMOVIE)
                        {
-                               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
+                               if (bits & U_EXTEND1)   MSG_WriteByte(&buf, bits>>16);
+                               if (bits & U_EXTEND2)   MSG_WriteByte(&buf, bits>>24);
                        }
-                       else
+                       if (bits & U_LONGENTITY)        MSG_WriteShort(&buf, s->number);
+                       else                                            MSG_WriteByte(&buf, s->number);
+
+                       if (bits & U_MODEL)
                        {
-                               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 (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;
+                       // 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);
                }
-               // write the message to the packet
-               SZ_Write(msg, buf.data, buf.cursize);
-               success = true;
-               ENTITYSIZEPROFILING_END(msg, s->number);
        }
        return success;
 }
@@ -1027,16 +1051,17 @@ void EntityState_WriteUpdate(const entity_state_t *ent, sizebuf_t *msg, const en
 {
        prvm_prog_t *prog = SVVM_prog;
        unsigned int bits;
-       ENTITYSIZEPROFILING_START(msg, ent->number);
        if (ent->active == ACTIVE_NETWORK)
        {
                // entity is active, check for changes from the delta
                if ((bits = EntityState_DeltaBits(delta, ent)))
                {
                        // write the update number, bits, and fields
+                       ENTITYSIZEPROFILING_START(msg, ent->number, bits);
                        MSG_WriteShort(msg, ent->number);
                        EntityState_WriteExtendBits(msg, bits);
                        EntityState_WriteFields(ent, msg, bits);
+                       ENTITYSIZEPROFILING_END(msg, ent->number, bits);
                }
        }
        else
@@ -1045,10 +1070,11 @@ void EntityState_WriteUpdate(const entity_state_t *ent, sizebuf_t *msg, const en
                if (delta->active == ACTIVE_NETWORK)
                {
                        // write the remove number
+                       ENTITYSIZEPROFILING_START(msg, ent->number, 0);
                        MSG_WriteShort(msg, ent->number | 0x8000);
+                       ENTITYSIZEPROFILING_END(msg, ent->number, 0);
                }
        }
-       ENTITYSIZEPROFILING_END(msg, ent->number);
 }
 
 int EntityState_ReadExtendBits(void)
@@ -2062,15 +2088,43 @@ static int EntityState5_Priority(entityframe5_database_t *d, int stateindex)
        return bound(1, priority, ENTITYFRAME5_PRIORITYLEVELS - 1);
 }
 
+static double anim_reducetime(double t, double frameduration, double maxtime)
+{
+       if(t < 0) // clamp to non-negative
+               return 0;
+       if(t <= maxtime) // time can be represented normally
+               return t;
+       if(frameduration == 0) // don't like dividing by zero
+               return t;
+       if(maxtime <= 2 * frameduration) // if two frames don't fit, we better not do this
+               return t;
+       t -= frameduration * ceil((t - maxtime) / frameduration);
+       // now maxtime - frameduration < t <= maxtime
+       return t;
+}
+
+// see VM_SV_frameduration
+static double anim_frameduration(dp_model_t *model, int framenum)
+{
+       if (!model || !model->animscenes || framenum < 0 || framenum >= model->numframes)
+               return 0;
+       if(model->animscenes[framenum].framerate)
+               return model->animscenes[framenum].framecount / model->animscenes[framenum].framerate;
+       return 0;
+}
+
 void EntityState5_WriteUpdate(int number, const entity_state_t *s, int changedbits, sizebuf_t *msg)
 {
        prvm_prog_t *prog = SVVM_prog;
        unsigned int bits = 0;
        //dp_model_t *model;
-       ENTITYSIZEPROFILING_START(msg, s->number);
 
        if (s->active != ACTIVE_NETWORK)
+       {
+               ENTITYSIZEPROFILING_START(msg, s->number, 0);
                MSG_WriteShort(msg, number | 0x8000);
+               ENTITYSIZEPROFILING_END(msg, s->number, 0);
+       }
        else
        {
                if (PRVM_serveredictfunction((&prog->edicts[s->number]), SendEntity))
@@ -2108,185 +2162,194 @@ void EntityState5_WriteUpdate(int number, const entity_state_t *s, int changedbi
                        bits |= E5_EXTEND2;
                if (bits >= 16777216)
                        bits |= E5_EXTEND3;
-               MSG_WriteShort(msg, number);
-               MSG_WriteByte(msg, bits & 0xFF);
-               if (bits & E5_EXTEND1)
-                       MSG_WriteByte(msg, (bits >> 8) & 0xFF);
-               if (bits & E5_EXTEND2)
-                       MSG_WriteByte(msg, (bits >> 16) & 0xFF);
-               if (bits & E5_EXTEND3)
-                       MSG_WriteByte(msg, (bits >> 24) & 0xFF);
-               if (bits & E5_FLAGS)
-                       MSG_WriteByte(msg, s->flags);
-               if (bits & E5_ORIGIN)
                {
-                       if (bits & E5_ORIGIN32)
+                       ENTITYSIZEPROFILING_START(msg, s->number, bits);
+                       MSG_WriteShort(msg, number);
+                       MSG_WriteByte(msg, bits & 0xFF);
+                       if (bits & E5_EXTEND1)
+                               MSG_WriteByte(msg, (bits >> 8) & 0xFF);
+                       if (bits & E5_EXTEND2)
+                               MSG_WriteByte(msg, (bits >> 16) & 0xFF);
+                       if (bits & E5_EXTEND3)
+                               MSG_WriteByte(msg, (bits >> 24) & 0xFF);
+                       if (bits & E5_FLAGS)
+                               MSG_WriteByte(msg, s->flags);
+                       if (bits & E5_ORIGIN)
                        {
-                               MSG_WriteCoord32f(msg, s->origin[0]);
-                               MSG_WriteCoord32f(msg, s->origin[1]);
-                               MSG_WriteCoord32f(msg, s->origin[2]);
+                               if (bits & E5_ORIGIN32)
+                               {
+                                       MSG_WriteCoord32f(msg, s->origin[0]);
+                                       MSG_WriteCoord32f(msg, s->origin[1]);
+                                       MSG_WriteCoord32f(msg, s->origin[2]);
+                               }
+                               else
+                               {
+                                       MSG_WriteCoord13i(msg, s->origin[0]);
+                                       MSG_WriteCoord13i(msg, s->origin[1]);
+                                       MSG_WriteCoord13i(msg, s->origin[2]);
+                               }
                        }
-                       else
+                       if (bits & E5_ANGLES)
+                       {
+                               if (bits & E5_ANGLES16)
+                               {
+                                       MSG_WriteAngle16i(msg, s->angles[0]);
+                                       MSG_WriteAngle16i(msg, s->angles[1]);
+                                       MSG_WriteAngle16i(msg, s->angles[2]);
+                               }
+                               else
+                               {
+                                       MSG_WriteAngle8i(msg, s->angles[0]);
+                                       MSG_WriteAngle8i(msg, s->angles[1]);
+                                       MSG_WriteAngle8i(msg, s->angles[2]);
+                               }
+                       }
+                       if (bits & E5_MODEL)
                        {
-                               MSG_WriteCoord13i(msg, s->origin[0]);
-                               MSG_WriteCoord13i(msg, s->origin[1]);
-                               MSG_WriteCoord13i(msg, s->origin[2]);
+                               if (bits & E5_MODEL16)
+                                       MSG_WriteShort(msg, s->modelindex);
+                               else
+                                       MSG_WriteByte(msg, s->modelindex);
                        }
-               }
-               if (bits & E5_ANGLES)
-               {
-                       if (bits & E5_ANGLES16)
+                       if (bits & E5_FRAME)
                        {
-                               MSG_WriteAngle16i(msg, s->angles[0]);
-                               MSG_WriteAngle16i(msg, s->angles[1]);
-                               MSG_WriteAngle16i(msg, s->angles[2]);
+                               if (bits & E5_FRAME16)
+                                       MSG_WriteShort(msg, s->frame);
+                               else
+                                       MSG_WriteByte(msg, s->frame);
                        }
-                       else
+                       if (bits & E5_SKIN)
+                               MSG_WriteByte(msg, s->skin);
+                       if (bits & E5_EFFECTS)
                        {
-                               MSG_WriteAngle8i(msg, s->angles[0]);
-                               MSG_WriteAngle8i(msg, s->angles[1]);
-                               MSG_WriteAngle8i(msg, s->angles[2]);
+                               if (bits & E5_EFFECTS32)
+                                       MSG_WriteLong(msg, s->effects);
+                               else if (bits & E5_EFFECTS16)
+                                       MSG_WriteShort(msg, s->effects);
+                               else
+                                       MSG_WriteByte(msg, s->effects);
                        }
-               }
-               if (bits & E5_MODEL)
-               {
-                       if (bits & E5_MODEL16)
-                               MSG_WriteShort(msg, s->modelindex);
-                       else
-                               MSG_WriteByte(msg, s->modelindex);
-               }
-               if (bits & E5_FRAME)
-               {
-                       if (bits & E5_FRAME16)
-                               MSG_WriteShort(msg, s->frame);
-                       else
-                               MSG_WriteByte(msg, s->frame);
-               }
-               if (bits & E5_SKIN)
-                       MSG_WriteByte(msg, s->skin);
-               if (bits & E5_EFFECTS)
-               {
-                       if (bits & E5_EFFECTS32)
-                               MSG_WriteLong(msg, s->effects);
-                       else if (bits & E5_EFFECTS16)
-                               MSG_WriteShort(msg, s->effects);
-                       else
-                               MSG_WriteByte(msg, s->effects);
-               }
-               if (bits & E5_ALPHA)
-                       MSG_WriteByte(msg, s->alpha);
-               if (bits & E5_SCALE)
-                       MSG_WriteByte(msg, s->scale);
-               if (bits & E5_COLORMAP)
-                       MSG_WriteByte(msg, s->colormap);
-               if (bits & E5_ATTACHMENT)
-               {
-                       MSG_WriteShort(msg, s->tagentity);
-                       MSG_WriteByte(msg, s->tagindex);
-               }
-               if (bits & E5_LIGHT)
-               {
-                       MSG_WriteShort(msg, s->light[0]);
-                       MSG_WriteShort(msg, s->light[1]);
-                       MSG_WriteShort(msg, s->light[2]);
-                       MSG_WriteShort(msg, s->light[3]);
-                       MSG_WriteByte(msg, s->lightstyle);
-                       MSG_WriteByte(msg, s->lightpflags);
-               }
-               if (bits & E5_GLOW)
-               {
-                       MSG_WriteByte(msg, s->glowsize);
-                       MSG_WriteByte(msg, s->glowcolor);
-               }
-               if (bits & E5_COLORMOD)
-               {
-                       MSG_WriteByte(msg, s->colormod[0]);
-                       MSG_WriteByte(msg, s->colormod[1]);
-                       MSG_WriteByte(msg, s->colormod[2]);
-               }
-               if (bits & E5_GLOWMOD)
-               {
-                       MSG_WriteByte(msg, s->glowmod[0]);
-                       MSG_WriteByte(msg, s->glowmod[1]);
-                       MSG_WriteByte(msg, s->glowmod[2]);
-               }
-               if (bits & E5_COMPLEXANIMATION)
-               {
-                       if (s->skeletonobject.model && s->skeletonobject.relativetransforms)
+                       if (bits & E5_ALPHA)
+                               MSG_WriteByte(msg, s->alpha);
+                       if (bits & E5_SCALE)
+                               MSG_WriteByte(msg, s->scale);
+                       if (bits & E5_COLORMAP)
+                               MSG_WriteByte(msg, s->colormap);
+                       if (bits & E5_ATTACHMENT)
                        {
-                               int numbones = s->skeletonobject.model->num_bones;
-                               int bonenum;
-                               short pose6s[6];
-                               MSG_WriteByte(msg, 4);
-                               MSG_WriteShort(msg, s->modelindex);
-                               MSG_WriteByte(msg, numbones);
-                               for (bonenum = 0;bonenum < numbones;bonenum++)
-                               {
-                                       Matrix4x4_ToBonePose6s(s->skeletonobject.relativetransforms + bonenum, 64, pose6s);
-                                       MSG_WriteShort(msg, pose6s[0]);
-                                       MSG_WriteShort(msg, pose6s[1]);
-                                       MSG_WriteShort(msg, pose6s[2]);
-                                       MSG_WriteShort(msg, pose6s[3]);
-                                       MSG_WriteShort(msg, pose6s[4]);
-                                       MSG_WriteShort(msg, pose6s[5]);
-                               }
+                               MSG_WriteShort(msg, s->tagentity);
+                               MSG_WriteByte(msg, s->tagindex);
                        }
-                       else if (s->framegroupblend[3].lerp > 0)
+                       if (bits & E5_LIGHT)
                        {
-                               MSG_WriteByte(msg, 3);
-                               MSG_WriteShort(msg, s->framegroupblend[0].frame);
-                               MSG_WriteShort(msg, s->framegroupblend[1].frame);
-                               MSG_WriteShort(msg, s->framegroupblend[2].frame);
-                               MSG_WriteShort(msg, s->framegroupblend[3].frame);
-                               MSG_WriteShort(msg, (int)((sv.time - s->framegroupblend[0].start) * 1000.0));
-                               MSG_WriteShort(msg, (int)((sv.time - s->framegroupblend[1].start) * 1000.0));
-                               MSG_WriteShort(msg, (int)((sv.time - s->framegroupblend[2].start) * 1000.0));
-                               MSG_WriteShort(msg, (int)((sv.time - s->framegroupblend[3].start) * 1000.0));
-                               MSG_WriteByte(msg, s->framegroupblend[0].lerp * 255.0f);
-                               MSG_WriteByte(msg, s->framegroupblend[1].lerp * 255.0f);
-                               MSG_WriteByte(msg, s->framegroupblend[2].lerp * 255.0f);
-                               MSG_WriteByte(msg, s->framegroupblend[3].lerp * 255.0f);
+                               MSG_WriteShort(msg, s->light[0]);
+                               MSG_WriteShort(msg, s->light[1]);
+                               MSG_WriteShort(msg, s->light[2]);
+                               MSG_WriteShort(msg, s->light[3]);
+                               MSG_WriteByte(msg, s->lightstyle);
+                               MSG_WriteByte(msg, s->lightpflags);
                        }
-                       else if (s->framegroupblend[2].lerp > 0)
+                       if (bits & E5_GLOW)
                        {
-                               MSG_WriteByte(msg, 2);
-                               MSG_WriteShort(msg, s->framegroupblend[0].frame);
-                               MSG_WriteShort(msg, s->framegroupblend[1].frame);
-                               MSG_WriteShort(msg, s->framegroupblend[2].frame);
-                               MSG_WriteShort(msg, (int)((sv.time - s->framegroupblend[0].start) * 1000.0));
-                               MSG_WriteShort(msg, (int)((sv.time - s->framegroupblend[1].start) * 1000.0));
-                               MSG_WriteShort(msg, (int)((sv.time - s->framegroupblend[2].start) * 1000.0));
-                               MSG_WriteByte(msg, s->framegroupblend[0].lerp * 255.0f);
-                               MSG_WriteByte(msg, s->framegroupblend[1].lerp * 255.0f);
-                               MSG_WriteByte(msg, s->framegroupblend[2].lerp * 255.0f);
+                               MSG_WriteByte(msg, s->glowsize);
+                               MSG_WriteByte(msg, s->glowcolor);
                        }
-                       else if (s->framegroupblend[1].lerp > 0)
+                       if (bits & E5_COLORMOD)
                        {
-                               MSG_WriteByte(msg, 1);
-                               MSG_WriteShort(msg, s->framegroupblend[0].frame);
-                               MSG_WriteShort(msg, s->framegroupblend[1].frame);
-                               MSG_WriteShort(msg, (int)((sv.time - s->framegroupblend[0].start) * 1000.0));
-                               MSG_WriteShort(msg, (int)((sv.time - s->framegroupblend[1].start) * 1000.0));
-                               MSG_WriteByte(msg, s->framegroupblend[0].lerp * 255.0f);
-                               MSG_WriteByte(msg, s->framegroupblend[1].lerp * 255.0f);
+                               MSG_WriteByte(msg, s->colormod[0]);
+                               MSG_WriteByte(msg, s->colormod[1]);
+                               MSG_WriteByte(msg, s->colormod[2]);
                        }
-                       else
+                       if (bits & E5_GLOWMOD)
                        {
-                               MSG_WriteByte(msg, 0);
-                               MSG_WriteShort(msg, s->framegroupblend[0].frame);
-                               MSG_WriteShort(msg, (int)((sv.time - s->framegroupblend[0].start) * 1000.0));
+                               MSG_WriteByte(msg, s->glowmod[0]);
+                               MSG_WriteByte(msg, s->glowmod[1]);
+                               MSG_WriteByte(msg, s->glowmod[2]);
                        }
+                       if (bits & E5_COMPLEXANIMATION)
+                       {
+                               if (s->skeletonobject.model && s->skeletonobject.relativetransforms)
+                               {
+                                       int numbones = s->skeletonobject.model->num_bones;
+                                       int bonenum;
+                                       short pose7s[7];
+                                       MSG_WriteByte(msg, 4);
+                                       MSG_WriteShort(msg, s->modelindex);
+                                       MSG_WriteByte(msg, numbones);
+                                       for (bonenum = 0;bonenum < numbones;bonenum++)
+                                       {
+                                               Matrix4x4_ToBonePose7s(s->skeletonobject.relativetransforms + bonenum, 64, pose7s);
+                                               MSG_WriteShort(msg, pose7s[0]);
+                                               MSG_WriteShort(msg, pose7s[1]);
+                                               MSG_WriteShort(msg, pose7s[2]);
+                                               MSG_WriteShort(msg, pose7s[3]);
+                                               MSG_WriteShort(msg, pose7s[4]);
+                                               MSG_WriteShort(msg, pose7s[5]);
+                                               MSG_WriteShort(msg, pose7s[6]);
+                                       }
+                               }
+                               else
+                               {
+                                       dp_model_t *model = SV_GetModelByIndex(s->modelindex);
+                                       if (s->framegroupblend[3].lerp > 0)
+                                       {
+                                               MSG_WriteByte(msg, 3);
+                                               MSG_WriteShort(msg, s->framegroupblend[0].frame);
+                                               MSG_WriteShort(msg, s->framegroupblend[1].frame);
+                                               MSG_WriteShort(msg, s->framegroupblend[2].frame);
+                                               MSG_WriteShort(msg, s->framegroupblend[3].frame);
+                                               MSG_WriteShort(msg, (int)(anim_reducetime(sv.time - s->framegroupblend[0].start, anim_frameduration(model, s->framegroupblend[0].frame), 65.535) * 1000.0));
+                                               MSG_WriteShort(msg, (int)(anim_reducetime(sv.time - s->framegroupblend[1].start, anim_frameduration(model, s->framegroupblend[1].frame), 65.535) * 1000.0));
+                                               MSG_WriteShort(msg, (int)(anim_reducetime(sv.time - s->framegroupblend[2].start, anim_frameduration(model, s->framegroupblend[2].frame), 65.535) * 1000.0));
+                                               MSG_WriteShort(msg, (int)(anim_reducetime(sv.time - s->framegroupblend[3].start, anim_frameduration(model, s->framegroupblend[3].frame), 65.535) * 1000.0));
+                                               MSG_WriteByte(msg, s->framegroupblend[0].lerp * 255.0f);
+                                               MSG_WriteByte(msg, s->framegroupblend[1].lerp * 255.0f);
+                                               MSG_WriteByte(msg, s->framegroupblend[2].lerp * 255.0f);
+                                               MSG_WriteByte(msg, s->framegroupblend[3].lerp * 255.0f);
+                                       }
+                                       else if (s->framegroupblend[2].lerp > 0)
+                                       {
+                                               MSG_WriteByte(msg, 2);
+                                               MSG_WriteShort(msg, s->framegroupblend[0].frame);
+                                               MSG_WriteShort(msg, s->framegroupblend[1].frame);
+                                               MSG_WriteShort(msg, s->framegroupblend[2].frame);
+                                               MSG_WriteShort(msg, (int)(anim_reducetime(sv.time - s->framegroupblend[0].start, anim_frameduration(model, s->framegroupblend[0].frame), 65.535) * 1000.0));
+                                               MSG_WriteShort(msg, (int)(anim_reducetime(sv.time - s->framegroupblend[1].start, anim_frameduration(model, s->framegroupblend[1].frame), 65.535) * 1000.0));
+                                               MSG_WriteShort(msg, (int)(anim_reducetime(sv.time - s->framegroupblend[2].start, anim_frameduration(model, s->framegroupblend[2].frame), 65.535) * 1000.0));
+                                               MSG_WriteByte(msg, s->framegroupblend[0].lerp * 255.0f);
+                                               MSG_WriteByte(msg, s->framegroupblend[1].lerp * 255.0f);
+                                               MSG_WriteByte(msg, s->framegroupblend[2].lerp * 255.0f);
+                                       }
+                                       else if (s->framegroupblend[1].lerp > 0)
+                                       {
+                                               MSG_WriteByte(msg, 1);
+                                               MSG_WriteShort(msg, s->framegroupblend[0].frame);
+                                               MSG_WriteShort(msg, s->framegroupblend[1].frame);
+                                               MSG_WriteShort(msg, (int)(anim_reducetime(sv.time - s->framegroupblend[0].start, anim_frameduration(model, s->framegroupblend[0].frame), 65.535) * 1000.0));
+                                               MSG_WriteShort(msg, (int)(anim_reducetime(sv.time - s->framegroupblend[1].start, anim_frameduration(model, s->framegroupblend[1].frame), 65.535) * 1000.0));
+                                               MSG_WriteByte(msg, s->framegroupblend[0].lerp * 255.0f);
+                                               MSG_WriteByte(msg, s->framegroupblend[1].lerp * 255.0f);
+                                       }
+                                       else
+                                       {
+                                               MSG_WriteByte(msg, 0);
+                                               MSG_WriteShort(msg, s->framegroupblend[0].frame);
+                                               MSG_WriteShort(msg, (int)(anim_reducetime(sv.time - s->framegroupblend[0].start, anim_frameduration(model, s->framegroupblend[0].frame), 65.535) * 1000.0));
+                                       }
+                               }
+                       }
+                       if (bits & E5_TRAILEFFECTNUM)
+                               MSG_WriteShort(msg, s->traileffectnum);
+                       ENTITYSIZEPROFILING_END(msg, s->number, bits);
                }
-               if (bits & E5_TRAILEFFECTNUM)
-                       MSG_WriteShort(msg, s->traileffectnum);
        }
-
-       ENTITYSIZEPROFILING_END(msg, s->number);
 }
 
 static void EntityState5_ReadUpdate(entity_state_t *s, int number)
 {
        int bits;
+       int startoffset = cl_message.readcount;
+       int bytes = 0;
        bits = MSG_ReadByte(&cl_message);
        if (bits & E5_EXTEND1)
        {
@@ -2405,7 +2468,7 @@ static void EntityState5_ReadUpdate(entity_state_t *s, int number)
                int type;
                int bonenum;
                int numbones;
-               short pose6s[6];
+               short pose7s[7];
                type = MSG_ReadByte(&cl_message);
                switch(type)
                {
@@ -2414,7 +2477,7 @@ static void EntityState5_ReadUpdate(entity_state_t *s, int number)
                        s->framegroupblend[1].frame = 0;
                        s->framegroupblend[2].frame = 0;
                        s->framegroupblend[3].frame = 0;
-                       s->framegroupblend[0].start = cl.time - (short)MSG_ReadShort(&cl_message) * (1.0f / 1000.0f);
+                       s->framegroupblend[0].start = cl.time - (unsigned short)MSG_ReadShort(&cl_message) * (1.0f / 1000.0f);
                        s->framegroupblend[1].start = 0;
                        s->framegroupblend[2].start = 0;
                        s->framegroupblend[3].start = 0;
@@ -2428,8 +2491,8 @@ static void EntityState5_ReadUpdate(entity_state_t *s, int number)
                        s->framegroupblend[1].frame = MSG_ReadShort(&cl_message);
                        s->framegroupblend[2].frame = 0;
                        s->framegroupblend[3].frame = 0;
-                       s->framegroupblend[0].start = cl.time - (short)MSG_ReadShort(&cl_message) * (1.0f / 1000.0f);
-                       s->framegroupblend[1].start = cl.time - (short)MSG_ReadShort(&cl_message) * (1.0f / 1000.0f);
+                       s->framegroupblend[0].start = cl.time - (unsigned short)MSG_ReadShort(&cl_message) * (1.0f / 1000.0f);
+                       s->framegroupblend[1].start = cl.time - (unsigned short)MSG_ReadShort(&cl_message) * (1.0f / 1000.0f);
                        s->framegroupblend[2].start = 0;
                        s->framegroupblend[3].start = 0;
                        s->framegroupblend[0].lerp = MSG_ReadByte(&cl_message) * (1.0f / 255.0f);
@@ -2442,9 +2505,9 @@ static void EntityState5_ReadUpdate(entity_state_t *s, int number)
                        s->framegroupblend[1].frame = MSG_ReadShort(&cl_message);
                        s->framegroupblend[2].frame = MSG_ReadShort(&cl_message);
                        s->framegroupblend[3].frame = 0;
-                       s->framegroupblend[0].start = cl.time - (short)MSG_ReadShort(&cl_message) * (1.0f / 1000.0f);
-                       s->framegroupblend[1].start = cl.time - (short)MSG_ReadShort(&cl_message) * (1.0f / 1000.0f);
-                       s->framegroupblend[2].start = cl.time - (short)MSG_ReadShort(&cl_message) * (1.0f / 1000.0f);
+                       s->framegroupblend[0].start = cl.time - (unsigned short)MSG_ReadShort(&cl_message) * (1.0f / 1000.0f);
+                       s->framegroupblend[1].start = cl.time - (unsigned short)MSG_ReadShort(&cl_message) * (1.0f / 1000.0f);
+                       s->framegroupblend[2].start = cl.time - (unsigned short)MSG_ReadShort(&cl_message) * (1.0f / 1000.0f);
                        s->framegroupblend[3].start = 0;
                        s->framegroupblend[0].lerp = MSG_ReadByte(&cl_message) * (1.0f / 255.0f);
                        s->framegroupblend[1].lerp = MSG_ReadByte(&cl_message) * (1.0f / 255.0f);
@@ -2456,10 +2519,10 @@ static void EntityState5_ReadUpdate(entity_state_t *s, int number)
                        s->framegroupblend[1].frame = MSG_ReadShort(&cl_message);
                        s->framegroupblend[2].frame = MSG_ReadShort(&cl_message);
                        s->framegroupblend[3].frame = MSG_ReadShort(&cl_message);
-                       s->framegroupblend[0].start = cl.time - (short)MSG_ReadShort(&cl_message) * (1.0f / 1000.0f);
-                       s->framegroupblend[1].start = cl.time - (short)MSG_ReadShort(&cl_message) * (1.0f / 1000.0f);
-                       s->framegroupblend[2].start = cl.time - (short)MSG_ReadShort(&cl_message) * (1.0f / 1000.0f);
-                       s->framegroupblend[3].start = cl.time - (short)MSG_ReadShort(&cl_message) * (1.0f / 1000.0f);
+                       s->framegroupblend[0].start = cl.time - (unsigned short)MSG_ReadShort(&cl_message) * (1.0f / 1000.0f);
+                       s->framegroupblend[1].start = cl.time - (unsigned short)MSG_ReadShort(&cl_message) * (1.0f / 1000.0f);
+                       s->framegroupblend[2].start = cl.time - (unsigned short)MSG_ReadShort(&cl_message) * (1.0f / 1000.0f);
+                       s->framegroupblend[3].start = cl.time - (unsigned short)MSG_ReadShort(&cl_message) * (1.0f / 1000.0f);
                        s->framegroupblend[0].lerp = MSG_ReadByte(&cl_message) * (1.0f / 255.0f);
                        s->framegroupblend[1].lerp = MSG_ReadByte(&cl_message) * (1.0f / 255.0f);
                        s->framegroupblend[2].lerp = MSG_ReadByte(&cl_message) * (1.0f / 255.0f);
@@ -2477,19 +2540,20 @@ static void EntityState5_ReadUpdate(entity_state_t *s, int number)
                        if (!skeleton->relativetransforms || skeleton->model != model)
                        {
                                skeleton->model = model;
-                               skeleton->relativetransforms = (matrix4x4_t *) Mem_Realloc(cls.levelmempool, skeleton->relativetransforms, sizeof(*skeleton->relativetransforms) * skeleton->model->num_bones);
-                               for (bonenum = 0;bonenum < model->num_bones;bonenum++)
+                               skeleton->relativetransforms = (matrix4x4_t *) Mem_Realloc(cls.levelmempool, skeleton->relativetransforms, sizeof(*skeleton->relativetransforms) * numbones);
+                               for (bonenum = 0;bonenum < numbones;bonenum++)
                                        skeleton->relativetransforms[bonenum] = identitymatrix;
                        }
                        for (bonenum = 0;bonenum < numbones;bonenum++)
                        {
-                               pose6s[0] = (short)MSG_ReadShort(&cl_message);
-                               pose6s[1] = (short)MSG_ReadShort(&cl_message);
-                               pose6s[2] = (short)MSG_ReadShort(&cl_message);
-                               pose6s[3] = (short)MSG_ReadShort(&cl_message);
-                               pose6s[4] = (short)MSG_ReadShort(&cl_message);
-                               pose6s[5] = (short)MSG_ReadShort(&cl_message);
-                               Matrix4x4_FromBonePose6s(skeleton->relativetransforms + bonenum, 1.0f / 64.0f, pose6s);
+                               pose7s[0] = (short)MSG_ReadShort(&cl_message);
+                               pose7s[1] = (short)MSG_ReadShort(&cl_message);
+                               pose7s[2] = (short)MSG_ReadShort(&cl_message);
+                               pose7s[3] = (short)MSG_ReadShort(&cl_message);
+                               pose7s[4] = (short)MSG_ReadShort(&cl_message);
+                               pose7s[5] = (short)MSG_ReadShort(&cl_message);
+                               pose7s[6] = (short)MSG_ReadShort(&cl_message);
+                               Matrix4x4_FromBonePose7s(skeleton->relativetransforms + bonenum, 1.0f / 64.0f, pose7s);
                        }
                        s->skeletonobject = *skeleton;
                        break;
@@ -2502,9 +2566,10 @@ static void EntityState5_ReadUpdate(entity_state_t *s, int number)
                s->traileffectnum = (unsigned short) MSG_ReadShort(&cl_message);
 
 
+       bytes = cl_message.readcount - startoffset;
        if (developer_networkentities.integer >= 2)
        {
-               Con_Printf("ReadFields e%i", number);
+               Con_Printf("ReadFields e%i (%i bytes)", number, bytes);
 
                if (bits & E5_ORIGIN)
                        Con_Printf(" E5_ORIGIN %f %f %f", s->origin[0], s->origin[1], s->origin[2]);
@@ -2557,6 +2622,10 @@ static void EntityState5_ReadUpdate(entity_state_t *s, int number)
                        Con_Printf(" E5_COLORMOD %f:%f:%f", s->colormod[0] / 32.0f, s->colormod[1] / 32.0f, s->colormod[2] / 32.0f);
                if (bits & E5_GLOWMOD)
                        Con_Printf(" E5_GLOWMOD %f:%f:%f", s->glowmod[0] / 32.0f, s->glowmod[1] / 32.0f, s->glowmod[2] / 32.0f);
+               if (bits & E5_COMPLEXANIMATION)
+                       Con_Printf(" E5_COMPLEXANIMATION");
+               if (bits & E5_TRAILEFFECTNUM)
+                       Con_Printf(" E5_TRAILEFFECTNUM %i", s->traileffectnum);
                Con_Print("\n");
        }
 }
@@ -2599,7 +2668,25 @@ static int EntityState5_DeltaBits(const entity_state_t *o, const entity_state_t
                if (o->glowmod[0] != n->glowmod[0] || o->glowmod[1] != n->glowmod[1] || o->glowmod[2] != n->glowmod[2])
                        bits |= E5_GLOWMOD;
                if (n->flags & RENDER_COMPLEXANIMATION)
-                       bits |= E5_COMPLEXANIMATION;
+               {
+                       if ((o->skeletonobject.model && o->skeletonobject.relativetransforms) != (n->skeletonobject.model && n->skeletonobject.relativetransforms))
+                       {
+                               bits |= E5_COMPLEXANIMATION;
+                       }
+                       else if (o->skeletonobject.model && o->skeletonobject.relativetransforms)
+                       {
+                               if(o->modelindex != n->modelindex)
+                                       bits |= E5_COMPLEXANIMATION;
+                               else if(o->skeletonobject.model->num_bones != n->skeletonobject.model->num_bones)
+                                       bits |= E5_COMPLEXANIMATION;
+                               else if(memcmp(o->skeletonobject.relativetransforms, n->skeletonobject.relativetransforms, o->skeletonobject.model->num_bones * sizeof(*o->skeletonobject.relativetransforms)))
+                                       bits |= E5_COMPLEXANIMATION;
+                       }
+                       else if (memcmp(o->framegroupblend, n->framegroupblend, sizeof(o->framegroupblend)))
+                       {
+                               bits |= E5_COMPLEXANIMATION;
+                       }
+               }
                if (o->traileffectnum != n->traileffectnum)
                        bits |= E5_TRAILEFFECTNUM;
        }
@@ -2745,7 +2832,7 @@ void EntityFrame5_AckFrame(entityframe5_database_t *d, int framenum)
                        d->packetlog[i].packetnumber = 0;
 }
 
-qboolean EntityFrame5_WriteFrame(sizebuf_t *msg, int maxsize, entityframe5_database_t *d, int numstates, const entity_state_t **states, int viewentnum, int movesequence, qboolean need_empty)
+qboolean EntityFrame5_WriteFrame(sizebuf_t *msg, int maxsize, entityframe5_database_t *d, int numstates, const entity_state_t **states, int viewentnum, unsigned int movesequence, qboolean need_empty)
 {
        prvm_prog_t *prog = SVVM_prog;
        const entity_state_t *n;
@@ -3209,7 +3296,6 @@ void EntityFrameQW_CL_ReadFrame(qboolean delta)
        newsnapindex = cl.qw_validsequence & QW_UPDATE_MASK;
        newsnap = d->snapshot + newsnapindex;
        memset(newsnap, 0, sizeof(*newsnap));
-       oldsnapindex = -1;
        oldsnap = NULL;
        if (delta)
        {