From: lordhavoc Date: Sun, 17 Feb 2002 20:56:00 +0000 (+0000) Subject: new entity protocol nearly complete, but still unused X-Git-Tag: RELEASE_0_2_0_RC1~638 X-Git-Url: https://git.xonotic.org/?a=commitdiff_plain;h=0e7aa755e6467531d8000efb969321ea752ba72f;p=xonotic%2Fdarkplaces.git new entity protocol nearly complete, but still unused git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@1530 d7cf8633-e32d-0410-b094-e92efae38249 --- diff --git a/protocol.c b/protocol.c index c2127571..fc5981aa 100644 --- a/protocol.c +++ b/protocol.c @@ -26,15 +26,26 @@ void EntityFrame_ClearDatabase(entity_database_t *d) d->ackframe = -1; } -// (server) acknowledge a frame as recieved by client (removes old frames from database, will use this new frame for delta compression) +// (server and client) removes frames older than 'frame' from database void EntityFrame_AckFrame(entity_database_t *d, int frame) { + int i; + for (i = 0;i < d->numframes && d->frames[i].framenum >= frame;i++); + // ignore outdated frame acks (out of order packets) + if (i == 0) + return; + d->ackframe = frame; + d->numframes -= i; + // if some queue is left, slide it down to beginning of array + if (d->numframes) + memcpy(&d->frames[0], &d->frames[i], sizeof(d->frames[0]) * d->numframes); } // (server) clears frame, to prepare for adding entities -void EntityFrame_Clear(entity_frame_t *f) +void EntityFrame_Clear(entity_frame_t *f, vec3_t eye) { memset(f, 0, sizeof(*f)); + VectorCopy(eye, f->eye); } // (server) allocates an entity slot in frame, returns NULL if full @@ -49,17 +60,376 @@ entity_state_t *EntityFrame_NewEntity(entity_frame_t *f, int number) return e; } +void EntityFrame_FetchFrame(entity_database_t *d, int framenum, entity_frame_t *f) +{ + int i, n; + memset(f, 0, sizeof(*f)); + for (i = 0;i < d->numframes && d->frames[i].framenum < framenum;i++); + if (framenum == d->frames[i].framenum) + { + f->framenum = framenum; + f->numentities = d->frames[i].endentity - d->frames[i].firstentity; + n = MAX_ENTITY_DATABASE - (d->frames[i].firstentity % MAX_ENTITY_DATABASE); + if (n > f->numentities) + n = f->numentities; + memcpy(f->entitydata, d->entitydata + d->frames[i].firstentity % MAX_ENTITY_DATABASE, sizeof(*f->entitydata) * n); + if (f->numentities > n) + memcpy(f->entitydata + n, d->entitydata, sizeof(*f->entitydata) * (f->numentities - n)); + } + else + f->framenum = -1; +} + +// (server and client) adds a entity_frame to the database, for future reference +void EntityFrame_AddFrame(entity_database_t *d, entity_frame_t *f) +{ + int n, e; + entity_frameinfo_t *info; + // figure out how many entity slots are used already + if (d->numframes) + { + n = d->frames[d->numframes - 1].endentity - d->frames[0].firstentity; + if (n + f->numentities > MAX_ENTITY_DATABASE || d->numframes >= MAX_ENTITY_HISTORY) + { + // ran out of room, dump database + EntityFrame_ClearDatabase(d); + } + } + + info = &d->frames[d->numframes]; + info->framenum = f->framenum; + // if database still has frames after that... + if (d->numframes) + info->firstentity = d->frames[d->numframes - 1].endentity; + else + info->firstentity = 0; + info->endentity = info->firstentity + f->numentities; + + n = info->firstentity % MAX_ENTITY_DATABASE; + e = MAX_ENTITY_DATABASE - n; + if (e > f->numentities) + e = f->numentities; + memcpy(d->entitydata + n, f->entitydata, sizeof(entity_state_t) * e); + if (f->numentities > e) + memcpy(d->entitydata, f->entitydata + e, sizeof(entity_state_t) * (f->numentities - e)); +} + // (server) writes a frame to network stream -void EntityFrame_Write(entity_database_t *d, entity_frame_t *f, int deltaframe, int newframe, sizebuf_t *msg) +void EntityFrame_Write(entity_database_t *d, entity_frame_t *f, int newframe, sizebuf_t *msg) { + int i, onum, bits, number; + entity_frame_t deltaframe, *o = &deltaframe; + entity_state_t *ent, *delta, baseline; + + EntityFrame_AddFrame(d, f); + + ClearStateToDefault(&baseline); + EntityFrame_FetchFrame(d, d->ackframe, o); + MSG_WriteByte (msg, svc_entities); + MSG_WriteLong (msg, o->framenum); + MSG_WriteLong (msg, newframe); + MSG_WriteFloat (msg, f->eye[0]); + MSG_WriteFloat (msg, f->eye[1]); + MSG_WriteFloat (msg, f->eye[2]); + + onum = 0; + for (i = 0;i < f->numentities;i++) + { + ent = f->entitydata + i; + number = ent->number; + for (;onum < o->numentities && o->entitydata[onum].number < number;onum++) + { + // write remove message + MSG_WriteShort(msg, o->entitydata[onum].number | 0x8000); + } + if (onum < o->numentities && (o->entitydata[onum].number == number)) + { + // delta from previous frame + delta = o->entitydata + onum; + // advance to next entity in delta frame + onum++; + } + else + { + // delta from baseline + delta = &baseline; + } + bits = 0; + if (ent->origin[0] != delta->origin[0]) + bits |= E_ORIGIN1; + if (ent->origin[1] != delta->origin[1]) + bits |= E_ORIGIN2; + if (ent->origin[2] != delta->origin[2]) + bits |= E_ORIGIN3; + if (ent->angles[0] != delta->angles[0]) + bits |= E_ANGLE1; + if (ent->angles[1] != delta->angles[1]) + bits |= E_ANGLE2; + if (ent->angles[2] != delta->angles[2]) + 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_WriteFloat(msg, ent->origin[0]); + if (bits & E_ORIGIN2) + MSG_WriteFloat(msg, ent->origin[1]); + if (bits & E_ORIGIN3) + MSG_WriteFloat(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); + } + } + for (;onum < o->numentities;onum++) + { + // write remove message + MSG_WriteShort(msg, o->entitydata[onum].number | 0x8000); + } + MSG_WriteShort(msg, 0xFFFF); } // (client) reads a frame from network stream -void EntityFrame_Read(entity_database_t *d, entity_frame_t *f) +void EntityFrame_Read(entity_database_t *d) +{ + int newframenum, deltaframenum, onum, number, removed, bits; + entity_frame_t framedata, *f = &framedata, deltaframedata, *delta = &deltaframedata; + entity_state_t *e, baseline; + + ClearStateToDefault(&baseline); + memset(f, 0, sizeof(*f)); + // read the frame header info + f->time = cl.mtime[0]; + deltaframenum = MSG_ReadLong(); + newframenum = MSG_ReadLong(); + f->eye[0] = MSG_ReadFloat(); + f->eye[1] = MSG_ReadFloat(); + f->eye[2] = MSG_ReadFloat(); + EntityFrame_AckFrame(d, deltaframenum); + EntityFrame_FetchFrame(d, deltaframenum, delta); + f->framenum = newframenum; + onum = 0; + f->numentities = 0; + // read entities until we hit the magic 0xFFFF end tag + while ((number = MSG_ReadShort()) != 0xFFFF) + { + if (msg_badread) + Host_Error("EntityFrame_Read: read error\n"); + removed = number & 0x8000; + number &= 0x7FFF; + if (number >= MAX_EDICTS) + Host_Error("EntityFrame_Read: number (%i) >= MAX_EDICTS (%i)\n", number, MAX_EDICTS); + + // seek to entity, while copying any skipped entities (assume unchanged) + while (onum < delta->numentities && delta->entitydata[onum].number < number) + { + if (f->numentities >= MAX_ENTITY_DATABASE) + Host_Error("EntityFrame_Read: entity list too big\n"); + memcpy(f->entitydata + f->numentities, delta->entitydata + onum, sizeof(entity_state_t)); + onum++; + f->numentities++; + } + if (removed) + { + if (onum < delta->numentities && delta->entitydata[onum].number == number) + onum++; + else + Con_Printf("EntityFrame_Read: REMOVE on unused entity!\n"); + } + else + { + if (f->numentities >= MAX_ENTITY_DATABASE) + Host_Error("EntityFrame_Read: entity list too big\n"); + if (onum < delta->numentities && delta->entitydata[onum].number == number) + { + // delta from old entity + memcpy(f->entitydata + f->numentities, delta->entitydata + onum++, sizeof(*e)); + } + else + { + // delta from baseline + memcpy(f->entitydata + f->numentities, &baseline, sizeof(*e)); + } + + // reserve this slot + e = f->entitydata + f->numentities++; + + e->active = true; + e->time = cl.mtime[0]; + + 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] = 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) | 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(); + } + } + while (onum < delta->numentities) + { + if (f->numentities >= MAX_ENTITY_DATABASE) + Host_Error("EntityFrame_Read: entity list too big\n"); + memcpy(f->entitydata + f->numentities, delta->entitydata + onum, sizeof(entity_state_t)); + onum++; + f->numentities++; + } + EntityFrame_AddFrame(d, f); +} + +void EntityFrame_FetchEye(entity_database_t *d, vec3_t eye, double time) +{ + float frac; + if (d->numframes == 0) + Host_Error("EntityFrame_FetchEye: no frames\n"); + if (d->numframes > 1 && d->frames[d->numframes - 2].time != d->frames[d->numframes - 1].time) + { + 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); + } + else + VectorCopy(d->frames[0].eye, eye); +} + +// (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) { + if (index < 0 || index >= f->numentities) + return false; + memcpy(e, f->entitydata + index, sizeof(*e)); + return true; } -// (client) fetchs an entity from the database, read with _Read, fills in structs for current and previous state -void EntityFrame_FetchEntity(entity_database_t *d, entity_state_t *previous, entity_state_t *current) +int EntityFrame_FetchEntityByNumber(entity_frame_t *f, entity_state_t *e, int number) { + int i; + for (i = 0;i < f->numentities;i++) + { + if (f->entitydata[i].number == number) + { + memcpy(e, f->entitydata + i, sizeof(*e)); + return true; + } + } + ClearStateToDefault(e); + return false; } diff --git a/protocol.h b/protocol.h index 22f18071..0df3b283 100644 --- a/protocol.h +++ b/protocol.h @@ -209,7 +209,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define svc_sound2 54 // short soundindex instead of byte #define svc_spawnbaseline2 55 // short modelindex instead of byte #define svc_spawnstatic2 56 // short modelindex instead of byte -#define svc_entities 57 // [short] numentities [int] deltaframe [float vector] eye [variable length] entitydata +#define svc_entities 57 // [int] deltaframe [int] thisframe [float vector] eye [variable length] entitydata #define svc_unusedlh3 58 #define svc_spawnstaticsound2 59 // [coord3] [short] samp [byte] vol [byte] aten @@ -267,6 +267,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define TE_FLAMEJET 74 // [vector] origin [vector] velocity [byte] count #define TE_PLASMABURN 75 // [vector] origin +// these are bits for the 'flags' field of the entity_state_t #define RENDER_STEP 1 #define RENDER_GLOWTRAIL 2 #define RENDER_VIEWMODEL 4 @@ -289,12 +290,13 @@ typedef struct byte glowsize; byte glowcolor; byte flags; - byte padding[3]; } entity_state_t; typedef struct { + double time; + vec3_t eye; int framenum; int firstentity; // index into entitydata, modulo MAX_ENTITY_DATABASE int endentity; // index into entitydata, firstentity + numentities @@ -324,24 +326,77 @@ entity_database_t; // build entity data in this, to pass to entity read/write functions typedef struct { + double time; int framenum; int numentities; + vec3_t eye; entity_state_t entitydata[MAX_ENTITY_DATABASE]; } entity_frame_t; +// LordHavoc: these are in approximately sorted order, according to cost and +// likelyhood of being used for numerous objects in a frame + +// note that the bytes are not written/read in this order, this is only the +// order of the bits to minimize overhead from extend bytes + +// enough to describe a nail, gib, shell casing, bullet hole, or rocket +#define E_ORIGIN1 (1<<0) +#define E_ORIGIN2 (1<<1) +#define E_ORIGIN3 (1<<2) +#define E_ANGLE1 (1<<3) +#define E_ANGLE2 (1<<4) +#define E_ANGLE3 (1<<5) +#define E_MODEL1 (1<<6) +#define E_EXTEND1 (1<<7) + +// enough to describe almost anything +#define E_FRAME1 (1<<8) +#define E_EFFECTS1 (1<<9) +#define E_ALPHA (1<<10) +#define E_SCALE (1<<11) +#define E_COLORMAP (1<<12) +#define E_SKIN (1<<13) +#define E_FLAGS (1<<14) +#define E_EXTEND2 (1<<15) + +// players, custom color glows, high model numbers +#define E_FRAME2 (1<<16) +#define E_MODEL2 (1<<17) +#define E_EFFECTS2 (1<<18) +#define E_GLOWSIZE (1<<19) +#define E_GLOWCOLOR (1<<20) +#define E_UNUSED1 (1<<21) +#define E_UNUSED2 (1<<22) +#define E_EXTEND3 (1<<23) + +#define E_SOUND1 (1<<24) +#define E_SOUNDVOL (1<<25) +#define E_SOUNDATTEN (1<<26) +#define E_UNUSED4 (1<<27) +#define E_UNUSED5 (1<<28) +#define E_UNUSED6 (1<<29) +#define E_UNUSED7 (1<<30) +#define E_EXTEND4 (1<<31) + void ClearStateToDefault(entity_state_t *s); -// (server) clears the database to contain no frames (thus delta compression compresses against nothing) +// (server) clears the database to contain no frames (thus delta compression +// compresses against nothing) void EntityFrame_ClearDatabase(entity_database_t *d); -// (server) acknowledge a frame as recieved by client (removes old frames from database, will use this new frame for delta compression) +// (server and client) removes frames older than 'frame' from database void EntityFrame_AckFrame(entity_database_t *d, int frame); // (server) clears frame, to prepare for adding entities -void EntityFrame_Clear(entity_frame_t *f); +void EntityFrame_Clear(entity_frame_t *f, vec3_t eye); // (server) allocates an entity slot in frame, returns NULL if full entity_state_t *EntityFrame_NewEntity(entity_frame_t *f, int number); +// (server and client) adds a entity_frame to the database, for future +// reference +void EntityFrame_AddFrame(entity_database_t *d, entity_frame_t *f); // (server) writes a frame to network stream -void EntityFrame_Write(entity_database_t *d, entity_frame_t *f, int deltaframe, int newframe, sizebuf_t *msg); +void EntityFrame_Write(entity_database_t *d, entity_frame_t *f, int newframe, sizebuf_t *msg); // (client) reads a frame from network stream -void EntityFrame_Read(entity_database_t *d, entity_frame_t *f); -// (client) fetchs an entity from the database, read with _Read, fills in structs for current and previous state -void EntityFrame_FetchEntity(entity_database_t *d, entity_state_t *previous, entity_state_t *current); +void EntityFrame_Read(entity_database_t *d); +// (client) fetchs an entity from the frame by index into the entity list +int EntityFrame_FetchEntityByIndex(entity_frame_t *f, entity_state_t *e, int index); +// (client) fetchs an entity from the frame by entity number +int EntityFrame_FetchEntityByNumber(entity_frame_t *f, entity_state_t *e, int number);