+ const entity_state_t *n;
+ int i, num, l, framenum, packetlognumber, priority;
+ sizebuf_t buf;
+ qbyte data[128];
+ entityframe5_packetlog_t *packetlog;
+
+ framenum = d->latestframenum + 1;
+
+ // prepare the buffer
+ memset(&buf, 0, sizeof(buf));
+ buf.data = data;
+ buf.maxsize = sizeof(data);
+
+ // detect changes in states
+ num = 0;
+ for (i = 0, n = states;i < numstates;i++, n++)
+ {
+ // mark gaps in entity numbering as removed entities
+ for (;num < n->number;num++)
+ {
+ // if the entity used to exist, clear it
+ if (CHECKPVSBIT(d->visiblebits, num))
+ {
+ CLEARPVSBIT(d->visiblebits, num);
+ d->deltabits[num] = E5_FULLUPDATE;
+ d->priorities[num] = EntityState5_Priority(d, d->states + viewentnum, d->states + num, d->deltabits[num], framenum - d->updateframenum[num]);
+ d->states[num] = defaultstate;
+ d->states[num].number = num;
+ }
+ }
+ // update the entity state data
+ if (!CHECKPVSBIT(d->visiblebits, num))
+ {
+ // entity just spawned in, don't let it completely hog priority
+ // because of being ancient on the first frame
+ d->updateframenum[num] = framenum;
+ }
+ SETPVSBIT(d->visiblebits, num);
+ d->deltabits[num] |= EntityState5_DeltaBits(d->states + num, n);
+ d->priorities[num] = EntityState5_Priority(d, d->states + viewentnum, d->states + num, d->deltabits[num], framenum - d->updateframenum[num]);
+ d->states[num] = *n;
+ d->states[num].number = num;
+ // advance to next entity so the next iteration doesn't immediately remove it
+ num++;
+ }
+ // all remaining entities are dead
+ for (;num < MAX_EDICTS;num++)
+ {
+ if (CHECKPVSBIT(d->visiblebits, num))
+ {
+ CLEARPVSBIT(d->visiblebits, num);
+ d->deltabits[num] = E5_FULLUPDATE;
+ d->priorities[num] = EntityState5_Priority(d, d->states + viewentnum, d->states + num, d->deltabits[num], framenum - d->updateframenum[num]);
+ d->states[num] = defaultstate;
+ d->states[num].number = num;
+ }
+ }
+
+ // build lists of entities by priority level
+ memset(entityframe5_prioritychaincounts, 0, sizeof(entityframe5_prioritychaincounts));
+ l = 0;
+ for (num = 0;num < MAX_EDICTS;num++)
+ {
+ if (d->priorities[num])
+ {
+ l = num;
+ priority = d->priorities[num];
+ if (entityframe5_prioritychaincounts[priority] < ENTITYFRAME5_MAXSTATES)
+ entityframe5_prioritychains[priority][entityframe5_prioritychaincounts[priority]++] = num;
+ }
+ }
+
+ // return early if there are no entities to send this time
+ if (l == 0)
+ return;
+
+ d->latestframenum = framenum;
+ MSG_WriteByte(msg, svc_entities);
+ MSG_WriteLong(msg, framenum);
+
+ // if packet log is full, an empty update is still written
+ // (otherwise the client might have nothing to ack to remove packetlogs)
+ for (packetlognumber = 0, packetlog = d->packetlog;packetlognumber < ENTITYFRAME5_MAXPACKETLOGS;packetlognumber++, packetlog++)
+ if (packetlog->packetnumber == 0)
+ break;
+ if (packetlognumber < ENTITYFRAME5_MAXPACKETLOGS)
+ {
+ // write to packet and log
+ packetlog->packetnumber = framenum;
+ packetlog->numstates = 0;
+ for (priority = E5_PROTOCOL_PRIORITYLEVELS - 1;priority >= 0 && packetlog->numstates < ENTITYFRAME5_MAXSTATES;priority--)
+ {
+ for (i = 0;i < entityframe5_prioritychaincounts[priority] && packetlog->numstates < ENTITYFRAME5_MAXSTATES;i++)
+ {
+ num = entityframe5_prioritychains[priority][i];
+ n = d->states + num;
+ if (d->deltabits[num] & E5_FULLUPDATE)
+ d->deltabits[num] = E5_FULLUPDATE | EntityState5_DeltaBits(&defaultstate, n);
+ buf.cursize = 0;
+ EntityState5_WriteUpdate(num, n, d->deltabits[num], &buf);
+ // if the entity won't fit, try the next one
+ if (msg->cursize + buf.cursize + 2 > msg->maxsize)
+ continue;
+ // write entity to the packet
+ SZ_Write(msg, buf.data, buf.cursize);
+ // mark age on entity for prioritization
+ d->updateframenum[num] = framenum;
+ // log entity so deltabits can be restored later if lost
+ packetlog->states[packetlog->numstates].number = num;
+ packetlog->states[packetlog->numstates].bits = d->deltabits[num];
+ packetlog->numstates++;
+ // clear deltabits and priority so it won't be sent again
+ d->deltabits[num] = 0;
+ d->priorities[num] = 0;
+ }
+ }
+ }
+
+ MSG_WriteShort(msg, 0x8000);