X-Git-Url: http://de.git.xonotic.org/?p=xonotic%2Fdarkplaces.git;a=blobdiff_plain;f=protocol.c;h=86e760a51210bd82da3b4700127b7cfbfc20f9af;hp=be9c9e7946d07a4ee292ff391f8865d0fab322e9;hb=c4ddb9412fdc65efb2e47807baedf7a25234f891;hpb=eb3d71b9feaefca24fe80b9970eb11d07fd295b5 diff --git a/protocol.c b/protocol.c index be9c9e79..86e760a5 100644 --- a/protocol.c +++ b/protocol.c @@ -1,22 +1,341 @@ #include "quakedef.h" +entity_state_t defaultstate = +{ + 0,//double time; // time this state was built + {0,0,0},//vec3_t origin; + {0,0,0},//vec3_t angles; + 0,//int number; // entity number this state is for + 0,//unsigned short active; // true if a valid state + 0,//unsigned short modelindex; + 0,//unsigned short frame; + 0,//unsigned short effects; + 0,//unsigned short tagentity; + 0,//unsigned short specialvisibilityradius; + 0,//unsigned short viewmodelforclient; + 0,//unsigned short exteriormodelforclient; + 0,//unsigned short nodrawtoclient; + 0,//unsigned short drawonlytoclient; + 0,//qbyte colormap; + 0,//qbyte skin; + 255,//qbyte alpha; + 16,//qbyte scale; + 0,//qbyte glowsize; + 254,//qbyte glowcolor; + 0,//qbyte flags; + 0,//qbyte tagindex; +}; + void ClearStateToDefault(entity_state_t *s) { - s->active = 0; - s->time = 0; - VectorClear(s->origin); - VectorClear(s->angles); - s->effects = 0; - s->modelindex = 0; - s->frame = 0; - s->colormap = 0; - s->skin = 0; + *s = defaultstate; + /* + memset(s, 0, sizeof(*s)); s->alpha = 255; s->scale = 16; - s->glowsize = 0; s->glowcolor = 254; - s->flags = 0; + */ +} + +void EntityState_Write(entity_state_t *ent, sizebuf_t *msg, entity_state_t *delta) +{ + int bits; + vec3_t org, deltaorg; + if (ent->active) + { + // if not active last frame, delta from defaults + if (!delta->active) + delta = &defaultstate; + bits = 0; + VectorCopy(ent->origin, org); + VectorCopy(delta->origin, deltaorg); + if (ent->flags & RENDER_LOWPRECISION) + { + if (org[0] > 0) + org[0] = (int) (org[0] + 0.5f); + else + org[0] = (int) (org[0] - 0.5f); + if (org[1] > 0) + org[1] = (int) (org[1] + 0.5f); + else + org[1] = (int) (org[1] - 0.5f); + if (org[2] > 0) + org[2] = (int) (org[2] + 0.5f); + else + org[2] = (int) (org[2] - 0.5f); + } + if (delta->flags & RENDER_LOWPRECISION) + { + if (deltaorg[0] > 0) + deltaorg[0] = (int) (deltaorg[0] + 0.5f); + else + deltaorg[0] = (int) (deltaorg[0] - 0.5f); + if (deltaorg[1] > 0) + deltaorg[1] = (int) (deltaorg[1] + 0.5f); + else + deltaorg[1] = (int) (deltaorg[1] - 0.5f); + if (deltaorg[2] > 0) + deltaorg[2] = (int) (deltaorg[2] + 0.5f); + else + deltaorg[2] = (int) (deltaorg[2] - 0.5f); + } + if (fabs(org[0] - deltaorg[0]) > 0.01f) + bits |= E_ORIGIN1; + if (fabs(org[1] - deltaorg[1]) > 0.01f) + bits |= E_ORIGIN2; + if (fabs(org[2] - deltaorg[2]) > 0.01f) + bits |= E_ORIGIN3; + if ((qbyte) (ent->angles[0] * (256.0f / 360.0f)) != (qbyte) (delta->angles[0] * (256.0f / 360.0f))) + bits |= E_ANGLE1; + if ((qbyte) (ent->angles[1] * (256.0f / 360.0f)) != (qbyte) (delta->angles[1] * (256.0f / 360.0f))) + bits |= E_ANGLE2; + if ((qbyte) (ent->angles[2] * (256.0f / 360.0f)) != (qbyte) (delta->angles[2] * (256.0f / 360.0f))) + bits |= E_ANGLE3; + if ((ent->modelindex ^ delta->modelindex) & 0x00FF) + bits |= E_MODEL1; + if ((ent->modelindex ^ delta->modelindex) & 0xFF00) + bits |= E_MODEL2; + if ((ent->frame ^ delta->frame) & 0x00FF) + bits |= E_FRAME1; + if ((ent->frame ^ delta->frame) & 0xFF00) + bits |= E_FRAME2; + if ((ent->effects ^ delta->effects) & 0x00FF) + bits |= E_EFFECTS1; + if ((ent->effects ^ delta->effects) & 0xFF00) + bits |= E_EFFECTS2; + if (ent->colormap != delta->colormap) + bits |= E_COLORMAP; + if (ent->skin != delta->skin) + bits |= E_SKIN; + if (ent->alpha != delta->alpha) + bits |= E_ALPHA; + if (ent->scale != delta->scale) + bits |= E_SCALE; + if (ent->glowsize != delta->glowsize) + bits |= E_GLOWSIZE; + if (ent->glowcolor != delta->glowcolor) + bits |= E_GLOWCOLOR; + if (ent->flags != delta->flags) + bits |= E_FLAGS; + if (ent->tagindex != delta->tagindex || ent->tagentity != delta->tagentity) + bits |= E_TAGATTACHMENT; + + if (bits) // don't send anything if it hasn't changed + { + if (bits & 0xFF000000) + bits |= E_EXTEND3; + if (bits & 0x00FF0000) + bits |= E_EXTEND2; + if (bits & 0x0000FF00) + bits |= E_EXTEND1; + + MSG_WriteShort(msg, ent->number); + MSG_WriteByte(msg, bits & 0xFF); + if (bits & E_EXTEND1) + { + MSG_WriteByte(msg, (bits >> 8) & 0xFF); + if (bits & E_EXTEND2) + { + MSG_WriteByte(msg, (bits >> 16) & 0xFF); + if (bits & E_EXTEND3) + MSG_WriteByte(msg, (bits >> 24) & 0xFF); + } + } + // LordHavoc: have to write flags first, as they can modify protocol + if (bits & E_FLAGS) + MSG_WriteByte(msg, ent->flags); + if (ent->flags & RENDER_LOWPRECISION) + { + if (bits & E_ORIGIN1) + MSG_WriteShort(msg, org[0]); + if (bits & E_ORIGIN2) + MSG_WriteShort(msg, org[1]); + if (bits & E_ORIGIN3) + MSG_WriteShort(msg, org[2]); + } + else + { + if (bits & E_ORIGIN1) + MSG_WriteFloat(msg, org[0]); + if (bits & E_ORIGIN2) + MSG_WriteFloat(msg, org[1]); + if (bits & E_ORIGIN3) + MSG_WriteFloat(msg, org[2]); + } + if (bits & E_ANGLE1) + MSG_WriteAngle(msg, ent->angles[0]); + if (bits & E_ANGLE2) + MSG_WriteAngle(msg, ent->angles[1]); + if (bits & E_ANGLE3) + MSG_WriteAngle(msg, ent->angles[2]); + if (bits & E_MODEL1) + MSG_WriteByte(msg, ent->modelindex & 0xFF); + if (bits & E_MODEL2) + MSG_WriteByte(msg, (ent->modelindex >> 8) & 0xFF); + if (bits & E_FRAME1) + MSG_WriteByte(msg, ent->frame & 0xFF); + if (bits & E_FRAME2) + MSG_WriteByte(msg, (ent->frame >> 8) & 0xFF); + if (bits & E_EFFECTS1) + MSG_WriteByte(msg, ent->effects & 0xFF); + if (bits & E_EFFECTS2) + MSG_WriteByte(msg, (ent->effects >> 8) & 0xFF); + if (bits & E_COLORMAP) + MSG_WriteByte(msg, ent->colormap); + if (bits & E_SKIN) + MSG_WriteByte(msg, ent->skin); + if (bits & E_ALPHA) + MSG_WriteByte(msg, ent->alpha); + if (bits & E_SCALE) + MSG_WriteByte(msg, ent->scale); + if (bits & E_GLOWSIZE) + MSG_WriteByte(msg, ent->glowsize); + if (bits & E_GLOWCOLOR) + MSG_WriteByte(msg, ent->glowcolor); + if (bits & E_TAGATTACHMENT) + { + MSG_WriteShort(msg, ent->tagentity); + MSG_WriteByte(msg, ent->tagindex); + } + } + } + else if (delta->active) + MSG_WriteShort(msg, ent->number | 0x8000); +} + +void EntityState_ReadUpdate(entity_state_t *e, int number) +{ + int bits; + cl_entities_active[number] = true; + e->active = true; + e->time = cl.mtime[0]; + e->number = number; + + bits = MSG_ReadByte(); + if (bits & E_EXTEND1) + { + bits |= MSG_ReadByte() << 8; + if (bits & E_EXTEND2) + { + bits |= MSG_ReadByte() << 16; + if (bits & E_EXTEND3) + bits |= MSG_ReadByte() << 24; + } + } + + if (cl.protocol == PROTOCOL_DARKPLACES2) + { + if (bits & E_ORIGIN1) + e->origin[0] = (signed short) MSG_ReadShort(); + if (bits & E_ORIGIN2) + e->origin[1] = (signed short) MSG_ReadShort(); + if (bits & E_ORIGIN3) + e->origin[2] = (signed short) MSG_ReadShort(); + } + else + { + if (bits & E_FLAGS) + e->flags = MSG_ReadByte(); + if (e->flags & RENDER_LOWPRECISION || cl.protocol == PROTOCOL_DARKPLACES2) + { + if (bits & E_ORIGIN1) + e->origin[0] = (signed short) MSG_ReadShort(); + if (bits & E_ORIGIN2) + e->origin[1] = (signed short) MSG_ReadShort(); + if (bits & E_ORIGIN3) + e->origin[2] = (signed short) MSG_ReadShort(); + } + else + { + if (bits & E_ORIGIN1) + e->origin[0] = MSG_ReadFloat(); + if (bits & E_ORIGIN2) + e->origin[1] = MSG_ReadFloat(); + if (bits & E_ORIGIN3) + e->origin[2] = MSG_ReadFloat(); + } + } + if (bits & E_ANGLE1) + e->angles[0] = MSG_ReadAngle(); + if (bits & E_ANGLE2) + e->angles[1] = MSG_ReadAngle(); + if (bits & E_ANGLE3) + e->angles[2] = MSG_ReadAngle(); + if (bits & E_MODEL1) + e->modelindex = (e->modelindex & 0xFF00) | (unsigned int) MSG_ReadByte(); + if (bits & E_MODEL2) + e->modelindex = (e->modelindex & 0x00FF) | ((unsigned int) MSG_ReadByte() << 8); + if (bits & E_FRAME1) + e->frame = (e->frame & 0xFF00) | (unsigned int) MSG_ReadByte(); + if (bits & E_FRAME2) + e->frame = (e->frame & 0x00FF) | ((unsigned int) MSG_ReadByte() << 8); + if (bits & E_EFFECTS1) + e->effects = (e->effects & 0xFF00) | (unsigned int) MSG_ReadByte(); + if (bits & E_EFFECTS2) + e->effects = (e->effects & 0x00FF) | ((unsigned int) MSG_ReadByte() << 8); + if (bits & E_COLORMAP) + e->colormap = MSG_ReadByte(); + if (bits & E_SKIN) + e->skin = MSG_ReadByte(); + if (bits & E_ALPHA) + e->alpha = MSG_ReadByte(); + if (bits & E_SCALE) + e->scale = MSG_ReadByte(); + if (bits & E_GLOWSIZE) + e->glowsize = MSG_ReadByte(); + if (bits & E_GLOWCOLOR) + e->glowcolor = MSG_ReadByte(); + if (cl.protocol == PROTOCOL_DARKPLACES2) + if (bits & E_FLAGS) + e->flags = MSG_ReadByte(); + if (bits & E_TAGATTACHMENT) + { + e->tagentity = MSG_ReadShort(); + e->tagindex = MSG_ReadByte(); + } + + if (developer_networkentities.integer >= 2) + { + Con_Printf("ReadUpdate e%i", number); + + if (bits & E_ORIGIN1) + Con_Printf(" E_ORIGIN1 %f", e->origin[0]); + if (bits & E_ORIGIN2) + Con_Printf(" E_ORIGIN2 %f", e->origin[1]); + if (bits & E_ORIGIN3) + Con_Printf(" E_ORIGIN3 %f", e->origin[2]); + if (bits & E_ANGLE1) + Con_Printf(" E_ANGLE1 %f", e->angles[0]); + if (bits & E_ANGLE2) + Con_Printf(" E_ANGLE2 %f", e->angles[1]); + if (bits & E_ANGLE3) + Con_Printf(" E_ANGLE3 %f", e->angles[2]); + if (bits & (E_MODEL1 | E_MODEL2)) + Con_Printf(" E_MODEL %i", e->modelindex); + + if (bits & (E_FRAME1 | E_FRAME2)) + Con_Printf(" E_FRAME %i", e->frame); + if (bits & (E_EFFECTS1 | E_EFFECTS2)) + Con_Printf(" E_EFFECTS %i", e->effects); + if (bits & E_ALPHA) + Con_Printf(" E_ALPHA %f", e->alpha / 255.0f); + if (bits & E_SCALE) + Con_Printf(" E_SCALE %f", e->scale / 16.0f); + if (bits & E_COLORMAP) + Con_Printf(" E_COLORMAP %i", e->colormap); + if (bits & E_SKIN) + Con_Printf(" E_SKIN %i", e->skin); + + if (bits & E_GLOWSIZE) + Con_Printf(" E_GLOWSIZE %i", e->glowsize * 8); + if (bits & E_GLOWCOLOR) + Con_Printf(" E_GLOWCOLOR %i", e->glowcolor); + + if (bits & E_TAGATTACHMENT) + Con_Printf(" E_TAGATTACHMENT e%i:%i", e->tagentity, e->tagindex); + Con_Printf("\n"); + } } // (server) clears the database to contain no frames (thus delta compression compresses against nothing) @@ -42,29 +361,36 @@ void EntityFrame_AckFrame(entity_database_t *d, int frame) } // (server) clears frame, to prepare for adding entities -void EntityFrame_Clear(entity_frame_t *f, vec3_t eye) +void EntityFrame_Clear(entity_frame_t *f, vec3_t eye, int framenum) { - memset(f, 0, sizeof(*f)); - VectorCopy(eye, f->eye); + f->time = 0; + f->framenum = framenum; + f->numentities = 0; + if (eye == NULL) + { + VectorClear(f->eye); + } + else + { + VectorCopy(eye, f->eye); + } } -// (server) allocates an entity slot in frame, returns NULL if full -entity_state_t *EntityFrame_NewEntity(entity_frame_t *f, int number) +// (server) adds an entity to frame +void EntityFrame_AddEntity(entity_frame_t *f, entity_state_t *s) { - entity_state_t *e; - if (f->numentities >= MAX_ENTITY_DATABASE) - return NULL; - e = &f->entitydata[f->numentities++]; - e->active = true; - e->number = number; - return e; + if (f->numentities < MAX_ENTITY_DATABASE) + { + f->entitydata[f->numentities] = *s; + f->entitydata[f->numentities++].active = true; + } } // (server and client) reads a frame from the database void EntityFrame_FetchFrame(entity_database_t *d, int framenum, entity_frame_t *f) { int i, n; - memset(f, 0, sizeof(*f)); + EntityFrame_Clear(f, NULL, -1); for (i = 0;i < d->numframes && d->frames[i].framenum < framenum;i++); if (i < d->numframes && framenum == d->frames[i].framenum) { @@ -78,8 +404,6 @@ void EntityFrame_FetchFrame(entity_database_t *d, int framenum, entity_frame_t * memcpy(f->entitydata + n, d->entitydata, sizeof(*f->entitydata) * (f->numentities - n)); VectorCopy(d->eye, f->eye); } - else - f->framenum = -1; } // (server and client) adds a entity_frame to the database, for future reference @@ -135,15 +459,15 @@ void EntityFrame_AddFrame(entity_database_t *d, entity_frame_t *f) } // (server) writes a frame to network stream +static entity_frame_t deltaframe; // FIXME? void EntityFrame_Write(entity_database_t *d, entity_frame_t *f, sizebuf_t *msg) { - int i, onum, bits, number; - entity_frame_t deltaframe, *o = &deltaframe; - entity_state_t *ent, *delta, baseline; + int i, onum, number; + entity_frame_t *o = &deltaframe; + entity_state_t *ent, *delta; EntityFrame_AddFrame(d, f); - ClearStateToDefault(&baseline); EntityFrame_FetchFrame(d, d->ackframe > 0 ? d->ackframe : -1, o); MSG_WriteByte (msg, svc_entities); MSG_WriteLong (msg, o->framenum); @@ -171,109 +495,10 @@ void EntityFrame_Write(entity_database_t *d, entity_frame_t *f, sizebuf_t *msg) } else { - // delta from baseline - delta = &baseline; - } - bits = 0; - if ((int) ent->origin[0] != (int) delta->origin[0]) - bits |= E_ORIGIN1; - if ((int) ent->origin[1] != (int) delta->origin[1]) - bits |= E_ORIGIN2; - if ((int) ent->origin[2] != (int) delta->origin[2]) - bits |= E_ORIGIN3; - if ((byte) (ent->angles[0] * (256.0f / 360.0f)) != (byte) (delta->angles[0] * (256.0f / 360.0f))) - bits |= E_ANGLE1; - if ((byte) (ent->angles[1] * (256.0f / 360.0f)) != (byte) (delta->angles[1] * (256.0f / 360.0f))) - bits |= E_ANGLE2; - if ((byte) (ent->angles[2] * (256.0f / 360.0f)) != (byte) (delta->angles[2] * (256.0f / 360.0f))) - bits |= E_ANGLE3; - if ((ent->modelindex ^ delta->modelindex) & 0x00FF) - bits |= E_MODEL1; - if ((ent->modelindex ^ delta->modelindex) & 0xFF00) - bits |= E_MODEL2; - if ((ent->frame ^ delta->frame) & 0x00FF) - bits |= E_FRAME1; - if ((ent->frame ^ delta->frame) & 0xFF00) - bits |= E_FRAME2; - if ((ent->effects ^ delta->effects) & 0x00FF) - bits |= E_EFFECTS1; - if ((ent->effects ^ delta->effects) & 0xFF00) - bits |= E_EFFECTS2; - if (ent->colormap != delta->colormap) - bits |= E_COLORMAP; - if (ent->skin != delta->skin) - bits |= E_SKIN; - if (ent->alpha != delta->alpha) - bits |= E_ALPHA; - if (ent->scale != delta->scale) - bits |= E_SCALE; - if (ent->glowsize != delta->glowsize) - bits |= E_GLOWSIZE; - if (ent->glowcolor != delta->glowcolor) - bits |= E_GLOWCOLOR; - if (ent->flags != delta->flags) - bits |= E_FLAGS; - - if (bits) // don't send anything if it hasn't changed - { - if (bits & 0xFF000000) - bits |= E_EXTEND3; - if (bits & 0x00FF0000) - bits |= E_EXTEND2; - if (bits & 0x0000FF00) - bits |= E_EXTEND1; - - MSG_WriteShort(msg, number); - MSG_WriteByte(msg, bits & 0xFF); - if (bits & E_EXTEND1) - { - MSG_WriteByte(msg, (bits >> 8) & 0xFF); - if (bits & E_EXTEND2) - { - MSG_WriteByte(msg, (bits >> 16) & 0xFF); - if (bits & E_EXTEND3) - MSG_WriteByte(msg, (bits >> 24) & 0xFF); - } - } - if (bits & E_ORIGIN1) - MSG_WriteShort(msg, ent->origin[0]); - if (bits & E_ORIGIN2) - MSG_WriteShort(msg, ent->origin[1]); - if (bits & E_ORIGIN3) - MSG_WriteShort(msg, ent->origin[2]); - if (bits & E_ANGLE1) - MSG_WriteAngle(msg, ent->angles[0]); - if (bits & E_ANGLE2) - MSG_WriteAngle(msg, ent->angles[1]); - if (bits & E_ANGLE3) - MSG_WriteAngle(msg, ent->angles[2]); - if (bits & E_MODEL1) - MSG_WriteByte(msg, ent->modelindex & 0x00FF); - if (bits & E_MODEL2) - MSG_WriteByte(msg, ent->modelindex & 0xFF00); - if (bits & E_FRAME1) - MSG_WriteByte(msg, ent->frame & 0x00FF); - if (bits & E_FRAME2) - MSG_WriteByte(msg, ent->frame & 0xFF00); - if (bits & E_EFFECTS1) - MSG_WriteByte(msg, ent->effects & 0x00FF); - if (bits & E_EFFECTS2) - MSG_WriteByte(msg, ent->effects & 0xFF00); - if (bits & E_COLORMAP) - MSG_WriteByte(msg, ent->colormap); - if (bits & E_SKIN) - MSG_WriteByte(msg, ent->skin); - if (bits & E_ALPHA) - MSG_WriteByte(msg, ent->alpha); - if (bits & E_SCALE) - MSG_WriteByte(msg, ent->scale); - if (bits & E_GLOWSIZE) - MSG_WriteByte(msg, ent->glowsize); - if (bits & E_GLOWCOLOR) - MSG_WriteByte(msg, ent->glowcolor); - if (bits & E_FLAGS) - MSG_WriteByte(msg, ent->flags); + // delta from defaults + delta = &defaultstate; } + EntityState_Write(ent, msg, delta); } for (;onum < o->numentities;onum++) { @@ -284,14 +509,15 @@ void EntityFrame_Write(entity_database_t *d, entity_frame_t *f, sizebuf_t *msg) } // (client) reads a frame from network stream +static entity_frame_t framedata; // FIXME? void EntityFrame_Read(entity_database_t *d) { - int number, removed, bits; - entity_frame_t framedata, *f = &framedata, deltaframedata, *delta = &deltaframedata; - entity_state_t *e, baseline, *old, *oldend; + int number, removed; + entity_frame_t *f = &framedata, *delta = &deltaframe; + entity_state_t *e, *old, *oldend; + + EntityFrame_Clear(f, NULL, -1); - ClearStateToDefault(&baseline); - memset(f, 0, sizeof(*f)); // read the frame header info f->time = cl.mtime[0]; number = MSG_ReadLong(); @@ -318,10 +544,8 @@ void EntityFrame_Read(entity_database_t *d) { if (f->numentities >= MAX_ENTITY_DATABASE) Host_Error("EntityFrame_Read: entity list too big\n"); - memcpy(f->entitydata + f->numentities, old, sizeof(entity_state_t)); - f->entitydata[f->numentities].time = cl.mtime[0]; - old++; - f->numentities++; + f->entitydata[f->numentities] = *old++; + f->entitydata[f->numentities++].time = cl.mtime[0]; } if (removed) { @@ -341,133 +565,300 @@ void EntityFrame_Read(entity_database_t *d) if (old < oldend && old->number == number) { // delta from old entity - memcpy(e, old++, sizeof(*e)); + *e = *old++; } else { - // delta from baseline - memcpy(e, &baseline, sizeof(*e)); + // delta from defaults + *e = defaultstate; } - e->active = true; - e->time = cl.mtime[0]; - e->number = number; - - bits = MSG_ReadByte(); - if (bits & E_EXTEND1) - { - bits |= MSG_ReadByte() << 8; - if (bits & E_EXTEND2) - { - bits |= MSG_ReadByte() << 16; - if (bits & E_EXTEND3) - bits |= MSG_ReadByte() << 24; - } - } - - if (bits & E_ORIGIN1) - e->origin[0] = (signed short) MSG_ReadShort(); - if (bits & E_ORIGIN2) - e->origin[1] = (signed short) MSG_ReadShort(); - if (bits & E_ORIGIN3) - e->origin[2] = (signed short) MSG_ReadShort(); - if (bits & E_ANGLE1) - e->angles[0] = MSG_ReadAngle(); - if (bits & E_ANGLE2) - e->angles[1] = MSG_ReadAngle(); - if (bits & E_ANGLE3) - e->angles[2] = MSG_ReadAngle(); - if (bits & E_MODEL1) - e->modelindex = (e->modelindex & 0xFF00) | MSG_ReadByte(); - if (bits & E_MODEL2) - e->modelindex = (e->modelindex & 0x00FF) | (MSG_ReadByte() << 8); - if (bits & E_FRAME1) - e->frame = (e->frame & 0xFF00) | MSG_ReadByte(); - if (bits & E_FRAME2) - e->frame = (e->frame & 0x00FF) | (MSG_ReadByte() << 8); - if (bits & E_EFFECTS1) - e->effects = (e->effects & 0xFF00) | MSG_ReadByte(); - if (bits & E_EFFECTS2) - e->effects = (e->effects & 0x00FF) | (MSG_ReadByte() << 8); - if (bits & E_COLORMAP) - e->colormap = MSG_ReadByte(); - if (bits & E_SKIN) - e->skin = MSG_ReadByte(); - if (bits & E_ALPHA) - e->alpha = MSG_ReadByte(); - if (bits & E_SCALE) - e->scale = MSG_ReadByte(); - if (bits & E_GLOWSIZE) - e->glowsize = MSG_ReadByte(); - if (bits & E_GLOWCOLOR) - e->glowcolor = MSG_ReadByte(); - if (bits & E_FLAGS) - e->flags = MSG_ReadByte(); + EntityState_ReadUpdate(e, number); } } while (old < oldend) { if (f->numentities >= MAX_ENTITY_DATABASE) Host_Error("EntityFrame_Read: entity list too big\n"); - memcpy(f->entitydata + f->numentities, old, sizeof(entity_state_t)); - f->entitydata[f->numentities].time = cl.mtime[0]; - old++; - f->numentities++; + f->entitydata[f->numentities] = *old++; + f->entitydata[f->numentities++].time = cl.mtime[0]; } EntityFrame_AddFrame(d, f); } -/* -// (client) reads (and interpolates) the eye location from the database, -// given a current time -int EntityFrame_FetchEye(entity_database_t *d, vec3_t eye, double time) + +// (client) returns the frame number of the most recent frame recieved +int EntityFrame_MostRecentlyRecievedFrameNum(entity_database_t *d) { - float frac; - if (d->numframes == 0) - return false; -// Host_Error("EntityFrame_FetchEye: no frames\n"); - if (d->numframes > 1 && d->frames[d->numframes - 2].time != d->frames[d->numframes - 1].time) + if (d->numframes) + return d->frames[d->numframes - 1].framenum; + else + return -1; +} + + + + + + +entity_state_t *EntityFrame4_GetReferenceEntity(entity_database4_t *d, int number) +{ + if (d->maxreferenceentities <= number) { - frac = (time - d->frames[d->numframes - 2].time) / (d->frames[d->numframes - 1].time - d->frames[d->numframes - 2].time); - frac = bound(0, frac, 1); - VectorSubtract(d->frames[d->numframes - 2].eye, d->frames[d->numframes - 1].eye, eye); - VectorMA(d->frames[d->numframes - 2].eye, frac, eye, eye); + int oldmax = d->maxreferenceentities; + entity_state_t *oldentity = d->referenceentity; + d->maxreferenceentities = (number + 15) & ~7; + d->referenceentity = Mem_Alloc(d->mempool, d->maxreferenceentities * sizeof(*d->referenceentity)); + if (oldentity) + { + memcpy(d->referenceentity, oldentity, oldmax * sizeof(*d->referenceentity)); + Mem_Free(oldentity); + } + // clear the newly created entities + for (;oldmax < d->maxreferenceentities;oldmax++) + { + d->referenceentity[oldmax] = defaultstate; + d->referenceentity[oldmax].number = oldmax; + } } - else - VectorCopy(d->frames[0].eye, eye); - return true; + return d->referenceentity + number; } -// (client) fetchs an entity from a frame, index is the index into the frame's entity list, returns false if index is out of bounds -int EntityFrame_FetchEntityByIndex(entity_frame_t *f, entity_state_t *e, int index) +void EntityFrame4_AddCommitEntity(entity_database4_t *d, entity_state_t *s) { - if (index < 0 || index >= f->numentities) - return false; - memcpy(e, f->entitydata + index, sizeof(*e)); - return true; + // 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; } -int EntityFrame_FetchEntityByNumber(entity_frame_t *f, entity_state_t *e, int number) +entity_database4_t *EntityFrame4_AllocDatabase(mempool_t *pool) +{ + entity_database4_t *d; + d = Mem_Alloc(pool, sizeof(*d)); + d->mempool = pool; + EntityFrame4_ResetDatabase(d); + d->ackframenum = -1; + return d; +} + +void EntityFrame4_FreeDatabase(entity_database4_t *d) { int i; - for (i = 0;i < f->numentities;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(entity_database4_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(entity_database4_t *d, int framenum) +{ + int i, j, found = false; + entity_database4_commit_t *commit; + if (d->referenceframenum == framenum) + return true; + if (framenum == -1) { - if (f->entitydata[i].number == number) + EntityFrame4_ResetDatabase(d); + return true; + } + for (i = 0, commit = d->commit;i < MAX_ENTITY_HISTORY;i++, commit++) + { + if (commit->numentities && commit->framenum <= framenum) { - memcpy(e, f->entitydata + i, sizeof(*e)); - return true; + 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; } } - ClearStateToDefault(e); - return false; + return found; } -*/ -// (client) returns the frame number of the most recent frame recieved -int EntityFrame_MostRecentlyRecievedFrameNum(entity_database_t *d) +int EntityFrame4_SV_WriteFrame_Entity(entity_database4_t *d, sizebuf_t *msg, int maxbytes, entity_state_t *s) { - if (d->numframes) - return d->frames[d->numframes - 1].framenum; - else - return -1; + qbyte data[128]; + sizebuf_t buf; + entity_state_t *e; + // prepare the buffer + memset(&buf, 0, sizeof(buf)); + buf.data = data; + buf.maxsize = sizeof(data); + // make the message + e = EntityFrame4_GetReferenceEntity(d, s->number); + // send an update (may update or remove the entity) + EntityState_Write(s, &buf, e); + // if the message is empty, skip out now + if (!buf.cursize) + return true; + // if the commit is full, we're done + if (msg->cursize + buf.cursize + 2 >= min(msg->maxsize, maxbytes)) + return false; + // add the entity to the commit + EntityFrame4_AddCommitEntity(d, s); + // write the message to the packet + SZ_Write(msg, buf.data, buf.cursize); + // carry on + return true; } + +extern void CL_MoveLerpEntityStates(entity_t *ent); +void EntityFrame4_CL_ReadFrame(entity_database4_t *d) +{ + int i, n, cnumber, referenceframenum, framenum, enumber, done, stopnumber, skip = false; + // read the number of the frame this refers to + referenceframenum = MSG_ReadLong(); + // read the number of this frame + framenum = MSG_ReadLong(); + // read the start number + enumber = MSG_ReadShort(); + if (developer_networkentities.integer >= 1) + { + Con_Printf("recv svc_entities ref:%i num:%i (database: ref:%i commits:", referenceframenum, framenum, d->referenceframenum); + for (i = 0;i < MAX_ENTITY_HISTORY;i++) + if (d->commit[i].numentities) + Con_Printf(" %i", d->commit[i].framenum); + Con_Printf("\n"); + } + if (!EntityFrame4_AckFrame(d, referenceframenum)) + { + Con_Printf("EntityFrame4_CL_ReadFrame: reference frame invalid (VERY BAD ERROR), this update will be skipped\n"); + skip = true; + d->ackframenum = -1; + EntityFrame4_ResetDatabase(d); + } + d->currentcommit = NULL; + for (i = 0;i < MAX_ENTITY_HISTORY;i++) + { + if (!d->commit[i].numentities) + { + d->currentcommit = d->commit + i; + d->currentcommit->framenum = d->ackframenum = 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; + // 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) + { + if (enumber == cnumber && (n & 0x8000) == 0) + { + entity_state_t tempstate; + EntityState_ReadUpdate(&tempstate, enumber); + } + 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); + // 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); + cl_entities[enumber].state_current = defaultstate; + } + else + { + // read the changes + if (developer_networkentities.integer >= 2) + Con_Printf("entity %i: update\n", enumber); + EntityState_ReadUpdate(&cl_entities[enumber].state_current, enumber); + } + } + else if (developer_networkentities.integer >= 4) + Con_Printf("entity %i: copy\n", enumber); + // fix the number (it gets wiped occasionally by copying from defaultstate) + cl_entities[enumber].state_current.number = enumber; + // check if we need to update the lerp stuff + if (cl_entities[enumber].state_current.active) + { + CL_MoveLerpEntityStates(&cl_entities[enumber]); + cl_entities_active[enumber] = true; + } + // 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; +} + +