+ if (skip)
+ EntityFrame4_ResetDatabase(d);
+}
+
+void EntityFrame4_WriteFrame(sizebuf_t *msg, entityframe4_database_t *d, int numstates, const entity_state_t *states)
+{
+ const entity_state_t *e, *s;
+ entity_state_t inactiveentitystate;
+ int i, n, startnumber;
+ sizebuf_t buf;
+ qbyte data[128];
+
+ // if there isn't enough space to accomplish anything, skip it
+ if (msg->cursize + 24 > msg->maxsize)
+ return;
+
+ // prepare the buffer
+ memset(&buf, 0, sizeof(buf));
+ buf.data = data;
+ buf.maxsize = sizeof(data);
+
+ for (i = 0;i < MAX_ENTITY_HISTORY;i++)
+ if (!d->commit[i].numentities)
+ break;
+ // if commit buffer full, just don't bother writing an update this frame
+ if (i == MAX_ENTITY_HISTORY)
+ return;
+ d->currentcommit = d->commit + i;
+
+ // this state's number gets played around with later
+ inactiveentitystate = defaultstate;
+
+ d->currentcommit->numentities = 0;
+ d->currentcommit->framenum = ++d->latestframenumber;
+ MSG_WriteByte(msg, svc_entities);
+ MSG_WriteLong(msg, d->referenceframenum);
+ MSG_WriteLong(msg, d->currentcommit->framenum);
+ if (developer_networkentities.integer >= 1)
+ {
+ Con_Printf("send svc_entities num:%i ref:%i (database: ref:%i commits:", d->currentcommit->framenum, d->referenceframenum, d->referenceframenum);
+ for (i = 0;i < MAX_ENTITY_HISTORY;i++)
+ if (d->commit[i].numentities)
+ Con_Printf(" %i", d->commit[i].framenum);
+ Con_Print(")\n");
+ }
+ if (d->currententitynumber >= sv.max_edicts)
+ startnumber = 1;
+ else
+ startnumber = bound(1, d->currententitynumber, sv.max_edicts - 1);
+ MSG_WriteShort(msg, startnumber);
+ // reset currententitynumber so if the loop does not break it we will
+ // start at beginning next frame (if it does break, it will set it)
+ d->currententitynumber = 1;
+ for (i = 0, n = startnumber;n < sv.max_edicts;n++)
+ {
+ // find the old state to delta from
+ e = EntityFrame4_GetReferenceEntity(d, n);
+ // prepare the buffer
+ SZ_Clear(&buf);
+ // entity exists, build an update (if empty there is no change)
+ // find the state in the list
+ for (;i < numstates && states[i].number < n;i++);
+ // make the message
+ s = states + i;
+ if (s->number == n)
+ {
+ // build the update
+ EntityState_WriteUpdate(s, &buf, e);
+ }
+ else
+ {
+ inactiveentitystate.number = n;
+ s = &inactiveentitystate;
+ if (e->active)
+ {
+ // entity used to exist but doesn't anymore, send remove
+ MSG_WriteShort(&buf, n | 0x8000);
+ }
+ }
+ // if the commit is full, we're done this frame
+ if (msg->cursize + buf.cursize > msg->maxsize - 4)
+ {
+ // next frame we will continue where we left off
+ break;
+ }
+ // add the entity to the commit
+ EntityFrame4_AddCommitEntity(d, s);
+ // if the message is empty, skip out now
+ if (buf.cursize)
+ {
+ // write the message to the packet
+ SZ_Write(msg, buf.data, buf.cursize);
+ }
+ }
+ d->currententitynumber = n;
+
+ // remove world message (invalid, and thus a good terminator)
+ MSG_WriteShort(msg, 0x8000);
+ // write the number of the end entity
+ MSG_WriteShort(msg, d->currententitynumber);
+ // just to be sure
+ d->currentcommit = NULL;
+}
+
+
+
+
+#define E5_PROTOCOL_PRIORITYLEVELS 32
+
+entityframe5_database_t *EntityFrame5_AllocDatabase(mempool_t *pool)
+{
+ entityframe5_database_t *d;
+ d = Mem_Alloc(pool, sizeof(*d));
+ EntityFrame5_ResetDatabase(d);
+ return d;
+}
+
+void EntityFrame5_FreeDatabase(entityframe5_database_t *d)
+{
+ Mem_Free(d);
+}
+
+void EntityFrame5_ResetDatabase(entityframe5_database_t *d)
+{
+ int i;
+ memset(d, 0, sizeof(*d));
+ d->latestframenum = 0;
+ for (i = 0;i < MAX_EDICTS;i++)
+ d->states[i] = defaultstate;
+}
+
+
+int EntityState5_Priority(entityframe5_database_t *d, entity_state_t *view, entity_state_t *s, int changedbits, int age)
+{
+ int lowprecision, limit, priority;
+ double distance;
+ if (!changedbits)
+ return 0;
+ if (!s->active/* && changedbits & E5_FULLUPDATE*/)
+ return E5_PROTOCOL_PRIORITYLEVELS - 1;
+ // check whole attachment chain to judge relevance to player
+ lowprecision = false;
+ for (limit = 0;limit < 256;limit++)
+ {
+ if (s == view)
+ return E5_PROTOCOL_PRIORITYLEVELS - 1;
+ if (s->flags & RENDER_VIEWMODEL)
+ return E5_PROTOCOL_PRIORITYLEVELS - 1;
+ if (s->flags & RENDER_LOWPRECISION)
+ lowprecision = true;
+ if (!s->tagentity)
+ {
+ if (VectorCompare(s->origin, view->origin))
+ return E5_PROTOCOL_PRIORITYLEVELS - 1;
+ break;
+ }
+ s = d->states + s->tagentity;
+ }
+ if (limit >= 256)
+ Con_Printf("Protocol: Runaway loop recursing tagentity links on entity %i\n", s->number);
+ // it's not a viewmodel for this client
+ distance = VectorDistance(view->origin, s->origin);
+ priority = (E5_PROTOCOL_PRIORITYLEVELS / 2) + age - (int)(distance * (E5_PROTOCOL_PRIORITYLEVELS / 16384.0f));
+ if (lowprecision)
+ priority -= (E5_PROTOCOL_PRIORITYLEVELS / 4);
+ //if (changedbits & E5_FULLUPDATE)
+ // priority += 4;
+ //if (changedbits & (E5_ATTACHMENT | E5_MODEL | E5_FLAGS | E5_COLORMAP))
+ // priority += 4;
+ return (int) bound(1, priority, E5_PROTOCOL_PRIORITYLEVELS - 1);
+}
+
+void EntityState5_WriteUpdate(int number, const entity_state_t *s, int changedbits, sizebuf_t *msg)
+{
+ unsigned int bits = 0;
+ if (!s->active)
+ MSG_WriteShort(msg, number | 0x8000);
+ else
+ {
+ bits = changedbits;
+ if ((bits & E5_ORIGIN) && (s->origin[0] < -4096 || s->origin[0] >= 4096 || s->origin[1] < -4096 || s->origin[1] >= 4096 || s->origin[2] < -4096 || s->origin[2] >= 4096))
+ bits |= E5_ORIGIN32;
+ if ((bits & E5_ANGLES) && !(s->flags & RENDER_LOWPRECISION))
+ bits |= E5_ANGLES16;
+ if ((bits & E5_MODEL) && s->modelindex >= 256)
+ bits |= E5_MODEL16;
+ if ((bits & E5_FRAME) && s->frame >= 256)
+ bits |= E5_FRAME16;
+ if (bits & E5_EFFECTS)
+ {
+ if (s->effects >= 65536)
+ bits |= E5_EFFECTS32;
+ else if (s->effects >= 256)
+ bits |= E5_EFFECTS16;
+ }
+ if (bits >= 256)
+ bits |= E5_EXTEND1;
+ if (bits >= 65536)
+ 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)
+ {
+ 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]);
+ }
+ }
+ 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)
+ {
+ 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]);
+ }
+ }
+}
+
+void EntityState5_ReadUpdate(entity_state_t *s)
+{
+ int bits;
+ bits = MSG_ReadByte();
+ if (bits & E5_EXTEND1)
+ {
+ bits |= MSG_ReadByte() << 8;
+ if (bits & E5_EXTEND2)
+ {
+ bits |= MSG_ReadByte() << 16;
+ if (bits & E5_EXTEND3)
+ bits |= MSG_ReadByte() << 24;
+ }
+ }
+ if (bits & E5_FULLUPDATE)
+ {
+ *s = defaultstate;
+ s->active = true;
+ }
+ if (bits & E5_FLAGS)
+ s->flags = MSG_ReadByte();
+ if (bits & E5_ORIGIN)
+ {
+ if (bits & E5_ORIGIN32)
+ {
+ s->origin[0] = MSG_ReadCoord32f();
+ s->origin[1] = MSG_ReadCoord32f();
+ s->origin[2] = MSG_ReadCoord32f();
+ }
+ else
+ {
+ s->origin[0] = MSG_ReadCoord13i();
+ s->origin[1] = MSG_ReadCoord13i();
+ s->origin[2] = MSG_ReadCoord13i();
+ }
+ }
+ if (bits & E5_ANGLES)
+ {
+ if (bits & E5_ANGLES16)
+ {
+ s->angles[0] = MSG_ReadAngle16i();
+ s->angles[1] = MSG_ReadAngle16i();
+ s->angles[2] = MSG_ReadAngle16i();
+ }
+ else
+ {
+ s->angles[0] = MSG_ReadAngle8i();
+ s->angles[1] = MSG_ReadAngle8i();
+ s->angles[2] = MSG_ReadAngle8i();
+ }
+ }
+ if (bits & E5_MODEL)
+ {
+ if (bits & E5_MODEL16)
+ s->modelindex = (unsigned short) MSG_ReadShort();
+ else
+ s->modelindex = MSG_ReadByte();
+ }
+ if (bits & E5_FRAME)
+ {
+ if (bits & E5_FRAME16)
+ s->frame = (unsigned short) MSG_ReadShort();
+ else
+ s->frame = MSG_ReadByte();
+ }
+ if (bits & E5_SKIN)
+ s->skin = MSG_ReadByte();
+ if (bits & E5_EFFECTS)
+ {
+ if (bits & E5_EFFECTS32)
+ s->effects = (unsigned int) MSG_ReadLong();
+ else if (bits & E5_EFFECTS16)
+ s->effects = (unsigned short) MSG_ReadShort();
+ else
+ s->effects = MSG_ReadByte();
+ }
+ if (bits & E5_ALPHA)
+ s->alpha = MSG_ReadByte();
+ if (bits & E5_SCALE)
+ s->scale = MSG_ReadByte();
+ if (bits & E5_COLORMAP)
+ s->colormap = MSG_ReadByte();
+ if (bits & E5_ATTACHMENT)
+ {
+ s->tagentity = (unsigned short) MSG_ReadShort();
+ s->tagindex = MSG_ReadByte();
+ }
+ if (bits & E5_LIGHT)
+ {
+ s->light[0] = (unsigned short) MSG_ReadShort();
+ s->light[1] = (unsigned short) MSG_ReadShort();
+ s->light[2] = (unsigned short) MSG_ReadShort();
+ s->light[3] = (unsigned short) MSG_ReadShort();
+ s->lightstyle = MSG_ReadByte();
+ s->lightpflags = MSG_ReadByte();
+ }
+ if (bits & E5_GLOW)
+ {
+ s->glowsize = MSG_ReadByte();
+ s->glowcolor = MSG_ReadByte();
+ }
+ if (bits & E5_COLORMOD)
+ {
+ s->colormod[0] = MSG_ReadByte();
+ s->colormod[1] = MSG_ReadByte();
+ s->colormod[2] = MSG_ReadByte();
+ }
+
+
+ if (developer_networkentities.integer >= 2)
+ {
+ Con_Printf("ReadFields e%i", s->number);
+
+ if (bits & E5_ORIGIN)
+ Con_Printf(" E5_ORIGIN %f %f %f", s->origin[0], s->origin[1], s->origin[2]);
+ if (bits & E5_ANGLES)
+ Con_Printf(" E5_ANGLES %f %f %f", s->angles[0], s->angles[1], s->angles[2]);
+ if (bits & E5_MODEL)
+ Con_Printf(" E5_MODEL %i", s->modelindex);
+ if (bits & E5_FRAME)
+ Con_Printf(" E5_FRAME %i", s->frame);
+ if (bits & E5_SKIN)
+ Con_Printf(" E5_SKIN %i", s->skin);
+ if (bits & E5_EFFECTS)
+ Con_Printf(" E5_EFFECTS %i", s->effects);
+ if (bits & E5_FLAGS)
+ {
+ Con_Printf(" E5_FLAGS %i (", s->flags);
+ if (s->flags & RENDER_STEP)
+ Con_Print(" STEP");
+ if (s->flags & RENDER_GLOWTRAIL)
+ Con_Print(" GLOWTRAIL");
+ if (s->flags & RENDER_VIEWMODEL)
+ Con_Print(" VIEWMODEL");
+ if (s->flags & RENDER_EXTERIORMODEL)
+ Con_Print(" EXTERIORMODEL");
+ if (s->flags & RENDER_LOWPRECISION)
+ Con_Print(" LOWPRECISION");
+ if (s->flags & RENDER_COLORMAPPED)
+ Con_Print(" COLORMAPPED");
+ if (s->flags & RENDER_SHADOW)
+ Con_Print(" SHADOW");
+ if (s->flags & RENDER_LIGHT)
+ Con_Print(" LIGHT");
+ Con_Print(")");
+ }
+ if (bits & E5_ALPHA)
+ Con_Printf(" E5_ALPHA %f", s->alpha / 255.0f);
+ if (bits & E5_SCALE)
+ Con_Printf(" E5_SCALE %f", s->scale / 16.0f);
+ if (bits & E5_COLORMAP)
+ Con_Printf(" E5_COLORMAP %i", s->colormap);
+ if (bits & E5_ATTACHMENT)
+ Con_Printf(" E5_ATTACHMENT e%i:%i", s->tagentity, s->tagindex);
+ if (bits & E5_LIGHT)
+ Con_Printf(" E5_LIGHT %i:%i:%i:%i %i:%i", s->light[0], s->light[1], s->light[2], s->light[3], s->lightstyle, s->lightpflags);
+ if (bits & E5_GLOW)
+ Con_Printf(" E5_GLOW %i:%i", s->glowsize * 4, s->glowcolor);
+ if (bits & E5_COLORMOD)
+ Con_Printf(" E5_COLORMOD %f:%f:%f", s->colormod[0] / 32.0f, s->colormod[1] / 32.0f, s->colormod[2] / 32.0f);
+ Con_Print("\n");
+ }
+}
+
+int EntityState5_DeltaBits(const entity_state_t *o, const entity_state_t *n)
+{
+ unsigned int bits = 0;
+ if (n->active)
+ {
+ if (!o->active)
+ bits |= E5_FULLUPDATE;
+ if (!VectorCompare(o->origin, n->origin))
+ bits |= E5_ORIGIN;
+ if (!VectorCompare(o->angles, n->angles))
+ bits |= E5_ANGLES;
+ if (o->modelindex != n->modelindex)
+ bits |= E5_MODEL;
+ if (o->frame != n->frame)
+ bits |= E5_FRAME;
+ if (o->skin != n->skin)
+ bits |= E5_SKIN;
+ if (o->effects != n->effects)
+ bits |= E5_EFFECTS;
+ if (o->flags != n->flags)
+ bits |= E5_FLAGS;
+ if (o->alpha != n->alpha)
+ bits |= E5_ALPHA;
+ if (o->scale != n->scale)
+ bits |= E5_SCALE;
+ if (o->colormap != n->colormap)
+ bits |= E5_COLORMAP;
+ if (o->tagentity != n->tagentity || o->tagindex != n->tagindex)
+ bits |= E5_ATTACHMENT;
+ if (o->light[0] != n->light[0] || o->light[1] != n->light[1] || o->light[2] != n->light[2] || o->light[3] != n->light[3] || o->lightstyle != n->lightstyle || o->lightpflags != n->lightpflags)
+ bits |= E5_LIGHT;
+ if (o->glowsize != n->glowsize || o->glowcolor != n->glowcolor)
+ bits |= E5_GLOW;
+ if (o->colormod[0] != n->colormod[0] || o->colormod[1] != n->colormod[1] || o->colormod[2] != n->colormod[2])
+ bits |= E5_COLORMOD;
+ }
+ else
+ if (o->active)
+ bits |= E5_FULLUPDATE;
+ return bits;