From 55fdd472aa5f61698dbc7fc20406d5342dde3648 Mon Sep 17 00:00:00 2001 From: havoc Date: Sun, 21 Oct 2007 11:21:47 +0000 Subject: [PATCH] reverted the cleanup of entity state building because it was sapping massive amounts of performance even with only 16 players, now it once again builds them only once per frame and filters them per client git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@7648 d7cf8633-e32d-0410-b094-e92efae38249 --- protocol.c | 10 +- protocol.h | 10 +- server.h | 8 ++ sv_main.c | 393 ++++++++++++++++++++++++++++++----------------------- 4 files changed, 250 insertions(+), 171 deletions(-) diff --git a/protocol.c b/protocol.c index bc21122d..71cfa159 100644 --- a/protocol.c +++ b/protocol.c @@ -9,11 +9,17 @@ entity_state_t defaultstate = {0,0,0},//float netcenter[3]; // ! for network prioritization, this is the center of the bounding box (which may differ from the origin) {0,0,0},//float origin[3]; {0,0,0},//float angles[3]; - 0,//int number; // entity number this state is for 0,//int effects; + 0,//unsigned int customizeentityforclient; // ! + 0,//unsigned short number; // entity number this state is for 0,//unsigned short modelindex; 0,//unsigned short frame; 0,//unsigned short tagentity; + 0,//unsigned short specialvisibilityradius; // ! larger if it has effects/light + 0,//unsigned short viewmodelforclient; // ! + 0,//unsigned short exteriormodelforclient; // ! not shown if first person viewing from this entity, shown in all other cases + 0,//unsigned short nodrawtoclient; // ! + 0,//unsigned short drawonlytoclient; // ! {0,0,0,0},//unsigned short light[4]; // color*256 (0.00 to 255.996), and radius*1 0,//unsigned char active; // true if a valid state 0,//unsigned char lightstyle; @@ -29,7 +35,7 @@ entity_state_t defaultstate = 0,//unsigned char tagindex; {32, 32, 32},//unsigned char colormod[3]; // padding to a multiple of 8 bytes (to align the double time) - 0//unsigned char unused; // ! + {0,0,0,0,0}//unsigned char unused[5]; // ! }; // LordHavoc: I own protocol ranges 96, 97, 3500-3599 diff --git a/protocol.h b/protocol.h index ddc9542c..a0f340b9 100644 --- a/protocol.h +++ b/protocol.h @@ -337,7 +337,7 @@ void Protocol_Names(char *buffer, size_t buffersize); #define RENDER_NOSELFSHADOW 262144 // render lighting on this entity before its own shadow is added to the scene // (note: all RENDER_NOSELFSHADOW entities are grouped together and rendered in a batch before their shadows are rendered, so they can not shadow eachother either) -// this is 80 bytes +// this is 96 bytes typedef struct entity_state_s { // ! means this is not sent to client @@ -346,10 +346,16 @@ typedef struct entity_state_s float origin[3]; float angles[3]; int effects; + unsigned int customizeentityforclient; // ! unsigned short number; // entity number this state is for unsigned short modelindex; unsigned short frame; unsigned short tagentity; + unsigned short specialvisibilityradius; // ! larger if it has effects/light + unsigned short viewmodelforclient; // ! + unsigned short exteriormodelforclient; // ! not shown if first person viewing from this entity, shown in all other cases + unsigned short nodrawtoclient; // ! + unsigned short drawonlytoclient; // ! unsigned short light[4]; // color*256 (0.00 to 255.996), and radius*1 unsigned char active; // true if a valid state unsigned char lightstyle; @@ -365,7 +371,7 @@ typedef struct entity_state_s unsigned char tagindex; unsigned char colormod[3]; // padding to a multiple of 8 bytes (to align the double time) - unsigned char unused; + unsigned char unused[5]; } entity_state_t; diff --git a/server.h b/server.h index 16ddb66a..3e9b7119 100644 --- a/server.h +++ b/server.h @@ -139,6 +139,14 @@ typedef struct server_s int writeentitiestoclient_pvsbytes; unsigned char writeentitiestoclient_pvs[MAX_MAP_LEAFS/8]; entity_state_t writeentitiestoclient_sendstates[MAX_EDICTS]; + + int numsendentities; + entity_state_t sendentities[MAX_EDICTS]; + entity_state_t *sendentitiesindex[MAX_EDICTS]; + + int sententitiesmark; + int sententities[MAX_EDICTS]; + int sententitiesconsideration[MAX_EDICTS]; } server_t; // if defined this does ping smoothing, otherwise it does not diff --git a/sv_main.c b/sv_main.c index b2afd263..4f0dce0b 100644 --- a/sv_main.c +++ b/sv_main.c @@ -916,29 +916,16 @@ crosses a waterline. ============================================================================= */ -static qboolean SV_BuildEntityState (entity_state_t *cs, prvm_edict_t *ent, int enumber) +static qboolean SV_PrepareEntityForSending (prvm_edict_t *ent, entity_state_t *cs, int enumber) { int i; - unsigned int tagentity; unsigned int modelindex, effects, flags, glowsize, lightstyle, lightpflags, light[4], specialvisibilityradius; unsigned int customizeentityforclient; float f; - vec3_t cullmins, cullmaxs, netcenter; + vec3_t cullmins, cullmaxs; model_t *model; prvm_eval_t *val; - // see if the customizeentityforclient extension is used by this entity - customizeentityforclient = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.customizeentityforclient)->function; - if (customizeentityforclient) - { - prog->globals.server->self = enumber; - prog->globals.server->other = sv.writeentitiestoclient_cliententitynumber; - PRVM_ExecuteProgram(customizeentityforclient, "customizeentityforclient: NULL function"); - // customizeentityforclient can return false to reject the entity - if (!PRVM_G_FLOAT(OFS_RETURN)) - return false; - } - // this 2 billion unit check is actually to detect NAN origins // (we really don't want to send those) if (!(VectorLength2(ent->fields.server->origin) < 2000000000.0*2000000000.0)) @@ -1020,73 +1007,137 @@ static qboolean SV_BuildEntityState (entity_state_t *cs, prvm_edict_t *ent, int specialvisibilityradius = max(specialvisibilityradius, 100); } - // don't send uninteresting entities - if (enumber != sv.writeentitiestoclient_cliententitynumber) + // early culling checks + // (final culling is done by SV_MarkWriteEntityStateToClient) + customizeentityforclient = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.customizeentityforclient)->function; + if (!customizeentityforclient && enumber > svs.maxclients && (!modelindex && !specialvisibilityradius)) + return false; + + *cs = defaultstate; + cs->active = true; + cs->number = enumber; + VectorCopy(ent->fields.server->origin, cs->origin); + VectorCopy(ent->fields.server->angles, cs->angles); + cs->flags = flags; + cs->effects = effects; + cs->colormap = (unsigned)ent->fields.server->colormap; + cs->modelindex = modelindex; + cs->skin = (unsigned)ent->fields.server->skin; + cs->frame = (unsigned)ent->fields.server->frame; + cs->viewmodelforclient = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.viewmodelforclient)->edict; + cs->exteriormodelforclient = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.exteriormodeltoclient)->edict; + cs->nodrawtoclient = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.nodrawtoclient)->edict; + cs->drawonlytoclient = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.drawonlytoclient)->edict; + cs->customizeentityforclient = customizeentityforclient; + cs->tagentity = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.tag_entity)->edict; + cs->tagindex = (unsigned char)PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.tag_index)->_float; + cs->glowsize = glowsize; + + // don't need to init cs->colormod because the defaultstate did that for us + //cs->colormod[0] = cs->colormod[1] = cs->colormod[2] = 32; + val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.colormod); + if (val->vector[0] || val->vector[1] || val->vector[2]) { - if (!modelindex && !specialvisibilityradius) - return false; - if (PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.nodrawtoclient)->edict == sv.writeentitiestoclient_cliententitynumber) - return false; - if (PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.drawonlytoclient)->edict && PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.drawonlytoclient)->edict != sv.writeentitiestoclient_cliententitynumber) - return false; - if (PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.viewmodelforclient)->edict && PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.viewmodelforclient)->edict != sv.writeentitiestoclient_cliententitynumber) - return false; - if (flags & RENDER_VIEWMODEL && PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.viewmodelforclient)->edict != sv.writeentitiestoclient_cliententitynumber) - return false; - if (effects & EF_NODRAW) - return false; + i = (int)(val->vector[0] * 32.0f);cs->colormod[0] = bound(0, i, 255); + i = (int)(val->vector[1] * 32.0f);cs->colormod[1] = bound(0, i, 255); + i = (int)(val->vector[2] * 32.0f);cs->colormod[2] = bound(0, i, 255); } - // don't send child if parent was rejected - // FIXME: it would be better to force the parent to send... - tagentity = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.tag_entity)->edict; - if (tagentity && !SV_BuildEntityState(NULL, PRVM_EDICT_NUM(tagentity), tagentity)) - return false; + cs->modelindex = modelindex; + + cs->alpha = 255; + f = (PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.alpha)->_float * 255.0f); + if (f) + { + i = (int)f; + cs->alpha = (unsigned char)bound(0, i, 255); + } + // halflife + f = (PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.renderamt)->_float); + if (f) + { + i = (int)f; + cs->alpha = (unsigned char)bound(0, i, 255); + } + + cs->scale = 16; + f = (PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.scale)->_float * 16.0f); + if (f) + { + i = (int)f; + cs->scale = (unsigned char)bound(0, i, 255); + } + + cs->glowcolor = 254; + f = (PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.glow_color)->_float); + if (f) + cs->glowcolor = (int)f; + + if (PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.fullbright)->_float) + cs->effects |= EF_FULLBRIGHT; + + val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.modelflags); + if (val && val->_float) + cs->effects |= ((unsigned int)val->_float & 0xff) << 24; + + if (ent->fields.server->movetype == MOVETYPE_STEP) + cs->flags |= RENDER_STEP; + if (cs->number != sv.writeentitiestoclient_cliententitynumber && (cs->effects & EF_LOWPRECISION) && cs->origin[0] >= -32768 && cs->origin[1] >= -32768 && cs->origin[2] >= -32768 && cs->origin[0] <= 32767 && cs->origin[1] <= 32767 && cs->origin[2] <= 32767) + cs->flags |= RENDER_LOWPRECISION; + if (ent->fields.server->colormap >= 1024) + cs->flags |= RENDER_COLORMAPPED; + if (cs->viewmodelforclient) + cs->flags |= RENDER_VIEWMODEL; // show relative to the view + + cs->light[0] = light[0]; + cs->light[1] = light[1]; + cs->light[2] = light[2]; + cs->light[3] = light[3]; + cs->lightstyle = lightstyle; + cs->lightpflags = lightpflags; + + cs->specialvisibilityradius = specialvisibilityradius; // calculate the visible box of this entity (don't use the physics box // as that is often smaller than a model, and would not count // specialvisibilityradius) if ((model = sv.models[modelindex])) { - float scale = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.glow_color)->_float; - if (scale) - scale *= (1.0f / 16.0f); - else - scale = 1; - if (ent->fields.server->angles[0] || ent->fields.server->angles[2]) // pitch and roll + float scale = cs->scale * (1.0f / 16.0f); + if (cs->angles[0] || cs->angles[2]) // pitch and roll { - VectorMA(ent->fields.server->origin, scale, model->rotatedmins, cullmins); - VectorMA(ent->fields.server->origin, scale, model->rotatedmaxs, cullmaxs); + VectorMA(cs->origin, scale, model->rotatedmins, cullmins); + VectorMA(cs->origin, scale, model->rotatedmaxs, cullmaxs); } - else if (ent->fields.server->angles[1]) + else if (cs->angles[1]) { - VectorMA(ent->fields.server->origin, scale, model->yawmins, cullmins); - VectorMA(ent->fields.server->origin, scale, model->yawmaxs, cullmaxs); + VectorMA(cs->origin, scale, model->yawmins, cullmins); + VectorMA(cs->origin, scale, model->yawmaxs, cullmaxs); } else { - VectorMA(ent->fields.server->origin, scale, model->normalmins, cullmins); - VectorMA(ent->fields.server->origin, scale, model->normalmaxs, cullmaxs); + VectorMA(cs->origin, scale, model->normalmins, cullmins); + VectorMA(cs->origin, scale, model->normalmaxs, cullmaxs); } } else { // if there is no model (or it could not be loaded), use the physics box - VectorAdd(ent->fields.server->origin, ent->fields.server->mins, cullmins); - VectorAdd(ent->fields.server->origin, ent->fields.server->maxs, cullmaxs); + VectorAdd(cs->origin, ent->fields.server->mins, cullmins); + VectorAdd(cs->origin, ent->fields.server->maxs, cullmaxs); } if (specialvisibilityradius) { - cullmins[0] = min(cullmins[0], ent->fields.server->origin[0] - specialvisibilityradius); - cullmins[1] = min(cullmins[1], ent->fields.server->origin[1] - specialvisibilityradius); - cullmins[2] = min(cullmins[2], ent->fields.server->origin[2] - specialvisibilityradius); - cullmaxs[0] = max(cullmaxs[0], ent->fields.server->origin[0] + specialvisibilityradius); - cullmaxs[1] = max(cullmaxs[1], ent->fields.server->origin[1] + specialvisibilityradius); - cullmaxs[2] = max(cullmaxs[2], ent->fields.server->origin[2] + specialvisibilityradius); + cullmins[0] = min(cullmins[0], cs->origin[0] - specialvisibilityradius); + cullmins[1] = min(cullmins[1], cs->origin[1] - specialvisibilityradius); + cullmins[2] = min(cullmins[2], cs->origin[2] - specialvisibilityradius); + cullmaxs[0] = max(cullmaxs[0], cs->origin[0] + specialvisibilityradius); + cullmaxs[1] = max(cullmaxs[1], cs->origin[1] + specialvisibilityradius); + cullmaxs[2] = max(cullmaxs[2], cs->origin[2] + specialvisibilityradius); } // calculate center of bbox for network prioritization purposes - VectorMAM(0.5f, cullmins, 0.5f, cullmaxs, netcenter); + VectorMAM(0.5f, cullmins, 0.5f, cullmaxs, cs->netcenter); // if culling box has moved, update pvs cluster links if (!VectorCompare(cullmins, ent->priv.server->cullmins) || !VectorCompare(cullmaxs, ent->priv.server->cullmaxs)) @@ -1106,36 +1157,107 @@ static qboolean SV_BuildEntityState (entity_state_t *cs, prvm_edict_t *ent, int } } - if (enumber != sv.writeentitiestoclient_cliententitynumber && !(effects & EF_NODEPTHTEST) && !(flags & RENDER_VIEWMODEL) && !tagentity) + return true; +} + +void SV_PrepareEntitiesForSending(void) +{ + int e; + prvm_edict_t *ent; + // send all entities that touch the pvs + sv.numsendentities = 0; + sv.sendentitiesindex[0] = NULL; + memset(sv.sendentitiesindex, 0, prog->num_edicts * sizeof(*sv.sendentitiesindex)); + for (e = 1, ent = PRVM_NEXT_EDICT(prog->edicts);e < prog->num_edicts;e++, ent = PRVM_NEXT_EDICT(ent)) + { + if (!ent->priv.server->free && SV_PrepareEntityForSending(ent, sv.sendentities + sv.numsendentities, e)) + { + sv.sendentitiesindex[e] = sv.sendentities + sv.numsendentities; + sv.numsendentities++; + } + } +} + +void SV_MarkWriteEntityStateToClient(entity_state_t *s) +{ + int isbmodel; + model_t *model; + prvm_edict_t *ed; + if (sv.sententitiesconsideration[s->number] == sv.sententitiesmark) + return; + sv.sententitiesconsideration[s->number] = sv.sententitiesmark; + sv.writeentitiestoclient_stats_totalentities++; + + if (s->customizeentityforclient) + { + prog->globals.server->self = s->number; + prog->globals.server->other = sv.writeentitiestoclient_cliententitynumber; + PRVM_ExecuteProgram(s->customizeentityforclient, "customizeentityforclient: NULL function"); + if(!PRVM_G_FLOAT(OFS_RETURN) || !SV_PrepareEntityForSending(PRVM_EDICT_NUM(s->number), s, s->number)) + return; + } + + // never reject player + if (s->number != sv.writeentitiestoclient_cliententitynumber) { - qboolean isbmodel = (model = sv.models[modelindex]) != NULL && model->name[0] == '*'; - if (!isbmodel || !sv_cullentities_nevercullbmodels.integer) + // check various rejection conditions + if (s->nodrawtoclient == sv.writeentitiestoclient_cliententitynumber) + return; + if (s->drawonlytoclient && s->drawonlytoclient != sv.writeentitiestoclient_cliententitynumber) + return; + if (s->effects & EF_NODRAW) + return; + // LordHavoc: only send entities with a model or important effects + if (!s->modelindex && s->specialvisibilityradius == 0) + return; + + isbmodel = (model = sv.models[s->modelindex]) != NULL && model->name[0] == '*'; + // viewmodels don't have visibility checking + if (s->viewmodelforclient) { - // cull based on visibility + if (s->viewmodelforclient != sv.writeentitiestoclient_cliententitynumber) + return; + } + else if (s->tagentity) + { + // tag attached entities simply check their parent + if (!sv.sendentitiesindex[s->tagentity]) + return; + SV_MarkWriteEntityStateToClient(sv.sendentitiesindex[s->tagentity]); + if (sv.sententities[s->tagentity] != sv.sententitiesmark) + return; + } + // always send world submodels in newer protocols because they don't + // generate much traffic (in old protocols they hog bandwidth) + // but only if sv_cullentities_nevercullbmodels is off + else if (!(s->effects & EF_NODEPTHTEST) && (!isbmodel || !sv_cullentities_nevercullbmodels.integer || sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE)) + { + // entity has survived every check so far, check if visible + ed = PRVM_EDICT_NUM(s->number); // if not touching a visible leaf if (sv_cullentities_pvs.integer && sv.writeentitiestoclient_pvsbytes) { - if (ent->priv.server->pvs_numclusters < 0) + if (ed->priv.server->pvs_numclusters < 0) { // entity too big for clusters list - if (sv.worldmodel && sv.worldmodel->brush.BoxTouchingPVS && !sv.worldmodel->brush.BoxTouchingPVS(sv.worldmodel, sv.writeentitiestoclient_pvs, cullmins, cullmaxs)) + if (sv.worldmodel && sv.worldmodel->brush.BoxTouchingPVS && !sv.worldmodel->brush.BoxTouchingPVS(sv.worldmodel, sv.writeentitiestoclient_pvs, ed->priv.server->cullmins, ed->priv.server->cullmaxs)) { sv.writeentitiestoclient_stats_culled_pvs++; - return false; + return; } } else { int i; // check cached clusters list - for (i = 0;i < ent->priv.server->pvs_numclusters;i++) - if (CHECKPVSBIT(sv.writeentitiestoclient_pvs, ent->priv.server->pvs_clusterlist[i])) + for (i = 0;i < ed->priv.server->pvs_numclusters;i++) + if (CHECKPVSBIT(sv.writeentitiestoclient_pvs, ed->priv.server->pvs_clusterlist[i])) break; - if (i == ent->priv.server->pvs_numclusters) + if (i == ed->priv.server->pvs_numclusters) { sv.writeentitiestoclient_stats_culled_pvs++; - return false; + return; } } } @@ -1143,14 +1265,14 @@ static qboolean SV_BuildEntityState (entity_state_t *cs, prvm_edict_t *ent, int // or not seen by random tracelines if (sv_cullentities_trace.integer && !isbmodel) { - int samples = specialvisibilityradius ? sv_cullentities_trace_samples_extra.integer : sv_cullentities_trace_samples.integer; + int samples = s->specialvisibilityradius ? sv_cullentities_trace_samples_extra.integer : sv_cullentities_trace_samples.integer; float enlarge = sv_cullentities_trace_enlarge.value; - qboolean visible = true; + qboolean visible = TRUE; do { - if(Mod_CanSeeBox_Trace(samples, enlarge, sv.worldmodel, sv.writeentitiestoclient_testeye, cullmins, cullmaxs)) + if(Mod_CanSeeBox_Trace(samples, enlarge, sv.worldmodel, sv.writeentitiestoclient_testeye, ed->priv.server->cullmins, ed->priv.server->cullmaxs)) break; // directly visible from the server's view if(sv_cullentities_trace_prediction.integer) @@ -1165,7 +1287,7 @@ static qboolean SV_BuildEntityState (entity_state_t *cs, prvm_edict_t *ent, int VectorMA(sv.writeentitiestoclient_testeye, predtime, host_client->edict->fields.server->velocity, predeye); if(sv.worldmodel->brush.TraceLineOfSight(sv.worldmodel, sv.writeentitiestoclient_testeye, predeye)) // must be able to go there... { - if(Mod_CanSeeBox_Trace(samples, enlarge, sv.worldmodel, predeye, cullmins, cullmaxs)) + if(Mod_CanSeeBox_Trace(samples, enlarge, sv.worldmodel, predeye, ed->priv.server->cullmins, ed->priv.server->cullmaxs)) break; // directly visible from the predicted view } else @@ -1180,107 +1302,27 @@ static qboolean SV_BuildEntityState (entity_state_t *cs, prvm_edict_t *ent, int while(0); if(visible) - svs.clients[sv.writeentitiestoclient_clientnumber].visibletime[enumber] = realtime + sv_cullentities_trace_delay.value; - else if (realtime > svs.clients[sv.writeentitiestoclient_clientnumber].visibletime[enumber]) + svs.clients[sv.writeentitiestoclient_clientnumber].visibletime[s->number] = realtime + sv_cullentities_trace_delay.value; + else if (realtime > svs.clients[sv.writeentitiestoclient_clientnumber].visibletime[s->number]) { sv.writeentitiestoclient_stats_culled_trace++; - return false; + return; } } } } - // if the caller was just checking... return true - if (!cs) - return true; - - *cs = defaultstate; - cs->active = true; - cs->number = enumber; - VectorCopy(netcenter, cs->netcenter); - VectorCopy(ent->fields.server->origin, cs->origin); - VectorCopy(ent->fields.server->angles, cs->angles); - cs->flags = flags; - cs->effects = effects; - cs->colormap = (unsigned)ent->fields.server->colormap; - cs->modelindex = modelindex; - cs->skin = (unsigned)ent->fields.server->skin; - cs->frame = (unsigned)ent->fields.server->frame; - cs->tagentity = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.tag_entity)->edict; - cs->tagindex = (unsigned char)PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.tag_index)->_float; - cs->glowsize = glowsize; - - // don't need to init cs->colormod because the defaultstate did that for us - //cs->colormod[0] = cs->colormod[1] = cs->colormod[2] = 32; - val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.colormod); - if (val->vector[0] || val->vector[1] || val->vector[2]) - { - i = (int)(val->vector[0] * 32.0f);cs->colormod[0] = bound(0, i, 255); - i = (int)(val->vector[1] * 32.0f);cs->colormod[1] = bound(0, i, 255); - i = (int)(val->vector[2] * 32.0f);cs->colormod[2] = bound(0, i, 255); - } - - cs->modelindex = modelindex; - - cs->alpha = 255; - f = (PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.alpha)->_float * 255.0f); - if (f) - { - i = (int)f; - cs->alpha = (unsigned char)bound(0, i, 255); - } - // halflife - f = (PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.renderamt)->_float); - if (f) - { - i = (int)f; - cs->alpha = (unsigned char)bound(0, i, 255); - } - - cs->scale = 16; - f = (PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.scale)->_float * 16.0f); - if (f) - { - i = (int)f; - cs->scale = (unsigned char)bound(0, i, 255); - } - - cs->glowcolor = 254; - f = (PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.glow_color)->_float); - if (f) - cs->glowcolor = (int)f; - - if (PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.fullbright)->_float) - cs->effects |= EF_FULLBRIGHT; - - val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.modelflags); - if (val && val->_float) - cs->effects |= ((unsigned int)val->_float & 0xff) << 24; - - if (ent->fields.server->movetype == MOVETYPE_STEP) - cs->flags |= RENDER_STEP; - if (cs->number != sv.writeentitiestoclient_cliententitynumber && (cs->effects & EF_LOWPRECISION) && cs->origin[0] >= -32768 && cs->origin[1] >= -32768 && cs->origin[2] >= -32768 && cs->origin[0] <= 32767 && cs->origin[1] <= 32767 && cs->origin[2] <= 32767) - cs->flags |= RENDER_LOWPRECISION; - if (ent->fields.server->colormap >= 1024) - cs->flags |= RENDER_COLORMAPPED; - if (PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.glow_trail)->edict && PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.glow_trail)->edict == sv.writeentitiestoclient_cliententitynumber) - cs->flags |= RENDER_EXTERIORMODEL; - - cs->light[0] = light[0]; - cs->light[1] = light[1]; - cs->light[2] = light[2]; - cs->light[3] = light[3]; - cs->lightstyle = lightstyle; - cs->lightpflags = lightpflags; - - return true; + // this just marks it for sending + // FIXME: it would be more efficient to send here, but the entity + // compressor isn't that flexible + sv.writeentitiestoclient_stats_visibleentities++; + sv.sententities[s->number] = sv.sententitiesmark; } -static void SV_WriteEntitiesToClient(client_t *client, prvm_edict_t *clent, sizebuf_t *msg) +void SV_WriteEntitiesToClient(client_t *client, prvm_edict_t *clent, sizebuf_t *msg) { - int i; - int numsendstates; - prvm_edict_t *ent; + int i, numsendstates; + entity_state_t *s; // if there isn't enough space to accomplish anything, skip it if (msg->cursize + 25 > msg->maxsize) @@ -1303,11 +1345,22 @@ static void SV_WriteEntitiesToClient(client_t *client, prvm_edict_t *clent, size sv.writeentitiestoclient_cliententitynumber = PRVM_EDICT_TO_PROG(clent); // LordHavoc: for comparison purposes - // send all entities that touch the pvs + sv.sententitiesmark++; + + for (i = 0;i < sv.numsendentities;i++) + SV_MarkWriteEntityStateToClient(sv.sendentities + i); + numsendstates = 0; - for (i = 1, ent = PRVM_NEXT_EDICT(prog->edicts);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent)) - if (!ent->priv.server->free && SV_BuildEntityState(sv.writeentitiestoclient_sendstates + numsendstates, ent, i)) - numsendstates++; + for (i = 0;i < sv.numsendentities;i++) + { + if (sv.sententities[sv.sendentities[i].number] == sv.sententitiesmark) + { + s = &sv.writeentitiestoclient_sendstates[numsendstates++]; + *s = sv.sendentities[i]; + if (s->exteriormodelforclient && s->exteriormodelforclient == sv.writeentitiestoclient_cliententitynumber) + s->flags |= RENDER_EXTERIORMODEL; + } + } if (sv_cullentities_stats.integer) Con_Printf("client \"%s\" entities: %d total, %d visible, %d culled by: %d pvs %d trace\n", client->name, sv.writeentitiestoclient_stats_totalentities, sv.writeentitiestoclient_stats_visibleentities, sv.writeentitiestoclient_stats_culled_pvs + sv.writeentitiestoclient_stats_culled_trace, sv.writeentitiestoclient_stats_culled_pvs, sv.writeentitiestoclient_stats_culled_trace); @@ -1904,7 +1957,7 @@ SV_SendClientMessages */ void SV_SendClientMessages (void) { - int i; + int i, prepared = false; if (sv.protocol == PROTOCOL_QUAKEWORLD) Sys_Error("SV_SendClientMessages: no quakeworld support\n"); @@ -1928,6 +1981,12 @@ void SV_SendClientMessages (void) continue; } + if (!prepared) + { + prepared = true; + // only prepare entities once per frame + SV_PrepareEntitiesForSending(); + } SV_SendClientDatagram (host_client); } -- 2.39.2