This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
#include "quakedef.h"
+cvar_t sv_pvscheckentities = {0, "sv_pvscheckentities", "1"};
+cvar_t sv_vischeckentities = {0, "sv_vischeckentities", "1"};
+cvar_t sv_reportvischeckentities = {0, "sv_reportvischeckentities", "0"};
+int sv_vischeckentitycullcount = 0;
+
server_t sv;
server_static_t svs;
Cvar_RegisterVariable (&sv_nostep);
Cvar_RegisterVariable (&sv_predict);
Cvar_RegisterVariable (&sv_deltacompress);
+ Cvar_RegisterVariable (&sv_pvscheckentities);
+ Cvar_RegisterVariable (&sv_vischeckentities);
+ Cvar_RegisterVariable (&sv_reportvischeckentities);
for (i = 0;i < MAX_MODELS;i++)
sprintf (localmodels[i], "*%i", i);
=============================================================================
*/
-/*
+/*
==================
SV_StartParticle
int i, v;
if (sv.datagram.cursize > MAX_DATAGRAM-16)
- return;
+ return;
MSG_WriteByte (&sv.datagram, svc_particle);
MSG_WriteFloatCoord (&sv.datagram, org[0]);
MSG_WriteFloatCoord (&sv.datagram, org[1]);
}
MSG_WriteByte (&sv.datagram, count);
MSG_WriteByte (&sv.datagram, color);
-}
+}
-/*
+/*
==================
SV_StartEffect
void SV_StartEffect (vec3_t org, int modelindex, int startframe, int framecount, int framerate)
{
if (sv.datagram.cursize > MAX_DATAGRAM-18)
- return;
+ return;
if (modelindex >= 256 || startframe >= 256)
{
MSG_WriteByte (&sv.datagram, svc_effect2);
MSG_WriteByte (&sv.datagram, framecount);
MSG_WriteByte (&sv.datagram, framerate);
}
-}
+}
-/*
+/*
==================
SV_StartSound
Larger attenuations will drop off. (max 4 attenuation)
==================
-*/
+*/
void SV_StartSound (edict_t *entity, int channel, char *sample, int volume,
float attenuation)
-{
+{
int sound_num;
int field_mask;
int i;
int ent;
-
+
if (volume < 0 || volume > 255)
Host_Error ("SV_StartSound: volume = %i", volume);
Host_Error ("SV_StartSound: channel = %i", channel);
if (sv.datagram.cursize > MAX_DATAGRAM-16)
- return;
+ return;
// find precache number for sound
for (sound_num=1 ; sound_num<MAX_SOUNDS
// set up the client_t
netconnection = client->netconnection;
-
+
if (sv.loadgame)
memcpy (spawn_parms, client->spawn_parms, sizeof(spawn_parms));
memset (client, 0, sizeof(*client));
//
// init a new client structure
- //
+ //
for (i=0 ; i<svs.maxclients ; i++)
if (!svs.clients[i].active)
break;
//=============================================================================
+int SV_BoxTouchingPVS (byte *pvs, vec3_t mins, vec3_t maxs, mnode_t *node)
+{
+ int leafnum;
+loc0:
+ if (node->contents < 0)
+ {
+ // leaf
+ if (node->contents == CONTENTS_SOLID)
+ return false;
+ leafnum = (mleaf_t *)node - sv.worldmodel->leafs - 1;
+ return pvs[leafnum >> 3] & (1 << (leafnum & 7));
+ }
+
+ // node - recurse down the BSP tree
+ switch (BOX_ON_PLANE_SIDE(mins, maxs, node->plane))
+ {
+ case 1: // front
+ node = node->children[0];
+ goto loc0;
+ case 2: // back
+ node = node->children[1];
+ goto loc0;
+ default: // crossing
+ if (node->children[0]->contents != CONTENTS_SOLID)
+ if (SV_BoxTouchingPVS (pvs, mins, maxs, node->children[0]))
+ return true;
+ node = node->children[1];
+ goto loc0;
+ }
+ // never reached
+ return false;
+}
+
+
/*
=============
SV_WriteEntitiesToClient
{
int e, i, clentnum, bits, alpha, glowcolor, glowsize, scale, colormod, effects;
byte *pvs;
- vec3_t org, origin, angles;
+ vec3_t org, origin, angles, entmins, entmaxs;
float movelerp, moveilerp, nextfullupdate;
edict_t *ent;
eval_t *val;
entity_state_t *baseline; // LordHavoc: delta or startup baseline
+ trace_t trace;
// find the client's PVS
VectorAdd (clent->v.origin, clent->v.view_ofs, org);
pvs = SV_FatPVS (org);
/*
- if (dpprotocol)
- {
- MSG_WriteByte(msg, svc_playerposition);
- MSG_WriteFloat(msg, org[0]);
- MSG_WriteFloat(msg, org[1]);
- MSG_WriteFloat(msg, org[2]);
- }
+ // dp protocol
+ MSG_WriteByte(msg, svc_playerposition);
+ MSG_WriteFloat(msg, org[0]);
+ MSG_WriteFloat(msg, org[1]);
+ MSG_WriteFloat(msg, org[2]);
*/
clentnum = EDICT_TO_PROG(clent); // LordHavoc: for comparison purposes
continue;
if ((val = GETEDICTFIELDVALUE(ent, eval_drawonlytoclient)) && val->edict && val->edict != clentnum)
continue;
- // ignore if not touching a PV leaf
- for (i = 0;i < ent->num_leafs;i++)
- if (pvs[ent->leafnums[i] >> 3] & (1 << (ent->leafnums[i]&7) ))
- break;
-
- if (i == ent->num_leafs)
- {
- // not visible
+ }
+ }
+
+ glowsize = 0;
+
+ if ((val = GETEDICTFIELDVALUE(ent, eval_glow_size)))
+ glowsize = (int) val->_float >> 2;
+ if (glowsize > 255) glowsize = 255;
+ if (glowsize < 0) glowsize = 0;
+
+ if ((val = GETEDICTFIELDVALUE(ent, eval_glow_trail)))
+ if (val->_float != 0)
+ bits |= U_GLOWTRAIL;
+
+ if (ent->v.modelindex == 0 || pr_strings[ent->v.model] == 0) // no model
+ if (ent != clent) // LordHavoc: always send player
+ if (glowsize == 0 && (bits & U_GLOWTRAIL) == 0) // no effects
continue;
- }
+
+ if (ent->v.movetype == MOVETYPE_STEP && ((int) ent->v.flags & (FL_ONGROUND | FL_FLY | FL_SWIM))) // monsters have smoothed walking/flying/swimming movement
+ {
+ if (!ent->steplerptime || ent->steplerptime > sv.time) // when the level just started...
+ {
+ ent->steplerptime = sv.time;
+ VectorCopy(ent->v.origin, ent->stepoldorigin);
+ VectorCopy(ent->v.angles, ent->stepoldangles);
+ VectorCopy(ent->v.origin, ent->steporigin);
+ VectorCopy(ent->v.angles, ent->stepangles);
+ }
+ VectorSubtract(ent->v.origin, ent->steporigin, origin);
+ VectorSubtract(ent->v.angles, ent->stepangles, angles);
+ if (DotProduct(origin, origin) >= 0.125 || DotProduct(angles, angles) >= 1.4)
+ {
+ // update lerp positions
+ ent->steplerptime = sv.time;
+ VectorCopy(ent->steporigin, ent->stepoldorigin);
+ VectorCopy(ent->stepangles, ent->stepoldangles);
+ VectorCopy(ent->v.origin, ent->steporigin);
+ VectorCopy(ent->v.angles, ent->stepangles);
+ }
+ movelerp = (sv.time - ent->steplerptime) * 10.0;
+ if (movelerp > 1) movelerp = 1;
+ moveilerp = 1 - movelerp;
+ origin[0] = ent->stepoldorigin[0] * moveilerp + ent->steporigin[0] * movelerp;
+ origin[1] = ent->stepoldorigin[1] * moveilerp + ent->steporigin[1] * movelerp;
+ origin[2] = ent->stepoldorigin[2] * moveilerp + ent->steporigin[2] * movelerp;
+ // choose shortest rotate (to avoid 'spin around' situations)
+ VectorSubtract(ent->stepangles, ent->stepoldangles, angles);
+ if (angles[0] < -180) angles[0] += 360;if (angles[0] >= 180) angles[0] -= 360;
+ if (angles[1] < -180) angles[1] += 360;if (angles[1] >= 180) angles[1] -= 360;
+ if (angles[2] < -180) angles[2] += 360;if (angles[2] >= 180) angles[2] -= 360;
+ angles[0] = angles[0] * movelerp + ent->stepoldangles[0];
+ angles[1] = angles[1] * movelerp + ent->stepoldangles[1];
+ angles[2] = angles[2] * movelerp + ent->stepoldangles[2];
+ //VectorMA(origin, host_client->latency, ent->v.velocity, origin);
+ }
+ else // copy as they are
+ {
+ VectorCopy(ent->v.angles, angles);
+ if (DotProduct(ent->v.velocity, ent->v.velocity) >= 1.0f)
+ {
+ VectorMA(ent->v.origin, host_client->latency, ent->v.velocity, origin);
+ // LordHavoc: trace predicted movement to avoid putting things in walls
+ trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, origin, MOVE_NORMAL, ent);
+ VectorCopy(trace.endpos, origin);
+ }
+ else
+ {
+ VectorCopy(ent->v.origin, origin);
+ }
+ if (ent->v.movetype == MOVETYPE_STEP) // monster, but airborn, update lerp info
+ {
+ // update lerp positions
+ ent->steplerptime = sv.time;
+ VectorCopy(ent->v.origin, ent->stepoldorigin);
+ VectorCopy(ent->v.angles, ent->stepoldangles);
+ VectorCopy(ent->v.origin, ent->steporigin);
+ VectorCopy(ent->v.angles, ent->stepangles);
}
}
- if ((val = GETEDICTFIELDVALUE(ent, eval_exteriormodeltoclient)) && val->edict == clentnum)
- bits = bits | U_EXTERIORMODEL;
+ // ent has survived every check so far, check if it is visible
+ if (ent != clent && ((bits & U_VIEWMODEL) == 0))
+ {
+ // use the predicted origin
+ entmins[0] = ent->v.mins[0] + origin[0] - 1.0f;
+ entmins[1] = ent->v.mins[1] + origin[1] - 1.0f;
+ entmins[2] = ent->v.mins[2] + origin[2] - 1.0f;
+ entmaxs[0] = ent->v.maxs[0] + origin[0] + 1.0f;
+ entmaxs[1] = ent->v.maxs[1] + origin[1] + 1.0f;
+ entmaxs[2] = ent->v.maxs[2] + origin[2] + 1.0f;
+
+ // if not touching a visible leaf
+ if (sv_pvscheckentities.value && !SV_BoxTouchingPVS(pvs, entmins, entmaxs, sv.worldmodel->nodes))
+ continue;
+
+ // or not visible through the portals
+ if (sv_vischeckentities.value && !Portal_CheckBox(sv.worldmodel, org, entmins, entmaxs))
+ {
+ sv_vischeckentitycullcount++;
+ continue;
+ }
+ }
- // don't send if flagged for NODRAW and there are no effects
alpha = 255;
scale = 16;
- glowsize = 0;
glowcolor = 254;
colormod = 255;
effects = ent->v.effects;
alpha = 255;
alpha = bound(0, alpha, 255);
- if ((val = GETEDICTFIELDVALUE(ent, eval_glow_size)))
- glowsize = (int) val->_float >> 2;
- if (glowsize > 255) glowsize = 255;
- if (glowsize < 0) glowsize = 0;
-
if ((val = GETEDICTFIELDVALUE(ent, eval_scale)))
if ((scale = (int) (val->_float * 16.0)) == 0) scale = 16;
if (scale < 0) scale = 0;
if (scale > 255) scale = 255;
- if ((val = GETEDICTFIELDVALUE(ent, eval_glow_trail)))
- if (val->_float != 0)
- bits |= U_GLOWTRAIL;
-
if ((val = GETEDICTFIELDVALUE(ent, eval_glow_color)))
if (val->_float != 0)
glowcolor = (int) val->_float;
if (ent != clent)
{
- if (glowsize == 0 && (bits & U_GLOWTRAIL) == 0) // no effects
- {
- if (ent->v.modelindex && pr_strings[ent->v.model]) // model
- {
- if (sv.models[ (int)ent->v.modelindex ]->flags == 0 && (ent->v.effects == EF_NODRAW || scale <= 0 || alpha <= 0))
+// if (glowsize == 0 && (bits & U_GLOWTRAIL) == 0) // no effects
+// {
+// if (ent->v.modelindex && pr_strings[ent->v.model]) // model
+// {
+ // don't send if flagged for NODRAW and there are no effects
+ if (sv.models[(int)ent->v.modelindex]->flags == 0 && ((effects & EF_NODRAW) || scale <= 0 || alpha <= 0))
continue;
- }
- else // no model and no effects
- continue;
- }
+// }
+// else // no model and no effects
+// continue;
+// }
}
if (msg->maxsize - msg->cursize < 32) // LordHavoc: increased check from 16 to 32
return;
}
+ if ((val = GETEDICTFIELDVALUE(ent, eval_exteriormodeltoclient)) && val->edict == clentnum)
+ bits = bits | U_EXTERIORMODEL;
+
// send an update
baseline = &ent->baseline;
else
nextfullupdate = realtime + 0.5f;
- // restore nextfullupdate since this is being sent
+ // restore nextfullupdate since this is being sent for real
client->nextfullupdate[e] = nextfullupdate;
if (e >= 256)
if (ent->v.movetype == MOVETYPE_STEP)
bits |= U_STEP;
-
- if (ent->v.movetype == MOVETYPE_STEP && ((int) ent->v.flags & (FL_ONGROUND | FL_FLY | FL_SWIM))) // monsters have smoothed walking/flying/swimming movement
- {
- if (!ent->steplerptime || ent->steplerptime > sv.time) // when the level just started...
- {
- ent->steplerptime = sv.time;
- VectorCopy(ent->v.origin, ent->stepoldorigin);
- VectorCopy(ent->v.angles, ent->stepoldangles);
- VectorCopy(ent->v.origin, ent->steporigin);
- VectorCopy(ent->v.angles, ent->stepangles);
- }
- VectorSubtract(ent->v.origin, ent->steporigin, origin);
- VectorSubtract(ent->v.angles, ent->stepangles, angles);
- if (DotProduct(origin, origin) >= 0.125 || DotProduct(angles, angles) >= 1.4)
- {
- // update lerp positions
- ent->steplerptime = sv.time;
- VectorCopy(ent->steporigin, ent->stepoldorigin);
- VectorCopy(ent->stepangles, ent->stepoldangles);
- VectorCopy(ent->v.origin, ent->steporigin);
- VectorCopy(ent->v.angles, ent->stepangles);
- }
- movelerp = (sv.time - ent->steplerptime) * 10.0;
- if (movelerp > 1) movelerp = 1;
- moveilerp = 1 - movelerp;
- origin[0] = ent->stepoldorigin[0] * moveilerp + ent->steporigin[0] * movelerp;
- origin[1] = ent->stepoldorigin[1] * moveilerp + ent->steporigin[1] * movelerp;
- origin[2] = ent->stepoldorigin[2] * moveilerp + ent->steporigin[2] * movelerp;
- // choose shortest rotate (to avoid 'spin around' situations)
- VectorSubtract(ent->stepangles, ent->stepoldangles, angles);
- if (angles[0] < -180) angles[0] += 360;if (angles[0] >= 180) angles[0] -= 360;
- if (angles[1] < -180) angles[1] += 360;if (angles[1] >= 180) angles[1] -= 360;
- if (angles[2] < -180) angles[2] += 360;if (angles[2] >= 180) angles[2] -= 360;
- angles[0] = angles[0] * movelerp + ent->stepoldangles[0];
- angles[1] = angles[1] * movelerp + ent->stepoldangles[1];
- angles[2] = angles[2] * movelerp + ent->stepoldangles[2];
- VectorMA(origin, host_client->latency, ent->v.velocity, origin);
- }
- else // copy as they are
- {
-// VectorCopy(ent->v.origin, origin);
- VectorCopy(ent->v.angles, angles);
- VectorMA(ent->v.origin, host_client->latency, ent->v.velocity, origin);
- if (ent->v.movetype == MOVETYPE_STEP) // monster, but airborn, update lerp info
- {
- // update lerp positions
- ent->steplerptime = sv.time;
- VectorCopy(ent->v.origin, ent->stepoldorigin);
- VectorCopy(ent->v.angles, ent->stepoldangles);
- VectorCopy(ent->v.origin, ent->steporigin);
- VectorCopy(ent->v.angles, ent->stepangles);
- }
- }
// LordHavoc: old stuff, but rewritten to have more exact tolerances
// if ((int)(origin[0]*8.0) != (int)(baseline->origin[0]*8.0)) bits |= U_ORIGIN1;
if (bits & U_FRAME2) MSG_WriteByte(msg, (int)ent->v.frame >> 8);
if (bits & U_MODEL2) MSG_WriteByte(msg, (int)ent->v.modelindex >> 8);
}
+
+ if (sv_reportvischeckentities.value)
+ Con_Printf("sv_vischeck culled entities: %d\n", sv_vischeckentitycullcount);
+ sv_vischeckentitycullcount = 0;
}
/*
{
int e;
edict_t *ent;
-
+
ent = NEXT_EDICT(sv.edicts);
for (e=1 ; e<sv.num_edicts ; e++, ent = NEXT_EDICT(ent))
{
MSG_WriteByte (msg, ent->v.dmg_take);
for (i=0 ; i<3 ; i++)
MSG_WriteFloatCoord (msg, other->v.origin[i] + 0.5*(other->v.mins[i] + other->v.maxs[i]));
-
+
ent->v.dmg_take = 0;
ent->v.dmg_save = 0;
}
}
bits = 0;
-
+
if (ent->v.view_ofs[2] != DEFAULT_VIEWHEIGHT)
bits |= SU_VIEWHEIGHT;
-
+
if (ent->v.idealpitch)
bits |= SU_IDEALPITCH;
if (ent->v.velocity[i])
bits |= (SU_VELOCITY1<<i);
}
-
+
if (ent->v.weaponframe)
bits |= SU_WEAPONFRAME;
SV_DropClient (true);// if the message couldn't send, kick off
return false;
}
-
+
return true;
}
sv.signon.maxsize = sizeof(sv.signon_buf);
sv.signon.cursize = 0;
sv.signon.data = sv.signon_buf;
-
+
// leave slots at start for clients only
sv.num_edicts = svs.maxclients+1;
for (i=0 ; i<svs.maxclients ; i++)
return;
}
sv.models[1] = sv.worldmodel;
-
+
//
// clear world interaction links
//
ent->v.modelindex = 1; // world model
ent->v.solid = SOLID_BSP;
ent->v.movetype = MOVETYPE_PUSH;
- ent->v.angles[0] = ent->v.angles[1] = ent->v.angles[2] = 0;
if (coop.value)
pr_global_struct->coop = coop.value;
ED_LoadFromFile (sv.worldmodel->entities);
// LordHavoc: clear world angles (to fix e3m3.bsp)
- sv.edicts->v.angles[0] = sv.edicts->v.angles[1] = sv.edicts->v.angles[2] = 0;
+ VectorClear(sv.edicts->v.angles);
sv.active = true;
// all setup is completed, any further precache statements are errors
sv.state = ss_active;
-
+
// run two frames to allow everything to settle
sv.frametime = pr_global_struct->frametime = host_frametime = 0.1;
SV_Physics ();
for (i=0,host_client = svs.clients ; i<svs.maxclients ; i++, host_client++)
if (host_client->active)
SV_SendServerinfo (host_client);
-
+
Con_DPrintf ("Server spawned.\n");
}