#include "quakedef.h"
-#define ENTITYSIZEPROFILING_START(msg, num, flags) \
- int entityprofiling_startsize = msg->cursize
-
-#define ENTITYSIZEPROFILING_END(msg, num, flags) \
- if(developer_networkentities.integer >= 2) \
- { \
- prvm_edict_t *edict = prog->edicts + num; \
- Con_Printf("sent entity update of size %u for %d classname %s flags %d\n", (msg->cursize - entityprofiling_startsize), num, PRVM_serveredictstring(edict, classname) ? PRVM_GetString(prog, PRVM_serveredictstring(edict, classname)) : "(no classname)", flags); \
- }
-
-// CSQC entity scope values. Bitflags!
-#define SCOPE_WANTREMOVE 1 // Set if a remove has been scheduled. Never set together with WANTUPDATE.
-#define SCOPE_WANTUPDATE 2 // Set if an update has been scheduled.
-#define SCOPE_WANTSEND (SCOPE_WANTREMOVE | SCOPE_WANTUPDATE)
-#define SCOPE_EXISTED_ONCE 4 // Set if the entity once existed. All these get resent on a full loss.
-#define SCOPE_ASSUMED_EXISTING 8 // Set if the entity is currently assumed existing and therefore needs removes.
-
// this is 88 bytes (must match entity_state_t in protocol.h)
entity_state_t defaultstate =
{
}
}
-void EntityFrameQuake_ReadEntity(int bits)
-{
- int num;
- entity_t *ent;
- entity_state_t s;
-
- if (bits & U_MOREBITS)
- bits |= (MSG_ReadByte(&cl_message)<<8);
- if ((bits & U_EXTEND1) && cls.protocol != PROTOCOL_NEHAHRAMOVIE)
- {
- bits |= MSG_ReadByte(&cl_message) << 16;
- if (bits & U_EXTEND2)
- bits |= MSG_ReadByte(&cl_message) << 24;
- }
-
- if (bits & U_LONGENTITY)
- num = (unsigned short) MSG_ReadShort(&cl_message);
- else
- num = MSG_ReadByte(&cl_message);
-
- if (num >= MAX_EDICTS)
- Host_Error("EntityFrameQuake_ReadEntity: entity number (%i) >= MAX_EDICTS (%i)", num, MAX_EDICTS);
- if (num < 1)
- Host_Error("EntityFrameQuake_ReadEntity: invalid entity number (%i)", num);
-
- if (cl.num_entities <= num)
- {
- cl.num_entities = num + 1;
- if (num >= cl.max_entities)
- CL_ExpandEntities(num);
- }
-
- ent = cl.entities + num;
-
- // note: this inherits the 'active' state of the baseline chosen
- // (state_baseline is always active, state_current may not be active if
- // the entity was missing in the last frame)
- if (bits & U_DELTA)
- s = ent->state_current;
- else
- {
- s = ent->state_baseline;
- s.active = ACTIVE_NETWORK;
- }
-
- cl.isquakeentity[num] = true;
- if (cl.lastquakeentity < num)
- cl.lastquakeentity = num;
- s.number = num;
- s.time = cl.mtime[0];
- s.flags = 0;
- if (bits & U_MODEL)
- {
- if (cls.protocol == PROTOCOL_NEHAHRABJP || cls.protocol == PROTOCOL_NEHAHRABJP2 || cls.protocol == PROTOCOL_NEHAHRABJP3)
- s.modelindex = (unsigned short) MSG_ReadShort(&cl_message);
- else
- s.modelindex = (s.modelindex & 0xFF00) | MSG_ReadByte(&cl_message);
- }
- if (bits & U_FRAME) s.frame = (s.frame & 0xFF00) | MSG_ReadByte(&cl_message);
- if (bits & U_COLORMAP) s.colormap = MSG_ReadByte(&cl_message);
- if (bits & U_SKIN) s.skin = MSG_ReadByte(&cl_message);
- if (bits & U_EFFECTS) s.effects = (s.effects & 0xFF00) | MSG_ReadByte(&cl_message);
- if (bits & U_ORIGIN1) s.origin[0] = MSG_ReadCoord(&cl_message, cls.protocol);
- if (bits & U_ANGLE1) s.angles[0] = MSG_ReadAngle(&cl_message, cls.protocol);
- if (bits & U_ORIGIN2) s.origin[1] = MSG_ReadCoord(&cl_message, cls.protocol);
- if (bits & U_ANGLE2) s.angles[1] = MSG_ReadAngle(&cl_message, cls.protocol);
- if (bits & U_ORIGIN3) s.origin[2] = MSG_ReadCoord(&cl_message, cls.protocol);
- if (bits & U_ANGLE3) s.angles[2] = MSG_ReadAngle(&cl_message, cls.protocol);
- if (bits & U_STEP) s.flags |= RENDER_STEP;
- if (bits & U_ALPHA) s.alpha = MSG_ReadByte(&cl_message);
- if (bits & U_SCALE) s.scale = MSG_ReadByte(&cl_message);
- if (bits & U_EFFECTS2) s.effects = (s.effects & 0x00FF) | (MSG_ReadByte(&cl_message) << 8);
- if (bits & U_GLOWSIZE) s.glowsize = MSG_ReadByte(&cl_message);
- if (bits & U_GLOWCOLOR) s.glowcolor = MSG_ReadByte(&cl_message);
- if (bits & U_COLORMOD) {int c = MSG_ReadByte(&cl_message);s.colormod[0] = (unsigned char)(((c >> 5) & 7) * (32.0f / 7.0f));s.colormod[1] = (unsigned char)(((c >> 2) & 7) * (32.0f / 7.0f));s.colormod[2] = (unsigned char)((c & 3) * (32.0f / 3.0f));}
- if (bits & U_GLOWTRAIL) s.flags |= RENDER_GLOWTRAIL;
- if (bits & U_FRAME2) s.frame = (s.frame & 0x00FF) | (MSG_ReadByte(&cl_message) << 8);
- if (bits & U_MODEL2) s.modelindex = (s.modelindex & 0x00FF) | (MSG_ReadByte(&cl_message) << 8);
- if (bits & U_VIEWMODEL) s.flags |= RENDER_VIEWMODEL;
- if (bits & U_EXTERIORMODEL) s.flags |= RENDER_EXTERIORMODEL;
-
- // LadyHavoc: to allow playback of the Nehahra movie
- if (cls.protocol == PROTOCOL_NEHAHRAMOVIE && (bits & U_EXTEND1))
- {
- // LadyHavoc: evil format
- int i = (int)MSG_ReadFloat(&cl_message);
- int j = (int)(MSG_ReadFloat(&cl_message) * 255.0f);
- if (i == 2)
- {
- i = (int)MSG_ReadFloat(&cl_message);
- if (i)
- s.effects |= EF_FULLBRIGHT;
- }
- if (j < 0)
- s.alpha = 0;
- else if (j == 0 || j >= 255)
- s.alpha = 255;
- else
- s.alpha = j;
- }
-
- ent->state_previous = ent->state_current;
- ent->state_current = s;
- if (ent->state_current.active == ACTIVE_NETWORK)
- {
- CL_MoveLerpEntityStates(ent);
- cl.entities_active[ent->state_current.number] = true;
- }
-
- if (cl_message.badread)
- Host_Error("EntityFrameQuake_ReadEntity: read error");
-}
-
-void EntityFrameQuake_ISeeDeadEntities(void)
-{
- int num, lastentity;
- if (cl.lastquakeentity == 0)
- return;
- lastentity = cl.lastquakeentity;
- cl.lastquakeentity = 0;
- for (num = 0;num <= lastentity;num++)
- {
- if (cl.isquakeentity[num])
- {
- if (cl.entities_active[num] && cl.entities[num].state_current.time == cl.mtime[0])
- {
- cl.isquakeentity[num] = true;
- cl.lastquakeentity = num;
- }
- else
- {
- cl.isquakeentity[num] = false;
- cl.entities_active[num] = ACTIVE_NOT;
- cl.entities[num].state_current = defaultstate;
- cl.entities[num].state_current.number = num;
- }
- }
- }
-}
-
-// NOTE: this only works with DP5 protocol and upwards. For lower protocols
-// (including QUAKE), no packet loss handling for CSQC is done, which makes
-// CSQC basically useless.
-// Always use the DP5 protocol, or a higher one, when using CSQC entities.
-static void EntityFrameCSQC_LostAllFrames(client_t *client)
-{
- prvm_prog_t *prog = SVVM_prog;
- // mark ALL csqc entities as requiring a FULL resend!
- // I know this is a bad workaround, but better than nothing.
- int i, n;
- prvm_edict_t *ed;
-
- n = client->csqcnumedicts;
- for(i = 0; i < n; ++i)
- {
- if(client->csqcentityscope[i] & SCOPE_EXISTED_ONCE)
- {
- ed = prog->edicts + i;
- client->csqcentitysendflags[i] |= 0xFFFFFF; // FULL RESEND. We can't clear SCOPE_ASSUMED_EXISTING yet as this would cancel removes on a rejected send attempt.
- if (!PRVM_serveredictfunction(ed, SendEntity)) // If it was ever sent to that client as a CSQC entity...
- client->csqcentityscope[i] |= SCOPE_ASSUMED_EXISTING; // FORCE REMOVE.
- }
- }
-}
-void EntityFrameCSQC_LostFrame(client_t *client, int framenum)
-{
- // marks a frame as lost
- int i, j;
- qboolean valid;
- int ringfirst, ringlast;
- static int recoversendflags[MAX_EDICTS]; // client only
- csqcentityframedb_t *d;
-
- if(client->csqcentityframe_lastreset < 0)
- return;
- if(framenum < client->csqcentityframe_lastreset)
- return; // no action required, as we resent that data anyway
-
- // is our frame out of history?
- ringfirst = client->csqcentityframehistory_next; // oldest entry
- ringlast = (ringfirst + NUM_CSQCENTITYDB_FRAMES - 1) % NUM_CSQCENTITYDB_FRAMES; // most recently added entry
-
- valid = false;
-
- for(j = 0; j < NUM_CSQCENTITYDB_FRAMES; ++j)
- {
- d = &client->csqcentityframehistory[(ringfirst + j) % NUM_CSQCENTITYDB_FRAMES];
- if(d->framenum < 0)
- continue;
- if(d->framenum == framenum)
- break;
- else if(d->framenum < framenum)
- valid = true;
- }
- if(j == NUM_CSQCENTITYDB_FRAMES)
- {
- if(valid) // got beaten, i.e. there is a frame < framenum
- {
- // a non-csqc frame got lost... great
- return;
- }
- else
- {
- // a too old frame got lost... sorry, cannot handle this
- Con_DPrintf("CSQC entity DB: lost a frame too early to do any handling (resending ALL)...\n");
- Con_DPrintf("Lost frame = %d\n", framenum);
- Con_DPrintf("Entity DB = %d to %d\n", client->csqcentityframehistory[ringfirst].framenum, client->csqcentityframehistory[ringlast].framenum);
- EntityFrameCSQC_LostAllFrames(client);
- client->csqcentityframe_lastreset = -1;
- }
- return;
- }
-
- // so j is the frame that got lost
- // ringlast is the frame that we have to go to
- ringfirst = (ringfirst + j) % NUM_CSQCENTITYDB_FRAMES;
- if(ringlast < ringfirst)
- ringlast += NUM_CSQCENTITYDB_FRAMES;
-
- memset(recoversendflags, 0, sizeof(recoversendflags));
-
- for(j = ringfirst; j <= ringlast; ++j)
- {
- d = &client->csqcentityframehistory[j % NUM_CSQCENTITYDB_FRAMES];
- if(d->framenum < 0)
- {
- // deleted frame
- }
- else if(d->framenum < framenum)
- {
- // a frame in the past... should never happen
- Con_Printf("CSQC entity DB encountered a frame from the past when recovering from PL...?\n");
- }
- else if(d->framenum == framenum)
- {
- // handling the actually lost frame now
- for(i = 0; i < d->num; ++i)
- {
- int sf = d->sendflags[i];
- int ent = d->entno[i];
- if(sf < 0) // remove
- recoversendflags[ent] |= -1; // all bits, including sign
- else if(sf > 0)
- recoversendflags[ent] |= sf;
- }
- }
- else
- {
- // handling the frames that followed it now
- for(i = 0; i < d->num; ++i)
- {
- int sf = d->sendflags[i];
- int ent = d->entno[i];
- if(sf < 0) // remove
- {
- recoversendflags[ent] = 0; // no need to update, we got a more recent remove (and will fix it THEN)
- break; // no flags left to remove...
- }
- else if(sf > 0)
- recoversendflags[ent] &= ~sf; // no need to update these bits, we already got them later
- }
- }
- }
-
- for(i = 0; i < client->csqcnumedicts; ++i)
- {
- if(recoversendflags[i] < 0)
- client->csqcentityscope[i] |= SCOPE_ASSUMED_EXISTING; // FORCE REMOVE.
- else
- client->csqcentitysendflags[i] |= recoversendflags[i];
- }
-}
-static int EntityFrameCSQC_AllocFrame(client_t *client, int framenum)
-{
- int ringfirst = client->csqcentityframehistory_next; // oldest entry
- client->csqcentityframehistory_next += 1;
- client->csqcentityframehistory_next %= NUM_CSQCENTITYDB_FRAMES;
- client->csqcentityframehistory[ringfirst].framenum = framenum;
- client->csqcentityframehistory[ringfirst].num = 0;
- return ringfirst;
-}
-static void EntityFrameCSQC_DeallocFrame(client_t *client, int framenum)
-{
- int ringfirst = client->csqcentityframehistory_next; // oldest entry
- int ringlast = (ringfirst + NUM_CSQCENTITYDB_FRAMES - 1) % NUM_CSQCENTITYDB_FRAMES; // most recently added entry
- if(framenum == client->csqcentityframehistory[ringlast].framenum)
- {
- client->csqcentityframehistory[ringlast].framenum = -1;
- client->csqcentityframehistory[ringlast].num = 0;
- client->csqcentityframehistory_next = ringlast;
- }
- else
- Con_Printf("Trying to dealloc the wrong entity frame\n");
-}
-
-//[515]: we use only one array per-client for SendEntity feature
-// TODO: add some handling for entity send priorities, to better deal with huge
-// amounts of csqc networked entities
-qboolean EntityFrameCSQC_WriteFrame (sizebuf_t *msg, int maxsize, int numnumbers, const unsigned short *numbers, int framenum)
-{
- prvm_prog_t *prog = SVVM_prog;
- int num, number, end, sendflags;
- qboolean sectionstarted = false;
- const unsigned short *n;
- prvm_edict_t *ed;
- client_t *client = svs.clients + sv.writeentitiestoclient_clientnumber;
- int dbframe = EntityFrameCSQC_AllocFrame(client, framenum);
- csqcentityframedb_t *db = &client->csqcentityframehistory[dbframe];
-
- if(client->csqcentityframe_lastreset < 0)
- client->csqcentityframe_lastreset = framenum;
-
- maxsize -= 24; // always fit in an empty svc_entities message (for packet loss detection!)
-
- // make sure there is enough room to store the svc_csqcentities byte,
- // the terminator (0x0000) and at least one entity update
- if (msg->cursize + 32 >= maxsize)
- return false;
-
- if (client->csqcnumedicts < prog->num_edicts)
- client->csqcnumedicts = prog->num_edicts;
-
- number = 1;
- for (num = 0, n = numbers;num < numnumbers;num++, n++)
- {
- end = *n;
- for (;number < end;number++)
- {
- client->csqcentityscope[number] &= ~SCOPE_WANTSEND;
- if (client->csqcentityscope[number] & SCOPE_ASSUMED_EXISTING)
- client->csqcentityscope[number] |= SCOPE_WANTREMOVE;
- client->csqcentitysendflags[number] = 0xFFFFFF;
- }
- ed = prog->edicts + number;
- client->csqcentityscope[number] &= ~SCOPE_WANTSEND;
- if (PRVM_serveredictfunction(ed, SendEntity))
- client->csqcentityscope[number] |= SCOPE_WANTUPDATE;
- else
- {
- if (client->csqcentityscope[number] & SCOPE_ASSUMED_EXISTING)
- client->csqcentityscope[number] |= SCOPE_WANTREMOVE;
- client->csqcentitysendflags[number] = 0xFFFFFF;
- }
- number++;
- }
- end = client->csqcnumedicts;
- for (;number < end;number++)
- {
- client->csqcentityscope[number] &= ~SCOPE_WANTSEND;
- if (client->csqcentityscope[number] & SCOPE_ASSUMED_EXISTING)
- client->csqcentityscope[number] |= SCOPE_WANTREMOVE;
- client->csqcentitysendflags[number] = 0xFFFFFF;
- }
-
- // now try to emit the entity updates
- // (FIXME: prioritize by distance?)
- end = client->csqcnumedicts;
- for (number = 1;number < end;number++)
- {
- if (!(client->csqcentityscope[number] & SCOPE_WANTSEND))
- continue;
- if(db->num >= NUM_CSQCENTITIES_PER_FRAME)
- break;
- ed = prog->edicts + number;
- if (client->csqcentityscope[number] & SCOPE_WANTREMOVE) // Also implies ASSUMED_EXISTING.
- {
- // A removal. SendFlags have no power here.
- // write a remove message
- // first write the message identifier if needed
- if(!sectionstarted)
- {
- sectionstarted = 1;
- MSG_WriteByte(msg, svc_csqcentities);
- }
- // write the remove message
- {
- ENTITYSIZEPROFILING_START(msg, number, 0);
- MSG_WriteShort(msg, (unsigned short)number | 0x8000);
- client->csqcentityscope[number] &= ~(SCOPE_WANTSEND | SCOPE_ASSUMED_EXISTING);
- client->csqcentitysendflags[number] = 0xFFFFFF; // resend completely if it becomes active again
- db->entno[db->num] = number;
- db->sendflags[db->num] = -1;
- db->num += 1;
- ENTITYSIZEPROFILING_END(msg, number, 0);
- }
- if (msg->cursize + 17 >= maxsize)
- break;
- }
- else
- {
- // save the cursize value in case we overflow and have to rollback
- int oldcursize = msg->cursize;
-
- // An update.
- sendflags = client->csqcentitysendflags[number];
- // Nothing to send? FINE.
- if (!sendflags)
- continue;
- // If it's a new entity, always assume sendflags 0xFFFFFF.
- if (!(client->csqcentityscope[number] & SCOPE_ASSUMED_EXISTING))
- sendflags = 0xFFFFFF;
-
- // write an update
- if (PRVM_serveredictfunction(ed, SendEntity))
- {
- if(!sectionstarted)
- MSG_WriteByte(msg, svc_csqcentities);
- {
- int oldcursize2 = msg->cursize;
- ENTITYSIZEPROFILING_START(msg, number, sendflags);
- MSG_WriteShort(msg, number);
- msg->allowoverflow = true;
- PRVM_G_INT(OFS_PARM0) = sv.writeentitiestoclient_cliententitynumber;
- PRVM_G_FLOAT(OFS_PARM1) = sendflags;
- PRVM_serverglobaledict(self) = number;
- prog->ExecuteProgram(prog, PRVM_serveredictfunction(ed, SendEntity), "Null SendEntity\n");
- msg->allowoverflow = false;
- if(!PRVM_G_FLOAT(OFS_RETURN))
- {
- // Send rejected by CSQC. This means we want to remove it.
- // CSQC requests we remove this one.
- if (client->csqcentityscope[number] & SCOPE_ASSUMED_EXISTING)
- {
- msg->cursize = oldcursize2;
- msg->overflowed = false;
- MSG_WriteShort(msg, (unsigned short)number | 0x8000);
- client->csqcentityscope[number] &= ~(SCOPE_WANTSEND | SCOPE_ASSUMED_EXISTING);
- client->csqcentitysendflags[number] = 0;
- db->entno[db->num] = number;
- db->sendflags[db->num] = -1;
- db->num += 1;
- // and take note that we have begun the svc_csqcentities
- // section of the packet
- sectionstarted = 1;
- ENTITYSIZEPROFILING_END(msg, number, 0);
- if (msg->cursize + 17 >= maxsize)
- break;
- }
- else
- {
- // Nothing to do. Just don't do it again.
- msg->cursize = oldcursize;
- msg->overflowed = false;
- client->csqcentityscope[number] &= ~SCOPE_WANTSEND;
- client->csqcentitysendflags[number] = 0;
- }
- continue;
- }
- else if(PRVM_G_FLOAT(OFS_RETURN) && msg->cursize + 2 <= maxsize)
- {
- // an update has been successfully written
- client->csqcentitysendflags[number] = 0;
- db->entno[db->num] = number;
- db->sendflags[db->num] = sendflags;
- db->num += 1;
- client->csqcentityscope[number] &= ~SCOPE_WANTSEND;
- client->csqcentityscope[number] |= SCOPE_EXISTED_ONCE | SCOPE_ASSUMED_EXISTING;
- // and take note that we have begun the svc_csqcentities
- // section of the packet
- sectionstarted = 1;
- ENTITYSIZEPROFILING_END(msg, number, sendflags);
- if (msg->cursize + 17 >= maxsize)
- break;
- continue;
- }
- }
- }
- // self.SendEntity returned false (or does not exist) or the
- // update was too big for this packet - rollback the buffer to its
- // state before the writes occurred, we'll try again next frame
- msg->cursize = oldcursize;
- msg->overflowed = false;
- }
- }
- if (sectionstarted)
- {
- // write index 0 to end the update (0 is never used by real entities)
- MSG_WriteShort(msg, 0);
- }
-
- if(db->num == 0)
- // if no single ent got added, remove the frame from the DB again, to allow
- // for a larger history
- EntityFrameCSQC_DeallocFrame(client, framenum);
-
- return sectionstarted;
-}
-
void Protocol_UpdateClientStats(const int *stats)
{
int i;
}
}
-
-qboolean EntityFrameQuake_WriteFrame(sizebuf_t *msg, int maxsize, int numstates, const entity_state_t **states)
-{
- prvm_prog_t *prog = SVVM_prog;
- const entity_state_t *s;
- entity_state_t baseline;
- int i, bits;
- sizebuf_t buf;
- unsigned char data[128];
- qboolean success = false;
-
- // prepare the buffer
- memset(&buf, 0, sizeof(buf));
- buf.data = data;
- buf.maxsize = sizeof(data);
-
- for (i = 0;i < numstates;i++)
- {
- s = states[i];
- if(PRVM_serveredictfunction((&prog->edicts[s->number]), SendEntity))
- continue;
-
- // prepare the buffer
- SZ_Clear(&buf);
-
-// send an update
- bits = 0;
- if (s->number >= 256)
- bits |= U_LONGENTITY;
- if (s->flags & RENDER_STEP)
- bits |= U_STEP;
- if (s->flags & RENDER_VIEWMODEL)
- bits |= U_VIEWMODEL;
- if (s->flags & RENDER_GLOWTRAIL)
- bits |= U_GLOWTRAIL;
- if (s->flags & RENDER_EXTERIORMODEL)
- bits |= U_EXTERIORMODEL;
-
- // LadyHavoc: old stuff, but rewritten to have more exact tolerances
- baseline = prog->edicts[s->number].priv.server->baseline;
- if (baseline.origin[0] != s->origin[0])
- bits |= U_ORIGIN1;
- if (baseline.origin[1] != s->origin[1])
- bits |= U_ORIGIN2;
- if (baseline.origin[2] != s->origin[2])
- bits |= U_ORIGIN3;
- if (baseline.angles[0] != s->angles[0])
- bits |= U_ANGLE1;
- if (baseline.angles[1] != s->angles[1])
- bits |= U_ANGLE2;
- if (baseline.angles[2] != s->angles[2])
- bits |= U_ANGLE3;
- if (baseline.colormap != s->colormap)
- bits |= U_COLORMAP;
- if (baseline.skin != s->skin)
- bits |= U_SKIN;
- if (baseline.frame != s->frame)
- {
- bits |= U_FRAME;
- if (s->frame & 0xFF00)
- bits |= U_FRAME2;
- }
- if (baseline.effects != s->effects)
- {
- bits |= U_EFFECTS;
- if (s->effects & 0xFF00)
- bits |= U_EFFECTS2;
- }
- if (baseline.modelindex != s->modelindex)
- {
- bits |= U_MODEL;
- if ((s->modelindex & 0xFF00) && sv.protocol != PROTOCOL_NEHAHRABJP && sv.protocol != PROTOCOL_NEHAHRABJP2 && sv.protocol != PROTOCOL_NEHAHRABJP3)
- bits |= U_MODEL2;
- }
- if (baseline.alpha != s->alpha)
- bits |= U_ALPHA;
- if (baseline.scale != s->scale)
- bits |= U_SCALE;
- if (baseline.glowsize != s->glowsize)
- bits |= U_GLOWSIZE;
- if (baseline.glowcolor != s->glowcolor)
- bits |= U_GLOWCOLOR;
- if (!VectorCompare(baseline.colormod, s->colormod))
- bits |= U_COLORMOD;
-
- // if extensions are disabled, clear the relevant update flags
- if (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_NEHAHRAMOVIE)
- bits &= 0x7FFF;
- if (sv.protocol == PROTOCOL_NEHAHRAMOVIE)
- if (s->alpha != 255 || s->effects & EF_FULLBRIGHT)
- bits |= U_EXTEND1;
-
- // write the message
- if (bits >= 16777216)
- bits |= U_EXTEND2;
- if (bits >= 65536)
- bits |= U_EXTEND1;
- if (bits >= 256)
- bits |= U_MOREBITS;
- bits |= U_SIGNAL;
-
- {
- ENTITYSIZEPROFILING_START(msg, states[i]->number, bits);
-
- MSG_WriteByte (&buf, bits);
- if (bits & U_MOREBITS) MSG_WriteByte(&buf, bits>>8);
- if (sv.protocol != PROTOCOL_NEHAHRAMOVIE)
- {
- if (bits & U_EXTEND1) MSG_WriteByte(&buf, bits>>16);
- if (bits & U_EXTEND2) MSG_WriteByte(&buf, bits>>24);
- }
- if (bits & U_LONGENTITY) MSG_WriteShort(&buf, s->number);
- else MSG_WriteByte(&buf, s->number);
-
- if (bits & U_MODEL)
- {
- if (sv.protocol == PROTOCOL_NEHAHRABJP || sv.protocol == PROTOCOL_NEHAHRABJP2 || sv.protocol == PROTOCOL_NEHAHRABJP3)
- MSG_WriteShort(&buf, s->modelindex);
- else
- MSG_WriteByte(&buf, s->modelindex);
- }
- if (bits & U_FRAME) MSG_WriteByte(&buf, s->frame);
- if (bits & U_COLORMAP) MSG_WriteByte(&buf, s->colormap);
- if (bits & U_SKIN) MSG_WriteByte(&buf, s->skin);
- if (bits & U_EFFECTS) MSG_WriteByte(&buf, s->effects);
- if (bits & U_ORIGIN1) MSG_WriteCoord(&buf, s->origin[0], sv.protocol);
- if (bits & U_ANGLE1) MSG_WriteAngle(&buf, s->angles[0], sv.protocol);
- if (bits & U_ORIGIN2) MSG_WriteCoord(&buf, s->origin[1], sv.protocol);
- if (bits & U_ANGLE2) MSG_WriteAngle(&buf, s->angles[1], sv.protocol);
- if (bits & U_ORIGIN3) MSG_WriteCoord(&buf, s->origin[2], sv.protocol);
- if (bits & U_ANGLE3) MSG_WriteAngle(&buf, s->angles[2], sv.protocol);
- if (bits & U_ALPHA) MSG_WriteByte(&buf, s->alpha);
- if (bits & U_SCALE) MSG_WriteByte(&buf, s->scale);
- if (bits & U_EFFECTS2) MSG_WriteByte(&buf, s->effects >> 8);
- if (bits & U_GLOWSIZE) MSG_WriteByte(&buf, s->glowsize);
- if (bits & U_GLOWCOLOR) MSG_WriteByte(&buf, s->glowcolor);
- if (bits & U_COLORMOD) {int c = ((int)bound(0, s->colormod[0] * (7.0f / 32.0f), 7) << 5) | ((int)bound(0, s->colormod[1] * (7.0f / 32.0f), 7) << 2) | ((int)bound(0, s->colormod[2] * (3.0f / 32.0f), 3) << 0);MSG_WriteByte(&buf, c);}
- if (bits & U_FRAME2) MSG_WriteByte(&buf, s->frame >> 8);
- if (bits & U_MODEL2) MSG_WriteByte(&buf, s->modelindex >> 8);
-
- // the nasty protocol
- if ((bits & U_EXTEND1) && sv.protocol == PROTOCOL_NEHAHRAMOVIE)
- {
- if (s->effects & EF_FULLBRIGHT)
- {
- MSG_WriteFloat(&buf, 2); // QSG protocol version
- MSG_WriteFloat(&buf, s->alpha <= 0 ? 0 : (s->alpha >= 255 ? 1 : s->alpha * (1.0f / 255.0f))); // alpha
- MSG_WriteFloat(&buf, 1); // fullbright
- }
- else
- {
- MSG_WriteFloat(&buf, 1); // QSG protocol version
- MSG_WriteFloat(&buf, s->alpha <= 0 ? 0 : (s->alpha >= 255 ? 1 : s->alpha * (1.0f / 255.0f))); // alpha
- }
- }
-
- // if the commit is full, we're done this frame
- if (msg->cursize + buf.cursize > maxsize)
- {
- // next frame we will continue where we left off
- break;
- }
- // write the message to the packet
- SZ_Write(msg, buf.data, buf.cursize);
- success = true;
- ENTITYSIZEPROFILING_END(msg, s->number, bits);
- }
- }
- return success;
-}
-
int EntityState_DeltaBits(const entity_state_t *o, const entity_state_t *n)
{
unsigned int bits;
}
}
-// (client) adds a entity_frame to the database, for future reference
-void EntityFrame_AddFrame_Client(entityframe_database_t *d, vec3_t eye, int framenum, int numentities, const entity_state_t *entitydata)
-{
- int n, e;
- entity_frameinfo_t *info;
-
- VectorCopy(eye, d->eye);
-
- // 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 + 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 = framenum;
- e = -1000;
- // make sure we check the newly added frame as well, but we haven't incremented numframes yet
- for (n = 0;n <= d->numframes;n++)
- {
- if (e >= d->frames[n].framenum)
- {
- if (e == framenum)
- Con_Print("EntityFrame_AddFrame: tried to add out of sequence frame to database\n");
- else
- Con_Print("EntityFrame_AddFrame: out of sequence frames in database\n");
- return;
- }
- e = d->frames[n].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 + numentities;
- d->numframes++;
-
- n = info->firstentity % MAX_ENTITY_DATABASE;
- e = MAX_ENTITY_DATABASE - n;
- if (e > numentities)
- e = numentities;
- memcpy(d->entitydata + n, entitydata, sizeof(entity_state_t) * e);
- if (numentities > e)
- memcpy(d->entitydata, entitydata + e, sizeof(entity_state_t) * (numentities - e));
-}
-
// (server) adds a entity_frame to the database, for future reference
void EntityFrame_AddFrame_Server(entityframe_database_t *d, vec3_t eye, int framenum, int numentities, const entity_state_t **entitydata)
{
return true;
}
-// (client) reads a frame from network stream
-void EntityFrame_CL_ReadFrame(void)
-{
- int i, number, removed;
- entity_frame_t *f, *delta;
- entity_state_t *e, *old, *oldend;
- entity_t *ent;
- entityframe_database_t *d;
- if (!cl.entitydatabase)
- cl.entitydatabase = EntityFrame_AllocDatabase(cls.levelmempool);
- d = cl.entitydatabase;
- f = &d->framedata;
- delta = &d->deltaframe;
-
- EntityFrame_Clear(f, NULL, -1);
-
- // read the frame header info
- f->time = cl.mtime[0];
- number = MSG_ReadLong(&cl_message);
- f->framenum = MSG_ReadLong(&cl_message);
- CL_NewFrameReceived(f->framenum);
- f->eye[0] = MSG_ReadFloat(&cl_message);
- f->eye[1] = MSG_ReadFloat(&cl_message);
- f->eye[2] = MSG_ReadFloat(&cl_message);
- EntityFrame_AckFrame(d, number);
- EntityFrame_FetchFrame(d, number, delta);
- old = delta->entitydata;
- oldend = old + delta->numentities;
- // read entities until we hit the magic 0xFFFF end tag
- while ((number = (unsigned short) MSG_ReadShort(&cl_message)) != 0xFFFF && !cl_message.badread)
- {
- if (cl_message.badread)
- Host_Error("EntityFrame_Read: read error");
- removed = number & 0x8000;
- number &= 0x7FFF;
- if (number >= MAX_EDICTS)
- Host_Error("EntityFrame_Read: number (%i) >= MAX_EDICTS (%i)", number, MAX_EDICTS);
-
- // seek to entity, while copying any skipped entities (assume unchanged)
- while (old < oldend && old->number < number)
- {
- if (f->numentities >= MAX_ENTITY_DATABASE)
- Host_Error("EntityFrame_Read: entity list too big");
- f->entitydata[f->numentities] = *old++;
- f->entitydata[f->numentities++].time = cl.mtime[0];
- }
- if (removed)
- {
- if (old < oldend && old->number == number)
- old++;
- else
- Con_Printf("EntityFrame_Read: REMOVE on unused entity %i\n", number);
- }
- else
- {
- if (f->numentities >= MAX_ENTITY_DATABASE)
- Host_Error("EntityFrame_Read: entity list too big");
-
- // reserve this slot
- e = f->entitydata + f->numentities++;
-
- if (old < oldend && old->number == number)
- {
- // delta from old entity
- *e = *old++;
- }
- else
- {
- // delta from defaults
- *e = defaultstate;
- }
-
- if (cl.num_entities <= number)
- {
- cl.num_entities = number + 1;
- if (number >= cl.max_entities)
- CL_ExpandEntities(number);
- }
- cl.entities_active[number] = true;
- e->active = ACTIVE_NETWORK;
- e->time = cl.mtime[0];
- e->number = number;
- EntityState_ReadFields(e, EntityState_ReadExtendBits());
- }
- }
- while (old < oldend)
- {
- if (f->numentities >= MAX_ENTITY_DATABASE)
- Host_Error("EntityFrame_Read: entity list too big");
- f->entitydata[f->numentities] = *old++;
- f->entitydata[f->numentities++].time = cl.mtime[0];
- }
- EntityFrame_AddFrame_Client(d, f->eye, f->framenum, f->numentities, f->entitydata);
-
- memset(cl.entities_active, 0, cl.num_entities * sizeof(unsigned char));
- number = 1;
- for (i = 0;i < f->numentities;i++)
- {
- for (;number < f->entitydata[i].number && number < cl.num_entities;number++)
- {
- if (cl.entities_active[number])
- {
- cl.entities_active[number] = false;
- cl.entities[number].state_current.active = ACTIVE_NOT;
- }
- }
- if (number >= cl.num_entities)
- break;
- // update the entity
- ent = &cl.entities[number];
- ent->state_previous = ent->state_current;
- ent->state_current = f->entitydata[i];
- CL_MoveLerpEntityStates(ent);
- // the entity lives again...
- cl.entities_active[number] = true;
- number++;
- }
- for (;number < cl.num_entities;number++)
- {
- if (cl.entities_active[number])
- {
- cl.entities_active[number] = false;
- cl.entities[number].state_current.active = ACTIVE_NOT;
- }
- }
-}
-
-
-// (client) returns the frame number of the most recent frame recieved
-int EntityFrame_MostRecentlyRecievedFrameNum(entityframe_database_t *d)
-{
- if (d->numframes)
- return d->frames[d->numframes - 1].framenum;
- else
- return -1;
-}
-
-
-
-
-
-
entity_state_t *EntityFrame4_GetReferenceEntity(entityframe4_database_t *d, int number)
{
if (d->maxreferenceentities <= number)