/* =============== 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; }
/* =============== 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); } }
/* =============== 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_BuildClientSnapshot Decides which entities are going to be visible to the client, and copies off the playerstate and areabits. This properly handles multiple recursive portals, but the render currently doesn't. For viewing through other player's eyes, clent can be something other than client->gentity ============= */ static void SV_BuildClientSnapshot( client_t* client ) { vec3_c org; clientSnapshot_t* frame; snapshotEntityNumbers_t entityNumbers; int i; edict_s* ent; entityState_s* state; svEntity_t* svEnt; edict_s* clent; int clientNum; playerState_s* ps; // bump the counter used to prevent double adding sv.snapshotCounter++; // this is the frame we are creating frame = &client->frames[ client->netchan.outgoingSequence & PACKET_MASK ]; // clear everything in this snapshot entityNumbers.numSnapshotEntities = 0; memset( frame->areabits, 0, sizeof( frame->areabits ) ); // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=62 frame->num_entities = 0; clent = client->gentity; if ( !clent || client->state == CS_ZOMBIE ) { return; } // grab the current playerState_s ps = SV_GameClientNum( client - svs.clients ); frame->ps = *ps; // never send client's own entity, because it can // be regenerated from the playerstate clientNum = frame->ps.clientNum; if ( clientNum < 0 || clientNum >= MAX_GENTITIES ) { Com_Error( ERR_DROP, "SV_SvEntityForGentity: bad gEnt" ); } svEnt = &sv.svEntities[ clientNum ]; svEnt->snapshotCounter = sv.snapshotCounter; // find the client's viewpoint org = ps->origin; org[2] += ps->viewheight; // add all the entities directly visible to the eye, which // may include portal entities that merge other viewpoints bitSet_c areaBits; if ( sv_bsp ) { areaBits.init( sv_bsp->getNumAreas(), false ); } SV_AddEntitiesVisibleFromPoint( org, frame, &entityNumbers, false, areaBits ); memcpy( frame->areabits, areaBits.getArray(), areaBits.getSizeInBytes() ); frame->areabytes = areaBits.getSizeInBytes(); // if there were portals visible, there may be out of order entities // in the list which will need to be resorted for the delta compression // to work correctly. This also catches the error condition // of an entity being included twice. qsort( entityNumbers.snapshotEntities, entityNumbers.numSnapshotEntities, sizeof( entityNumbers.snapshotEntities[0] ), SV_QsortEntityNumbers ); // now that all viewpoint's areabits have been OR'd together, invert // all of them to make it a mask vector, which is what the renderer wants for ( i = 0 ; i < MAX_MAP_AREA_BYTES / 4 ; i++ ) { ( ( int* )frame->areabits )[i] = ( ( int* )frame->areabits )[i] ^ -1; } // copy the entity states out frame->num_entities = 0; frame->first_entity = svs.nextSnapshotEntities; for ( i = 0 ; i < entityNumbers.numSnapshotEntities ; i++ ) { ent = SV_GentityNum( entityNumbers.snapshotEntities[i] ); state = &svs.snapshotEntities[svs.nextSnapshotEntities % svs.numSnapshotEntities]; // copy entityState_s from entity/player *state = *ent->s; svs.nextSnapshotEntities++; // this should never hit, map should always be restarted first in SV_Frame if ( svs.nextSnapshotEntities >= 0x7FFFFFFE ) { Com_Error( ERR_FATAL, "svs.nextSnapshotEntities wrapped" ); } frame->num_entities++; } }
/* ============= SV_BuildClientSnapshot Decides which entities are going to be visible to the client, and copies off the playerstate and areabits. This properly handles multiple recursive portals, but the render currently doesn't. For viewing through other player's eyes, clent can be something other than client->gentity ============= */ static void SV_BuildClientSnapshot( client_t *client ) { vec3_t org; clientSnapshot_t *frame; snapshotEntityNumbers_t entityNumbers; int i; sharedEntity_t *ent; entityState_t *state; svEntity_t *svEnt; sharedEntity_t *clent; playerState_t *ps; // bump the counter used to prevent double adding sv.snapshotCounter++; // this is the frame we are creating frame = &client->frames[ client->netchan.outgoingSequence & PACKET_MASK ]; // clear everything in this snapshot entityNumbers.numSnapshotEntities = 0; Com_Memset( frame->areabits, 0, sizeof( frame->areabits ) ); frame->num_entities = 0; clent = client->gentity; if ( !clent || client->state == CS_ZOMBIE ) { return; } // grab the current playerState_t ps = SV_GameClientNum( client - svs.clients ); frame->ps = *ps; #ifdef _ONEBIT_COMBO frame->pDeltaOneBit = &ps->deltaOneBits; frame->pDeltaNumBit = &ps->deltaNumBits; #endif if (ps->m_iVehicleNum) { //get the vehicle's playerstate too then sharedEntity_t *veh = SV_GentityNum(ps->m_iVehicleNum); if (veh && veh->playerState) { //Now VMA it and we've got ourselves a playerState playerState_t *vps = ((playerState_t *)VM_ArgPtr((int)veh->playerState)); frame->vps = *vps; #ifdef _ONEBIT_COMBO frame->pDeltaOneBitVeh = &vps->deltaOneBits; frame->pDeltaNumBitVeh = &vps->deltaNumBits; #endif } } int clientNum; // never send client's own entity, because it can // be regenerated from the playerstate clientNum = frame->ps.clientNum; if ( clientNum < 0 || clientNum >= MAX_GENTITIES ) { Com_Error( ERR_DROP, "SV_SvEntityForGentity: bad gEnt" ); } svEnt = &sv.svEntities[ clientNum ]; svEnt->snapshotCounter = sv.snapshotCounter; // find the client's viewpoint VectorCopy( ps->origin, org ); org[2] += ps->viewheight; // add all the entities directly visible to the eye, which // may include portal entities that merge other viewpoints SV_AddEntitiesVisibleFromPoint( org, frame, &entityNumbers, qfalse ); // if there were portals visible, there may be out of order entities // in the list which will need to be resorted for the delta compression // to work correctly. This also catches the error condition // of an entity being included twice. qsort( entityNumbers.snapshotEntities, entityNumbers.numSnapshotEntities, sizeof( entityNumbers.snapshotEntities[0] ), SV_QsortEntityNumbers ); // now that all viewpoint's areabits have been OR'd together, invert // all of them to make it a mask vector, which is what the renderer wants for ( i = 0 ; i < MAX_MAP_AREA_BYTES/4 ; i++ ) { ((int *)frame->areabits)[i] = ((int *)frame->areabits)[i] ^ -1; } // copy the entity states out frame->num_entities = 0; frame->first_entity = svs.nextSnapshotEntities; for ( i = 0 ; i < entityNumbers.numSnapshotEntities ; i++ ) { ent = SV_GentityNum(entityNumbers.snapshotEntities[i]); state = &svs.snapshotEntities[svs.nextSnapshotEntities % svs.numSnapshotEntities]; *state = ent->s; svs.nextSnapshotEntities++; // this should never hit, map should always be restarted first in SV_Frame if ( svs.nextSnapshotEntities >= 0x7FFFFFFE ) { Com_Error(ERR_FATAL, "svs.nextSnapshotEntities wrapped"); } frame->num_entities++; } }
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 ) { 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 ); } }
/* =============== 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 ); } }
/* =============== 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_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 ); } } } }
/* ============= SV_BuildClientSnapshot Decides which entities are going to be visible to the client, and copies off the playerstate and areabits. This properly handles multiple recursive portals, but the render currently doesn't. For viewing through other player's eyes, clent can be something other than client->gentity ============= */ static clientSnapshot_t *SV_BuildClientSnapshot( client_t *client ) { vec3_t org; clientSnapshot_t *frame; snapshotEntityNumbers_t entityNumbers; int i; gentity_t *ent; entityState_t *state; gentity_t *clent; // bump the counter used to prevent double adding sv.snapshotCounter++; // this is the frame we are creating frame = &client->frames[ client->netchan.outgoingSequence & PACKET_MASK ]; // clear everything in this snapshot entityNumbers.numSnapshotEntities = 0; memset( frame->areabits, 0, sizeof( frame->areabits ) ); clent = client->gentity; if ( !clent ) { return frame; } // grab the current playerState_t frame->ps = *clent->client; // this stops the main client entity playerstate from being sent across, which has the effect of breaking // looping sounds for the main client. So I took it out. /* { int clientNum; svEntity_t *svEnt; clientNum = frame->ps.clientNum; if ( clientNum < 0 || clientNum >= MAX_GENTITIES ) { Com_Error( ERR_DROP, "SV_SvEntityForGentity: bad gEnt" ); } svEnt = &sv.svEntities[ clientNum ]; // never send client's own entity, because it can // be regenerated from the playerstate svEnt->snapshotCounter = sv.snapshotCounter; } */ // find the client's viewpoint //if in camera mode use camera position instead if ( VM_Call( CG_CAMERA_POS, org)) { //org[2] += clent->client->viewheight; } else { VectorCopy( clent->client->origin, org ); org[2] += clent->client->viewheight; //============ // need to account for lean, or areaportal doors don't draw properly... -slc if (frame->ps.leanofs != 0) { vec3_t right; //add leaning offset vec3_t v3ViewAngles; VectorCopy(clent->client->viewangles, v3ViewAngles); v3ViewAngles[2] += (float)frame->ps.leanofs/2; AngleVectors(v3ViewAngles, NULL, right, NULL); VectorMA(org, (float)frame->ps.leanofs, right, org); } //============ } VectorCopy( org, frame->ps.serverViewOrg ); VectorCopy( org, clent->client->serverViewOrg ); // add all the entities directly visible to the eye, which // may include portal entities that merge other viewpoints SV_AddEntitiesVisibleFromPoint( org, frame, &entityNumbers, qfalse ); /* //was in here for debugging- print list of all entities in snapshot when you go over the limit if ( entityNumbers.numSnapshotEntities >= 256 ) { for ( int xxx = 0; xxx < entityNumbers.numSnapshotEntities; xxx++ ) { Com_Printf("%d - ", xxx ); ge->PrintEntClassname( entityNumbers.snapshotEntities[xxx] ); } } else if ( entityNumbers.numSnapshotEntities >= 200 ) { Com_Printf(S_COLOR_RED"%d snapshot entities!", entityNumbers.numSnapshotEntities ); } else if ( entityNumbers.numSnapshotEntities >= 128 ) { Com_Printf(S_COLOR_YELLOW"%d snapshot entities", entityNumbers.numSnapshotEntities ); } */ // if there were portals visible, there may be out of order entities // in the list which will need to be resorted for the delta compression // to work correctly. This also catches the error condition // of an entity being included twice. qsort( entityNumbers.snapshotEntities, entityNumbers.numSnapshotEntities, sizeof( entityNumbers.snapshotEntities[0] ), SV_QsortEntityNumbers ); // now that all viewpoint's areabits have been OR'd together, invert // all of them to make it a mask vector, which is what the renderer wants for ( i = 0 ; i < MAX_MAP_AREA_BYTES/4 ; i++ ) { ((int *)frame->areabits)[i] = ((int *)frame->areabits)[i] ^ -1; } // copy the entity states out frame->num_entities = 0; frame->first_entity = svs.nextSnapshotEntities; for ( i = 0 ; i < entityNumbers.numSnapshotEntities ; i++ ) { ent = SV_GentityNum(entityNumbers.snapshotEntities[i]); state = &svs.snapshotEntities[svs.nextSnapshotEntities % svs.numSnapshotEntities]; *state = ent->s; svs.nextSnapshotEntities++; frame->num_entities++; } return frame; }
/* =============== 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 } } }