-
-void EntityFrame4_AddCommitEntity(entityframe4_database_t *d, const entity_state_t *s)
-{
- // resize commit's entity list if full
- if (d->currentcommit->maxentities <= d->currentcommit->numentities)
- {
- entity_state_t *oldentity = d->currentcommit->entity;
- d->currentcommit->maxentities += 8;
- d->currentcommit->entity = Mem_Alloc(d->mempool, d->currentcommit->maxentities * sizeof(*d->currentcommit->entity));
- if (oldentity)
- {
- memcpy(d->currentcommit->entity, oldentity, d->currentcommit->numentities * sizeof(*d->currentcommit->entity));
- Mem_Free(oldentity);
- }
- }
- d->currentcommit->entity[d->currentcommit->numentities++] = *s;
-}
-
-entityframe4_database_t *EntityFrame4_AllocDatabase(mempool_t *pool)
-{
- entityframe4_database_t *d;
- d = Mem_Alloc(pool, sizeof(*d));
- d->mempool = pool;
- EntityFrame4_ResetDatabase(d);
- return d;
-}
-
-void EntityFrame4_FreeDatabase(entityframe4_database_t *d)
-{
- int i;
- for (i = 0;i < MAX_ENTITY_HISTORY;i++)
- if (d->commit[i].entity)
- Mem_Free(d->commit[i].entity);
- if (d->referenceentity)
- Mem_Free(d->referenceentity);
- Mem_Free(d);
-}
-
-void EntityFrame4_ResetDatabase(entityframe4_database_t *d)
-{
- int i;
- d->referenceframenum = -1;
- for (i = 0;i < MAX_ENTITY_HISTORY;i++)
- d->commit[i].numentities = 0;
- for (i = 0;i < d->maxreferenceentities;i++)
- d->referenceentity[i] = defaultstate;
-}
-
-int EntityFrame4_AckFrame(entityframe4_database_t *d, int framenum, int servermode)
-{
- int i, j, found;
- entity_database4_commit_t *commit;
- if (framenum == -1)
- {
- // reset reference, but leave commits alone
- d->referenceframenum = -1;
- for (i = 0;i < d->maxreferenceentities;i++)
- d->referenceentity[i] = defaultstate;
- // if this is the server, remove commits
- for (i = 0, commit = d->commit;i < MAX_ENTITY_HISTORY;i++, commit++)
- commit->numentities = 0;
- found = true;
- }
- else if (d->referenceframenum == framenum)
- found = true;
- else
- {
- found = false;
- for (i = 0, commit = d->commit;i < MAX_ENTITY_HISTORY;i++, commit++)
- {
- if (commit->numentities && commit->framenum <= framenum)
- {
- if (commit->framenum == framenum)
- {
- found = true;
- d->referenceframenum = framenum;
- if (developer_networkentities.integer >= 3)
- {
- for (j = 0;j < commit->numentities;j++)
- {
- entity_state_t *s = EntityFrame4_GetReferenceEntity(d, commit->entity[j].number);
- if (commit->entity[j].active != s->active)
- {
- if (commit->entity[j].active)
- Con_Printf("commit entity %i has become active (modelindex %i)\n", commit->entity[j].number, commit->entity[j].modelindex);
- else
- Con_Printf("commit entity %i has become inactive (modelindex %i)\n", commit->entity[j].number, commit->entity[j].modelindex);
- }
- *s = commit->entity[j];
- }
- }
- else
- for (j = 0;j < commit->numentities;j++)
- *EntityFrame4_GetReferenceEntity(d, commit->entity[j].number) = commit->entity[j];
- }
- commit->numentities = 0;
- }
- }
- }
- if (developer_networkentities.integer >= 1)
- {
- Con_Printf("ack ref:%i database updated to: ref:%i commits:", framenum, 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");
- }
- return found;
-}
-
-void EntityFrame4_CL_ReadFrame(void)
-{
- int i, n, cnumber, referenceframenum, framenum, enumber, done, stopnumber, skip = false;
- entity_state_t *s;
- entityframe4_database_t *d;
- if (!cl.entitydatabase4)
- cl.entitydatabase4 = EntityFrame4_AllocDatabase(cl_mempool);
- d = cl.entitydatabase4;
- // read the number of the frame this refers to
- referenceframenum = MSG_ReadLong();
- // read the number of this frame
- for (i = 0;i < LATESTFRAMENUMS-1;i++)
- cl.latestframenums[i] = cl.latestframenums[i+1];
- cl.latestframenums[LATESTFRAMENUMS-1] = framenum = MSG_ReadLong();
- // read the start number
- enumber = (unsigned short) MSG_ReadShort();
- if (developer_networkentities.integer >= 1)
- {
- Con_Printf("recv svc_entities num:%i ref:%i database: ref:%i commits:", framenum, 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 (!EntityFrame4_AckFrame(d, referenceframenum, false))
- {
- Con_Print("EntityFrame4_CL_ReadFrame: reference frame invalid (VERY BAD ERROR), this update will be skipped\n");
- skip = true;
- }
- d->currentcommit = NULL;
- for (i = 0;i < MAX_ENTITY_HISTORY;i++)
- {
- if (!d->commit[i].numentities)
- {
- d->currentcommit = d->commit + i;
- d->currentcommit->framenum = framenum;
- d->currentcommit->numentities = 0;
- }
- }
- if (d->currentcommit == NULL)
- {
- Con_Printf("EntityFrame4_CL_ReadFrame: error while decoding frame %i: database full, reading but not storing this update\n", framenum);
- skip = true;
- }
- done = false;
- while (!done && !msg_badread)
- {
- // read the number of the modified entity
- // (gaps will be copied unmodified)
- n = (unsigned short)MSG_ReadShort();
- if (n == 0x8000)
- {
- // no more entities in this update, but we still need to copy the
- // rest of the reference entities (final gap)
- done = true;
- // read end of range number, then process normally
- n = (unsigned short)MSG_ReadShort();
- }
- // high bit means it's a remove message
- cnumber = n & 0x7FFF;
- // if this is a live entity we may need to expand the array
- if (cl_num_entities <= cnumber && !(n & 0x8000))
- {
- cl_num_entities = cnumber + 1;
- if (cnumber >= cl_max_entities)
- CL_ExpandEntities(cnumber);
- }
- // add one (the changed one) if not done
- stopnumber = cnumber + !done;
- // process entities in range from the last one to the changed one
- for (;enumber < stopnumber;enumber++)
- {
- if (skip || enumber >= cl_num_entities)
- {
- if (enumber == cnumber && (n & 0x8000) == 0)
- {
- entity_state_t tempstate;
- EntityState_ReadFields(&tempstate, EntityState_ReadExtendBits());
- }
- continue;
- }
- // slide the current into the previous slot
- cl_entities[enumber].state_previous = cl_entities[enumber].state_current;
- // copy a new current from reference database
- cl_entities[enumber].state_current = *EntityFrame4_GetReferenceEntity(d, enumber);
- s = &cl_entities[enumber].state_current;
- // if this is the one to modify, read more data...
- if (enumber == cnumber)
- {
- if (n & 0x8000)
- {
- // simply removed
- if (developer_networkentities.integer >= 2)
- Con_Printf("entity %i: remove\n", enumber);
- *s = defaultstate;
- }
- else
- {
- // read the changes
- if (developer_networkentities.integer >= 2)
- Con_Printf("entity %i: update\n", enumber);
- s->active = true;
- EntityState_ReadFields(s, EntityState_ReadExtendBits());
- }
- }
- else if (developer_networkentities.integer >= 4)
- Con_Printf("entity %i: copy\n", enumber);
- // set the cl_entities_active flag
- cl_entities_active[enumber] = s->active;
- // set the update time
- s->time = cl.mtime[0];
- // fix the number (it gets wiped occasionally by copying from defaultstate)
- s->number = enumber;
- // check if we need to update the lerp stuff
- if (s->active)
- CL_MoveLerpEntityStates(&cl_entities[enumber]);
- // add this to the commit entry whether it is modified or not
- if (d->currentcommit)
- EntityFrame4_AddCommitEntity(d, &cl_entities[enumber].state_current);
- // print extra messages if desired
- if (developer_networkentities.integer >= 2 && cl_entities[enumber].state_current.active != cl_entities[enumber].state_previous.active)
- {
- if (cl_entities[enumber].state_current.active)
- Con_Printf("entity #%i has become active\n", enumber);
- else if (cl_entities[enumber].state_previous.active)
- Con_Printf("entity #%i has become inactive\n", enumber);
- }
- }
- }
- d->currentcommit = NULL;
- 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 >= 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++)
- {
- // 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)
-{
- // 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_ResetDatabase(entityframe5_database_t *d)
-{
- int i;
- memset(d, 0, sizeof(*d));
- d->latestframenum = 0;
- for (i = 0;i < d->maxedicts;i++)
- d->states[i] = defaultstate;
-}
-
-void EntityFrame5_ExpandEdicts(entityframe5_database_t *d, int newmax)
-{
- if (d->maxedicts < newmax)
- {
- qbyte *data;
- int oldmaxedicts = d->maxedicts;
- int *olddeltabits = d->deltabits;
- qbyte *oldpriorities = d->priorities;
- int *oldupdateframenum = d->updateframenum;
- entity_state_t *oldstates = d->states;
- qbyte *oldvisiblebits = d->visiblebits;
- d->maxedicts = newmax;
- data = Mem_Alloc(sv_mempool, d->maxedicts * sizeof(int) + d->maxedicts * sizeof(qbyte) + d->maxedicts * sizeof(int) + d->maxedicts * sizeof(entity_state_t) + (d->maxedicts+7)/8 * sizeof(qbyte));
- d->deltabits = (void *)data;data += d->maxedicts * sizeof(int);
- d->priorities = (void *)data;data += d->maxedicts * sizeof(qbyte);
- d->updateframenum = (void *)data;data += d->maxedicts * sizeof(int);
- d->states = (void *)data;data += d->maxedicts * sizeof(entity_state_t);
- d->visiblebits = (void *)data;data += (d->maxedicts+7)/8 * sizeof(qbyte);
- if (oldmaxedicts)
- {
- memcpy(d->deltabits, olddeltabits, oldmaxedicts * sizeof(int));
- memcpy(d->priorities, oldpriorities, oldmaxedicts * sizeof(qbyte));
- memcpy(d->updateframenum, oldupdateframenum, oldmaxedicts * sizeof(int));
- memcpy(d->states, oldstates, oldmaxedicts * sizeof(entity_state_t));
- memcpy(d->visiblebits, oldvisiblebits, (oldmaxedicts+7)/8 * sizeof(qbyte));
- // the previous buffers were a single allocation, so just one free
- Mem_Free(olddeltabits);
- }
- }
-}
-
-int EntityState5_Priority(entityframe5_database_t *d, int stateindex)
-{
- int lowprecision, limit, priority;
- double distance;
- int changedbits;
- int age;
- entity_state_t *view, *s;
- changedbits = d->deltabits[stateindex];
- if (!changedbits)
- return 0;
- if (!d->states[stateindex].active/* && changedbits & E5_FULLUPDATE*/)
- return E5_PROTOCOL_PRIORITYLEVELS - 1;
- // check whole attachment chain to judge relevance to player
- view = d->states + d->viewentnum;
- lowprecision = false;
- for (limit = 0;limit < 256;limit++)
- {
- if (d->maxedicts < stateindex)
- EntityFrame5_ExpandEdicts(d, (stateindex+256)&~255);
- s = d->states + stateindex;
- 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;
- }
- stateindex = s->tagentity;
- }
- if (limit >= 256)
- {
- Con_Printf("Protocol: Runaway loop recursing tagentity links on entity %i\n", stateindex);
- return 0;
- }
- // it's not a viewmodel for this client
- distance = VectorDistance(view->origin, s->origin);
- age = d->latestframenum - d->updateframenum[stateindex];
- 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->flags & RENDER_LOWPRECISION) || 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;
-}
-
-void EntityFrame5_CL_ReadFrame(void)
-{
- int i, n, enumber;
- entity_t *ent;
- entity_state_t *s;
- // read the number of this frame to echo back in next input packet
- for (i = 0;i < LATESTFRAMENUMS-1;i++)
- cl.latestframenums[i] = cl.latestframenums[i+1];
- cl.latestframenums[LATESTFRAMENUMS-1] = MSG_ReadLong();
- if (cl.protocol != PROTOCOL_QUAKE && cl.protocol != PROTOCOL_QUAKEDP && cl.protocol != PROTOCOL_NEHAHRAMOVIE && cl.protocol != PROTOCOL_DARKPLACES1 && cl.protocol != PROTOCOL_DARKPLACES2 && cl.protocol != PROTOCOL_DARKPLACES3 && cl.protocol != PROTOCOL_DARKPLACES4 && cl.protocol != PROTOCOL_DARKPLACES5 && cl.protocol != PROTOCOL_DARKPLACES6)
- cl.servermovesequence = MSG_ReadLong();
- // read entity numbers until we find a 0x8000
- // (which would be remove world entity, but is actually a terminator)
- while ((n = (unsigned short)MSG_ReadShort()) != 0x8000 && !msg_badread)
- {
- // get the entity number
- enumber = n & 0x7FFF;
- // we may need to expand the array
- if (cl_num_entities <= enumber)
- {
- cl_num_entities = enumber + 1;
- if (enumber >= cl_max_entities)
- CL_ExpandEntities(enumber);
- }
- // look up the entity
- ent = cl_entities + enumber;
- // slide the current into the previous slot
- ent->state_previous = ent->state_current;
- // read the update
- s = &ent->state_current;
- if (n & 0x8000)
- {
- // remove entity
- *s = defaultstate;
- }
- else
- {
- // update entity
- EntityState5_ReadUpdate(s);
- }
- // set the cl_entities_active flag
- cl_entities_active[enumber] = s->active;
- // set the update time
- s->time = cl.mtime[0];
- // fix the number (it gets wiped occasionally by copying from defaultstate)
- s->number = enumber;
- // check if we need to update the lerp stuff
- if (s->active)
- CL_MoveLerpEntityStates(&cl_entities[enumber]);
- // print extra messages if desired
- if (developer_networkentities.integer >= 2 && cl_entities[enumber].state_current.active != cl_entities[enumber].state_previous.active)
- {
- if (cl_entities[enumber].state_current.active)
- Con_Printf("entity #%i has become active\n", enumber);
- else if (cl_entities[enumber].state_previous.active)
- Con_Printf("entity #%i has become inactive\n", enumber);
- }
- }
-}
-
-void EntityFrame5_LostFrame(entityframe5_database_t *d, int framenum)
-{
- int i, j, k, l, bits;
- entityframe5_changestate_t *s, *s2;
- entityframe5_packetlog_t *p, *p2;
- qbyte statsdeltabits[(MAX_CL_STATS+7)/8];
- // scan for packets that were lost
- for (i = 0, p = d->packetlog;i < ENTITYFRAME5_MAXPACKETLOGS;i++, p++)
- {
- if (p->packetnumber && p->packetnumber <= framenum)
- {
- // packet was lost - merge deltabits into the main array so they
- // will be re-sent, but only if there is no newer update of that
- // bit in the logs (as those will arrive before this update)
- for (j = 0, s = p->states;j < p->numstates;j++, s++)
- {
- // check for any newer updates to this entity and mask off any
- // overlapping bits (we don't need to send something again if
- // it has already been sent more recently)
- bits = s->bits & ~d->deltabits[s->number];
- for (k = 0, p2 = d->packetlog;k < ENTITYFRAME5_MAXPACKETLOGS && bits;k++, p2++)
- {
- if (p2->packetnumber > framenum)
- {
- for (l = 0, s2 = p2->states;l < p2->numstates;l++, s2++)
- {
- if (s2->number == s->number)
- {
- bits &= ~s2->bits;
- break;
- }
- }
- }
- }
- // if the bits haven't all been cleared, there were some bits
- // lost with this packet, so set them again now
- if (bits)
- d->deltabits[s->number] |= bits;
- }
- // mark lost stats
- for (j = 0;j < MAX_CL_STATS;j++)
- {
- for (l = 0;l < (MAX_CL_STATS+7)/8;l++)
- statsdeltabits[l] = p->statsdeltabits[l] & ~d->statsdeltabits[l];
- for (k = 0, p2 = d->packetlog;k < ENTITYFRAME5_MAXPACKETLOGS;k++, p2++)
- if (p2->packetnumber > framenum)
- for (l = 0;l < (MAX_CL_STATS+7)/8;l++)
- statsdeltabits[l] = p->statsdeltabits[l] & ~p2->statsdeltabits[l];
- for (l = 0;l < (MAX_CL_STATS+7)/8;l++)
- d->statsdeltabits[l] |= statsdeltabits[l];
- }
- // delete this packet log as it is now obsolete
- p->packetnumber = 0;
- }
- }
-}
-
-void EntityFrame5_AckFrame(entityframe5_database_t *d, int framenum)
-{
- int i;
- // scan for packets made obsolete by this ack and delete them
- for (i = 0;i < ENTITYFRAME5_MAXPACKETLOGS;i++)
- if (d->packetlog[i].packetnumber <= framenum)
- d->packetlog[i].packetnumber = 0;
-}
-
-int entityframe5_prioritychaincounts[E5_PROTOCOL_PRIORITYLEVELS];
-unsigned short entityframe5_prioritychains[E5_PROTOCOL_PRIORITYLEVELS][ENTITYFRAME5_MAXSTATES];
-
-void EntityFrame5_WriteFrame(sizebuf_t *msg, entityframe5_database_t *d, int numstates, const entity_state_t *states, int viewentnum, int *stats, int movesequence)
-{
- const entity_state_t *n;
- int i, num, l, framenum, packetlognumber, priority;
- sizebuf_t buf;
- qbyte data[128];
- entityframe5_packetlog_t *packetlog;
-
- if (prog->max_edicts > d->maxedicts)
- EntityFrame5_ExpandEdicts(d, prog->max_edicts);
-
- framenum = d->latestframenum + 1;
- d->viewentnum = viewentnum;
-
- // if packet log is full, mark all frames as lost, this will cause
- // it to send the lost data again
- for (packetlognumber = 0;packetlognumber < ENTITYFRAME5_MAXPACKETLOGS;packetlognumber++)
- if (d->packetlog[packetlognumber].packetnumber == 0)
- break;
- if (packetlognumber == ENTITYFRAME5_MAXPACKETLOGS)
- {
- Con_DPrintf("EntityFrame5_WriteFrame: packetlog overflow for a client, resetting\n");
- EntityFrame5_LostFrame(d, framenum);
- packetlognumber = 0;
- }
-
- // prepare the buffer
- memset(&buf, 0, sizeof(buf));
- buf.data = data;
- buf.maxsize = sizeof(data);
-
- // detect changes in stats
- for (i = 0;i < MAX_CL_STATS;i++)
- {
- if (d->stats[i] != stats[i])
- {
- d->statsdeltabits[i>>3] |= (1<<(i&7));
- d->stats[i] = stats[i];
- }
- }
-
- // 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, 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, 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 < d->maxedicts;num++)
- {
- if (CHECKPVSBIT(d->visiblebits, num))
- {
- CLEARPVSBIT(d->visiblebits, num);
- d->deltabits[num] = E5_FULLUPDATE;
- d->priorities[num] = EntityState5_Priority(d, num);
- d->states[num] = defaultstate;
- d->states[num].number = num;
- }
- }
-
- // if there isn't at least enough room for an empty svc_entities,
- // don't bother trying...
- if (buf.cursize + 11 > buf.maxsize)
- return;
-
- // build lists of entities by priority level
- memset(entityframe5_prioritychaincounts, 0, sizeof(entityframe5_prioritychaincounts));
- l = 0;
- for (num = 0;num < d->maxedicts;num++)
- {
- if (d->priorities[num])
- {
- l = num;
- priority = d->priorities[num];
- if (entityframe5_prioritychaincounts[priority] < ENTITYFRAME5_MAXSTATES)
- entityframe5_prioritychains[priority][entityframe5_prioritychaincounts[priority]++] = num;
- }
- }
-
- // add packetlog entry
- packetlog = d->packetlog + packetlognumber;
- packetlog->packetnumber = framenum;
- packetlog->numstates = 0;
- // write stat updates
- if (sv.protocol != PROTOCOL_QUAKE && sv.protocol != PROTOCOL_QUAKEDP && sv.protocol != PROTOCOL_NEHAHRAMOVIE && sv.protocol != PROTOCOL_DARKPLACES1 && sv.protocol != PROTOCOL_DARKPLACES2 && sv.protocol != PROTOCOL_DARKPLACES3 && sv.protocol != PROTOCOL_DARKPLACES4 && sv.protocol != PROTOCOL_DARKPLACES5)
- {
- for (i = 0;i < MAX_CL_STATS && msg->cursize + 6 + 11 <= msg->maxsize;i++)
- {
- if (d->statsdeltabits[i>>3] & (1<<(i&7)))
- {
- d->statsdeltabits[i>>3] &= ~(1<<(i&7));
- packetlog->statsdeltabits[i>>3] |= (1<<(i&7));
- if (d->stats[i] >= 0 && d->stats[i] < 256)
- {
- MSG_WriteByte(msg, svc_updatestatubyte);
- MSG_WriteByte(msg, i);
- MSG_WriteByte(msg, d->stats[i]);
- }
- else
- {
- MSG_WriteByte(msg, svc_updatestat);
- MSG_WriteByte(msg, i);
- MSG_WriteLong(msg, d->stats[i]);
- }
- }
- }
- }
- // write state updates
- d->latestframenum = framenum;
- MSG_WriteByte(msg, svc_entities);
- MSG_WriteLong(msg, framenum);
- if (sv.protocol != PROTOCOL_QUAKE && sv.protocol != PROTOCOL_QUAKEDP && sv.protocol != PROTOCOL_NEHAHRAMOVIE && sv.protocol != PROTOCOL_DARKPLACES1 && sv.protocol != PROTOCOL_DARKPLACES2 && sv.protocol != PROTOCOL_DARKPLACES3 && sv.protocol != PROTOCOL_DARKPLACES4 && sv.protocol != PROTOCOL_DARKPLACES5 && sv.protocol != PROTOCOL_DARKPLACES6)
- MSG_WriteLong(msg, movesequence);
- 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);
-}
-