static inline int TV_Module_CM_LeafArea( relay_t *relay, int leafnum ) { if( !relay ) { Com_Printf( "Error: TV_Module_CM_LeafArea: Relay not set\n" ); return 0; } return CM_LeafArea( relay->cms, leafnum ); }
/* ================= SV_inPVSIgnorePortals Does NOT check portalareas ================= */ bool SV_inPVSIgnorePortals(const vec3_t p1, const vec3_t p2) { int leafnum, cluster, area1, area2; byte *mask; leafnum = CM_PointLeafnum(p1); cluster = CM_LeafCluster(leafnum); area1 = CM_LeafArea(leafnum); mask = CM_ClusterPVS(cluster); leafnum = CM_PointLeafnum(p2); cluster = CM_LeafCluster(leafnum); area2 = CM_LeafArea(leafnum); if(mask && (!(mask[cluster >> 3] & (1 << (cluster & 7))))) { return false; } return true; }
/* ================= SV_inPVS Also checks portalareas so that doors block sight ================= */ bool SV_inPVS(const vec3_t p1, const vec3_t p2) { int leafnum, cluster, area1, area2; byte *mask; leafnum = CM_PointLeafnum(p1); cluster = CM_LeafCluster(leafnum); area1 = CM_LeafArea(leafnum); mask = CM_ClusterPVS(cluster); leafnum = CM_PointLeafnum(p2); cluster = CM_LeafCluster(leafnum); area2 = CM_LeafArea(leafnum); if(mask && (!(mask[cluster >> 3] & (1 << (cluster & 7))))) { return false; } if(!CM_AreasConnected(area1, area2)) { return false; // a door blocks sight } return true; }
/* * CM_MergeVisSets */ int CM_MergeVisSets( cmodel_state_t *cms, vec3_t org, qbyte *pvs, qbyte *areabits ) { int area; assert( pvs || areabits ); if( pvs ) CM_MergePVS( cms, org, pvs ); area = CM_PointLeafnum( cms, org ); area = CM_LeafArea( cms, area ); if( areabits && area > -1 ) CM_MergeAreaBits( cms, areabits, area ); return CM_AreaRowSize( cms ); // areabytes }
/* =============== SV_AddEntitiesVisibleFromPoint =============== */ static void SV_AddEntitiesVisibleFromPoint(vec3_t origin, clientSnapshot_t *frame, snapshotEntityNumbers_t *eNums) { int e, i; sharedEntity_t *ent; svEntity_t *svEnt; int l; int clientarea, clientcluster; int leafnum; byte *clientpvs; byte *bitvector; // during an error shutdown message we may need to transmit // the shutdown message after the server has shutdown, so // specfically check for it if (!sv.state) { return; } leafnum = CM_PointLeafnum(origin); clientarea = CM_LeafArea(leafnum); clientcluster = CM_LeafCluster(leafnum); // calculate the visible areas frame->areabytes = CM_WriteAreaBits(frame->areabits, clientarea); clientpvs = CM_ClusterPVS(clientcluster); for (e = 0; e < sv.num_entities; e++) { ent = SV_GentityNum(e); // never send entities that aren't linked in if (!ent->r.linked) { continue; } if (ent->s.number != e) { Com_DPrintf("FIXING ENT->S.NUMBER!!!\n"); ent->s.number = e; } // entities can be flagged to explicitly not be sent to the client if (ent->r.svFlags & SVF_NOCLIENT) { continue; } // entities can be flagged to be sent to only one client if (ent->r.svFlags & SVF_SINGLECLIENT) { if (ent->r.singleClient != frame->ps.clientNum) { continue; } } // entities can be flagged to be sent to everyone but one client if (ent->r.svFlags & SVF_NOTSINGLECLIENT) { if (ent->r.singleClient == frame->ps.clientNum) { continue; } } // entities can be flagged to be sent to a given mask of clients if (ent->r.svFlags & SVF_CLIENTMASK) { if (frame->ps.clientNum >= 32) { if (~ent->r.hack.generic1 & (1 << (frame->ps.clientNum - 32))) continue; } else { if (~ent->r.singleClient & (1 << frame->ps.clientNum)) continue; } } svEnt = SV_SvEntityForGentity(ent); // don't double add an entity through portals if (svEnt->snapshotCounter == sv.snapshotCounter) { continue; } // broadcast entities are always sent if (ent->r.svFlags & SVF_BROADCAST) { SV_AddEntToSnapshot(svEnt, ent, eNums); continue; } // ignore if not touching a PV leaf // check area if (!CM_AreasConnected(clientarea, svEnt->areanum)) { // doors can legally straddle two areas, so // we may need to check another one if (!CM_AreasConnected(clientarea, svEnt->areanum2)) { continue; // blocked by a door } } bitvector = clientpvs; // check individual leafs if (!svEnt->numClusters) { continue; } l = 0; for (i = 0; i < svEnt->numClusters; i++) { l = svEnt->clusternums[i]; if (bitvector[l >> 3] & (1 << (l & 7))) { break; } } // if we haven't found it to be visible, // check overflow clusters that coudln't be stored if (i == svEnt->numClusters) { if (svEnt->lastCluster) { for (; l <= svEnt->lastCluster; l++) { if (bitvector[l >> 3] & (1 << (l & 7))) { break; } } if (l == svEnt->lastCluster) { continue; // not visible } } else { continue; } } // add it SV_AddEntToSnapshot(svEnt, ent, eNums); // if it's a portal entity, add everything visible from its camera position if (ent->r.svFlags & SVF_PORTAL) { if (ent->s.generic1) { vec3_t dir; VectorSubtract(ent->r.currentOrigin, origin, dir); if (VectorLengthSquared(dir) > (float)ent->s.generic1 * ent->s.generic1) { continue; } } SV_AddEntitiesVisibleFromPoint(ent->s.origin2, frame, eNums); } }
static void SV_AddEntitiesVisibleFromPoint(vec3_t origin, clientSnapshot_t *frame, snapshotEntityNumbers_t *eNums) #endif { int e, i; sharedEntity_t *ent, *playerEnt, *ment; #ifdef FEATURE_ANTICHEAT sharedEntity_t *client; #endif svEntity_t *svEnt; int l; int clientarea, clientcluster; int leafnum; byte *clientpvs; byte *bitvector; // during an error shutdown message we may need to transmit // the shutdown message after the server has shutdown, so // specfically check for it if (!sv.state) { return; } leafnum = CM_PointLeafnum(origin); clientarea = CM_LeafArea(leafnum); clientcluster = CM_LeafCluster(leafnum); // calculate the visible areas frame->areabytes = CM_WriteAreaBits(frame->areabits, clientarea); clientpvs = CM_ClusterPVS(clientcluster); playerEnt = SV_GentityNum(frame->ps.clientNum); if (playerEnt->r.svFlags & SVF_SELF_PORTAL) { #ifdef FEATURE_ANTICHEAT SV_AddEntitiesVisibleFromPoint(playerEnt->s.origin2, frame, eNums, qtrue); // portal qtrue?! #else SV_AddEntitiesVisibleFromPoint(playerEnt->s.origin2, frame, eNums); #endif } for (e = 0 ; e < sv.num_entities ; e++) { ent = SV_GentityNum(e); // never send entities that aren't linked in if (!ent->r.linked) { continue; } if (ent->s.number != e) { Com_DPrintf("FIXING ENT->S.NUMBER!!!\n"); ent->s.number = e; } // entities can be flagged to explicitly not be sent to the client if (ent->r.svFlags & SVF_NOCLIENT) { continue; } // entities can be flagged to be sent to only one client if (ent->r.svFlags & SVF_SINGLECLIENT) { if (ent->r.singleClient != frame->ps.clientNum) { continue; } } // entities can be flagged to be sent to everyone but one client if (ent->r.svFlags & SVF_NOTSINGLECLIENT) { if (ent->r.singleClient == frame->ps.clientNum) { continue; } } svEnt = SV_SvEntityForGentity(ent); // don't double add an entity through portals if (svEnt->snapshotCounter == sv.snapshotCounter) { continue; } // broadcast entities are always sent if (ent->r.svFlags & SVF_BROADCAST) { SV_AddEntToSnapshot(playerEnt, svEnt, ent, eNums); continue; } bitvector = clientpvs; // just check origin for being in pvs, ignore bmodel extents if (ent->r.svFlags & SVF_IGNOREBMODELEXTENTS) { if (bitvector[svEnt->originCluster >> 3] & (1 << (svEnt->originCluster & 7))) { SV_AddEntToSnapshot(playerEnt, svEnt, ent, eNums); } continue; } // ignore if not touching a PV leaf // check area if (!CM_AreasConnected(clientarea, svEnt->areanum)) { // doors can legally straddle two areas, so // we may need to check another one if (!CM_AreasConnected(clientarea, svEnt->areanum2)) { continue; } } // check individual leafs if (!svEnt->numClusters) { continue; } l = 0; for (i = 0 ; i < svEnt->numClusters ; i++) { l = svEnt->clusternums[i]; if (bitvector[l >> 3] & (1 << (l & 7))) { break; } } // if we haven't found it to be visible, // check overflow clusters that coudln't be stored if (i == svEnt->numClusters) { if (svEnt->lastCluster) { for ( ; l <= svEnt->lastCluster ; l++) { if (bitvector[l >> 3] & (1 << (l & 7))) { break; } } if (l == svEnt->lastCluster) { continue; // not visible } } else { continue; } } // added "visibility dummies" if (ent->r.svFlags & SVF_VISDUMMY) { // find master; ment = SV_GentityNum(ent->s.otherEntityNum); if (ment) { svEntity_t *master = 0; master = SV_SvEntityForGentity(ment); if (master->snapshotCounter == sv.snapshotCounter || !ment->r.linked) { continue; } SV_AddEntToSnapshot(playerEnt, master, ment, eNums); } continue; // master needs to be added, but not this dummy ent } else if (ent->r.svFlags & SVF_VISDUMMY_MULTIPLE) { int h; sharedEntity_t *ment = 0; svEntity_t *master = 0; for (h = 0; h < sv.num_entities; h++) { ment = SV_GentityNum(h); if (ment == ent) { continue; } if (ment) { master = SV_SvEntityForGentity(ment); } else { continue; } if (!(ment->r.linked)) { continue; } if (ment->s.number != h) { Com_DPrintf("FIXING vis dummy multiple ment->S.NUMBER!!!\n"); ment->s.number = h; } if (ment->r.svFlags & SVF_NOCLIENT) { continue; } if (master->snapshotCounter == sv.snapshotCounter) { continue; } if (ment->s.otherEntityNum == ent->s.number) { SV_AddEntToSnapshot(playerEnt, master, ment, eNums); } } continue; } #ifdef FEATURE_ANTICHEAT if (sv_wh_active->integer > 0 && e < sv_maxclients->integer) // client { // note: !r.linked is already exclused - see above if (e == frame->ps.clientNum) { continue; } client = SV_GentityNum(frame->ps.clientNum); // exclude bots and free flying specs if (!portal && !(client->r.svFlags & SVF_BOT) && (frame->ps.persistant[PERS_TEAM] != TEAM_SPECTATOR) && !(frame->ps.pm_flags & PMF_FOLLOW)) { if (!SV_CanSee(frame->ps.clientNum, e)) { SV_RandomizePos(frame->ps.clientNum, e); SV_AddEntToSnapshot(client, svEnt, ent, eNums); continue; } } } #endif // add it SV_AddEntToSnapshot(playerEnt, svEnt, ent, eNums); // if its a portal entity, add everything visible from its camera position if (ent->r.svFlags & SVF_PORTAL) { #ifdef FEATURE_ANTICHEAT SV_AddEntitiesVisibleFromPoint(ent->s.origin2, frame, eNums, qtrue /*localClient*/); #else SV_AddEntitiesVisibleFromPoint(ent->s.origin2, frame, eNums /*, qtrue, localClient*/); #endif } continue; }
/* =============== SV_AddEntitiesVisibleFromPoint =============== */ static void SV_AddEntitiesVisibleFromPoint( vec3_t origin, clientSnapshot_t *frame, // snapshotEntityNumbers_t *eNums, qboolean portal, clientSnapshot_t *oldframe, qboolean localClient ) { // snapshotEntityNumbers_t *eNums, qboolean portal ) { snapshotEntityNumbers_t *eNums /*, qboolean portal, qboolean localClient */ ) { int e, i; sharedEntity_t *ent, *playerEnt; svEntity_t *svEnt; int l; int clientarea, clientcluster; int leafnum; // int c_fullsend; byte *clientpvs; byte *bitvector; // during an error shutdown message we may need to transmit // the shutdown message after the server has shutdown, so // specfically check for it if ( !sv.state ) { return; } leafnum = CM_PointLeafnum( origin ); clientarea = CM_LeafArea( leafnum ); clientcluster = CM_LeafCluster( leafnum ); // calculate the visible areas frame->areabytes = CM_WriteAreaBits( frame->areabits, clientarea ); clientpvs = CM_ClusterPVS( clientcluster ); // c_fullsend = 0; playerEnt = SV_GentityNum( frame->ps.clientNum ); if ( playerEnt->r.svFlags & SVF_SELF_PORTAL ) { SV_AddEntitiesVisibleFromPoint( playerEnt->s.origin2, frame, eNums ); } for ( e = 0; e < sv.num_entities; e++ ) { ent = SV_GentityNum( e ); // never send entities that aren't linked in if ( !ent->r.linked ) { continue; } if ( ent->s.number != e ) { Com_DPrintf( "FIXING ENT->S.NUMBER!!!\n" ); ent->s.number = e; } // entities can be flagged to explicitly not be sent to the client if ( ent->r.svFlags & SVF_NOCLIENT ) { continue; } // entities can be flagged to be sent to only one client if ( ent->r.svFlags & SVF_SINGLECLIENT ) { if ( ent->r.singleClient != frame->ps.clientNum ) { continue; } } // entities can be flagged to be sent to everyone but one client if ( ent->r.svFlags & SVF_NOTSINGLECLIENT ) { if ( ent->r.singleClient == frame->ps.clientNum ) { continue; } } // entities can be flagged to be sent to only a given mask of clients if ( ent->r.svFlags & SVF_CLIENTMASK ) { if ( frame->ps.clientNum >= 32 ) { if ( ~ent->r.hiMask & ( 1 << ( frame->ps.clientNum - 32 ) ) ) { continue; } } else { if ( ~ent->r.loMask & ( 1 << frame->ps.clientNum ) ) { continue; } } } svEnt = SV_SvEntityForGentity( ent ); // don't double add an entity through portals if ( svEnt->snapshotCounter == sv.snapshotCounter ) { continue; } // broadcast entities are always sent if ( ent->r.svFlags & SVF_BROADCAST ) { SV_AddEntToSnapshot( playerEnt, svEnt, ent, eNums ); continue; } // send entity if the client is in range if ( (ent->r.svFlags & SVF_CLIENTS_IN_RANGE) && Distance( ent->s.origin, playerEnt->s.origin ) <= ent->r.clientRadius ) { SV_AddEntToSnapshot( playerEnt, svEnt, ent, eNums ); continue; } bitvector = clientpvs; // Gordon: just check origin for being in pvs, ignore bmodel extents if ( ent->r.svFlags & SVF_IGNOREBMODELEXTENTS ) { if ( bitvector[ ent->r.originCluster >> 3 ] & ( 1 << ( ent->r.originCluster & 7 ) ) ) { SV_AddEntToSnapshot( playerEnt, svEnt, ent, eNums ); } continue; } // ignore if not touching a PV leaf // check area if ( !CM_AreasConnected( clientarea, ent->r.areanum ) ) { // doors can legally straddle two areas, so // we may need to check another one if ( !CM_AreasConnected( clientarea, ent->r.areanum2 ) ) { continue; } } // check individual leafs if ( !ent->r.numClusters ) { continue; } l = 0; for ( i = 0; i < ent->r.numClusters; i++ ) { l = ent->r.clusternums[ i ]; if ( bitvector[ l >> 3 ] & ( 1 << ( l & 7 ) ) ) { break; } } // if we haven't found it to be visible, // check the overflow clusters that couldn't be stored if ( i == ent->r.numClusters ) { if ( ent->r.lastCluster ) { for ( ; l <= ent->r.lastCluster; l++ ) { if ( bitvector[ l >> 3 ] & ( 1 << ( l & 7 ) ) ) { break; } } if ( l == ent->r.lastCluster ) { continue; } } else { continue; } } //----(SA) added "visibility dummies" if ( ent->r.svFlags & SVF_VISDUMMY ) { sharedEntity_t *ment = 0; //find master; ment = SV_GentityNum( ent->s.otherEntityNum ); if ( ment ) { svEntity_t *master = 0; master = SV_SvEntityForGentity( ment ); if ( master->snapshotCounter == sv.snapshotCounter || !ment->r.linked ) { continue; } SV_AddEntToSnapshot( playerEnt, master, ment, eNums ); } continue; // master needs to be added, but not this dummy ent } //----(SA) end else if ( ent->r.svFlags & SVF_VISDUMMY_MULTIPLE ) { { int h; sharedEntity_t *ment = 0; svEntity_t *master = 0; for ( h = 0; h < sv.num_entities; h++ ) { ment = SV_GentityNum( h ); if ( ment == ent ) { continue; } if ( ment ) { master = SV_SvEntityForGentity( ment ); } else { continue; } if ( !( ment->r.linked ) ) { continue; } if ( ment->s.number != h ) { Com_DPrintf( "FIXING vis dummy multiple ment->S.NUMBER!!!\n" ); ment->s.number = h; } if ( ment->r.svFlags & SVF_NOCLIENT ) { continue; } if ( master->snapshotCounter == sv.snapshotCounter ) { continue; } if ( ment->s.otherEntityNum == ent->s.number ) { SV_AddEntToSnapshot( playerEnt, master, ment, eNums ); } } continue; } } // add it SV_AddEntToSnapshot( playerEnt, svEnt, ent, eNums ); // if it's a portal entity, add everything visible from its camera position if ( ent->r.svFlags & SVF_PORTAL ) { if ( ent->s.generic1 ) { vec3_t dir; VectorSubtract( ent->s.origin, origin, dir ); if ( VectorLengthSquared( dir ) > ( float ) ent->s.generic1 * ent->s.generic1 ) { continue; } } // SV_AddEntitiesVisibleFromPoint( ent->s.origin2, frame, eNums, qtrue, oldframe, localClient ); SV_AddEntitiesVisibleFromPoint( ent->s.origin2, frame, eNums /*, qtrue, localClient */ ); } continue; }
void SV_LinkEntity( sharedEntity_t *gEnt ) { worldSector_t *node; int leafs[MAX_TOTAL_ENT_LEAFS]; int cluster; int num_leafs; int i, j, k; int area; int lastLeaf; float *origin, *angles; svEntity_t *ent; ent = SV_SvEntityForGentity( gEnt ); if ( ent->worldSector ) { SV_UnlinkEntity( gEnt ); // unlink from old position } // encode the size into the entityState_t for client prediction if ( gEnt->r.bmodel ) { gEnt->s.solid = SOLID_BMODEL; // a solid_box will never create this value } else if ( gEnt->r.contents & ( CONTENTS_SOLID | CONTENTS_BODY ) ) { // assume that x/y are equal and symetric i = gEnt->r.maxs[0]; if (i<1) i = 1; if (i>255) i = 255; // z is not symetric j = (-gEnt->r.mins[2]); if (j<1) j = 1; if (j>255) j = 255; // and z maxs can be negative... k = (gEnt->r.maxs[2]+32); if (k<1) k = 1; if (k>255) k = 255; gEnt->s.solid = (k<<16) | (j<<8) | i; } else { gEnt->s.solid = 0; } // get the position origin = gEnt->r.currentOrigin; angles = gEnt->r.currentAngles; // set the abs box if ( gEnt->r.bmodel && (angles[0] || angles[1] || angles[2]) ) { // expand for rotation float max; int i; max = RadiusFromBounds( gEnt->r.mins, gEnt->r.maxs ); for (i=0 ; i<3 ; i++) { gEnt->r.absmin[i] = origin[i] - max; gEnt->r.absmax[i] = origin[i] + max; } } else { // normal VectorAdd (origin, gEnt->r.mins, gEnt->r.absmin); VectorAdd (origin, gEnt->r.maxs, gEnt->r.absmax); } // because movement is clipped an epsilon away from an actual edge, // we must fully check even when bounding boxes don't quite touch gEnt->r.absmin[0] -= 1; gEnt->r.absmin[1] -= 1; gEnt->r.absmin[2] -= 1; gEnt->r.absmax[0] += 1; gEnt->r.absmax[1] += 1; gEnt->r.absmax[2] += 1; // link to PVS leafs ent->numClusters = 0; ent->lastCluster = 0; ent->areanum = -1; ent->areanum2 = -1; //get all leafs, including solids num_leafs = CM_BoxLeafnums( gEnt->r.absmin, gEnt->r.absmax, leafs, MAX_TOTAL_ENT_LEAFS, &lastLeaf ); // if none of the leafs were inside the map, the // entity is outside the world and can be considered unlinked if ( !num_leafs ) { return; } // set areas, even from clusters that don't fit in the entity array for (i=0 ; i<num_leafs ; i++) { area = CM_LeafArea (leafs[i]); if (area != -1) { // doors may legally straggle two areas, // but nothing should evern need more than that if (ent->areanum != -1 && ent->areanum != area) { if (ent->areanum2 != -1 && ent->areanum2 != area && sv.state == SS_LOADING) { Com_DPrintf ("Object %i touching 3 areas at %f %f %f\n", gEnt->s.number, gEnt->r.absmin[0], gEnt->r.absmin[1], gEnt->r.absmin[2]); } ent->areanum2 = area; } else { ent->areanum = area; } } } // store as many explicit clusters as we can ent->numClusters = 0; for (i=0 ; i < num_leafs ; i++) { cluster = CM_LeafCluster( leafs[i] ); if ( cluster != -1 ) { ent->clusternums[ent->numClusters++] = cluster; if ( ent->numClusters == MAX_ENT_CLUSTERS ) { break; } } } // store off a last cluster if we need to if ( i != num_leafs ) { ent->lastCluster = CM_LeafCluster( lastLeaf ); } gEnt->r.linkcount++; // find the first world sector node that the ent's box crosses node = sv_worldSectors; while (1) { if (node->axis == -1) break; if ( gEnt->r.absmin[node->axis] > node->dist) node = node->children[0]; else if ( gEnt->r.absmax[node->axis] < node->dist) node = node->children[1]; else break; // crosses the node } // link it in ent->worldSector = node; ent->nextEntityInWorldSector = node->entities; node->entities = ent; gEnt->r.linked = qtrue; }
/* =============== SV_AddEntitiesVisibleFromPoint =============== */ static void SV_AddEntitiesVisibleFromPoint( vec3_t origin, clientSnapshot_t *frame, snapshotEntityNumbers_t *eNums, qboolean portal ) { int e, i; gentity_t *ent; svEntity_t *svEnt; int l; int clientarea, clientcluster; int leafnum; int c_fullsend; byte *clientpvs; byte *bitvector; // during an error shutdown message we may need to transmit // the shutdown message after the server has shutdown, so // specfically check for it if ( !sv.state ) { return; } leafnum = CM_PointLeafnum (origin); clientarea = CM_LeafArea (leafnum); clientcluster = CM_LeafCluster (leafnum); // calculate the visible areas frame->areabytes = CM_WriteAreaBits( frame->areabits, clientarea ); clientpvs = CM_ClusterPVS (clientcluster); c_fullsend = 0; for ( e = 0 ; e < ge->num_entities ; e++ ) { ent = SV_GentityNum(e); if (!ent->inuse) { continue; } if (ent->s.number != e) { Com_DPrintf ("FIXING ENT->S.NUMBER!!!\n"); ent->s.number = e; } // never send entities that aren't linked in if ( !ent->linked ) { continue; } // entities can be flagged to explicitly not be sent to the client if ( ent->svFlags & SVF_NOCLIENT ) { continue; } svEnt = SV_SvEntityForGentity( ent ); // don't double add an entity through portals if ( svEnt->snapshotCounter == sv.snapshotCounter ) { continue; } // broadcast entities are always sent, and so is the main player so we don't see noclip weirdness if ( ent->svFlags & SVF_BROADCAST || !e) { SV_AddEntToSnapshot( svEnt, ent, eNums ); continue; } // ignore if not touching a PV leaf // check area if ( !CM_AreasConnected( clientarea, svEnt->areanum ) ) { // doors can legally straddle two areas, so // we may need to check another one if ( !CM_AreasConnected( clientarea, svEnt->areanum2 ) ) { continue; // blocked by a door } } bitvector = clientpvs; // check individual leafs if ( !svEnt->numClusters ) { continue; } l = 0; for ( i=0 ; i < svEnt->numClusters ; i++ ) { l = svEnt->clusternums[i]; if ( bitvector[l >> 3] & (1 << (l&7) ) ) { break; } } // if we haven't found it to be visible, // check overflow clusters that coudln't be stored if ( i == svEnt->numClusters ) { if ( svEnt->lastCluster ) { for ( ; l <= svEnt->lastCluster ; l++ ) { if ( bitvector[l >> 3] & (1 << (l&7) ) ) { break; } } if ( l == svEnt->lastCluster ) { continue; // not visible } } else { continue; } } // add it SV_AddEntToSnapshot( svEnt, ent, eNums ); // if its a portal entity, add everything visible from its camera position if ( ent->svFlags & SVF_PORTAL ) { SV_AddEntitiesVisibleFromPoint( ent->s.origin2, frame, eNums, qtrue ); } }
static void SV_AddEntitiesVisibleFromPoint( vec3_t origin, clientSnapshot_t *frame, snapshotEntityNumbers_t *eNums, qboolean portal ) { int e, i; sharedEntity_t *ent; svEntity_t *svEnt; int l; int clientarea, clientcluster; int leafnum; byte *clientpvs; byte *bitvector; vec3_t difference; float length, radius; // during an error shutdown message we may need to transmit // the shutdown message after the server has shutdown, so // specfically check for it if ( !sv.state ) { return; } leafnum = CM_PointLeafnum (origin); clientarea = CM_LeafArea (leafnum); clientcluster = CM_LeafCluster (leafnum); // calculate the visible areas frame->areabytes = CM_WriteAreaBits( frame->areabits, clientarea ); clientpvs = CM_ClusterPVS (clientcluster); for ( e = 0 ; e < sv.num_entities ; e++ ) { ent = SV_GentityNum(e); // never send entities that aren't linked in if ( !ent->r.linked ) { continue; } if (ent->s.eFlags & EF_PERMANENT) { // he's permanent, so don't send him down! continue; } if (ent->s.number != e) { Com_DPrintf ("FIXING ENT->S.NUMBER!!!\n"); ent->s.number = e; } // entities can be flagged to explicitly not be sent to the client if ( ent->r.svFlags & SVF_NOCLIENT ) { continue; } // entities can be flagged to be sent to only one client if ( ent->r.svFlags & SVF_SINGLECLIENT ) { if ( ent->r.singleClient != frame->ps.clientNum ) { continue; } } // entities can be flagged to be sent to everyone but one client if ( ent->r.svFlags & SVF_NOTSINGLECLIENT ) { if ( ent->r.singleClient == frame->ps.clientNum ) { continue; } } svEnt = SV_SvEntityForGentity( ent ); // don't double add an entity through portals if ( svEnt->snapshotCounter == sv.snapshotCounter ) { continue; } // broadcast entities are always sent, and so is the main player so we don't see noclip weirdness if ( ent->r.svFlags & SVF_BROADCAST || (e == frame->ps.clientNum) || (ent->r.broadcastClients[frame->ps.clientNum/32] & (1<<(frame->ps.clientNum%32)))) { SV_AddEntToSnapshot( svEnt, ent, eNums ); continue; } if (ent->s.isPortalEnt) { //rww - portal entities are always sent as well SV_AddEntToSnapshot( svEnt, ent, eNums ); continue; } if (com_RMG && com_RMG->integer) { VectorAdd(ent->r.absmax, ent->r.absmin, difference); VectorScale(difference, 0.5f, difference); VectorSubtract(origin, difference, difference); length = VectorLength(difference); // calculate the diameter VectorSubtract(ent->r.absmax, ent->r.absmin, difference); radius = VectorLength(difference); if (length-radius < /*sv_RMGDistanceCull->integer*/5000.0f) { // more of a diameter check SV_AddEntToSnapshot( svEnt, ent, eNums ); } } else { // ignore if not touching a PV leaf // check area if ( !CM_AreasConnected( clientarea, svEnt->areanum ) ) { // doors can legally straddle two areas, so // we may need to check another one if ( !CM_AreasConnected( clientarea, svEnt->areanum2 ) ) { continue; // blocked by a door } } bitvector = clientpvs; // check individual leafs if ( !svEnt->numClusters ) { continue; } l = 0; for ( i=0 ; i < svEnt->numClusters ; i++ ) { l = svEnt->clusternums[i]; if ( bitvector[l >> 3] & (1 << (l&7) ) ) { break; } } // if we haven't found it to be visible, // check overflow clusters that coudln't be stored if ( i == svEnt->numClusters ) { if ( svEnt->lastCluster ) { for ( ; l <= svEnt->lastCluster ; l++ ) { if ( bitvector[l >> 3] & (1 << (l&7) ) ) { break; } } if ( l == svEnt->lastCluster ) { continue; // not visible } } else { continue; } } if (g_svCullDist != -1.0f) { //do a distance cull check VectorAdd(ent->r.absmax, ent->r.absmin, difference); VectorScale(difference, 0.5f, difference); VectorSubtract(origin, difference, difference); length = VectorLength(difference); // calculate the diameter VectorSubtract(ent->r.absmax, ent->r.absmin, difference); radius = VectorLength(difference); if (length-radius >= g_svCullDist) { //then don't add it continue; } } // add it SV_AddEntToSnapshot( svEnt, ent, eNums ); // if its a portal entity, add everything visible from its camera position if ( ent->r.svFlags & SVF_PORTAL ) { if ( ent->s.generic1 ) { vec3_t dir; VectorSubtract(ent->s.origin, origin, dir); if ( VectorLengthSquared(dir) > (float) ent->s.generic1 * ent->s.generic1 ) { continue; } } SV_AddEntitiesVisibleFromPoint( ent->s.origin2, frame, eNums, qtrue ); } } }
/* =============== SV_AddEntitiesVisibleFromPoint =============== */ static void SV_AddEntitiesVisibleFromPoint( vec3_t origin, clientSnapshot_t *frame, snapshotEntityNumbers_t *eNums, qboolean portal ) { int e, i; sharedEntity_t *ent; svEntity_t *svEnt; int l; int clientarea, clientcluster; int leafnum; byte *clientpvs; byte *bitvector; client_t *cl; vec3_t diff; // during an error shutdown message we may need to transmit // the shutdown message after the server has shutdown, so // specfically check for it if ( !sv.state ) { return; } leafnum = CM_PointLeafnum (origin); clientarea = CM_LeafArea (leafnum); clientcluster = CM_LeafCluster (leafnum); // calculate the visible areas frame->areabytes = CM_WriteAreaBits( frame->areabits, clientarea ); clientpvs = CM_ClusterPVS (clientcluster); for ( e = 0 ; e < sv.num_entities ; e++ ) { ent = SV_GentityNum(e); // never send entities that aren't linked in if ( !ent->r.linked ) { continue; } if (ent->s.number != e) { Com_DPrintf ("FIXING ENT->S.NUMBER!!!\n"); ent->s.number = e; } // entities can be flagged to explicitly not be sent to the client if ( ent->r.svFlags & SVF_NOCLIENT ) { continue; } // entities can be flagged to be sent to only one client if ( ent->r.svFlags & SVF_SINGLECLIENT ) { if ( ent->r.singleClient != frame->ps.clientNum ) { continue; } } // entities can be flagged to be sent to everyone but one client if ( ent->r.svFlags & SVF_NOTSINGLECLIENT ) { if ( ent->r.singleClient == frame->ps.clientNum ) { continue; } } // entities can be flagged to be sent to a given mask of clients if ( ent->r.svFlags & SVF_CLIENTMASK ) { if (frame->ps.clientNum >= 32) Com_Error( ERR_DROP, "SVF_CLIENTMASK: clientNum >= 32" ); if (~ent->r.singleClient & (1 << frame->ps.clientNum)) continue; } svEnt = SV_SvEntityForGentity( ent ); // don't double add an entity through portals if ( svEnt->snapshotCounter == sv.snapshotCounter ) { continue; } // broadcast entities are always sent cl = svs.clients+frame->ps.clientNum; // if ( ent->r.svFlags & SVF_BROADCAST ) { if ( ent->r.svFlags & SVF_BROADCAST && (ent->s.eType != ET_PLAYER || sv_antiwallhack->integer == 0 || recentlySeen(cl,ent->s.clientNum))) {// broadcast only non-players. Warning: in case sv_antiwallhack equals 1, bots will be disappearing on dm_train when touching doors, unless you compensate with "recentlySeen". SV_AddEntToSnapshot( svEnt, ent, eNums ); continue; } //if (!(ent->r.svFlags & SVF_BOT)) {// if this entity is not a bot ... (compensation for the broadcast restriction to non-players) // ignore if not touching a PV leaf // check area if ( !CM_AreasConnected( clientarea, svEnt->areanum ) ) { // doors can legally straddle two areas, so // we may need to check another one if ( !CM_AreasConnected( clientarea, svEnt->areanum2 ) ) { continue; // blocked by a door } } bitvector = clientpvs; // check individual leafs if ( !svEnt->numClusters ) { continue; } l = 0; for ( i=0 ; i < svEnt->numClusters ; i++ ) { l = svEnt->clusternums[i]; if ( bitvector[l >> 3] & (1 << (l&7) ) ) { break; } } // if we haven't found it to be visible, // check overflow clusters that coudln't be stored if ( i == svEnt->numClusters ) { if ( svEnt->lastCluster ) { for ( ; l <= svEnt->lastCluster ; l++ ) { if ( bitvector[l >> 3] & (1 << (l&7) ) ) { break; } } if ( l == svEnt->lastCluster ) { continue; // not visible } } else { continue; } } if (sv_antiwallhack->integer == 1 && (cl->netchan.remoteAddress.type != NA_BOT) && ent->s.eType == ET_PLAYER) { if (!SV_IsPlayerVisibleFromPoint(origin,frame,ent,diff)) { continue; } } // add it SV_AddEntToSnapshot( svEnt, ent, eNums ); // reset ps.pm_type? // if it's a portal entity, add everything visible from its camera position if ( ent->r.svFlags & SVF_PORTAL ) { if ( ent->s.generic1 ) { vec3_t dir; VectorSubtract(ent->s.origin, origin, dir); if ( VectorLengthSquared(dir) > (float) ent->s.generic1 * ent->s.generic1 ) { continue; } } SV_AddEntitiesVisibleFromPoint( ent->s.origin2, frame, eNums, qtrue ); } }
void SV_LinkEdict (edict_t *ent) { areanode_t *node; int leafs[MAX_TOTAL_ENT_LEAFS]; int clusters[MAX_TOTAL_ENT_LEAFS]; int num_leafs; int i, j, k; int area; int topnode; if (ent->area.prev) SV_UnlinkEdict (ent); // unlink from old position if (ent == ge->edicts) return; // don't add the world if (!ent->inuse) return; // set the size VectorSubtract (ent->maxs, ent->mins, ent->size); // encode the size into the entity_state for client prediction if (ent->solid == SOLID_BBOX && !(ent->svflags & SVF_DEADMONSTER)) { // assume that x/y are equal and symetric i = ent->maxs[0]/8; if (i<1) i = 1; if (i>31) i = 31; // z is not symetric j = (-ent->mins[2])/8; if (j<1) j = 1; if (j>31) j = 31; // and z maxs can be negative... k = (ent->maxs[2]+32)/8; if (k<1) k = 1; if (k>63) k = 63; ent->s.solid = (k<<10) | (j<<5) | i; } else if (ent->solid == SOLID_BSP) { ent->s.solid = 31; // a solid_bbox will never create this value } else ent->s.solid = 0; // set the abs box if (ent->solid == SOLID_BSP && (ent->s.angles[0] || ent->s.angles[1] || ent->s.angles[2]) ) { // expand for rotation float max, v; int i; max = 0; for (i=0 ; i<3 ; i++) { v =fabs( ent->mins[i]); if (v > max) max = v; v =fabs( ent->maxs[i]); if (v > max) max = v; } for (i=0 ; i<3 ; i++) { ent->absmin[i] = ent->s.origin[i] - max; ent->absmax[i] = ent->s.origin[i] + max; } } else { // normal VectorAdd (ent->s.origin, ent->mins, ent->absmin); VectorAdd (ent->s.origin, ent->maxs, ent->absmax); } // because movement is clipped an epsilon away from an actual edge, // we must fully check even when bounding boxes don't quite touch ent->absmin[0] -= 1; ent->absmin[1] -= 1; ent->absmin[2] -= 1; ent->absmax[0] += 1; ent->absmax[1] += 1; ent->absmax[2] += 1; // link to PVS leafs ent->num_clusters = 0; ent->areanum = 0; ent->areanum2 = 0; //get all leafs, including solids num_leafs = CM_BoxLeafnums (ent->absmin, ent->absmax, leafs, MAX_TOTAL_ENT_LEAFS, &topnode); // set areas for (i=0 ; i<num_leafs ; i++) { clusters[i] = CM_LeafCluster (leafs[i]); area = CM_LeafArea (leafs[i]); if (area) { // doors may legally straggle two areas, // but nothing should evern need more than that if (ent->areanum && ent->areanum != area) { if (ent->areanum2 && ent->areanum2 != area && sv.state == ss_loading) Com_DPrintf ("Object touching 3 areas at %f %f %f\n", ent->absmin[0], ent->absmin[1], ent->absmin[2]); ent->areanum2 = area; } else ent->areanum = area; } } if (num_leafs >= MAX_TOTAL_ENT_LEAFS) { // assume we missed some leafs, and mark by headnode ent->num_clusters = -1; ent->headnode = topnode; } else { ent->num_clusters = 0; for (i=0 ; i<num_leafs ; i++) { if (clusters[i] == -1) continue; // not a visible leaf for (j=0 ; j<i ; j++) if (clusters[j] == clusters[i]) break; if (j == i) { if (ent->num_clusters == MAX_ENT_CLUSTERS) { // assume we missed some leafs, and mark by headnode ent->num_clusters = -1; ent->headnode = topnode; break; } ent->clusternums[ent->num_clusters++] = clusters[i]; } } } // if first time, make sure old_origin is valid if (!ent->linkcount) { VectorCopy (ent->s.origin, ent->s.old_origin); } ent->linkcount++; if (ent->solid == SOLID_NOT) return; // find the first node that the ent's box crosses node = sv_areanodes; while (1) { if (node->axis == -1) break; if (ent->absmin[node->axis] > node->dist) node = node->children[0]; else if (ent->absmax[node->axis] < node->dist) node = node->children[1]; else break; // crosses the node } // link it in if (ent->solid == SOLID_TRIGGER) InsertLinkBefore (&ent->area, &node->trigger_edicts); else InsertLinkBefore (&ent->area, &node->solid_edicts); }
static inline int PF_CM_LeafArea( int leafnum ) { return CM_LeafArea( svs.cms, leafnum ); }
/* =============== SV_AddEntitiesVisibleFromPoint =============== */ static void SV_AddEntitiesVisibleFromPoint( vec3_t origin, clientSnapshot_t *frame, snapshotEntityNumbers_t *eNums, qboolean portal ) { int e, i; gentity_t *ent; svEntity_t *svEnt; int l; int clientarea, clientcluster; int leafnum; int c_fullsend; const byte *clientpvs; const byte *bitvector; qboolean sightOn = qfalse; // during an error shutdown message we may need to transmit // the shutdown message after the server has shutdown, so // specfically check for it if ( !sv.state ) { return; } leafnum = CM_PointLeafnum (origin); clientarea = CM_LeafArea (leafnum); clientcluster = CM_LeafCluster (leafnum); // calculate the visible areas frame->areabytes = CM_WriteAreaBits( frame->areabits, clientarea ); clientpvs = CM_ClusterPVS (clientcluster); c_fullsend = 0; if ( !portal ) {//not if this if through a portal...??? James said to do this... if ( (frame->ps.forcePowersActive&(1<<FP_SEE)) ) { sightOn = qtrue; } } for ( e = 0 ; e < ge->num_entities ; e++ ) { ent = SV_GentityNum(e); if (!ent->inuse) { continue; } if (ent->s.eFlags & EF_PERMANENT) { // he's permanent, so don't send him down! continue; } if (ent->s.number != e) { Com_DPrintf ("FIXING ENT->S.NUMBER!!!\n"); ent->s.number = e; } // never send entities that aren't linked in if ( !ent->linked ) { continue; } // entities can be flagged to explicitly not be sent to the client if ( ent->svFlags & SVF_NOCLIENT ) { continue; } svEnt = SV_SvEntityForGentity( ent ); // don't double add an entity through portals if ( svEnt->snapshotCounter == sv.snapshotCounter ) { continue; } // broadcast entities are always sent, and so is the main player so we don't see noclip weirdness if ( ent->svFlags & SVF_BROADCAST || !e) { SV_AddEntToSnapshot( svEnt, ent, eNums ); continue; } if (ent->s.isPortalEnt) { //rww - portal entities are always sent as well SV_AddEntToSnapshot( svEnt, ent, eNums ); continue; } if ( sightOn ) {//force sight is on, sees through portals, so draw them always if in radius if ( SV_PlayerCanSeeEnt( ent, frame->ps.forcePowerLevel[FP_SEE] ) ) {//entity is visible SV_AddEntToSnapshot( svEnt, ent, eNums ); continue; } } // ignore if not touching a PV leaf // check area if ( !CM_AreasConnected( clientarea, svEnt->areanum ) ) { // doors can legally straddle two areas, so // we may need to check another one if ( !CM_AreasConnected( clientarea, svEnt->areanum2 ) ) { continue; // blocked by a door } } bitvector = clientpvs; // check individual leafs if ( !svEnt->numClusters ) { continue; } l = 0; #ifdef _XBOX if(bitvector) { #endif for ( i=0 ; i < svEnt->numClusters ; i++ ) { l = svEnt->clusternums[i]; if ( bitvector[l >> 3] & (1 << (l&7) ) ) { break; } } #ifdef _XBOX } #endif // if we haven't found it to be visible, // check overflow clusters that coudln't be stored #ifdef _XBOX if ( bitvector && i == svEnt->numClusters ) { #else if ( i == svEnt->numClusters ) { #endif if ( svEnt->lastCluster ) { for ( ; l <= svEnt->lastCluster ; l++ ) { if ( bitvector[l >> 3] & (1 << (l&7) ) ) { break; } } if ( l == svEnt->lastCluster ) { continue; // not visible } } else { continue; } } // add it SV_AddEntToSnapshot( svEnt, ent, eNums ); // if its a portal entity, add everything visible from its camera position if ( ent->svFlags & SVF_PORTAL ) { SV_AddEntitiesVisibleFromPoint( ent->s.origin2, frame, eNums, qtrue ); #ifdef _XBOX //Must get clientpvs again since above call destroyed it. clientpvs = CM_ClusterPVS (clientcluster); #endif } } }
/* =============== SV_AddEntitiesVisibleFromPoint =============== */ static void SV_AddEntitiesVisibleFromPoint( vec3_t origin, clientSnapshot_t *frame, snapshotEntityNumbers_t *eNums, qboolean portal ) { int e, i; sharedEntity_t *ent; svEntity_t *svEnt; int l; int clientarea, clientcluster; int leafnum; int c_fullsend; byte *clientpvs; byte *bitvector; // during an error shutdown message we may need to transmit // the shutdown message after the server has shutdown, so // specfically check for it if ( !sv.state ) { return; } leafnum = CM_PointLeafnum (origin); clientarea = CM_LeafArea (leafnum); clientcluster = CM_LeafCluster (leafnum); // calculate the visible areas frame->areabytes = CM_WriteAreaBits( frame->areabits, clientarea ); clientpvs = CM_ClusterPVS (clientcluster); c_fullsend = 0; for ( e = 0 ; e < sv.num_entities ; e++ ) { ent = SV_GentityNum(e); // never send entities that aren't linked in if ( !ent->r.linked ) { continue; } if (ent->s.number != e) { Com_DPrintf ("FIXING ENT->S.NUMBER!!!\n"); ent->s.number = e; } svEnt = SV_SvEntityForGentity( ent ); if ( sv.gentitiesMV != NULL && sv.gentitySizeMV > 0 ) { mvsharedEntity_t *mvEnt = MV_EntityNum(e); if ( VM_MVAPILevel( gvm ) >= 2 ) { // MV entities can be flagged to be sent only to // spectators or non-spectators if ( frame->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR || (frame->ps.pm_flags & PMF_FOLLOW) ) { if ( mvEnt->mvFlags & MVF_NOSPEC ) continue; } else { if ( mvEnt->mvFlags & MVF_SPECONLY ) continue; } } // MV entities can be flagged to be sent only to specific // clients (can't filter following spectators this way) if ( mvEnt->snapshotIgnore[frame->ps.clientNum] ) continue; else if ( mvEnt->snapshotEnforce[frame->ps.clientNum] ) { SV_AddEntToSnapshot( svEnt, ent, eNums ); continue; } } // entities can be flagged to explicitly not be sent to the client if ( ent->r.svFlags & SVF_NOCLIENT ) { continue; } // entities can be flagged to be sent to only one client if ( ent->r.svFlags & SVF_SINGLECLIENT ) { if ( ent->r.singleClient != frame->ps.clientNum ) { continue; } } // entities can be flagged to be sent to everyone but one client if ( ent->r.svFlags & SVF_NOTSINGLECLIENT ) { if ( ent->r.singleClient == frame->ps.clientNum ) { continue; } } // don't double add an entity through portals if ( svEnt->snapshotCounter == sv.snapshotCounter ) { continue; } // broadcast entities are always sent, and so is the main player so we don't see noclip weirdness if ( ent->r.svFlags & SVF_BROADCAST || (e == frame->ps.clientNum) || (ent->r.broadcastClients[frame->ps.clientNum/32] & (1<<(frame->ps.clientNum%32)))) { SV_AddEntToSnapshot( svEnt, ent, eNums ); continue; } // ignore if not touching a PV leaf // check area if ( !CM_AreasConnected( clientarea, svEnt->areanum ) ) { // doors can legally straddle two areas, so // we may need to check another one if ( !CM_AreasConnected( clientarea, svEnt->areanum2 ) ) { continue; // blocked by a door } } bitvector = clientpvs; // check individual leafs if ( !svEnt->numClusters ) { continue; } l = 0; for ( i=0 ; i < svEnt->numClusters ; i++ ) { l = svEnt->clusternums[i]; if ( bitvector[l >> 3] & (1 << (l&7) ) ) { break; } } // if we haven't found it to be visible, // check overflow clusters that coudln't be stored if ( i == svEnt->numClusters ) { if ( svEnt->lastCluster ) { for ( ; l <= svEnt->lastCluster ; l++ ) { if ( bitvector[l >> 3] & (1 << (l&7) ) ) { break; } } if ( l == svEnt->lastCluster ) { continue; // not visible } } else { continue; } } // add it SV_AddEntToSnapshot( svEnt, ent, eNums ); // if its a portal entity, add everything visible from its camera position if ( ent->r.svFlags & SVF_PORTAL ) { if ( ent->s.generic1 ) { vec3_t dir; VectorSubtract(ent->s.origin, origin, dir); if ( VectorLengthSquared(dir) > (float) ent->s.generic1 * ent->s.generic1 ) { continue; } } SV_AddEntitiesVisibleFromPoint( ent->s.origin2, frame, eNums, qtrue ); } }
static void SVT3_AddEntitiesVisibleFromPoint( int clientNum, const vec3_t origin, q3clientSnapshot_t* frame, snapshotEntityNumbers_t* eNums, bool portal, bool localClient ) { // during an error shutdown message we may need to transmit // the shutdown message after the server has shutdown, so // specfically check for it if ( !sv.state ) { return; } int leafnum = CM_PointLeafnum( origin ); int clientarea = CM_LeafArea( leafnum ); int clientcluster = CM_LeafCluster( leafnum ); // calculate the visible areas frame->areabytes = CM_WriteAreaBits( frame->areabits, clientarea ); byte* clientpvs = CM_ClusterPVS( clientcluster ); idEntity3* playerEnt = SVT3_EntityNum( clientNum ); if ( playerEnt->GetSvFlagSelfPortal() ) { SVT3_AddEntitiesVisibleFromPoint( clientNum, playerEnt->GetOrigin2(), frame, eNums, portal, localClient ); } int l; for ( int e = 0; e < sv.q3_num_entities; e++ ) { idEntity3* ent = SVT3_EntityNum( e ); // never send entities that aren't linked in if ( !ent->GetLinked() ) { continue; } if ( ent->GetNumber() != e ) { common->DPrintf( "FIXING ENT->S.NUMBER!!!\n" ); ent->SetNumber( e ); } // entities can be flagged to explicitly not be sent to the client if ( ent->GetSvFlags() & Q3SVF_NOCLIENT ) { continue; } // entities can be flagged to be sent to only one client if ( ent->GetSvFlagSingleClient() ) { if ( ent->GetSingleClient() != clientNum ) { continue; } } // entities can be flagged to be sent to everyone but one client if ( ent->GetSvFlagNotSingleClient() ) { if ( ent->GetSingleClient() == clientNum ) { continue; } } // entities can be flagged to be sent to a given mask of clients if ( ent->GetSvFlagClientMask() ) { if ( clientNum >= 32 ) { common->Error( "Q3SVF_CLIENTMASK: cientNum > 32\n" ); } if ( ~ent->GetSingleClient() & ( 1 << clientNum ) ) { continue; } } q3svEntity_t* svEnt = &sv.q3_svEntities[ e ]; // don't double add an entity through portals if ( svEnt->snapshotCounter == sv.q3_snapshotCounter ) { continue; } // if this client is viewing from a camera, only add ents visible from portal ents if ( playerEnt->GetEFlagViewingCamera() && !portal ) { if ( ent->GetSvFlags() & Q3SVF_PORTAL ) { SVT3_AddEntToSnapshot( clientNum, svEnt, ent, eNums ); SVT3_AddEntitiesVisibleFromPoint( clientNum, ent->GetOrigin2(), frame, eNums, true, localClient ); } continue; } // broadcast entities are always sent if ( ent->GetSvFlags() & Q3SVF_BROADCAST ) { SVT3_AddEntToSnapshot( clientNum, svEnt, ent, eNums ); continue; } byte* bitvector = clientpvs; // Gordon: just check origin for being in pvs, ignore bmodel extents if ( ent->GetSvFlagIgnoreBModelExtents() ) { if ( bitvector[ svEnt->originCluster >> 3 ] & ( 1 << ( svEnt->originCluster & 7 ) ) ) { SVT3_AddEntToSnapshot( clientNum, svEnt, ent, eNums ); } continue; } // ignore if not touching a PV leaf // check area if ( !CM_AreasConnected( clientarea, svEnt->areanum ) ) { // doors can legally straddle two areas, so // we may need to check another one if ( !CM_AreasConnected( clientarea, svEnt->areanum2 ) ) { goto notVisible; // blocked by a door } } // check individual leafs if ( !svEnt->numClusters ) { goto notVisible; } l = 0; int i; for ( i = 0; i < svEnt->numClusters; i++ ) { l = svEnt->clusternums[ i ]; if ( bitvector[ l >> 3 ] & ( 1 << ( l & 7 ) ) ) { break; } } // if we haven't found it to be visible, // check overflow clusters that coudln't be stored if ( i == svEnt->numClusters ) { if ( svEnt->lastCluster ) { for (; l <= svEnt->lastCluster; l++ ) { if ( bitvector[ l >> 3 ] & ( 1 << ( l & 7 ) ) ) { break; } } if ( l == svEnt->lastCluster ) { goto notVisible; // not visible } } else { goto notVisible; } } //----(SA) added "visibility dummies" if ( ent->GetSvFlags() & WOLFSVF_VISDUMMY ) { //find master; idEntity3* ment = SVT3_EntityNum( ent->GetOtherEntityNum() ); q3svEntity_t* master = &sv.q3_svEntities[ ent->GetOtherEntityNum() ]; if ( master->snapshotCounter == sv.q3_snapshotCounter || !ment->GetLinked() ) { goto notVisible; } SVT3_AddEntToSnapshot( clientNum, master, ment, eNums ); // master needs to be added, but not this dummy ent goto notVisible; } else if ( ent->GetSvFlags() & WOLFSVF_VISDUMMY_MULTIPLE ) { { for ( int h = 0; h < sv.q3_num_entities; h++ ) { idEntity3* ment = SVT3_EntityNum( h ); if ( ment == ent ) { continue; } q3svEntity_t* master = &sv.q3_svEntities[ h ]; if ( !ment->GetLinked() ) { continue; } if ( ment->GetNumber() != h ) { common->DPrintf( "FIXING vis dummy multiple ment->S.NUMBER!!!\n" ); ment->SetNumber( h ); } if ( ment->GetSvFlags() & Q3SVF_NOCLIENT ) { continue; } if ( master->snapshotCounter == sv.q3_snapshotCounter ) { continue; } if ( ment->GetOtherEntityNum() == ent->GetNumber() ) { SVT3_AddEntToSnapshot( clientNum, master, ment, eNums ); } } goto notVisible; } } // add it SVT3_AddEntToSnapshot( clientNum, svEnt, ent, eNums ); // if its a portal entity, add everything visible from its camera position if ( ent->GetSvFlags() & Q3SVF_PORTAL ) { if ( ent->GetGeneric1() ) { vec3_t dir; VectorSubtract( ent->GetOrigin(), origin, dir ); if ( VectorLengthSquared( dir ) > ( float )ent->GetGeneric1() * ent->GetGeneric1() ) { continue; } } SVT3_AddEntitiesVisibleFromPoint( clientNum, ent->GetOrigin2(), frame, eNums, true, localClient ); } continue; notVisible: // Ridah, if this entity has changed events, then send it regardless of whether we can see it or not // DHM - Nerve :: not in multiplayer please if ( GGameType & ( GAME_WolfSP | GAME_WolfMP ) && svt3_gametype->integer == Q3GT_SINGLE_PLAYER && localClient ) { if ( ent->GetEventTime() == svs.q3_time ) { ent->SetEFlagNoDraw(); // don't draw, just process event SVT3_AddEntToSnapshot( clientNum, svEnt, ent, eNums ); } else if ( ent->GetEType() == Q3ET_PLAYER ) { // keep players around if they are alive and active (so sounds dont get messed up) if ( !ent->GetEFlagDead() ) { ent->SetEFlagNoDraw(); // don't draw, just process events and sounds SVT3_AddEntToSnapshot( clientNum, svEnt, ent, eNums ); } } } }
/* =============== SV_AddEntitiesVisibleFromPoint =============== */ static void SV_AddEntitiesVisibleFromPoint( int psIndex, int clientNum, vec3_t origin, clientSnapshot_t *frame, snapshotEntityNumbers_t *eNums, qboolean portal ) { int e, i; sharedEntity_t *ent; svEntity_t *svEnt; int l; int clientarea, clientcluster; int leafnum; byte *clientpvs; byte *bitvector; // during an error shutdown message we may need to transmit // the shutdown message after the server has shutdown, so // specfically check for it if ( !sv.state ) { return; } leafnum = CM_PointLeafnum (origin); clientarea = CM_LeafArea (leafnum); clientcluster = CM_LeafCluster (leafnum); // calculate the visible areas frame->areabytes[psIndex] = CM_WriteAreaBits( frame->areabits[psIndex], clientarea ); clientpvs = CM_ClusterPVS (clientcluster); for ( e = 0 ; e < sv.num_entities ; e++ ) { ent = SV_GentityNum(e); // never send entities that aren't linked in if ( !ent->r.linked ) { continue; } if (ent->s.number != e) { Com_DPrintf ("FIXING ENT->S.NUMBER!!!\n"); ent->s.number = e; } // entities can be flagged to explicitly not be sent to the client if ( ent->r.svFlags & SVF_NOCLIENT ) { continue; } // entities can be flagged to be sent to a given mask of clients if ( ent->r.svFlags & SVF_CLIENTMASK ) { if ( !Com_ClientListContains( &ent->r.sendClients, clientNum ) ) continue; } svEnt = SV_SvEntityForGentity( ent ); // don't double add an entity through portals if ( svEnt->snapshotCounter == sv.snapshotCounter ) { continue; } // limit based on distance if ( ent->r.cullDistance ) { vec3_t dir; VectorSubtract(ent->s.origin, origin, dir); if ( VectorLengthSquared(dir) > (float) ent->r.cullDistance * ent->r.cullDistance ) { continue; } } // broadcast entities are always sent if ( ent->r.svFlags & SVF_BROADCAST ) { SV_AddEntToSnapshot( frame, svEnt, ent, eNums ); continue; } // ignore if not touching a PV leaf // check area if ( !CM_AreasConnected( clientarea, svEnt->areanum ) ) { // doors can legally straddle two areas, so // we may need to check another one if ( !CM_AreasConnected( clientarea, svEnt->areanum2 ) ) { continue; // blocked by a door } } bitvector = clientpvs; // check individual leafs if ( !svEnt->numClusters ) { continue; } l = 0; for ( i=0 ; i < svEnt->numClusters ; i++ ) { l = svEnt->clusternums[i]; if ( bitvector[l >> 3] & (1 << (l&7) ) ) { break; } } // if we haven't found it to be visible, // check overflow clusters that coudln't be stored if ( i == svEnt->numClusters ) { if ( svEnt->lastCluster ) { for ( ; l <= svEnt->lastCluster ; l++ ) { if ( bitvector[l >> 3] & (1 << (l&7) ) ) { break; } } if ( l == svEnt->lastCluster ) { continue; // not visible } } else { continue; } } // visibility dummies if ( ent->r.svFlags & SVF_VISDUMMY ) { sharedEntity_t *ment = NULL; // find master ment = SV_GentityNum( ent->r.visDummyNum ); if ( ment ) { svEntity_t *master = NULL; master = SV_SvEntityForGentity( ment ); if ( master->snapshotCounter == sv.snapshotCounter || !ment->r.linked ) { continue; } SV_AddEntToSnapshot( frame, master, ment, eNums ); } // master needs to be added, but not this dummy ent continue; } else if ( ent->r.svFlags & SVF_VISDUMMY_MULTIPLE ) { int h; sharedEntity_t *ment = NULL; svEntity_t *master = NULL; for ( h = 0; h < sv.num_entities; h++ ) { ment = SV_GentityNum( h ); if ( ment == ent ) { continue; } if ( ment ) { master = SV_SvEntityForGentity( ment ); } else { continue; } if ( !ment->r.linked ) { continue; } if ( ment->s.number != h ) { Com_DPrintf( "FIXING vis dummy multiple ment->S.NUMBER!!!\n" ); ment->s.number = h; } if ( ment->r.svFlags & SVF_NOCLIENT ) { continue; } if ( master->snapshotCounter == sv.snapshotCounter ) { continue; } if ( ment->r.visDummyNum == ent->s.number ) { SV_AddEntToSnapshot( frame, master, ment, eNums ); } } // masters need to be added, but not this dummy ent continue; } // add it SV_AddEntToSnapshot( frame, svEnt, ent, eNums ); // if it's a portal entity, add everything visible from its camera position if ( ent->r.svFlags & SVF_PORTAL ) { if ( ent->r.portalCullDistance ) { vec3_t dir; VectorSubtract(ent->s.origin, origin, dir); if ( VectorLengthSquared(dir) > (float) ent->r.portalCullDistance * ent->r.portalCullDistance ) { continue; } } SV_AddEntitiesVisibleFromPoint( psIndex, clientNum, ent->s.origin2, frame, eNums, qtrue ); } }
/* =============== SV_LinkEdict General purpose routine shared between game DLL and MVD code. Links entity to PVS leafs. =============== */ void SV_LinkEdict(cm_t *cm, edict_t *ent) { mleaf_t *leafs[MAX_TOTAL_ENT_LEAFS]; int clusters[MAX_TOTAL_ENT_LEAFS]; int num_leafs; int i, j; int area; mnode_t *topnode; // set the size VectorSubtract(ent->maxs, ent->mins, ent->size); // set the abs box if (ent->solid == SOLID_BSP && (ent->s.angles[0] || ent->s.angles[1] || ent->s.angles[2])) { // expand for rotation float max, v; int i; max = 0; for (i = 0; i < 3; i++) { v = Q_fabs(ent->mins[i]); if (v > max) max = v; v = Q_fabs(ent->maxs[i]); if (v > max) max = v; } for (i = 0; i < 3; i++) { ent->absmin[i] = ent->s.origin[i] - max; ent->absmax[i] = ent->s.origin[i] + max; } } else { // normal VectorAdd(ent->s.origin, ent->mins, ent->absmin); VectorAdd(ent->s.origin, ent->maxs, ent->absmax); } // because movement is clipped an epsilon away from an actual edge, // we must fully check even when bounding boxes don't quite touch ent->absmin[0] -= 1; ent->absmin[1] -= 1; ent->absmin[2] -= 1; ent->absmax[0] += 1; ent->absmax[1] += 1; ent->absmax[2] += 1; // link to PVS leafs ent->num_clusters = 0; ent->areanum = 0; ent->areanum2 = 0; //get all leafs, including solids num_leafs = CM_BoxLeafs(cm, ent->absmin, ent->absmax, leafs, MAX_TOTAL_ENT_LEAFS, &topnode); // set areas for (i = 0; i < num_leafs; i++) { clusters[i] = CM_LeafCluster(leafs[i]); area = CM_LeafArea(leafs[i]); if (area) { // doors may legally straggle two areas, // but nothing should evern need more than that if (ent->areanum && ent->areanum != area) { if (ent->areanum2 && ent->areanum2 != area && sv.state == ss_loading) { Com_DPrintf("Object touching 3 areas at %f %f %f\n", ent->absmin[0], ent->absmin[1], ent->absmin[2]); } ent->areanum2 = area; } else ent->areanum = area; } } if (num_leafs >= MAX_TOTAL_ENT_LEAFS) { // assume we missed some leafs, and mark by headnode ent->num_clusters = -1; ent->headnode = CM_NumNode(cm, topnode); } else { ent->num_clusters = 0; for (i = 0; i < num_leafs; i++) { if (clusters[i] == -1) continue; // not a visible leaf for (j = 0; j < i; j++) if (clusters[j] == clusters[i]) break; if (j == i) { if (ent->num_clusters == MAX_ENT_CLUSTERS) { // assume we missed some leafs, and mark by headnode ent->num_clusters = -1; ent->headnode = CM_NumNode(cm, topnode); break; } ent->clusternums[ent->num_clusters++] = clusters[i]; } } } }
/* =============== SV_AddEntitiesVisibleFromPoint =============== */ static void SV_AddEntitiesVisibleFromPoint( vec3_t origin, clientSnapshot_t *frame, // snapshotEntityNumbers_t *eNums, qboolean portal, clientSnapshot_t *oldframe, qboolean localClient ) { // snapshotEntityNumbers_t *eNums, qboolean portal ) { snapshotEntityNumbers_t *eNums, qboolean portal, qboolean localClient ) { int e, i; sharedEntity_t *ent, *playerEnt; svEntity_t *svEnt; int l; int clientarea, clientcluster; int leafnum; int c_fullsend; byte *clientpvs; byte *bitvector; // during an error shutdown message we may need to transmit // the shutdown message after the server has shutdown, so // specfically check for it if ( !sv.state ) { return; } leafnum = CM_PointLeafnum( origin ); clientarea = CM_LeafArea( leafnum ); clientcluster = CM_LeafCluster( leafnum ); // calculate the visible areas frame->areabytes = CM_WriteAreaBits( frame->areabits, clientarea ); clientpvs = CM_ClusterPVS( clientcluster ); c_fullsend = 0; playerEnt = SV_GentityNum( frame->ps.clientNum ); for ( e = 0 ; e < sv.num_entities ; e++ ) { ent = SV_GentityNum( e ); // never send entities that aren't linked in if ( !ent->r.linked ) { continue; } if ( ent->s.number != e ) { Com_DPrintf( "FIXING ENT->S.NUMBER!!!\n" ); ent->s.number = e; } // entities can be flagged to explicitly not be sent to the client if ( ent->r.svFlags & SVF_NOCLIENT ) { continue; } // entities can be flagged to be sent to only one client if ( ent->r.svFlags & SVF_SINGLECLIENT ) { if ( ent->r.singleClient != frame->ps.clientNum ) { continue; } } // entities can be flagged to be sent to everyone but one client if ( ent->r.svFlags & SVF_NOTSINGLECLIENT ) { if ( ent->r.singleClient == frame->ps.clientNum ) { continue; } } svEnt = SV_SvEntityForGentity( ent ); // don't double add an entity through portals if ( svEnt->snapshotCounter == sv.snapshotCounter ) { continue; } // if this client is viewing from a camera, only add ents visible from portal ents if ( ( playerEnt->s.eFlags & EF_VIEWING_CAMERA ) && !portal ) { if ( ent->r.svFlags & SVF_PORTAL ) { SV_AddEntToSnapshot( svEnt, ent, eNums ); // SV_AddEntitiesVisibleFromPoint( ent->s.origin2, frame, eNums, qtrue, oldframe, localClient ); SV_AddEntitiesVisibleFromPoint( ent->s.origin2, frame, eNums, qtrue, localClient ); } continue; } // broadcast entities are always sent if ( ent->r.svFlags & SVF_BROADCAST ) { SV_AddEntToSnapshot( svEnt, ent, eNums ); continue; } // ignore if not touching a PV leaf // check area if ( !CM_AreasConnected( clientarea, svEnt->areanum ) ) { // doors can legally straddle two areas, so // we may need to check another one if ( !CM_AreasConnected( clientarea, svEnt->areanum2 ) ) { goto notVisible; // blocked by a door } } bitvector = clientpvs; // check individual leafs if ( !svEnt->numClusters ) { goto notVisible; } l = 0; for ( i = 0 ; i < svEnt->numClusters ; i++ ) { l = svEnt->clusternums[i]; if ( bitvector[l >> 3] & ( 1 << ( l & 7 ) ) ) { break; } } // if we haven't found it to be visible, // check overflow clusters that coudln't be stored if ( i == svEnt->numClusters ) { if ( svEnt->lastCluster ) { for ( ; l <= svEnt->lastCluster ; l++ ) { if ( bitvector[l >> 3] & ( 1 << ( l & 7 ) ) ) { break; } } if ( l == svEnt->lastCluster ) { goto notVisible; // not visible } } else { goto notVisible; } } //----(SA) added "visibility dummies" if ( ent->r.svFlags & SVF_VISDUMMY ) { sharedEntity_t *ment = 0; //find master; ment = SV_GentityNum( ent->s.otherEntityNum ); if ( ment ) { svEntity_t *master = 0; master = SV_SvEntityForGentity( ment ); if ( master->snapshotCounter == sv.snapshotCounter || !ment->r.linked ) { goto notVisible; //continue; } SV_AddEntToSnapshot( master, ment, eNums ); } goto notVisible; //continue; // master needs to be added, but not this dummy ent } //----(SA) end else if ( ent->r.svFlags & SVF_VISDUMMY_MULTIPLE ) { { int h; sharedEntity_t *ment = 0; svEntity_t *master = 0; for ( h = 0; h < sv.num_entities; h++ ) { ment = SV_GentityNum( h ); if ( ment == ent ) { continue; } if ( ment ) { master = SV_SvEntityForGentity( ment ); } else { continue; } if ( !( ment->r.linked ) ) { continue; } if ( ment->s.number != h ) { Com_DPrintf( "FIXING vis dummy multiple ment->S.NUMBER!!!\n" ); ment->s.number = h; } if ( ment->r.svFlags & SVF_NOCLIENT ) { continue; } if ( master->snapshotCounter == sv.snapshotCounter ) { continue; } if ( ment->s.otherEntityNum == ent->s.number ) { SV_AddEntToSnapshot( master, ment, eNums ); } } goto notVisible; } } // add it SV_AddEntToSnapshot( svEnt, ent, eNums ); // if its a portal entity, add everything visible from its camera position if ( ent->r.svFlags & SVF_PORTAL ) { // SV_AddEntitiesVisibleFromPoint( ent->s.origin2, frame, eNums, qtrue, oldframe, localClient ); SV_AddEntitiesVisibleFromPoint( ent->s.origin2, frame, eNums, qtrue, localClient ); } continue; notVisible: // Ridah, if this entity has changed events, then send it regardless of whether we can see it or not // DHM - Nerve :: not in multiplayer please if ( sv_gametype->integer == GT_SINGLE_PLAYER && localClient ) { if ( ent->r.eventTime == svs.time ) { ent->s.eFlags |= EF_NODRAW; // don't draw, just process event SV_AddEntToSnapshot( svEnt, ent, eNums ); } else if ( ent->s.eType == ET_PLAYER ) { // keep players around if they are alive and active (so sounds dont get messed up) if ( !( ent->s.eFlags & EF_DEAD ) ) { ent->s.eFlags |= EF_NODRAW; // don't draw, just process events and sounds SV_AddEntToSnapshot( svEnt, ent, eNums ); } } } }
//----------------------------------------------------------------------------- // Purpose: Builds the cluster list for an entity // Input : *pEdict - //----------------------------------------------------------------------------- void SV_BuildEntityClusterList( edict_t *pEdict ) { int i, j; int topnode; int leafCount; int leafs[MAX_TOTAL_ENT_LEAFS], clusters[MAX_TOTAL_ENT_LEAFS]; int area; IServerEntity *serverEntity = pEdict->GetIServerEntity(); Assert( serverEntity ); if ( !serverEntity ) return; pEdict->clusterCount = 0; topnode = -1; pEdict->areanum = 0; pEdict->areanum2 = 0; //get all leafs, including solids leafCount = CM_BoxLeafnums( serverEntity->GetAbsMins(), serverEntity->GetAbsMaxs(), leafs, MAX_TOTAL_ENT_LEAFS, &topnode ); // set areas for ( i = 0; i < leafCount; i++ ) { clusters[i] = CM_LeafCluster( leafs[i] ); area = CM_LeafArea( leafs[i] ); if ( area ) { // doors may legally straggle two areas, // but nothing should evern need more than that if ( pEdict->areanum && pEdict->areanum != area ) { if ( pEdict->areanum2 && pEdict->areanum2 != area && sv.state == ss_loading ) { Con_DPrintf ("Object touching 3 areas at %f %f %f\n", serverEntity->GetAbsMins()[0], serverEntity->GetAbsMins()[1], serverEntity->GetAbsMins()[2]); } pEdict->areanum2 = area; } else { pEdict->areanum = area; } } } pEdict->headnode = topnode; // save headnode if ( leafCount >= MAX_TOTAL_ENT_LEAFS ) { // assume we missed some leafs, and mark by headnode pEdict->clusterCount = -1; } else { for ( i = 0; i < leafCount; i++ ) { if (clusters[i] == -1) continue; // not a visible leaf for ( j = 0; j < i; j++ ) { if (clusters[j] == clusters[i]) break; } if ( j == i ) { if ( pEdict->clusterCount == MAX_ENT_CLUSTERS ) { // assume we missed some leafs, and mark by headnode pEdict->clusterCount = -1; break; } pEdict->clusters[pEdict->clusterCount++] = clusters[i]; } } } }
static void SV_AddEntitiesToPacket( edict_t *pViewEnt, edict_t *pClient, client_frame_t *frame, sv_ents_t *ents, bool portal ) { edict_t *ent; vec3_t origin; byte *pset; sv_client_t *cl; int leafnum, fullvis = false; int e, clientcluster, clientarea; bool force = false; // during an error shutdown message we may need to transmit // the shutdown message after the server has shutdown, so // specfically check for it if( !sv.state ) return; if( pClient && !portal ) { // portals can't change hostflags sv.hostflags &= ~SVF_SKIPLOCALHOST; cl = SV_ClientFromEdict( pClient, true ); Com_Assert( cl == NULL ); // setup hostflags if( com.atoi( Info_ValueForKey( cl->userinfo, "cl_lw" )) == 1 ) sv.hostflags |= SVF_SKIPLOCALHOST; } e = svgame.dllFuncs.pfnSetupVisibility( pViewEnt, pClient, portal, origin ); if( e == 0 ) return; // invalid viewent ? if( e == -1 ) fullvis = true; clientpvs = CM_FatPVS( origin, portal ); leafnum = CM_PointLeafnum( origin ); clientarea = CM_LeafArea( leafnum ); clientcluster = CM_LeafCluster( leafnum ); clientphs = CM_FatPHS( clientcluster, portal ); // calculate the visible areas if( fullvis ) { if( !portal ) Mem_Set( frame->areabits, 0xFF, sizeof( frame->areabits )); frame->areabits_size = sizeof( frame->areabits ); } else frame->areabits_size = CM_WriteAreaBits( frame->areabits, clientarea, portal ); for( e = 1; e < svgame.globals->numEntities; e++ ) { ent = EDICT_NUM( e ); if( ent->free ) continue; if( ent->serialnumber != e ) { // this should never happens MsgDev( D_NOTE, "fixing ent->serialnumber from %i to %i\n", ent->serialnumber, e ); ent->serialnumber = e; } // don't double add an entity through portals (already added) if( ent->pvServerData->framenum == sv.net_framenum ) continue; if( fullvis ) force = true; else force = false; // clear forceflag // NOTE: always add himslef to list if( !portal && ( ent == pClient )) force = true; if( ent->v.flags & FL_PHS_FILTER ) pset = clientphs; else pset = clientpvs; if( !force ) { // run custom user filter if( !svgame.dllFuncs.pfnAddToFullPack( pViewEnt, pClient, ent, sv.hostflags, clientarea, pset )) continue; } SV_AddEntToSnapshot( ent->pvServerData, ent, ents ); // add it if( fullvis ) continue; // portal ents will be added anywhere, ignore recursion // if its a portal entity, add everything visible from its camera position if( !portal && (ent->pvServerData->s.ed_type == ED_PORTAL || ent->pvServerData->s.ed_type == ED_SKYPORTAL)) SV_AddEntitiesToPacket( ent, pClient, frame, ents, true ); } }