+ 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;
+ unsigned char data[128];
+ prvm_eval_t *val;
+
+ // 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 >= 10)
+ {
+ 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 >= prog->max_edicts)
+ startnumber = 1;
+ else
+ startnumber = bound(1, d->currententitynumber, prog->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 < prog->max_edicts;n++)
+ {
+ val = PRVM_EDICTFIELDVALUE((&prog->edicts[n]), prog->fieldoffsets.SendEntity);
+ if(val && val->function)
+ continue;
+ // 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;
+}
+
+
+
+
+entityframe5_database_t *EntityFrame5_AllocDatabase(mempool_t *pool)
+{
+ int i;
+ entityframe5_database_t *d;
+ d = (entityframe5_database_t *)Mem_Alloc(pool, sizeof(*d));
+ d->latestframenum = 0;
+ for (i = 0;i < d->maxedicts;i++)
+ d->states[i] = defaultstate;
+ return d;
+}
+
+void EntityFrame5_FreeDatabase(entityframe5_database_t *d)
+{
+ // all the [maxedicts] memory is allocated at once, so there's only one
+ // thing to free
+ if (d->maxedicts)
+ Mem_Free(d->deltabits);
+ Mem_Free(d);
+}
+
+void EntityFrame5_ExpandEdicts(entityframe5_database_t *d, int newmax)
+{
+ if (d->maxedicts < newmax)
+ {
+ unsigned char *data;
+ int oldmaxedicts = d->maxedicts;
+ int *olddeltabits = d->deltabits;
+ unsigned char *oldpriorities = d->priorities;
+ int *oldupdateframenum = d->updateframenum;
+ entity_state_t *oldstates = d->states;
+ unsigned char *oldvisiblebits = d->visiblebits;
+ d->maxedicts = newmax;
+ data = (unsigned char *)Mem_Alloc(sv_mempool, d->maxedicts * sizeof(int) + d->maxedicts * sizeof(unsigned char) + d->maxedicts * sizeof(int) + d->maxedicts * sizeof(entity_state_t) + (d->maxedicts+7)/8 * sizeof(unsigned char));
+ d->deltabits = (int *)data;data += d->maxedicts * sizeof(int);
+ d->priorities = (unsigned char *)data;data += d->maxedicts * sizeof(unsigned char);
+ d->updateframenum = (int *)data;data += d->maxedicts * sizeof(int);
+ d->states = (entity_state_t *)data;data += d->maxedicts * sizeof(entity_state_t);
+ d->visiblebits = (unsigned char *)data;data += (d->maxedicts+7)/8 * sizeof(unsigned char);
+ if (oldmaxedicts)
+ {
+ memcpy(d->deltabits, olddeltabits, oldmaxedicts * sizeof(int));
+ memcpy(d->priorities, oldpriorities, oldmaxedicts * sizeof(unsigned char));
+ memcpy(d->updateframenum, oldupdateframenum, oldmaxedicts * sizeof(int));
+ memcpy(d->states, oldstates, oldmaxedicts * sizeof(entity_state_t));
+ memcpy(d->visiblebits, oldvisiblebits, (oldmaxedicts+7)/8 * sizeof(unsigned char));
+ // the previous buffers were a single allocation, so just one free
+ Mem_Free(olddeltabits);
+ }
+ }
+}
+
+int EntityState5_Priority(entityframe5_database_t *d, int stateindex)
+{
+ int limit, priority;
+ entity_state_t *s;
+ // if it is the player, update urgently
+ if (stateindex == d->viewentnum)
+ return ENTITYFRAME5_PRIORITYLEVELS - 1;
+ // priority increases each frame no matter what happens
+ priority = d->priorities[stateindex] + 1;
+ // players get an extra priority boost
+ if (stateindex <= svs.maxclients)
+ priority++;
+ // remove dead entities very quickly because they are just 2 bytes
+ if (!d->states[stateindex].active)
+ {
+ priority++;
+ return bound(1, priority, ENTITYFRAME5_PRIORITYLEVELS - 1);
+ }
+ // certain changes are more noticable than others
+ if (d->deltabits[stateindex] & (E5_FULLUPDATE | E5_ATTACHMENT | E5_MODEL | E5_FLAGS | E5_COLORMAP))
+ priority++;
+ // find the root entity this one is attached to, and judge relevance by it
+ for (limit = 0;limit < 256;limit++)
+ {
+ s = d->states + stateindex;
+ if (s->flags & RENDER_VIEWMODEL)
+ stateindex = d->viewentnum;
+ else if (s->tagentity)
+ stateindex = s->tagentity;
+ else
+ break;
+ if (d->maxedicts < stateindex)
+ EntityFrame5_ExpandEdicts(d, (stateindex+256)&~255);
+ }
+ if (limit >= 256)
+ Con_DPrintf("Protocol: Runaway loop recursing tagentity links on entity %i\n", stateindex);
+ // now that we have the parent entity we can make some decisions based on
+ // distance from the player
+ if (VectorDistance(d->states[d->viewentnum].netcenter, s->netcenter) < 1024.0f)
+ priority++;
+ return bound(1, priority, ENTITYFRAME5_PRIORITYLEVELS - 1);
+}
+
+void EntityState5_WriteUpdate(int number, const entity_state_t *s, int changedbits, sizebuf_t *msg)
+{
+ unsigned int bits = 0;
+
+ prvm_eval_t *val;
+ val = PRVM_EDICTFIELDVALUE((&prog->edicts[s->number]), prog->fieldoffsets.SendEntity);
+ if(val && val->function)
+ return;
+
+ if (!s->active)
+ MSG_WriteShort(msg, number | 0x8000);
+ else
+ {
+ bits = changedbits;
+ if ((bits & E5_ORIGIN) && ((s->flags & RENDER_EXTERIORMODEL) || 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 & 0xFFFF0000)
+ bits |= E5_EFFECTS32;
+ else if (s->effects & 0xFFFFFF00)
+ 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 number)
+{
+ 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", 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");
+ if (s->flags & RENDER_NOSELFSHADOW)
+ Con_Print(" NOSELFSHADOW");
+ 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;