X-Git-Url: http://de.git.xonotic.org/?a=blobdiff_plain;f=sv_main.c;h=d4e7485c4fc59aad42a89a9b8cf1f4cefb0adce4;hb=cb16a7014e2bf0080ea13bd228e09a7e9e7e611e;hp=0db7e30363cb787e3775ebf8a3ebde6054af6527;hpb=fa8c676e225398a6157e591248a12d6847cef9a4;p=xonotic%2Fdarkplaces.git diff --git a/sv_main.c b/sv_main.c index 0db7e303..d4e7485c 100644 --- a/sv_main.c +++ b/sv_main.c @@ -59,7 +59,7 @@ cvar_t sv_checkforpacketsduringsleep = {0, "sv_checkforpacketsduringsleep", "0", 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"}; @@ -594,7 +594,7 @@ void SV_StartSound (prvm_edict_t *entity, int channel, const char *sample, int v } 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); @@ -699,6 +699,9 @@ void SV_SendServerinfo (client_t *client) // 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) @@ -709,7 +712,7 @@ void SV_SendServerinfo (client_t *client) 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); @@ -750,7 +753,7 @@ void SV_SendServerinfo (client_t *client) 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 @@ -913,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)) @@ -1017,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)) @@ -1103,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)) { - 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; } } } @@ -1140,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) @@ -1162,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 @@ -1177,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) @@ -1296,15 +1341,26 @@ static void SV_WriteEntitiesToClient(client_t *client, prvm_edict_t *clent, size 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); @@ -1445,7 +1501,7 @@ void SV_WriteClientdataToMessage (client_t *client, prvm_edict_t *ent, sizebuf_t { if (ent->fields.server->punchangle[i]) bits |= (SU_PUNCH1<fields.server->velocity[i]) @@ -1497,7 +1553,7 @@ void SV_WriteClientdataToMessage (client_t *client, prvm_edict_t *ent, sizebuf_t 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; @@ -1533,7 +1589,7 @@ void SV_WriteClientdataToMessage (client_t *client, prvm_edict_t *ent, sizebuf_t { if (bits & (SU_PUNCH1<fields.server->punchangle[i]); else MSG_WriteAngle16i(msg, ent->fields.server->punchangle[i]); @@ -1547,7 +1603,7 @@ void SV_WriteClientdataToMessage (client_t *client, prvm_edict_t *ent, sizebuf_t } if (bits & (SU_VELOCITY1<fields.server->velocity[i] * (1.0f / 16.0f))); else MSG_WriteCoord32f(msg, ent->fields.server->velocity[i]); @@ -1575,14 +1631,19 @@ void SV_WriteClientdataToMessage (client_t *client, prvm_edict_t *ent, sizebuf_t 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]); @@ -1689,7 +1750,7 @@ static void SV_SendClientDatagram (client_t *client) 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 @@ -1896,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"); @@ -1920,6 +1981,12 @@ void SV_SendClientMessages (void) continue; } + if (!prepared) + { + prepared = true; + // only prepare entities once per frame + SV_PrepareEntitiesForSending(); + } SV_SendClientDatagram (host_client); } @@ -2084,7 +2151,7 @@ SV_ModelIndex */ 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; @@ -2098,7 +2165,7 @@ int SV_ModelIndex(const char *s, int precachemode) { 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; @@ -2133,7 +2200,7 @@ SV_SoundIndex */ 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; @@ -2147,7 +2214,7 @@ int SV_SoundIndex(const char *s, int precachemode) { 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; @@ -2203,7 +2270,7 @@ int SV_ParticleEffectIndex(const char *name) 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) { @@ -2295,7 +2362,11 @@ static void SV_CreateBaseline (void) 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) @@ -2309,6 +2380,11 @@ static void SV_CreateBaseline (void) 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); @@ -2571,7 +2647,7 @@ void SV_SpawnServer (const char *server) 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 @@ -2764,6 +2840,8 @@ static void SV_VM_Setup(void) 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;