cvar_t sv_clmovement_enable = {0, "sv_clmovement_enable", "1", "whether to allow clients to use cl_movement prediction, which can cause choppy movement on the server which may annoy other players"};
cvar_t sv_clmovement_minping = {0, "sv_clmovement_minping", "0", "if client ping is below this time in milliseconds, then their ability to use cl_movement prediction is disabled for a while (as they don't need it)"};
cvar_t sv_clmovement_minping_disabletime = {0, "sv_clmovement_minping_disabletime", "1000", "when client falls below minping, disable their prediction for this many milliseconds (should be at least 1000 or else their prediction may turn on/off frequently)"};
-cvar_t sv_clmovement_waitforinput = {0, "sv_clmovement_waitforinput", "16", "when a client does not send input for this many frames, force them to move anyway (unlike QuakeWorld)"};
+cvar_t sv_clmovement_waitforinput = {0, "sv_clmovement_waitforinput", "4", "when a client does not send input for this many frames, force them to move anyway (unlike QuakeWorld)"};
cvar_t sv_cullentities_nevercullbmodels = {0, "sv_cullentities_nevercullbmodels", "0", "if enabled the clients are always notified of moving doors and lifts and other submodels of world (warning: eats a lot of network bandwidth on some levels!)"};
cvar_t sv_cullentities_pvs = {0, "sv_cullentities_pvs", "1", "fast but loose culling of hidden entities"};
cvar_t sv_cullentities_stats = {0, "sv_cullentities_stats", "0", "displays stats on network entities culled by various methods for each client"};
}
else
MSG_WriteShort (&sv.datagram, (ent<<3) | channel);
- if (field_mask & SND_LARGESOUND)
+ if ((field_mask & SND_LARGESOUND) || sv.protocol == PROTOCOL_NEHAHRABJP2)
MSG_WriteShort (&sv.datagram, sound_num);
else
MSG_WriteByte (&sv.datagram, sound_num);
// LordHavoc: clear entityframe tracking
client->latestframenum = 0;
+ // initialize the movetime, so a speedhack can't make use of the time before this client joined
+ client->cmd.time = sv.time;
+
if (client->entitydatabase)
EntityFrame_FreeDatabase(client->entitydatabase);
if (client->entitydatabase4)
memset(client->stats, 0, sizeof(client->stats));
memset(client->statsdeltabits, 0, sizeof(client->statsdeltabits));
- if (sv.protocol != PROTOCOL_QUAKE && sv.protocol != PROTOCOL_QUAKEDP && sv.protocol != PROTOCOL_NEHAHRAMOVIE)
+ if (sv.protocol != PROTOCOL_QUAKE && sv.protocol != PROTOCOL_QUAKEDP && sv.protocol != PROTOCOL_NEHAHRAMOVIE && sv.protocol != PROTOCOL_NEHAHRABJP && sv.protocol != PROTOCOL_NEHAHRABJP2 && sv.protocol != PROTOCOL_NEHAHRABJP3)
{
if (sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3)
client->entitydatabase = EntityFrame_AllocDatabase(sv_mempool);
if (sv_allowdownloads.integer)
{
MSG_WriteByte (&client->netconnection->message, svc_stufftext);
- MSG_WriteString (&client->netconnection->message, "cl_serverextension_download 1");
+ MSG_WriteString (&client->netconnection->message, "cl_serverextension_download 1\n");
}
// send at this time so it's guaranteed to get executed at the right time
=============================================================================
*/
-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))
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))
}
}
- 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))
{
- qboolean isbmodel = (model = sv.models[modelindex]) != NULL && model->name[0] == '*';
- if (!isbmodel || !sv_cullentities_nevercullbmodels.integer)
+ if (!ent->priv.server->free && SV_PrepareEntityForSending(ent, sv.sendentities + sv.numsendentities, e))
{
- // cull based on visibility
+ 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)
+ {
+ // 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)
+ {
+ 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;
}
}
}
// 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)
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
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)
VectorAdd(clent->fields.server->origin, clent->fields.server->view_ofs, sv.writeentitiestoclient_testeye);
sv.writeentitiestoclient_pvsbytes = 0;
if (sv.worldmodel && sv.worldmodel->brush.FatPVS)
- sv.writeentitiestoclient_pvsbytes = sv.worldmodel->brush.FatPVS(sv.worldmodel, sv.writeentitiestoclient_testeye, 8, sv.writeentitiestoclient_pvs, sizeof(sv.writeentitiestoclient_pvs));
+ sv.writeentitiestoclient_pvsbytes = sv.worldmodel->brush.FatPVS(sv.worldmodel, sv.writeentitiestoclient_testeye, 8, sv.writeentitiestoclient_pvs, sizeof(sv.writeentitiestoclient_pvs), false);
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);
{
if (ent->fields.server->punchangle[i])
bits |= (SU_PUNCH1<<i);
- if (sv.protocol != PROTOCOL_QUAKE && sv.protocol != PROTOCOL_QUAKEDP && sv.protocol != PROTOCOL_NEHAHRAMOVIE)
+ if (sv.protocol != PROTOCOL_QUAKE && sv.protocol != PROTOCOL_QUAKEDP && sv.protocol != PROTOCOL_NEHAHRAMOVIE && sv.protocol != PROTOCOL_NEHAHRABJP && sv.protocol != PROTOCOL_NEHAHRABJP2 && sv.protocol != PROTOCOL_NEHAHRABJP3)
if (punchvector[i])
bits |= (SU_PUNCHVEC1<<i);
if (ent->fields.server->velocity[i])
statsf[STAT_FRAGLIMIT] = fraglimit.value;
statsf[STAT_TIMELIMIT] = timelimit.value;
- if (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4 || sv.protocol == PROTOCOL_DARKPLACES5)
+ if (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_NEHAHRABJP || sv.protocol == PROTOCOL_NEHAHRABJP2 || sv.protocol == PROTOCOL_NEHAHRABJP3 || sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4 || sv.protocol == PROTOCOL_DARKPLACES5)
{
if (stats[STAT_VIEWHEIGHT] != DEFAULT_VIEWHEIGHT) bits |= SU_VIEWHEIGHT;
bits |= SU_ITEMS;
{
if (bits & (SU_PUNCH1<<i))
{
- if (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE)
+ if (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_NEHAHRABJP || sv.protocol == PROTOCOL_NEHAHRABJP2 || sv.protocol == PROTOCOL_NEHAHRABJP3)
MSG_WriteChar(msg, (int)ent->fields.server->punchangle[i]);
else
MSG_WriteAngle16i(msg, ent->fields.server->punchangle[i]);
}
if (bits & (SU_VELOCITY1<<i))
{
- if (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4)
+ if (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_NEHAHRABJP || sv.protocol == PROTOCOL_NEHAHRABJP2 || sv.protocol == PROTOCOL_NEHAHRABJP3 || sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4)
MSG_WriteChar(msg, (int)(ent->fields.server->velocity[i] * (1.0f / 16.0f)));
else
MSG_WriteCoord32f(msg, ent->fields.server->velocity[i]);
if (bits & SU_VIEWZOOM)
MSG_WriteShort (msg, bound(0, stats[STAT_VIEWZOOM], 65535));
}
- else if (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4)
+ else if (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_NEHAHRABJP || sv.protocol == PROTOCOL_NEHAHRABJP2 || sv.protocol == PROTOCOL_NEHAHRABJP3 || sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4)
{
if (bits & SU_WEAPONFRAME)
MSG_WriteByte (msg, stats[STAT_WEAPONFRAME]);
if (bits & SU_ARMOR)
MSG_WriteByte (msg, stats[STAT_ARMOR]);
if (bits & SU_WEAPON)
- MSG_WriteByte (msg, stats[STAT_WEAPON]);
+ {
+ if (sv.protocol == PROTOCOL_NEHAHRABJP || sv.protocol == PROTOCOL_NEHAHRABJP2 || sv.protocol == PROTOCOL_NEHAHRABJP3)
+ MSG_WriteShort (msg, stats[STAT_WEAPON]);
+ else
+ MSG_WriteByte (msg, stats[STAT_WEAPON]);
+ }
MSG_WriteShort (msg, stats[STAT_HEALTH]);
MSG_WriteByte (msg, stats[STAT_AMMO]);
MSG_WriteByte (msg, stats[STAT_SHELLS]);
maxsize = sizeof(sv_sendclientdatagram_buf);
maxsize2 = sizeof(sv_sendclientdatagram_buf);
}
- else if (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4)
+ else if (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_NEHAHRABJP || sv.protocol == PROTOCOL_NEHAHRABJP2 || sv.protocol == PROTOCOL_NEHAHRABJP3 || sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4)
{
// no packet size limit support on older protocols because DP1-4 kick
// the client off if they overflow, and quake protocol shows less than
// frags
host_client->frags = (int)host_client->edict->fields.server->frags;
+ if(gamemode == GAME_NEXUIZ)
+ if(!host_client->spawned)
+ host_client->frags = -666;
if (host_client->old_frags != host_client->frags)
{
host_client->old_frags = host_client->frags;
*/
void SV_SendClientMessages (void)
{
- int i;
+ int i, prepared = false;
if (sv.protocol == PROTOCOL_QUAKEWORLD)
Sys_Error("SV_SendClientMessages: no quakeworld support\n");
continue;
}
+ if (!prepared)
+ {
+ prepared = true;
+ // only prepare entities once per frame
+ SV_PrepareEntitiesForSending();
+ }
SV_SendClientDatagram (host_client);
}
*/
int SV_ModelIndex(const char *s, int precachemode)
{
- int i, limit = ((sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE) ? 256 : MAX_MODELS);
+ int i, limit = ((sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_NEHAHRABJP || sv.protocol == PROTOCOL_NEHAHRABJP2 || sv.protocol == PROTOCOL_NEHAHRABJP3) ? 256 : MAX_MODELS);
char filename[MAX_QPATH];
if (!s || !*s)
return 0;
{
if (precachemode)
{
- if (sv.state != ss_loading && (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4 || sv.protocol == PROTOCOL_DARKPLACES5))
+ if (sv.state != ss_loading && (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_NEHAHRABJP || sv.protocol == PROTOCOL_NEHAHRABJP2 || sv.protocol == PROTOCOL_NEHAHRABJP3 || sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4 || sv.protocol == PROTOCOL_DARKPLACES5))
{
Con_Printf("SV_ModelIndex(\"%s\"): precache_model can only be done in spawn functions\n", filename);
return 0;
*/
int SV_SoundIndex(const char *s, int precachemode)
{
- int i, limit = ((sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE) ? 256 : MAX_SOUNDS);
+ int i, limit = ((sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_NEHAHRABJP || sv.protocol == PROTOCOL_NEHAHRABJP2 || sv.protocol == PROTOCOL_NEHAHRABJP3) ? 256 : MAX_SOUNDS);
char filename[MAX_QPATH];
if (!s || !*s)
return 0;
{
if (precachemode)
{
- if (sv.state != ss_loading && (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4 || sv.protocol == PROTOCOL_DARKPLACES5))
+ if (sv.state != ss_loading && (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_NEHAHRABJP || sv.protocol == PROTOCOL_NEHAHRABJP2 || sv.protocol == PROTOCOL_NEHAHRABJP3 || sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4 || sv.protocol == PROTOCOL_DARKPLACES5))
{
Con_Printf("SV_SoundIndex(\"%s\"): precache_sound can only be done in spawn functions\n", filename);
return 0;
argc = 0;
for (;;)
{
- if (!COM_ParseToken_Simple(&text, true) || !strcmp(com_token, "\n"))
+ if (!COM_ParseToken_Simple(&text, true, false) || !strcmp(com_token, "\n"))
break;
if (argc < 16)
{
large = false;
if (svent->priv.server->baseline.modelindex & 0xFF00 || svent->priv.server->baseline.frame & 0xFF00)
+ {
large = true;
+ if (sv.protocol == PROTOCOL_NEHAHRABJP || sv.protocol == PROTOCOL_NEHAHRABJP2 || sv.protocol == PROTOCOL_NEHAHRABJP3)
+ large = false;
+ }
// add to the message
if (large)
MSG_WriteShort (&sv.signon, svent->priv.server->baseline.modelindex);
MSG_WriteShort (&sv.signon, svent->priv.server->baseline.frame);
}
+ else if (sv.protocol == PROTOCOL_NEHAHRABJP || sv.protocol == PROTOCOL_NEHAHRABJP2 || sv.protocol == PROTOCOL_NEHAHRABJP3)
+ {
+ MSG_WriteShort (&sv.signon, svent->priv.server->baseline.modelindex);
+ MSG_WriteByte (&sv.signon, svent->priv.server->baseline.frame);
+ }
else
{
MSG_WriteByte (&sv.signon, svent->priv.server->baseline.modelindex);
Mod_PurgeUnused();
// create a baseline for more efficient communications
- if (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE)
+ if (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_NEHAHRABJP || sv.protocol == PROTOCOL_NEHAHRABJP2 || sv.protocol == PROTOCOL_NEHAHRABJP3)
SV_CreateBaseline ();
// send serverinfo to all connected clients, and set up botclients coming back from a level change
prog->limit_edicts = 2048; // guessing
else if (sv.protocol == PROTOCOL_NEHAHRAMOVIE)
prog->limit_edicts = 2048; // guessing!
+ else if (sv.protocol == PROTOCOL_NEHAHRABJP || sv.protocol == PROTOCOL_NEHAHRABJP2 || sv.protocol == PROTOCOL_NEHAHRABJP3)
+ prog->limit_edicts = 4096; // guessing!
else
prog->limit_edicts = MAX_EDICTS;
prog->reserved_edicts = svs.maxclients;