- client->unreliablemsg.data = client->unreliablemsg_data;
- client->unreliablemsg.maxsize = sizeof(client->unreliablemsg_data);
- // updated by receiving "rate" command from client, this is also the default if not using a DP client
- client->rate = 1000000000;
- // no limits for local player
- if (client->netconnection && LHNETADDRESS_GetAddressType(&client->netconnection->peeraddress) == LHNETADDRESSTYPE_LOOP)
- client->rate = 1000000000;
- client->connecttime = realtime;
-
- if (!sv.loadgame)
- {
- // call the progs to get default spawn parms for the new client
- // set self to world to intentionally cause errors with broken SetNewParms code in some mods
- prog->globals.server->self = 0;
- PRVM_ExecuteProgram (prog->globals.server->SetNewParms, "QC function SetNewParms is missing");
- for (i=0 ; i<NUM_SPAWN_PARMS ; i++)
- client->spawn_parms[i] = (&prog->globals.server->parm1)[i];
-
- // set up the entity for this client (including .colormap, .team, etc)
- PRVM_ED_ClearEdict(client->edict);
- }
-
- // don't call SendServerinfo for a fresh botclient because its fields have
- // not been set up by the qc yet
- if (client->netconnection)
- SV_SendServerinfo (client);
- else
- client->spawned = true;
-}
-
-
-/*
-===============================================================================
-
-FRAME UPDATES
-
-===============================================================================
-*/
-
-/*
-=============================================================================
-
-The PVS must include a small area around the client to allow head bobbing
-or other small motion on the client side. Otherwise, a bob might cause an
-entity that should be visible to not show up, especially when the bob
-crosses a waterline.
-
-=============================================================================
-*/
-
-static qboolean SV_PrepareEntityForSending (prvm_edict_t *ent, entity_state_t *cs, int enumber)
-{
- int i;
- unsigned int sendflags;
- unsigned int version;
- unsigned int modelindex, effects, flags, glowsize, lightstyle, lightpflags, light[4], specialvisibilityradius;
- unsigned int customizeentityforclient;
- unsigned int sendentity;
- float f;
- vec3_t cullmins, cullmaxs;
- dp_model_t *model;
- prvm_eval_t *val, *val2;
-
- // fast path for games that do not use legacy entity networking
- // note: still networks clients even if they are legacy
- sendentity = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.SendEntity)->function;
- if (sv_onlycsqcnetworking.integer && !sendentity && enumber > svs.maxclients)
- 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))
- return false;
-
- // EF_NODRAW prevents sending for any reason except for your own
- // client, so we must keep all clients in this superset
- effects = (unsigned)ent->fields.server->effects;
-
- // we can omit invisible entities with no effects that are not clients
- // LordHavoc: this could kill tags attached to an invisible entity, I
- // just hope we never have to support that case
- i = (int)ent->fields.server->modelindex;
- modelindex = (i >= 1 && i < MAX_MODELS && ent->fields.server->model && *PRVM_GetString(ent->fields.server->model) && sv.models[i]) ? i : 0;
-
- flags = 0;
- i = (int)(PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.glow_size)->_float * 0.25f);
- glowsize = (unsigned char)bound(0, i, 255);
- if (PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.glow_trail)->_float)
- flags |= RENDER_GLOWTRAIL;
- if (PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.viewmodelforclient)->edict)
- flags |= RENDER_VIEWMODEL;
-
- f = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.color)->vector[0]*256;
- light[0] = (unsigned short)bound(0, f, 65535);
- f = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.color)->vector[1]*256;
- light[1] = (unsigned short)bound(0, f, 65535);
- f = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.color)->vector[2]*256;
- light[2] = (unsigned short)bound(0, f, 65535);
- f = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.light_lev)->_float;
- light[3] = (unsigned short)bound(0, f, 65535);
- lightstyle = (unsigned char)PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.style)->_float;
- lightpflags = (unsigned char)PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.pflags)->_float;
-
- if (gamemode == GAME_TENEBRAE)
- {
- // tenebrae's EF_FULLDYNAMIC conflicts with Q2's EF_NODRAW
- if (effects & 16)
- {
- effects &= ~16;
- lightpflags |= PFLAGS_FULLDYNAMIC;
- }
- // tenebrae's EF_GREEN conflicts with DP's EF_ADDITIVE
- if (effects & 32)
- {
- effects &= ~32;
- light[0] = (int)(0.2*256);
- light[1] = (int)(1.0*256);
- light[2] = (int)(0.2*256);
- light[3] = 200;
- lightpflags |= PFLAGS_FULLDYNAMIC;
- }
- }
-
- specialvisibilityradius = 0;
- if (lightpflags & PFLAGS_FULLDYNAMIC)
- specialvisibilityradius = max(specialvisibilityradius, light[3]);
- if (glowsize)
- specialvisibilityradius = max(specialvisibilityradius, glowsize * 4);
- if (flags & RENDER_GLOWTRAIL)
- specialvisibilityradius = max(specialvisibilityradius, 100);
- if (effects & (EF_BRIGHTFIELD | EF_MUZZLEFLASH | EF_BRIGHTLIGHT | EF_DIMLIGHT | EF_RED | EF_BLUE | EF_FLAME | EF_STARDUST))
- {
- if (effects & EF_BRIGHTFIELD)
- specialvisibilityradius = max(specialvisibilityradius, 80);
- if (effects & EF_MUZZLEFLASH)
- specialvisibilityradius = max(specialvisibilityradius, 100);
- if (effects & EF_BRIGHTLIGHT)
- specialvisibilityradius = max(specialvisibilityradius, 400);
- if (effects & EF_DIMLIGHT)
- specialvisibilityradius = max(specialvisibilityradius, 200);
- if (effects & EF_RED)
- specialvisibilityradius = max(specialvisibilityradius, 200);
- if (effects & EF_BLUE)
- specialvisibilityradius = max(specialvisibilityradius, 200);
- if (effects & EF_FLAME)
- specialvisibilityradius = max(specialvisibilityradius, 250);
- if (effects & EF_STARDUST)
- specialvisibilityradius = max(specialvisibilityradius, 100);
- }
-
- // 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 = ACTIVE_NETWORK;
- 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])
- {
- 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 need to init cs->glowmod because the defaultstate did that for us
- //cs->glowmod[0] = cs->glowmod[1] = cs->glowmod[2] = 32;
- val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.glowmod);
- if (val->vector[0] || val->vector[1] || val->vector[2])
- {
- i = (int)(val->vector[0] * 32.0f);cs->glowmod[0] = bound(0, i, 255);
- i = (int)(val->vector[1] * 32.0f);cs->glowmod[1] = bound(0, i, 255);
- i = (int)(val->vector[2] * 32.0f);cs->glowmod[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 (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_GetModelByIndex(modelindex)) && (model->type != mod_null))
- {
- float scale = cs->scale * (1.0f / 16.0f);
- if (cs->angles[0] || cs->angles[2]) // pitch and roll
- {
- VectorMA(cs->origin, scale, model->rotatedmins, cullmins);
- VectorMA(cs->origin, scale, model->rotatedmaxs, cullmaxs);
- }
- else if (cs->angles[1] || ((effects | model->effects) & EF_ROTATE))
- {
- VectorMA(cs->origin, scale, model->yawmins, cullmins);
- VectorMA(cs->origin, scale, model->yawmaxs, cullmaxs);
- }
- else
- {
- 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(cs->origin, ent->fields.server->mins, cullmins);
- VectorAdd(cs->origin, ent->fields.server->maxs, cullmaxs);
- }
- if (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, cs->netcenter);
-
- // if culling box has moved, update pvs cluster links
- if (!VectorCompare(cullmins, ent->priv.server->cullmins) || !VectorCompare(cullmaxs, ent->priv.server->cullmaxs))
- {
- VectorCopy(cullmins, ent->priv.server->cullmins);
- VectorCopy(cullmaxs, ent->priv.server->cullmaxs);
- // a value of -1 for pvs_numclusters indicates that the links are not
- // cached, and should be re-tested each time, this is the case if the
- // culling box touches too many pvs clusters to store, or if the world
- // model does not support FindBoxClusters
- ent->priv.server->pvs_numclusters = -1;
- if (sv.worldmodel && sv.worldmodel->brush.FindBoxClusters)
- {
- i = sv.worldmodel->brush.FindBoxClusters(sv.worldmodel, cullmins, cullmaxs, MAX_ENTITYCLUSTERS, ent->priv.server->pvs_clusterlist);
- if (i <= MAX_ENTITYCLUSTERS)
- ent->priv.server->pvs_numclusters = i;
- }
- }
-
- // we need to do some csqc entity upkeep here
- // get self.SendFlags and clear them
- // (to let the QC know that they've been read)
- if (sendentity)
- {
- val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.SendFlags);
- sendflags = (unsigned int)val->_float;
- val->_float = 0;
- // legacy self.Version system
- val2 = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.Version);
- if (val2->_float)
- {
- version = (unsigned int)val2->_float;
- if (sv.csqcentityversion[enumber] != version)
- sendflags = 0xFFFFFF;
- sv.csqcentityversion[enumber] = version;
- }
- // move sendflags into the per-client sendflags
- if (sendflags)
- for (i = 0;i < svs.maxclients;i++)
- svs.clients[i].csqcentitysendflags[enumber] |= sendflags;
- // mark it as inactive for non-csqc networking
- cs->active = ACTIVE_SHARED;
- }
-
- 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++;
- }
- }
-}
-
-#define MAX_LINEOFSIGHTTRACES 64
-
-qboolean SV_CanSeeBox(int numtraces, vec_t enlarge, vec3_t eye, vec3_t entboxmins, vec3_t entboxmaxs)
-{
- float pitchsign;
- float alpha;
- float starttransformed[3], endtransformed[3];
- int blocked = 0;
- int traceindex;
- int originalnumtouchedicts;
- int numtouchedicts = 0;
- int touchindex;
- matrix4x4_t matrix, imatrix;
- dp_model_t *model;
- prvm_edict_t *touch;
- static prvm_edict_t *touchedicts[MAX_EDICTS];
- vec3_t boxmins, boxmaxs;
- vec3_t clipboxmins, clipboxmaxs;
- vec3_t endpoints[MAX_LINEOFSIGHTTRACES];
-
- numtraces = min(numtraces, MAX_LINEOFSIGHTTRACES);
-
- // expand the box a little
- boxmins[0] = (enlarge+1) * entboxmins[0] - enlarge * entboxmaxs[0];
- boxmaxs[0] = (enlarge+1) * entboxmaxs[0] - enlarge * entboxmins[0];
- boxmins[1] = (enlarge+1) * entboxmins[1] - enlarge * entboxmaxs[1];
- boxmaxs[1] = (enlarge+1) * entboxmaxs[1] - enlarge * entboxmins[1];
- boxmins[2] = (enlarge+1) * entboxmins[2] - enlarge * entboxmaxs[2];
- boxmaxs[2] = (enlarge+1) * entboxmaxs[2] - enlarge * entboxmins[2];
-
- VectorMAM(0.5f, boxmins, 0.5f, boxmaxs, endpoints[0]);
- for (traceindex = 1;traceindex < numtraces;traceindex++)
- VectorSet(endpoints[traceindex], lhrandom(boxmins[0], boxmaxs[0]), lhrandom(boxmins[1], boxmaxs[1]), lhrandom(boxmins[2], boxmaxs[2]));
-
- // calculate sweep box for the entire swarm of traces
- VectorCopy(eye, clipboxmins);
- VectorCopy(eye, clipboxmaxs);
- for (traceindex = 0;traceindex < numtraces;traceindex++)
- {
- clipboxmins[0] = min(clipboxmins[0], endpoints[traceindex][0]);
- clipboxmins[1] = min(clipboxmins[1], endpoints[traceindex][1]);
- clipboxmins[2] = min(clipboxmins[2], endpoints[traceindex][2]);
- clipboxmaxs[0] = max(clipboxmaxs[0], endpoints[traceindex][0]);
- clipboxmaxs[1] = max(clipboxmaxs[1], endpoints[traceindex][1]);
- clipboxmaxs[2] = max(clipboxmaxs[2], endpoints[traceindex][2]);
- }
-
- // get the list of entities in the sweep box
- if (sv_cullentities_trace_entityocclusion.integer)
- numtouchedicts = World_EntitiesInBox(&sv.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
- if (numtouchedicts > MAX_EDICTS)
- {
- // this never happens
- Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
- numtouchedicts = MAX_EDICTS;
- }
- // iterate the entities found in the sweep box and filter them
- originalnumtouchedicts = numtouchedicts;
- numtouchedicts = 0;
- for (touchindex = 0;touchindex < originalnumtouchedicts;touchindex++)
- {
- touch = touchedicts[touchindex];
- if (touch->fields.server->solid != SOLID_BSP)
- continue;
- model = SV_GetModelFromEdict(touch);
- if (!model || !model->brush.TraceLineOfSight)
- continue;
- // skip obviously transparent entities
- alpha = PRVM_EDICTFIELDVALUE(touch, prog->fieldoffsets.alpha)->_float;
- if (alpha && alpha < 1)
- continue;
- if ((int)touch->fields.server->effects & EF_ADDITIVE)
- continue;
- touchedicts[numtouchedicts++] = touch;
- }
-
- // now that we have a filtered list of "interesting" entities, fire each
- // ray against all of them, this gives us an early-out case when something
- // is visible (which it often is)
-
- for (traceindex = 0;traceindex < numtraces;traceindex++)
- {
- // check world occlusion
- if (sv.worldmodel && sv.worldmodel->brush.TraceLineOfSight)
- if (!sv.worldmodel->brush.TraceLineOfSight(sv.worldmodel, eye, endpoints[traceindex]))
- continue;
- for (touchindex = 0;touchindex < numtouchedicts;touchindex++)
- {
- touch = touchedicts[touchindex];
- model = SV_GetModelFromEdict(touch);
- if(model && model->brush.TraceLineOfSight)
- {
- // get the entity matrix
- pitchsign = SV_GetPitchSign(touch);
- Matrix4x4_CreateFromQuakeEntity(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2], pitchsign * touch->fields.server->angles[0], touch->fields.server->angles[1], touch->fields.server->angles[2], 1);
- Matrix4x4_Invert_Simple(&imatrix, &matrix);
- // see if the ray hits this entity
- Matrix4x4_Transform(&imatrix, eye, starttransformed);
- Matrix4x4_Transform(&imatrix, endpoints[traceindex], endtransformed);
- if (!model->brush.TraceLineOfSight(model, starttransformed, endtransformed))
- {
- blocked++;
- break;
- }
- }
- }
- // check if the ray was blocked
- if (touchindex < numtouchedicts)
- continue;
- // return if the ray was not blocked
- return true;
- }
-
- // no rays survived
- return false;
-}
-
-void SV_MarkWriteEntityStateToClient(entity_state_t *s)
-{
- int isbmodel;
- dp_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_GetModelByIndex(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 && !r_novis.integer && sv.writeentitiestoclient_pvsbytes)
- {
- 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, ed->priv.server->cullmins, ed->priv.server->cullmaxs))
- {
- sv.writeentitiestoclient_stats_culled_pvs++;
- return;
- }
- }
- else
- {
- int i;
- // check cached clusters list
- for (i = 0;i < ed->priv.server->pvs_numclusters;i++)
- if (CHECKPVSBIT(sv.writeentitiestoclient_pvs, ed->priv.server->pvs_clusterlist[i]))
- break;
- if (i == ed->priv.server->pvs_numclusters)
- {
- sv.writeentitiestoclient_stats_culled_pvs++;
- return;
- }
- }
- }
-
- // or not seen by random tracelines
- if (sv_cullentities_trace.integer && !isbmodel && sv.worldmodel->brush.TraceLineOfSight)
- {
- int samples =
- s->number <= svs.maxclients
- ? sv_cullentities_trace_samples_players.integer
- :
- s->specialvisibilityradius
- ? sv_cullentities_trace_samples_extra.integer
- : sv_cullentities_trace_samples.integer;
- float enlarge = sv_cullentities_trace_enlarge.value;
-
- if(samples > 0)
- {
- int eyeindex;
- for (eyeindex = 0;eyeindex < sv.writeentitiestoclient_numeyes;eyeindex++)
- if(SV_CanSeeBox(samples, enlarge, sv.writeentitiestoclient_eyes[eyeindex], ed->priv.server->cullmins, ed->priv.server->cullmaxs))
- break;
- if(eyeindex < sv.writeentitiestoclient_numeyes)
- svs.clients[sv.writeentitiestoclient_clientnumber].visibletime[s->number] =
- realtime + (
- s->number <= svs.maxclients
- ? sv_cullentities_trace_delay_players.value
- : sv_cullentities_trace_delay.value
- );
- else if (realtime > svs.clients[sv.writeentitiestoclient_clientnumber].visibletime[s->number])
- {
- sv.writeentitiestoclient_stats_culled_trace++;
- return;
- }
- }
- }
- }
- }
-
- // 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;
-}
-
-#if MAX_LEVELNETWORKEYES > 0
-#define MAX_EYE_RECURSION 1 // increase if recursion gets supported by portals
-void SV_AddCameraEyes(void)
-{
- int e, i, j, k;
- prvm_edict_t *ed;
- static int cameras[MAX_LEVELNETWORKEYES];
- static vec3_t camera_origins[MAX_LEVELNETWORKEYES];
- static int eye_levels[MAX_CLIENTNETWORKEYES];
- int n_cameras = 0;
- vec3_t mi, ma;
- prvm_eval_t *valendpos, *val;
-
- if(!prog->fieldoffsets.camera_transform)
- return;
- valendpos = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_endpos);
- if(!valendpos)
- return;
-
- for(i = 0; i < sv.writeentitiestoclient_numeyes; ++i)
- eye_levels[i] = 0;
-
- // check line of sight to portal entities and add them to PVS
- for (e = 1, ed = PRVM_NEXT_EDICT(prog->edicts);e < prog->num_edicts;e++, ed = PRVM_NEXT_EDICT(ed))
- {
- if (!ed->priv.server->free)
- {
- if((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.camera_transform)) && val->function)
- {
- prog->globals.server->self = e;
- prog->globals.server->other = sv.writeentitiestoclient_cliententitynumber;
- VectorCopy(sv.writeentitiestoclient_eyes[0], valendpos->vector);
- VectorCopy(sv.writeentitiestoclient_eyes[0], PRVM_G_VECTOR(OFS_PARM0));
- VectorClear(PRVM_G_VECTOR(OFS_PARM1));
- PRVM_ExecuteProgram(val->function, "QC function e.camera_transform is missing");
- if(!VectorCompare(valendpos->vector, sv.writeentitiestoclient_eyes[0]))
- VectorCopy(valendpos->vector, camera_origins[n_cameras]);
- cameras[n_cameras] = e;
- ++n_cameras;
- if(n_cameras >= MAX_LEVELNETWORKEYES)
- break;
- }
- }
- }
-
- if(!n_cameras)
- return;
-
- // i is loop counter, is reset to 0 when an eye got added
- // j is camera index to check
- for(i = 0, j = 0; sv.writeentitiestoclient_numeyes < MAX_CLIENTNETWORKEYES && i < n_cameras; ++i, ++j, j %= n_cameras)
- {
- if(!cameras[j])
- continue;
- ed = PRVM_EDICT_NUM(cameras[j]);
- VectorAdd(ed->fields.server->origin, ed->fields.server->mins, mi);
- VectorAdd(ed->fields.server->origin, ed->fields.server->maxs, ma);
- for(k = 0; k < sv.writeentitiestoclient_numeyes; ++k)
- if(eye_levels[k] <= MAX_EYE_RECURSION)
- {
- if(SV_CanSeeBox(sv_cullentities_trace_samples.integer, sv_cullentities_trace_enlarge.value, sv.writeentitiestoclient_eyes[k], mi, ma))
- {
- eye_levels[sv.writeentitiestoclient_numeyes] = eye_levels[k] + 1;
- VectorCopy(camera_origins[j], sv.writeentitiestoclient_eyes[sv.writeentitiestoclient_numeyes]);
- // Con_Printf("added eye %d: %f %f %f because we can see %f %f %f .. %f %f %f from eye %d\n", j, sv.writeentitiestoclient_eyes[sv.writeentitiestoclient_numeyes][0], sv.writeentitiestoclient_eyes[sv.writeentitiestoclient_numeyes][1], sv.writeentitiestoclient_eyes[sv.writeentitiestoclient_numeyes][2], mi[0], mi[1], mi[2], ma[0], ma[1], ma[2], k);
- sv.writeentitiestoclient_numeyes++;
- cameras[j] = 0;
- i = 0;
- break;
- }
- }
- }
-}
-#else
-void SV_AddCameraEyes(void)
-{
-}
-#endif
-
-void SV_WriteEntitiesToClient(client_t *client, prvm_edict_t *clent, sizebuf_t *msg, int maxsize)
-{
- qboolean need_empty = false;
- int i, numsendstates, numcsqcsendstates;
- entity_state_t *s;
- prvm_edict_t *camera;
- qboolean success;
- vec3_t eye;
-
- // if there isn't enough space to accomplish anything, skip it
- if (msg->cursize + 25 > maxsize)
- return;
-
- sv.writeentitiestoclient_msg = msg;
- sv.writeentitiestoclient_clientnumber = client - svs.clients;
-
- sv.writeentitiestoclient_stats_culled_pvs = 0;
- sv.writeentitiestoclient_stats_culled_trace = 0;
- sv.writeentitiestoclient_stats_visibleentities = 0;
- sv.writeentitiestoclient_stats_totalentities = 0;
- sv.writeentitiestoclient_numeyes = 0;
-
- // get eye location
- sv.writeentitiestoclient_cliententitynumber = PRVM_EDICT_TO_PROG(clent); // LordHavoc: for comparison purposes
- camera = PRVM_EDICT_NUM( client->clientcamera );
- VectorAdd(camera->fields.server->origin, clent->fields.server->view_ofs, eye);
- sv.writeentitiestoclient_pvsbytes = 0;
- // get the PVS values for the eye location, later FatPVS calls will merge
- if (sv.worldmodel && sv.worldmodel->brush.FatPVS)
- sv.writeentitiestoclient_pvsbytes = sv.worldmodel->brush.FatPVS(sv.worldmodel, eye, 8, sv.writeentitiestoclient_pvs, sizeof(sv.writeentitiestoclient_pvs), sv.writeentitiestoclient_pvsbytes != 0);
-
- // add the eye to a list for SV_CanSeeBox tests
- VectorCopy(eye, sv.writeentitiestoclient_eyes[sv.writeentitiestoclient_numeyes]);
- sv.writeentitiestoclient_numeyes++;
-
- // calculate predicted eye origin for SV_CanSeeBox tests
- if (sv_cullentities_trace_prediction.integer)
- {
- vec_t predtime = bound(0, host_client->ping, sv_cullentities_trace_prediction_time.value);
- vec3_t predeye;
- VectorMA(eye, predtime, camera->fields.server->velocity, predeye);
- if (SV_CanSeeBox(1, 0, eye, predeye, predeye))
- {
- VectorCopy(predeye, sv.writeentitiestoclient_eyes[sv.writeentitiestoclient_numeyes]);
- sv.writeentitiestoclient_numeyes++;
- }
- //if (!sv.writeentitiestoclient_useprediction)
- // Con_DPrintf("Trying to walk into solid in a pingtime... not predicting for culling\n");
- }
-
- SV_AddCameraEyes();
-
- // build PVS from the new eyes
- if (sv.worldmodel && sv.worldmodel->brush.FatPVS)
- for(i = 1; i < sv.writeentitiestoclient_numeyes; ++i)
- sv.writeentitiestoclient_pvsbytes = sv.worldmodel->brush.FatPVS(sv.worldmodel, sv.writeentitiestoclient_eyes[i], 8, sv.writeentitiestoclient_pvs, sizeof(sv.writeentitiestoclient_pvs), sv.writeentitiestoclient_pvsbytes != 0);
-
- sv.sententitiesmark++;
-
- for (i = 0;i < sv.numsendentities;i++)
- SV_MarkWriteEntityStateToClient(sv.sendentities + i);
-
- numsendstates = 0;
- numcsqcsendstates = 0;
- for (i = 0;i < sv.numsendentities;i++)
- {
- s = &sv.sendentities[i];
- if (sv.sententities[s->number] == sv.sententitiesmark)
- {
- if(s->active == ACTIVE_NETWORK)
- {
- if (s->exteriormodelforclient)
- {
- if (s->exteriormodelforclient == sv.writeentitiestoclient_cliententitynumber)
- s->flags |= RENDER_EXTERIORMODEL;
- else
- s->flags &= ~RENDER_EXTERIORMODEL;
- }
- sv.writeentitiestoclient_sendstates[numsendstates++] = s;
- }
- else if(sv.sendentities[i].active == ACTIVE_SHARED)
- sv.writeentitiestoclient_csqcsendstates[numcsqcsendstates++] = s->number;
- else
- Con_Printf("entity %d is in sv.sendentities and marked, but not active, please breakpoint me\n", s->number);
- }
- }
-
- 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(client->entitydatabase5)
- need_empty = EntityFrameCSQC_WriteFrame(msg, maxsize, numcsqcsendstates, sv.writeentitiestoclient_csqcsendstates, client->entitydatabase5->latestframenum + 1);
- else
- EntityFrameCSQC_WriteFrame(msg, maxsize, numcsqcsendstates, sv.writeentitiestoclient_csqcsendstates, 0);
-
- if(client->num_skippedentityframes >= 10)
- need_empty = true; // force every 10th frame to be not empty (or cl_movement replay takes too long)
-
- if (client->entitydatabase5)
- success = EntityFrame5_WriteFrame(msg, maxsize, client->entitydatabase5, numsendstates, sv.writeentitiestoclient_sendstates, client - svs.clients + 1, client->movesequence, need_empty);
- else if (client->entitydatabase4)
- {
- success = EntityFrame4_WriteFrame(msg, maxsize, client->entitydatabase4, numsendstates, sv.writeentitiestoclient_sendstates);
- Protocol_WriteStatsReliable();
- }
- else if (client->entitydatabase)
- {
- success = EntityFrame_WriteFrame(msg, maxsize, client->entitydatabase, numsendstates, sv.writeentitiestoclient_sendstates, client - svs.clients + 1);
- Protocol_WriteStatsReliable();
- }
- else
- {
- success = EntityFrameQuake_WriteFrame(msg, maxsize, numsendstates, sv.writeentitiestoclient_sendstates);
- Protocol_WriteStatsReliable();
- }
-
- if(success)
- client->num_skippedentityframes = 0;
- else
- ++client->num_skippedentityframes;
-}
-
-/*
-=============
-SV_CleanupEnts
-
-=============
-*/
-static void SV_CleanupEnts (void)
-{
- int e;
- prvm_edict_t *ent;
-
- ent = PRVM_NEXT_EDICT(prog->edicts);
- for (e=1 ; e<prog->num_edicts ; e++, ent = PRVM_NEXT_EDICT(ent))
- ent->fields.server->effects = (int)ent->fields.server->effects & ~EF_MUZZLEFLASH;
-}
-
-/*
-==================
-SV_WriteClientdataToMessage
-
-==================
-*/
-void SV_WriteClientdataToMessage (client_t *client, prvm_edict_t *ent, sizebuf_t *msg, int *stats)
-{
- int bits;
- int i;
- prvm_edict_t *other;
- int items;
- prvm_eval_t *val;
- vec3_t punchvector;
- int viewzoom;
- const char *s;
- float *statsf = (float *)stats;
-
-//
-// send a damage message
-//
- if (ent->fields.server->dmg_take || ent->fields.server->dmg_save)
- {
- other = PRVM_PROG_TO_EDICT(ent->fields.server->dmg_inflictor);
- MSG_WriteByte (msg, svc_damage);
- MSG_WriteByte (msg, (int)ent->fields.server->dmg_save);
- MSG_WriteByte (msg, (int)ent->fields.server->dmg_take);
- for (i=0 ; i<3 ; i++)
- MSG_WriteCoord (msg, other->fields.server->origin[i] + 0.5*(other->fields.server->mins[i] + other->fields.server->maxs[i]), sv.protocol);
-
- ent->fields.server->dmg_take = 0;
- ent->fields.server->dmg_save = 0;
- }
-
-//
-// send the current viewpos offset from the view entity
-//
- SV_SetIdealPitch (); // how much to look up / down ideally
-
-// a fixangle might get lost in a dropped packet. Oh well.
- if(ent->fields.server->fixangle)
- {
- // angle fixing was requested by global thinking code...
- // so store the current angles for later use
- memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
- host_client->fixangle_angles_set = TRUE;
-
- // and clear fixangle for the next frame
- ent->fields.server->fixangle = 0;
- }
-
- if (host_client->fixangle_angles_set)
- {
- MSG_WriteByte (msg, svc_setangle);
- for (i=0 ; i < 3 ; i++)
- MSG_WriteAngle (msg, host_client->fixangle_angles[i], sv.protocol);
- host_client->fixangle_angles_set = FALSE;
- }
-
- // stuff the sigil bits into the high bits of items for sbar, or else
- // mix in items2
- val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.items2);
- if (gamemode == GAME_HIPNOTIC || gamemode == GAME_ROGUE)
- items = (int)ent->fields.server->items | ((int)val->_float << 23);
- else
- items = (int)ent->fields.server->items | ((int)prog->globals.server->serverflags << 28);
-
- VectorClear(punchvector);
- if ((val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.punchvector)))
- VectorCopy(val->vector, punchvector);
-
- // cache weapon model name and index in client struct to save time
- // (this search can be almost 1% of cpu time!)
- s = PRVM_GetString(ent->fields.server->weaponmodel);
- if (strcmp(s, client->weaponmodel))
- {
- strlcpy(client->weaponmodel, s, sizeof(client->weaponmodel));
- client->weaponmodelindex = SV_ModelIndex(s, 1);
- }
-
- viewzoom = 255;
- if ((val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.viewzoom)))
- viewzoom = (int)(val->_float * 255.0f);
- if (viewzoom == 0)
- viewzoom = 255;
-
- bits = 0;
-
- if ((int)ent->fields.server->flags & FL_ONGROUND)
- bits |= SU_ONGROUND;
- if (ent->fields.server->waterlevel >= 2)
- bits |= SU_INWATER;
- if (ent->fields.server->idealpitch)
- bits |= SU_IDEALPITCH;
-
- for (i=0 ; i<3 ; i++)
- {
- if (ent->fields.server->punchangle[i])
- bits |= (SU_PUNCH1<<i);
- 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])
- bits |= (SU_VELOCITY1<<i);
- }
-
- memset(stats, 0, sizeof(int[MAX_CL_STATS]));
- stats[STAT_VIEWHEIGHT] = (int)ent->fields.server->view_ofs[2];
- stats[STAT_ITEMS] = items;
- stats[STAT_WEAPONFRAME] = (int)ent->fields.server->weaponframe;
- stats[STAT_ARMOR] = (int)ent->fields.server->armorvalue;
- stats[STAT_WEAPON] = client->weaponmodelindex;
- stats[STAT_HEALTH] = (int)ent->fields.server->health;
- stats[STAT_AMMO] = (int)ent->fields.server->currentammo;
- stats[STAT_SHELLS] = (int)ent->fields.server->ammo_shells;
- stats[STAT_NAILS] = (int)ent->fields.server->ammo_nails;
- stats[STAT_ROCKETS] = (int)ent->fields.server->ammo_rockets;
- stats[STAT_CELLS] = (int)ent->fields.server->ammo_cells;
- stats[STAT_ACTIVEWEAPON] = (int)ent->fields.server->weapon;
- stats[STAT_VIEWZOOM] = viewzoom;
- stats[STAT_TOTALSECRETS] = (int)prog->globals.server->total_secrets;
- stats[STAT_TOTALMONSTERS] = (int)prog->globals.server->total_monsters;
- // the QC bumps these itself by sending svc_'s, so we have to keep them
- // zero or they'll be corrected by the engine
- //stats[STAT_SECRETS] = prog->globals.server->found_secrets;
- //stats[STAT_MONSTERS] = prog->globals.server->killed_monsters;
-
- // movement settings for prediction
- // note: these are not sent in protocols with lower MAX_CL_STATS limits
- stats[STAT_MOVEFLAGS] = MOVEFLAG_VALID
- | (sv_gameplayfix_q2airaccelerate.integer ? MOVEFLAG_Q2AIRACCELERATE : 0)
- | (sv_gameplayfix_nogravityonground.integer ? MOVEFLAG_NOGRAVITYONGROUND : 0)
- | (sv_gameplayfix_gravityunaffectedbyticrate.integer ? MOVEFLAG_GRAVITYUNAFFECTEDBYTICRATE : 0)
- ;
- statsf[STAT_MOVEVARS_TICRATE] = sys_ticrate.value;
- statsf[STAT_MOVEVARS_TIMESCALE] = slowmo.value;
- statsf[STAT_MOVEVARS_GRAVITY] = sv_gravity.value;
- statsf[STAT_MOVEVARS_STOPSPEED] = sv_stopspeed.value;
- statsf[STAT_MOVEVARS_MAXSPEED] = sv_maxspeed.value;
- statsf[STAT_MOVEVARS_SPECTATORMAXSPEED] = sv_maxspeed.value; // FIXME: QW has a separate cvar for this
- statsf[STAT_MOVEVARS_ACCELERATE] = sv_accelerate.value;
- statsf[STAT_MOVEVARS_AIRACCELERATE] = sv_airaccelerate.value >= 0 ? sv_airaccelerate.value : sv_accelerate.value;
- statsf[STAT_MOVEVARS_WATERACCELERATE] = sv_wateraccelerate.value >= 0 ? sv_wateraccelerate.value : sv_accelerate.value;
- val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity);
- statsf[STAT_MOVEVARS_ENTGRAVITY] = (val && val->_float != 0) ? val->_float : 1.0f;
- statsf[STAT_MOVEVARS_JUMPVELOCITY] = sv_jumpvelocity.value;
- statsf[STAT_MOVEVARS_EDGEFRICTION] = sv_edgefriction.value;
- statsf[STAT_MOVEVARS_MAXAIRSPEED] = sv_maxairspeed.value;
- statsf[STAT_MOVEVARS_STEPHEIGHT] = sv_stepheight.value;
- statsf[STAT_MOVEVARS_AIRACCEL_QW] = sv_airaccel_qw.value;
- statsf[STAT_MOVEVARS_AIRACCEL_SIDEWAYS_FRICTION] = sv_airaccel_sideways_friction.value;
- statsf[STAT_MOVEVARS_FRICTION] = sv_friction.value;
- statsf[STAT_MOVEVARS_WATERFRICTION] = sv_waterfriction.value >= 0 ? sv_waterfriction.value : sv_friction.value;
- statsf[STAT_MOVEVARS_AIRSTOPACCELERATE] = sv_airstopaccelerate.value;
- statsf[STAT_MOVEVARS_AIRSTRAFEACCELERATE] = sv_airstrafeaccelerate.value;
- statsf[STAT_MOVEVARS_MAXAIRSTRAFESPEED] = sv_maxairstrafespeed.value;
- statsf[STAT_MOVEVARS_AIRSTRAFEACCEL_QW] = sv_airstrafeaccel_qw.value;
- statsf[STAT_MOVEVARS_AIRCONTROL] = sv_aircontrol.value;
- statsf[STAT_MOVEVARS_AIRCONTROL_POWER] = sv_aircontrol_power.value;
- statsf[STAT_MOVEVARS_AIRCONTROL_PENALTY] = sv_aircontrol_penalty.value;
- statsf[STAT_MOVEVARS_WARSOWBUNNY_AIRFORWARDACCEL] = sv_warsowbunny_airforwardaccel.value;
- statsf[STAT_MOVEVARS_WARSOWBUNNY_ACCEL] = sv_warsowbunny_accel.value;
- statsf[STAT_MOVEVARS_WARSOWBUNNY_TOPSPEED] = sv_warsowbunny_topspeed.value;
- statsf[STAT_MOVEVARS_WARSOWBUNNY_TURNACCEL] = sv_warsowbunny_turnaccel.value;
- statsf[STAT_MOVEVARS_WARSOWBUNNY_BACKTOSIDERATIO] = sv_warsowbunny_backtosideratio.value;
- statsf[STAT_MOVEVARS_AIRSPEEDLIMIT_NONQW] = sv_airspeedlimit_nonqw.value;
- 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_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 (stats[STAT_WEAPONFRAME]) bits |= SU_WEAPONFRAME;
- if (stats[STAT_ARMOR]) bits |= SU_ARMOR;
- bits |= SU_WEAPON;
- // FIXME: which protocols support this? does PROTOCOL_DARKPLACES3 support viewzoom?
- if (sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4 || sv.protocol == PROTOCOL_DARKPLACES5)
- if (viewzoom != 255)
- bits |= SU_VIEWZOOM;
- }
-
- if (bits >= 65536)
- bits |= SU_EXTEND1;
- if (bits >= 16777216)
- bits |= SU_EXTEND2;
-
- // send the data
- MSG_WriteByte (msg, svc_clientdata);
- MSG_WriteShort (msg, bits);
- if (bits & SU_EXTEND1)
- MSG_WriteByte(msg, bits >> 16);
- if (bits & SU_EXTEND2)
- MSG_WriteByte(msg, bits >> 24);
-
- if (bits & SU_VIEWHEIGHT)
- MSG_WriteChar (msg, stats[STAT_VIEWHEIGHT]);
-
- if (bits & SU_IDEALPITCH)
- MSG_WriteChar (msg, (int)ent->fields.server->idealpitch);
-
- for (i=0 ; i<3 ; i++)
- {
- if (bits & (SU_PUNCH1<<i))
- {
- 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_PUNCHVEC1<<i))
- {
- if (sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4)
- MSG_WriteCoord16i(msg, punchvector[i]);
- else
- MSG_WriteCoord32f(msg, punchvector[i]);
- }
- if (bits & (SU_VELOCITY1<<i))
- {
- 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_ITEMS)
- MSG_WriteLong (msg, stats[STAT_ITEMS]);
-
- if (sv.protocol == PROTOCOL_DARKPLACES5)
- {
- if (bits & SU_WEAPONFRAME)
- MSG_WriteShort (msg, stats[STAT_WEAPONFRAME]);
- if (bits & SU_ARMOR)
- MSG_WriteShort (msg, stats[STAT_ARMOR]);
- if (bits & SU_WEAPON)
- MSG_WriteShort (msg, stats[STAT_WEAPON]);
- MSG_WriteShort (msg, stats[STAT_HEALTH]);
- MSG_WriteShort (msg, stats[STAT_AMMO]);
- MSG_WriteShort (msg, stats[STAT_SHELLS]);
- MSG_WriteShort (msg, stats[STAT_NAILS]);
- MSG_WriteShort (msg, stats[STAT_ROCKETS]);
- MSG_WriteShort (msg, stats[STAT_CELLS]);
- MSG_WriteShort (msg, stats[STAT_ACTIVEWEAPON]);
- 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_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)
- {
- 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]);
- MSG_WriteByte (msg, stats[STAT_NAILS]);
- MSG_WriteByte (msg, stats[STAT_ROCKETS]);
- MSG_WriteByte (msg, stats[STAT_CELLS]);
- if (gamemode == GAME_HIPNOTIC || gamemode == GAME_ROGUE || gamemode == GAME_NEXUIZ)
- {
- for (i = 0;i < 32;i++)
- if (stats[STAT_ACTIVEWEAPON] & (1<<i))
- break;
- MSG_WriteByte (msg, i);
- }
- else
- MSG_WriteByte (msg, stats[STAT_ACTIVEWEAPON]);
- if (bits & SU_VIEWZOOM)
- {
- if (sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3 || sv.protocol == PROTOCOL_DARKPLACES4)
- MSG_WriteByte (msg, bound(0, stats[STAT_VIEWZOOM], 255));
- else
- MSG_WriteShort (msg, bound(0, stats[STAT_VIEWZOOM], 65535));
- }
- }
-}