/** Unregisters a selection of entities from physics calculations. Returns 0 upon success or a positive number equal to the amount of entities that it failed to unregister. */ int PhysicsManager::UnregisterEntities(List<Entity*> & targetEntities){ int failedUnregistrations = 0; for (int i = 0; i < targetEntities.Size(); ++i){ if (UnregisterEntity(targetEntities[i]) != 0) ++failedUnregistrations; } return failedUnregistrations; }
/* ================ idGameLocal::ClientReadSnapshot ================ */ void idGameLocal::ClientReadSnapshot( const idSnapShot& ss ) { if( GetLocalClientNum() < 0 ) { return; } // if prediction is off, enable local client smoothing //localPlayer->SetSelfSmooth( dupeUsercmds > 2 ); // clear any debug lines from a previous frame gameRenderWorld->DebugClearLines( time ); // clear any debug polygons from a previous frame gameRenderWorld->DebugClearPolygons( time ); SelectTimeGroup( false ); // so that StartSound/StopSound doesn't risk skipping isNewFrame = true; // clear the snapshot entity list snapshotEntities.Clear(); // read all entities from the snapshot for( int o = 0; o < ss.NumObjects(); o++ ) { idBitMsg msg; int snapObjectNum = ss.GetObjectMsgByIndex( o, msg ); if( snapObjectNum < 0 ) { assert( false ); continue; } if( snapObjectNum == SNAP_GAMESTATE ) { mpGame.ReadFromSnapshot( msg ); continue; } if( snapObjectNum == SNAP_SHADERPARMS ) { for( int i = 0; i < MAX_GLOBAL_SHADER_PARMS; i++ ) { globalShaderParms[i] = msg.ReadFloat(); } continue; } if( snapObjectNum == SNAP_PORTALS ) { // update portals for opened doors int numPortals = msg.ReadLong(); assert( numPortals == gameRenderWorld->NumPortals() ); for( int i = 0; i < numPortals; i++ ) { gameRenderWorld->SetPortalState( ( qhandle_t )( i + 1 ), msg.ReadBits( NUM_RENDER_PORTAL_BITS ) ); } continue; } if( snapObjectNum >= SNAP_PLAYERSTATE && snapObjectNum < SNAP_PLAYERSTATE_END ) { int playerNumber = snapObjectNum - SNAP_PLAYERSTATE; idPlayer* otherPlayer = static_cast< idPlayer* >( entities[ playerNumber ] ); // Don't process Player Snapshots that are disconnected. const int lobbyIndex = session->GetActingGameStateLobbyBase().GetLobbyUserIndexFromLobbyUserID( lobbyUserIDs[ playerNumber ] ); if( lobbyIndex < 0 || session->GetActingGameStateLobbyBase().IsLobbyUserConnected( lobbyIndex ) == false ) { continue; } if( otherPlayer != NULL ) { otherPlayer->ReadPlayerStateFromSnapshot( msg ); if( otherPlayer != entities[ GetLocalClientNum() ] ) // This happens when we spectate another player { idWeapon* weap = otherPlayer->weapon.GetEntity(); if( weap && ( weap->GetRenderEntity()->bounds[0] == weap->GetRenderEntity()->bounds[1] ) ) { // update the weapon's viewmodel bounds so that the model doesn't flicker in the spectator's view weap->GetAnimator()->GetBounds( gameLocal.time, weap->GetRenderEntity()->bounds ); weap->UpdateVisuals(); } } } continue; } if( snapObjectNum >= SNAP_LAST_CLIENT_FRAME && snapObjectNum < SNAP_LAST_CLIENT_FRAME_END ) { int playerNumber = snapObjectNum - SNAP_LAST_CLIENT_FRAME; // Don't process Player Snapshots that are disconnected. const int lobbyIndex = session->GetActingGameStateLobbyBase().GetLobbyUserIndexFromLobbyUserID( lobbyUserIDs[ playerNumber ] ); if( lobbyIndex < 0 || session->GetActingGameStateLobbyBase().IsLobbyUserConnected( lobbyIndex ) == false ) { continue; } usercmdLastClientMilliseconds[playerNumber] = msg.ReadLong(); continue; } if( snapObjectNum < SNAP_ENTITIES || snapObjectNum >= SNAP_ENTITIES_END ) { continue; } int entityNumber = snapObjectNum - SNAP_ENTITIES; if( msg.GetSize() == 0 ) { delete entities[entityNumber]; continue; } bool debug = false; int spawnId = msg.ReadBits( 32 - GENTITYNUM_BITS ); int typeNum = msg.ReadBits( idClass::GetTypeNumBits() ); int entityDefNumber = ClientRemapDecl( DECL_ENTITYDEF, msg.ReadBits( entityDefBits ) ); const int predictedKey = msg.ReadBits( 32 ); idTypeInfo* typeInfo = idClass::GetType( typeNum ); if( !typeInfo ) { idLib::Error( "Unknown type number %d for entity %d with class number %d", typeNum, entityNumber, entityDefNumber ); } // If there is no entity on this client, but the server's entity matches a predictionKey, move the client's // predicted entity to the normal, replicated area in the entity list. if( entities[entityNumber] == NULL ) { if( predictedKey != idEntity::INVALID_PREDICTION_KEY ) { idLib::PrintfIf( debug, "Looking for predicted key %d.\n", predictedKey ); idEntity* predictedEntity = FindPredictedEntity( predictedKey, typeInfo ); if( predictedEntity != NULL ) { // This presentable better be in the proper place in the list or bad things will happen if we move this presentable around assert( predictedEntity->GetEntityNumber() >= ENTITYNUM_FIRST_NON_REPLICATED ); continue; #if 0 idProjectile* predictedProjectile = idProjectile::CastTo( predictedEntity ); if( predictedProjectile != NULL ) { for( int i = 0; i < MAX_PLAYERS; i++ ) { if( entities[i] == NULL ) { continue; } idPlayer* player = idPlayer::CastTo( entities[i] ); if( player != NULL ) { if( player->GetUniqueProjectile() == predictedProjectile ) { // Set new spawn id player->TrackUniqueProjectile( predictedProjectile ); } } } } idLib::PrintfIf( debug, "Found predicted EntNum old:%i new:%i spawnID:%i\n", predictedEntity->GetEntityNumber(), entityNumber, spawnId >> GENTITYNUM_BITS ); // move the entity RemoveEntityFromHash( predictedEntity->name.c_str(), predictedEntity ); UnregisterEntity( predictedEntity ); assert( entities[predictedEntity->GetEntityNumber()] == NULL ); predictedEntity->spawnArgs.SetInt( "spawn_entnum", entityNumber ); RegisterEntity( predictedEntity, spawnId, predictedEntity->spawnArgs ); predictedEntity->SetName( "" ); // now mark us as no longer predicted predictedEntity->BecomeReplicated(); #endif } //TODO make this work with non-client preditced entities /* else { idLib::Warning( "Could not find predicted entity - key: %d. EntityIndex: %d", predictedKey, entityNum ); } */ } }