bool GetSoundParams(CSoundParameters *soundParams, const char *soundname, cell_t entindex) { if ( !soundname[0] ) return false; #if SOURCE_ENGINE >= SE_PORTAL2 HSOUNDSCRIPTHASH index = (HSOUNDSCRIPTHASH)soundemitterbase->GetSoundIndex(soundname); #else HSOUNDSCRIPTHANDLE index = (HSOUNDSCRIPTHANDLE)soundemitterbase->GetSoundIndex(soundname); #endif if (!soundemitterbase->IsValidIndex(index)) return false; gender_t gender = GENDER_NONE; // I don't know if gender applies to any mutliplayer games, but just in case... // Of course, if it's SOUND_FROM_PLAYER, we have no idea which gender it is int ent = SoundReferenceToIndex(entindex); if (ent > 0) { edict_t *edict = gamehelpers->EdictOfIndex(ent); if (edict != NULL && !edict->IsFree()) { IServerEntity *serverEnt = edict->GetIServerEntity(); if (serverEnt != NULL) { const char *actormodel = STRING(serverEnt->GetModelName()); gender = soundemitterbase->GetActorGender(actormodel); } } } return soundemitterbase->GetParametersForSoundEx(soundname, index, *soundParams, gender); }
IterationRetval_t EnumElement( IHandleEntity *pHandleEntity ) { // skip static props, the game DLL doesn't care about them if ( StaticPropMgr()->IsStaticProp( pHandleEntity ) ) return ITERATION_CONTINUE; IServerNetworkable *pNetworkable = static_cast< IServerNetworkable* >( pHandleEntity ); Assert( pNetworkable ); // Convert the user ID to and edict_t*... edict_t* pTouch = pNetworkable->GetEdict(); // Can't ever touch itself because it's in the other list if ( pTouch == m_pTriggerEntity ) return ITERATION_CONTINUE; IServerEntity *serverEntity = pTouch->GetIServerEntity(); if ( !serverEntity ) return ITERATION_CONTINUE; if ( m_headnode >= 0 ) { Vector vecMins, vecMaxs; CM_WorldAlignBounds( serverEntity->GetCollideable(), &vecMins, &vecMaxs ); int contents = CM_TransformedBoxContents( serverEntity->GetAbsOrigin(), vecMins, vecMaxs, m_headnode, m_origin, m_angles ); if ( !(contents & MASK_SOLID) ) return ITERATION_CONTINUE; } m_TouchedEntities.AddToTail( pTouch ); return ITERATION_CONTINUE; }
int CheckBaseHandle(CBaseHandle &hndl) { if (!hndl.IsValid()) { return -1; } int index = hndl.GetEntryIndex(); edict_t *pStoredEdict; pStoredEdict = engine->PEntityOfEntIndex(index); if (pStoredEdict == NULL) { return -1; } IServerEntity *pSE = pStoredEdict->GetIServerEntity(); if (pSE == NULL) { return -1; } if (pSE->GetRefEHandle() != hndl) { return -1; } return index; }
//----------------------------------------------------------------------------- // Purpose: Hack to allow this code to run on a client that's not connected to a server // (i.e., demo playback, or multiplayer game ) // Input : ent_num - // origin - // mins - // maxs - // Output : static void //----------------------------------------------------------------------------- static bool GetEntityOriginClientOrServer( int ent_num, Vector& origin ) { // Assume failure origin.Init(); if ( sv.active ) { edict_t *e = EDICT_NUM( ent_num ); if ( e ) { IServerEntity *serverEntity = e->GetIServerEntity(); if ( serverEntity ) { CM_WorldSpaceCenter( serverEntity->GetCollideable(), &origin ); } return true; } } else { IClientEntity *clent = entitylist->GetClientEntity( ent_num ); if ( clent ) { CM_WorldSpaceCenter( clent->GetClientCollideable(), &origin ); return true; } } return false; }
CTriggerMoved( edict_t *pTriggerEntity ) : m_TouchedEntities( 8, 8 ) { m_headnode = -1; m_pTriggerEntity = pTriggerEntity; IServerEntity *pServerTrigger = pTriggerEntity->GetIServerEntity(); if ( !pServerTrigger ) { m_origin.Init(); m_angles.Init(); } else { model_t *pModel = sv.GetModel( pServerTrigger->GetModelIndex() ); if ( pModel && pModel->type == mod_brush ) { m_headnode = SV_HullForEntity( pTriggerEntity ); } m_origin = pServerTrigger->GetAbsOrigin(); m_angles = pServerTrigger->GetAbsAngles(); } }
//----------------------------------------------------------------------------- // Purpose: returns a headnode that can be used to collide against this entity // Input : *ent - // Output : int //----------------------------------------------------------------------------- int SV_HullForEntity( edict_t *ent ) { model_t *model; IServerEntity *serverEntity = ent->GetIServerEntity(); Assert( serverEntity ); if ( !serverEntity ) return -1; int modelindex = serverEntity->GetModelIndex(); model = sv.GetModel( modelindex ); if (model->type == mod_brush) { cmodel_t *pCModel = CM_InlineModelNumber( modelindex - 1 ); return pCModel->headnode; } ICollideable *pCollideable = serverEntity->GetCollideable(); Vector vecMins = pCollideable->WorldAlignMins(); Vector vecMaxs = pCollideable->WorldAlignMaxs(); return CM_HeadnodeForBoxHull( vecMins, vecMaxs ); }
edict_t *CHalfLife2::GetHandleEntity(CBaseHandle &hndl) { if (!hndl.IsValid()) { return NULL; } int index = hndl.GetEntryIndex(); edict_t *pStoredEdict; CBaseEntity *pStoredEntity; if (!IndexToAThings(index, &pStoredEntity, &pStoredEdict)) { return NULL; } if (pStoredEdict == NULL || pStoredEntity == NULL) { return NULL; } IServerEntity *pSE = pStoredEdict->GetIServerEntity(); if (pSE == NULL) { return NULL; } if (pSE->GetRefEHandle() != hndl) { return NULL; } return pStoredEdict; }
IterationRetval_t EnumElement( IHandleEntity *pHandleEntity ) { // Static props should never be in the trigger list Assert( !StaticPropMgr()->IsStaticProp( pHandleEntity ) ); IServerNetworkable *pNetworkable = static_cast<IServerNetworkable*>( pHandleEntity ); Assert( pNetworkable ); // Convert the IHandleEntity to an edict_t*... // Context is the thing we're testing everything against edict_t* pTouch = pNetworkable->GetEdict(); // Can't bump against itself if ( pTouch == m_pEnt ) return ITERATION_CONTINUE; IServerEntity *serverEntity = pTouch->GetIServerEntity(); if ( !serverEntity ) return ITERATION_CONTINUE; // Hmmm.. everything in this list should be a trigger.... ICollideable *pCollideable = serverEntity->GetCollideable(); Assert(pCollideable->GetSolidFlags() & FSOLID_TRIGGER ); if ( (pCollideable->GetSolidFlags() & FSOLID_TRIGGER) == 0 ) return ITERATION_CONTINUE; model_t* pModel = sv.GetModel( pCollideable->GetCollisionModelIndex() ); if ( pModel && pModel->type == mod_brush ) { int headnode = SV_HullForEntity( pTouch ); int contents; if (!m_Ray.m_IsSwept) { contents = CM_TransformedBoxContents( m_Ray.m_Start, m_mins, m_maxs, headnode, serverEntity->GetAbsOrigin(), serverEntity->GetAbsAngles() ); } else { trace_t trace; CM_TransformedBoxTrace( m_Ray, headnode, MASK_ALL, serverEntity->GetAbsOrigin(), serverEntity->GetAbsAngles(), trace ); contents = trace.contents; } if ( !(contents & MASK_SOLID) ) return ITERATION_CONTINUE; } m_TouchedEntities.AddToTail( pTouch ); return ITERATION_CONTINUE; }
CTouchLinks( edict_t* pEnt, const Vector* pPrevAbsOrigin ) : m_TouchedEntities( 8, 8 ) { m_pEnt = pEnt; IServerEntity *serverEntity = pEnt->GetIServerEntity(); Assert( serverEntity ); if (pPrevAbsOrigin) { Vector mins, maxs; VectorSubtract( serverEntity->GetAbsMins(), serverEntity->GetAbsOrigin(), mins ); VectorSubtract( serverEntity->GetAbsMaxs(), serverEntity->GetAbsOrigin(), maxs ); m_Ray.Init( *pPrevAbsOrigin, serverEntity->GetAbsOrigin(), mins, maxs ); } else { m_Ray.m_Start = serverEntity->GetAbsOrigin(); m_mins = serverEntity->GetAbsMins() - m_Ray.m_Start; m_maxs = serverEntity->GetAbsMaxs() - m_Ray.m_Start; m_Ray.m_IsSwept = false; } }
/* =============== SV_LinkEdict =============== */ void SV_LinkEdict( edict_t *ent, qboolean touch_triggers, const Vector* pPrevAbsOrigin ) { IServerEntity *pServerEntity = ent->GetIServerEntity(); if ( !pServerEntity ) return; // Remove it from whatever lists it may be in at the moment // We'll re-add it below if we need to. if (ent->partition != PARTITION_INVALID_HANDLE) { SpatialPartition()->Remove( ent->partition ); } if (ent->free) return; if (ent == sv.edicts) return; // don't add the world pServerEntity->CalcAbsolutePosition(); // set the abs box pServerEntity->SetObjectCollisionBox(); // link to PVS leafs SV_BuildEntityClusterList( ent ); // Update KD tree information ICollideable *pCollide = pServerEntity->GetCollideable(); if (ent->partition == PARTITION_INVALID_HANDLE) { // Here, we haven't added the entity to the partition before // So we have to make a new partition handle. ent->partition = SpatialPartition()->CreateHandle( pCollide->GetEntityHandle() ); } // Here, we indicate the entity has moved. Note that this call does // some fast early-outing to prevent unnecessary work SpatialPartition()->ElementMoved( ent->partition, pServerEntity->GetAbsMins(), pServerEntity->GetAbsMaxs() ); // Make sure it's in the list of all entities SpatialPartition()->Insert( PARTITION_ENGINE_NON_STATIC_EDICTS, ent->partition ); SolidType_t iSolid = pCollide->GetSolid(); int nSolidFlags = pCollide->GetSolidFlags(); bool bIsSolid = IsSolid( iSolid, nSolidFlags ) || ((nSolidFlags & FSOLID_TRIGGER) != 0); if ( !bIsSolid ) { // If this ent's touch list isn't empty, it's transitioning to not solid if ( pServerEntity->IsCurrentlyTouching() ) { // mark ent so that at the end of frame it will check to see if it's no longer touch ents pServerEntity->SetCheckUntouch( true ); } return; } // Insert it into the appropriate lists. // We have to continually reinsert it because its solid type may have changed SpatialPartitionListMask_t mask = 0; if (( nSolidFlags & FSOLID_NOT_SOLID ) == 0) { mask |= PARTITION_ENGINE_SOLID_EDICTS; } if (( nSolidFlags & FSOLID_TRIGGER ) != 0 ) { mask |= PARTITION_ENGINE_TRIGGER_EDICTS; } Assert( mask != 0 ); SpatialPartition()->Insert( mask, ent->partition ); if ( iSolid == SOLID_BSP ) { model_t *model = sv.GetModel( pServerEntity->GetModelIndex() ); if ( !model && strlen( STRING( pServerEntity->GetModelName() ) ) == 0 ) { Con_DPrintf( "Inserted %s with no model\n", STRING( ent->classname ) ); return; } } // if touch_triggers, touch all entities at this node and descend for more if (touch_triggers) { // mark ent so that at the end of frame it will check to see if it's no longer touch ents pServerEntity->SetCheckUntouch( true ); // If this is a trigger that's moved, then query the solid list instead if ( mask == PARTITION_ENGINE_TRIGGER_EDICTS ) { CTriggerMoved triggerEnum( ent ); SpatialPartition()->EnumerateElementsInBox( PARTITION_ENGINE_SOLID_EDICTS, pServerEntity->GetAbsMins(), pServerEntity->GetAbsMaxs(), false, &triggerEnum ); triggerEnum.HandleTouchedEntities( ); } else { if (!pPrevAbsOrigin) { CTouchLinks touchEnumerator(ent, NULL); SpatialPartition()->EnumerateElementsInBox( PARTITION_ENGINE_TRIGGER_EDICTS, pServerEntity->GetAbsMins(), pServerEntity->GetAbsMaxs(), false, &touchEnumerator ); touchEnumerator.HandleTouchedEntities( ); } else { CTouchLinks touchEnumerator(ent, pPrevAbsOrigin); // A version that checks against an extruded ray indicating the motion SpatialPartition()->EnumerateElementsAlongRay( PARTITION_ENGINE_TRIGGER_EDICTS, touchEnumerator.m_Ray, false, &touchEnumerator ); touchEnumerator.HandleTouchedEntities( ); } } } }
//----------------------------------------------------------------------------- // 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]; } } } }
//----------------------------------------------------------------------------- // Writes the compressed packet of entities to all clients //----------------------------------------------------------------------------- void SV_ComputeClientPacks( int clientCount, client_t** clients, CFrameSnapshot *snapshot, client_frame_t **pPack ) { VPROF( "SV_ComputeClientPacks" ); ClientPackInfo_t info[MAX_CLIENTS]; // Do some setup for each client int iClient; for (iClient = 0; iClient < clientCount; ++iClient) { ClientPackInfo_t *pInfo = &info[iClient]; // This helps handle it if they turn cl_LocalNetworkBackdoor on and off (which is useful to do for profiling). SV_HandleLocalNetworkBackdoorChange( clients[iClient] ); // This will force network string table updates for local client to go through the backdoor if it's active SV_CheckDirectUpdate( clients[iClient] ); SV_ComputeClientPackInfo( clients[iClient], pInfo ); // This is the frame we are creating, i.e., the next // frame after the last one that the client acknowledged client_frame_t& frame = clients[iClient]->frames[clients[iClient]->netchan.outgoing_sequence & SV_UPDATE_MASK]; pPack[iClient] = &frame; SV_SetupPack( pPack[iClient], snapshot ); // Clear the 'transmit edict' bits for this ent for this client. memset( pInfo->m_EdictBits, 0, PAD_NUMBER( sv.num_edicts, 8 ) / 8 ); pInfo->m_pPVS = pInfo->m_PVS; pInfo->m_pEdictBits = pInfo->m_EdictBits; // Since area to area visibility is determined by each player's PVS, copy // the area network lookups into the ClientPackInfo_t pInfo->m_AreasNetworked.RemoveAll(); int areaCount = g_AreasNetworked.Count(); for ( int i = 0; i < areaCount; i++ ) { pInfo->m_AreasNetworked.AddToTail( g_AreasNetworked[ i ] ); } } // Figure out which entities should be sent. int validEdicts[MAX_EDICTS]; int nValidEdicts = 0; for ( int iEdict=0; iEdict < sv.num_edicts; iEdict++ ) { edict_t* ent = SV_GetEdictToTransmit( iEdict, snapshot ); if ( !ent || !ent->m_pEnt ) continue; // Get the edict send table SendTable* pSendTable = GetEntSendTable( ent ); assert( pSendTable ); if ( !pSendTable ) continue; validEdicts[nValidEdicts++] = iEdict; for ( int iClient=0; iClient < clientCount; iClient++ ) { ClientPackInfo_t *pInfo = &info[iClient]; Assert( pInfo ); int areaCount = pInfo->m_AreasNetworked.Count(); for ( int iArea=0; iArea < areaCount; iArea++ ) { // If the edict is already marked to send to this client, we don't need to call CheckTransmit on it. if ( pInfo->WillTransmit( iEdict ) ) break; pInfo->m_iArea = pInfo->m_AreasNetworked[ iArea ]; CServerDTITimer timer( pSendTable, SERVERDTI_SHOULDTRANSMIT ); ent->m_pEnt->CheckTransmit( pInfo ); } } } #ifndef SWDS int saveTicks = cl.tickcount; #endif if ( g_pLocalNetworkBackdoor ) { g_pLocalNetworkBackdoor->StartEntityStateUpdate(); #ifndef SWDS cl.tickcount = sv.tickcount; g_ClientGlobalVariables.tickcount = cl.tickcount; g_ClientGlobalVariables.curtime = cl.gettime(); #endif } // Send client all active entities in the pvs for ( int iValidEdict=0; iValidEdict < nValidEdicts; iValidEdict++ ) { int e = validEdicts[iValidEdict]; edict_t* ent = SV_GetEdictToTransmit(e, snapshot); SendTable* pSendTable = GetEntSendTable( ent ); // Check to see if the entity changed this frame... EntityChange_t changeType = ent->m_pEnt->DetectNetworkStateChanges(); ServerDTI_RegisterNetworkStateChange( pSendTable, changeType ); // Have we packed this entity this frame? bool packedThisEntity = false; IServerEntity *serverEntity = ent->GetIServerEntity(); if ( serverEntity ) { serverEntity->SetSentLastFrame( false ); } // Compute the entity bit + byte for the entity_in_pvs bitfield int entityByte = e >> 3; byte entityBit = ( 1 << ( e & 7 ) ); for (int i = 0; i < clientCount; ++i ) { ClientPackInfo_t *pInfo = &info[i]; if ( g_pLocalNetworkBackdoor ) { // this is a bit of a hack to ensure that we get a "preview" of the // packet timstamp that the server will send so that things that // are encoded relative to packet time will be correct int serialNum = ent->m_pEnt->GetRefEHandle().GetSerialNumber() & (1 << NUM_NETWORKED_EHANDLE_SERIAL_NUMBER_BITS) - 1; if( pInfo->WillTransmit( e ) ) { CServerDTITimer timer( pSendTable, SERVERDTI_ENCODE ); // If we're using the fast path for a single-player game, just pass the entity // directly over to the client. ServerClass *pClass = ent->m_pEnt->GetServerClass(); g_pLocalNetworkBackdoor->EntState( e, serialNum, pClass->m_ClassID, pSendTable, ent->m_pEnt, changeType != ENTITY_CHANGE_NONE ); // Indicate we've actually put the changes into the pack ent->m_pEnt->ResetNetworkStateChanges(); } else { // Notify the client that the ent is still alive, but its ShouldTransmit returned false. g_pLocalNetworkBackdoor->EntityDormant( e, serialNum ); } } else { if( !pInfo->WillTransmit( e ) ) continue; // Mark that this player will have seen this entity created // (i.e., sent, at least once) ent->entity_created |= info[i].m_ClientBit; pPack[i]->entity_in_pvs[ entityByte ] |= entityBit; // Check to see if it's already been packed this frame... // If it hasn't, then pack it in, baby! if ( !packedThisEntity ) { packedThisEntity = true; SV_PackEntity( e, ent, pSendTable, changeType, snapshot ); ent->m_pEnt->ResetNetworkStateChanges(); } } // We've got one more entity in this pack. ++pPack[i]->entities.num_entities; // Now this is our biggest ent index in the pack pPack[i]->entities.max_entities = e; } } if ( g_pLocalNetworkBackdoor ) { g_pLocalNetworkBackdoor->EndEntityStateUpdate(); #ifndef SWDS cl.tickcount = saveTicks; g_ClientGlobalVariables.tickcount = cl.tickcount; g_ClientGlobalVariables.curtime = cl.gettime(); #endif } }