aboutsummaryrefslogtreecommitdiff
path: root/mp/src/game/shared/physics_saverestore.cpp
diff options
context:
space:
mode:
authorJørgen P. Tjernø <[email protected]>2013-12-02 19:31:46 -0800
committerJørgen P. Tjernø <[email protected]>2013-12-02 19:46:31 -0800
commitf56bb35301836e56582a575a75864392a0177875 (patch)
treede61ddd39de3e7df52759711950b4c288592f0dc /mp/src/game/shared/physics_saverestore.cpp
parentMark some more files as text. (diff)
downloadsource-sdk-2013-f56bb35301836e56582a575a75864392a0177875.tar.xz
source-sdk-2013-f56bb35301836e56582a575a75864392a0177875.zip
Fix line endings. WHAMMY.
Diffstat (limited to 'mp/src/game/shared/physics_saverestore.cpp')
-rw-r--r--mp/src/game/shared/physics_saverestore.cpp1656
1 files changed, 828 insertions, 828 deletions
diff --git a/mp/src/game/shared/physics_saverestore.cpp b/mp/src/game/shared/physics_saverestore.cpp
index 14892ab7..434be1f4 100644
--- a/mp/src/game/shared/physics_saverestore.cpp
+++ b/mp/src/game/shared/physics_saverestore.cpp
@@ -1,828 +1,828 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-// $NoKeywords: $
-//=============================================================================//
-
-#include "cbase.h"
-
-#include "utlpriorityqueue.h"
-#include "utlmap.h"
-#include "isaverestore.h"
-#include "physics.h"
-#include "physics_saverestore.h"
-#include "saverestoretypes.h"
-#include "gamestringpool.h"
-#include "datacache/imdlcache.h"
-
-#if !defined( CLIENT_DLL )
-#include "entitylist.h"
-#endif
-
-// memdbgon must be the last include file in a .cpp file!!!
-#include "tier0/memdbgon.h"
-
-//-----------------------------------------------------------------------------
-
-static short PHYS_SAVE_RESTORE_VERSION = 5;
-
-struct PhysBlockHeader_t
-{
- int nSaved;
- IPhysicsObject *pWorldObject;
-
- inline void Clear()
- {
- nSaved = 0;
- pWorldObject = 0;
- }
-
- DECLARE_SIMPLE_DATADESC();
-};
-BEGIN_SIMPLE_DATADESC( PhysBlockHeader_t )
- DEFINE_FIELD( nSaved, FIELD_INTEGER ),
- // NOTE: We want to save the actual address here for remapping, so use an integer
- DEFINE_FIELD( pWorldObject, FIELD_INTEGER ),
-END_DATADESC()
-
-#if defined(_STATIC_LINKED) && defined(CLIENT_DLL)
-const char *g_ppszPhysTypeNames[PIID_NUM_TYPES] =
-{
- "Unknown",
- "IPhysicsObject",
- "IPhysicsFluidController",
- "IPhysicsSpring",
- "IPhysicsConstraintGroup",
- "IPhysicsConstraint",
- "IPhysicsShadowController",
- "IPhysicsPlayerController",
- "IPhysicsMotionController",
- "IPhysicsVehicleController",
-};
-#endif
-
-//-----------------------------------------------------------------------------
-
-struct BBox_t
-{
- Vector mins, maxs;
-};
-
-struct Sphere_t
-{
- float radius;
-};
-
-
-struct PhysObjectHeader_t
-{
- PhysObjectHeader_t()
- {
- memset( this, 0, sizeof(*this) );
- }
-
- PhysInterfaceId_t type;
- EHANDLE hEntity;
- string_t fieldName;
- int nObjects;
- string_t modelName;
- BBox_t bbox;
- Sphere_t sphere;
- int iCollide;
-
- DECLARE_SIMPLE_DATADESC();
-};
-
-BEGIN_SIMPLE_DATADESC( PhysObjectHeader_t )
- DEFINE_FIELD( type, FIELD_INTEGER ),
- DEFINE_FIELD( hEntity, FIELD_EHANDLE ),
- DEFINE_FIELD( fieldName, FIELD_STRING ),
- DEFINE_FIELD( nObjects, FIELD_INTEGER ),
- DEFINE_FIELD( modelName, FIELD_STRING ),
-
- // Silence, Classcheck!
- // DEFINE_FIELD( bbox, BBox_t ),
- // DEFINE_FIELD( sphere, Sphere_t ),
-
- DEFINE_FIELD( bbox.mins, FIELD_VECTOR ),
- DEFINE_FIELD( bbox.maxs, FIELD_VECTOR ),
- DEFINE_FIELD( sphere.radius, FIELD_FLOAT ),
- DEFINE_FIELD( iCollide, FIELD_INTEGER ),
-END_DATADESC()
-
-//-----------------------------------------------------------------------------
-// Purpose: The central manager of physics save/load
-//
-
-class CPhysSaveRestoreBlockHandler : public CDefSaveRestoreBlockHandler,
- public IPhysSaveRestoreManager
-#if !defined( CLIENT_DLL )
- , public IEntityListener
-#endif
-{
- struct QueuedItem_t;
-public:
- CPhysSaveRestoreBlockHandler()
- {
- m_QueuedSaves.SetLessFunc( SaveQueueFunc );
- SetDefLessFunc( m_QueuedRestores );
- SetDefLessFunc( m_PhysObjectModels );
- SetDefLessFunc( m_PhysObjectCustomModels );
- SetDefLessFunc( m_PhysCollideBBoxModels );
- }
-
- const char *GetBlockName()
- {
- return "Physics";
- }
-
- //---------------------------------
-
- virtual void PreSave( CSaveRestoreData * )
- {
- m_blockHeader.Clear();
- }
-
- //---------------------------------
-
- virtual void Save( ISave *pSave )
- {
- m_blockHeader.pWorldObject = g_PhysWorldObject;
- m_blockHeader.nSaved = m_QueuedSaves.Count();
-
- while ( m_QueuedSaves.Count() )
- {
- const QueuedItem_t &item = m_QueuedSaves.ElementAtHead();
-
- CBaseEntity *pOwner = item.header.hEntity.Get();
-
- if ( pOwner )
- {
- pSave->WriteAll( &item.header );
- pSave->StartBlock(); // Need block here in case entity is NULL on load
- if ( item.header.nObjects )
- {
- for ( int i = 0; i < item.header.nObjects; i++ )
- {
- // Starting a block here allows the implementation of any individual physics
- // class save/load to change non-trivially while retaining the overall
- // integrity of the savefile.
- pSave->StartBlock();
- SavePhysicsObject( pSave, pOwner, item.ppPhysObj[i], item.header.type );
- pSave->EndBlock();
- }
- }
- // else, it will simply be recreated on restore
- pSave->EndBlock();
- }
- m_QueuedSaves.RemoveAtHead();
- }
- }
-
- //---------------------------------
-
- virtual void WriteSaveHeaders( ISave *pSave )
- {
- pSave->WriteShort( &PHYS_SAVE_RESTORE_VERSION );
- pSave->WriteAll( &m_blockHeader );
- }
-
- //---------------------------------
-
- virtual void PostSave()
- {
- m_QueuedSaves.Purge();
- }
-
- //---------------------------------
-
- virtual void PreRestore()
- {
-#if !defined( CLIENT_DLL )
- gEntList.AddListenerEntity( this );
-#endif
-
- // UNDONE: This never runs!!!!
- if ( physenv )
- {
- physprerestoreparams_t params;
- params.recreatedObjectCount = 0;
- physenv->PreRestore( params );
- }
- }
-
- //---------------------------------
-
- virtual void ReadRestoreHeaders( IRestore *pRestore )
- {
- // No reason why any future version shouldn't try to retain backward compatability. The default here is to not do so.
- short version = pRestore->ReadShort();
- m_fDoLoad = ( version == PHYS_SAVE_RESTORE_VERSION );
-
- pRestore->ReadAll( &m_blockHeader );
- }
-
- //---------------------------------
-
- virtual void Restore( IRestore *pRestore, bool )
- {
- if ( m_fDoLoad )
- {
- if ( physenv )
- {
- physprerestoreparams_t params;
- params.recreatedObjectCount = 1;
- params.recreatedObjectList[0].pNewObject = g_PhysWorldObject;
- params.recreatedObjectList[0].pOldObject = m_blockHeader.pWorldObject;
- physenv->PreRestore( params );
- }
-
- PhysObjectHeader_t header;
-
- while ( m_blockHeader.nSaved-- )
- {
- pRestore->ReadAll( &header );
- pRestore->StartBlock();
-
- if ( header.hEntity != NULL )
- {
- RestoreBlock( pRestore, header );
- }
-
- pRestore->EndBlock();
- }
- }
- }
-
- //---------------------------------
-
- void RestoreBlock( IRestore *pRestore, const PhysObjectHeader_t &header )
- {
- CBaseEntity * pOwner = header.hEntity.Get();
- unsigned short iQueued = m_QueuedRestores.Find( pOwner );
-
- if ( iQueued != m_QueuedRestores.InvalidIndex() )
- {
- MDLCACHE_CRITICAL_SECTION();
- if ( pOwner->ShouldSavePhysics() && header.nObjects > 0 )
- {
- QueuedItem_t *pItem = m_QueuedRestores[iQueued]->FindItem( header.fieldName );
-
- if ( pItem )
- {
- int nObjects = MIN( header.nObjects, pItem->header.nObjects );
- if ( pItem->header.type == PIID_IPHYSICSOBJECT && nObjects == 1 )
- {
- RestorePhysicsObjectAndModel( pRestore, header, pItem, nObjects );
- }
- else
- {
- void **ppPhysObj = pItem->ppPhysObj;
-
- for ( int i = 0; i < nObjects; i++ )
- {
- pRestore->StartBlock();
- RestorePhysicsObject( pRestore, header, ppPhysObj + i );
- pRestore->EndBlock();
- if ( header.type == PIID_IPHYSICSMOTIONCONTROLLER )
- {
- void *pObj = ppPhysObj[i];
- IPhysicsMotionController *pController = (IPhysicsMotionController *)pObj;
- if ( pController )
- {
- // If the entity is the motion callback handler, then automatically set it
- // NOTE: This is usually the case
- IMotionEvent *pEvent = dynamic_cast<IMotionEvent *>(pOwner);
- if ( pEvent )
- {
- pController->SetEventHandler( pEvent );
- }
- }
- }
- }
- }
- }
- }
- else
- pOwner->CreateVPhysics();
- }
- }
-
-
- //---------------------------------
-
- void RestorePhysicsObjectAndModel( IRestore *pRestore, const PhysObjectHeader_t &header, CPhysSaveRestoreBlockHandler::QueuedItem_t *pItem, int nObjects )
- {
- if ( nObjects == 1 )
- {
- pRestore->StartBlock();
-
- CPhysCollide *pPhysCollide = NULL;
- int modelIndex = -1;
- bool fCustomCollide = false;
-
- if ( header.modelName != NULL_STRING )
- {
- CBaseEntity *pGlobalEntity = header.hEntity;
-#if !defined( CLIENT_DLL )
- if ( NULL_STRING != pGlobalEntity->m_iGlobalname )
- {
- modelIndex = pGlobalEntity->GetModelIndex();
- }
- else
-#endif
- {
- modelIndex = modelinfo->GetModelIndex( STRING( header.modelName ) );
- pGlobalEntity = NULL;
- }
-
- if ( modelIndex != -1 )
- {
- vcollide_t *pCollide = modelinfo->GetVCollide( modelIndex );
- if ( pCollide )
- {
- if ( pCollide->solidCount > 0 && pCollide->solids && header.iCollide < pCollide->solidCount )
- pPhysCollide = pCollide->solids[header.iCollide];
- }
- }
- }
- else if ( header.bbox.mins != vec3_origin || header.bbox.maxs != vec3_origin )
- {
- pPhysCollide = PhysCreateBbox( header.bbox.mins, header.bbox.maxs );
- fCustomCollide = true;
- }
- else if ( header.sphere.radius != 0 )
- {
- // HACKHACK: Handle spheres here!!!
- if ( !(*pItem->ppPhysObj) )
- {
- RestorePhysicsObject( pRestore, header, pItem->ppPhysObj, NULL );
- }
- return;
- }
-
- if ( pPhysCollide )
- {
- if ( !(*pItem->ppPhysObj) )
- {
- RestorePhysicsObject( pRestore, header, pItem->ppPhysObj, pPhysCollide );
- if ( (*pItem->ppPhysObj) )
- {
- IPhysicsObject *pObject = (IPhysicsObject *)(*pItem->ppPhysObj);
- if ( !fCustomCollide )
- {
- AssociateModel( pObject, modelIndex );
- }
- else
- {
- AssociateModel( pObject, pPhysCollide );
- }
- }
- else
- DevMsg( "Failed to restore physics object\n" );
- }
- else
- DevMsg( "Physics object pointer unexpectedly non-null before restore. Should be creating physics object in CreatePhysics()?\n" );
- }
- else
- DevMsg( "Failed to reestablish collision model for object\n" );
-
- pRestore->EndBlock();
- }
- else
- DevMsg( "Don't know how to reconsitite models for physobj array \n" );
- }
-
- //---------------------------------
-
- virtual void PostRestore()
- {
- if ( physenv )
- physenv->PostRestore();
-
- unsigned short i = m_QueuedRestores.FirstInorder();
- while ( i != m_QueuedRestores.InvalidIndex() )
- {
- delete m_QueuedRestores[i];
- i = m_QueuedRestores.NextInorder( i );
- }
-
- m_QueuedRestores.RemoveAll();
-#if !defined( CLIENT_DLL )
- gEntList.RemoveListenerEntity( this );
-#endif
- }
-
- //---------------------------------
-
- void QueueSave( CBaseEntity *pOwner, typedescription_t *pTypeDesc, void **ppPhysObj, PhysInterfaceId_t type )
- {
- if ( !pOwner )
- return;
-
- bool fOnlyNotingExistence = !pOwner->ShouldSavePhysics();
-
- QueuedItem_t item;
-
- item.ppPhysObj = ppPhysObj;
- item.header.hEntity = pOwner;
- item.header.type = type;
- item.header.nObjects = ( !fOnlyNotingExistence ) ? pTypeDesc->fieldSize : 0;
- item.header.fieldName = AllocPooledString( pTypeDesc->fieldName );
- // A pooled string is used here because there is no way
- // right now to save a non-string_t string and have it
- // compressed in the save symbol tables. Furthermore,
- // the field name would normally be in the string
- // pool anyway. (toml 12-10-02)
- item.header.modelName = NULL_STRING;
- memset( &item.header.bbox, 0, sizeof( item.header.bbox ) );
- item.header.sphere.radius = 0;
-
- if ( !fOnlyNotingExistence && type == PIID_IPHYSICSOBJECT )
- {
- // Don't doing the box thing for things like wheels on cars
- IPhysicsObject *pPhysObj = (IPhysicsObject *)(*ppPhysObj);
-
- if ( pPhysObj )
- {
- item.header.modelName = GetModelName( pPhysObj );
- item.header.iCollide = physcollision->CollideIndex( pPhysObj->GetCollide() );
- if ( item.header.modelName == NULL_STRING )
- {
- BBox_t *pBBox = GetBBox( pPhysObj );
- if ( pBBox != NULL )
- {
- item.header.bbox = *pBBox;
- }
- else
- {
- if ( pPhysObj && pPhysObj->GetSphereRadius() != 0 )
- {
- item.header.sphere.radius = pPhysObj->GetSphereRadius();
- }
- else
- {
- DevMsg( "Don't know how to save model for physics object (class \"%s\")\n", pOwner->GetClassname() );
- }
- }
- }
- }
- }
-
- m_QueuedSaves.Insert( item );
- }
-
- //---------------------------------
-
- void QueueRestore( CBaseEntity *pOwner, typedescription_t *pTypeDesc, void **ppPhysObj, PhysInterfaceId_t type )
- {
- CEntityRestoreSet *pEntitySet = NULL;
- unsigned short iEntitySet = m_QueuedRestores.Find( pOwner );
-
- if ( iEntitySet != m_QueuedRestores.InvalidIndex() )
- {
- pEntitySet = m_QueuedRestores[iEntitySet];
- }
- else
- {
- pEntitySet = new CEntityRestoreSet;
- m_QueuedRestores.Insert( pOwner, pEntitySet );
- }
-
- pEntitySet->Add( pOwner, pTypeDesc, ppPhysObj, type );
-
- memset( ppPhysObj, 0, pTypeDesc->fieldSize * sizeof( void * ) );
- }
-
- //---------------------------------
-
- void SavePhysicsObject( ISave *pSave, CBaseEntity *pOwner, void *pObject, PhysInterfaceId_t type )
- {
- if ( physenv )
- {
- if ( !pObject )
- return;
- physsaveparams_t params = { pSave, pObject, type };
- physenv->Save( params );
- }
- }
-
- //---------------------------------
-
- void RestorePhysicsObject( IRestore *pRestore, const PhysObjectHeader_t &header, void **ppObject, const CPhysCollide *pCollide = NULL )
- {
- if ( physenv )
- {
- physrestoreparams_t params = { pRestore, ppObject, header.type, header.hEntity.Get(), STRING(header.modelName), pCollide, physenv, physgametrace };
- physenv->Restore( params );
- }
- }
-#if !defined( CLIENT_DLL )
- //-----------------------------------------------------
- // IEntityListener methods
- // This object is only a listener during restore
- virtual void OnEntityCreated( CBaseEntity *pEntity )
- {
- }
-
- //---------------------------------
-
- virtual void OnEntityDeleted( CBaseEntity *pEntity )
- {
- unsigned short iEntitySet = m_QueuedRestores.Find( pEntity );
-
- if ( iEntitySet != m_QueuedRestores.InvalidIndex() )
- {
- delete m_QueuedRestores[iEntitySet];
- m_QueuedRestores.RemoveAt( iEntitySet );
- }
- }
-#endif
-
- //-----------------------------------------------------
- // IPhysSaveRestoreManager methods
-
- virtual void NoteBBox( const Vector &mins, const Vector &maxs, CPhysCollide *pCollide )
- {
- if ( pCollide && m_PhysCollideBBoxModels.Find( pCollide ) == m_PhysCollideBBoxModels.InvalidIndex() )
- {
- BBox_t box;
- box.mins = mins;
- box.maxs = maxs;
- m_PhysCollideBBoxModels.Insert( pCollide, box );
- }
- }
-
- //---------------------------------
-
- virtual void AssociateModel( IPhysicsObject *pObject, int modelIndex )
- {
- Assert( m_PhysObjectModels.Find( pObject ) == m_PhysObjectModels.InvalidIndex() );
- m_PhysObjectModels.Insert( pObject, modelIndex );
- }
-
- //---------------------------------
-
- virtual void AssociateModel( IPhysicsObject *pObject, const CPhysCollide *pModel )
- {
- Assert( m_PhysObjectCustomModels.Find( pObject ) == m_PhysObjectCustomModels.InvalidIndex() );
- m_PhysObjectCustomModels.Insert( pObject, pModel );
- }
-
- //---------------------------------
-
- virtual void ForgetModel( IPhysicsObject *pObject )
- {
- if ( !m_PhysObjectModels.Remove( pObject ) )
- m_PhysObjectCustomModels.Remove( pObject );
- }
-
- //---------------------------------
-
- virtual void ForgetAllModels()
- {
- m_PhysObjectModels.RemoveAll();
- m_PhysObjectCustomModels.RemoveAll();
- m_PhysCollideBBoxModels.RemoveAll();
- }
-
- //---------------------------------
-
- string_t GetModelName( IPhysicsObject *pObject )
- {
- int i = m_PhysObjectModels.Find( pObject );
- if ( i == m_PhysObjectModels.InvalidIndex() )
- return NULL_STRING;
- return AllocPooledString( modelinfo->GetModelName( modelinfo->GetModel( m_PhysObjectModels[i] ) ) );
- }
-
- //---------------------------------
-
- BBox_t * GetBBox( IPhysicsObject *pObject )
- {
- int i = m_PhysObjectCustomModels.Find( pObject );
- if ( i == m_PhysObjectCustomModels.InvalidIndex() )
- return NULL;
- i = m_PhysCollideBBoxModels.Find( m_PhysObjectCustomModels[i] );
- if ( i == m_PhysCollideBBoxModels.InvalidIndex() )
- return NULL;
- return &(m_PhysCollideBBoxModels[i]);
- }
-
- //---------------------------------
-
-private:
- struct QueuedItem_t
- {
- PhysObjectHeader_t header;
- void ** ppPhysObj;
- };
-
- class CEntityRestoreSet : public CUtlVector<QueuedItem_t>
- {
- public:
- int Add( CBaseEntity *pOwner, typedescription_t *pTypeDesc, void **ppPhysObj, PhysInterfaceId_t type )
- {
- int i = AddToTail();
-
- Assert( ppPhysObj );
- Assert( *ppPhysObj == NULL ); // expected field to have been cleared
- Assert( pOwner );
-
- QueuedItem_t &item = Element( i );
-
- item.ppPhysObj = ppPhysObj;
- item.header.hEntity = pOwner;
- item.header.type = type;
- item.header.nObjects = pTypeDesc->fieldSize;
- item.header.fieldName = AllocPooledString( pTypeDesc->fieldName ); // See comment in CPhysSaveRestoreBlockHandler::QueueSave()
-
- return i;
- }
-
- QueuedItem_t *FindItem( string_t itemFieldName )
- {
- // generally, the set is very small, usually one, so linear search is not too gruesome;
- for ( int i = 0; i < Count(); i++ )
- {
- string_t testName = Element(i).header.fieldName;
- Assert( ( testName == itemFieldName && strcmp( STRING( testName ), STRING( itemFieldName ) ) == 0 ) ||
- ( testName != itemFieldName && strcmp( STRING( testName ), STRING( itemFieldName ) ) != 0 ) );
-
- if ( testName == itemFieldName )
- return &(Element(i));
- }
- return NULL;
- }
- };
-
- //---------------------------------
-
- static bool SaveQueueFunc( const QueuedItem_t &left, const QueuedItem_t &right )
- {
- if ( left.header.type == right.header.type )
- return ( left.header.hEntity->entindex() > right.header.hEntity->entindex() );
-
- return ( left.header.type > right.header.type );
- }
-
- //---------------------------------
-
- CUtlPriorityQueue<QueuedItem_t> m_QueuedSaves;
- CUtlMap<CBaseEntity *, CEntityRestoreSet *> m_QueuedRestores;
- bool m_fDoLoad;
-
- //---------------------------------
-
- CUtlMap<IPhysicsObject *, int> m_PhysObjectModels;
- CUtlMap<IPhysicsObject *, const CPhysCollide *> m_PhysObjectCustomModels;
- CUtlMap<const CPhysCollide *, BBox_t> m_PhysCollideBBoxModels;
-
- //---------------------------------
-
- PhysBlockHeader_t m_blockHeader;
-};
-
-//-----------------------------------------------------------------------------
-
-CPhysSaveRestoreBlockHandler g_PhysSaveRestoreBlockHandler;
-
-IPhysSaveRestoreManager *g_pPhysSaveRestoreManager = &g_PhysSaveRestoreBlockHandler;
-
-//-------------------------------------
-
-ISaveRestoreBlockHandler *GetPhysSaveRestoreBlockHandler()
-{
- return &g_PhysSaveRestoreBlockHandler;
-}
-
-static bool IsValidEntityPointer( void *ptr )
-{
-#if !defined( CLIENT_DLL )
- return gEntList.IsEntityPtr( ptr );
-#else
- // Walk entities looking for pointer
- int c = ClientEntityList().GetHighestEntityIndex();
- for ( int i = 0; i <= c; i++ )
- {
- CBaseEntity *e = ClientEntityList().GetBaseEntity( i );
- if ( !e )
- continue;
-
- if ( e == ptr )
- return true;
- }
- return false;
-#endif
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Classifies field and queues it up for physics save/restore.
-//
-
-class CPhysObjSaveRestoreOps : public CDefSaveRestoreOps
-{
-public:
- virtual void Save( const SaveRestoreFieldInfo_t &fieldInfo, ISave *pSave )
- {
- CBaseEntity *pOwnerEntity = pSave->GetGameSaveRestoreInfo()->GetCurrentEntityContext();
-
- bool bFoundEntity = true;
-
- if ( IsValidEntityPointer(pOwnerEntity) == false )
- {
- bFoundEntity = false;
-
-#if defined( CLIENT_DLL )
- pOwnerEntity = ClientEntityList().GetBaseEntityFromHandle( pOwnerEntity->GetRefEHandle() );
-
- if ( pOwnerEntity )
- {
- bFoundEntity = true;
- }
-#endif
- }
-
- AssertMsg( pOwnerEntity && bFoundEntity == true, "Physics save/load is only suitable for entities" );
-
- if ( m_type == PIID_UNKNOWN )
- {
- AssertMsg( 0, "Unknown physics save/load type");
- return;
- }
- g_PhysSaveRestoreBlockHandler.QueueSave( pOwnerEntity, fieldInfo.pTypeDesc, (void **)fieldInfo.pField, m_type );
- }
-
- virtual void Restore( const SaveRestoreFieldInfo_t &fieldInfo, IRestore *pRestore )
- {
- CBaseEntity *pOwnerEntity = pRestore->GetGameSaveRestoreInfo()->GetCurrentEntityContext();
-
- bool bFoundEntity = true;
-
- if ( IsValidEntityPointer(pOwnerEntity) == false )
- {
- bFoundEntity = false;
-
-#if defined( CLIENT_DLL )
- pOwnerEntity = ClientEntityList().GetBaseEntityFromHandle( pOwnerEntity->GetRefEHandle() );
-
- if ( pOwnerEntity )
- {
- bFoundEntity = true;
- }
-#endif
- }
-
- AssertMsg( pOwnerEntity && bFoundEntity == true, "Physics save/load is only suitable for entities" );
-
- if ( m_type == PIID_UNKNOWN )
- {
- AssertMsg( 0, "Unknown physics save/load type");
- return;
- }
-
- g_PhysSaveRestoreBlockHandler.QueueRestore( pOwnerEntity, fieldInfo.pTypeDesc, (void **)fieldInfo.pField, m_type );
- }
-
- virtual void MakeEmpty( const SaveRestoreFieldInfo_t &fieldInfo )
- {
- memset( fieldInfo.pField, 0, fieldInfo.pTypeDesc->fieldSize * sizeof( void * ) );
- }
-
- virtual bool IsEmpty( const SaveRestoreFieldInfo_t &fieldInfo )
- {
- void **ppPhysObj = (void **)fieldInfo.pField;
- int nObjects = fieldInfo.pTypeDesc->fieldSize;
- for ( int i = 0; i < nObjects; i++ )
- {
- if ( ppPhysObj[i] != NULL )
- return false;
- }
- return true;
- }
-
- PhysInterfaceId_t m_type;
-};
-
-//-----------------------------------------------------------------------------
-
-CPhysObjSaveRestoreOps g_PhysObjSaveRestoreOps[PIID_NUM_TYPES];
-
-//-------------------------------------
-
-ISaveRestoreOps *GetPhysObjSaveRestoreOps( PhysInterfaceId_t type )
-{
- static bool inited;
- if ( !inited )
- {
- inited = true;
- for ( int i = 0; i < PIID_NUM_TYPES; i++ )
- {
- g_PhysObjSaveRestoreOps[i].m_type = (PhysInterfaceId_t)i;
- }
- }
- return &g_PhysObjSaveRestoreOps[type];
-}
-
-//=============================================================================
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "cbase.h"
+
+#include "utlpriorityqueue.h"
+#include "utlmap.h"
+#include "isaverestore.h"
+#include "physics.h"
+#include "physics_saverestore.h"
+#include "saverestoretypes.h"
+#include "gamestringpool.h"
+#include "datacache/imdlcache.h"
+
+#if !defined( CLIENT_DLL )
+#include "entitylist.h"
+#endif
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+//-----------------------------------------------------------------------------
+
+static short PHYS_SAVE_RESTORE_VERSION = 5;
+
+struct PhysBlockHeader_t
+{
+ int nSaved;
+ IPhysicsObject *pWorldObject;
+
+ inline void Clear()
+ {
+ nSaved = 0;
+ pWorldObject = 0;
+ }
+
+ DECLARE_SIMPLE_DATADESC();
+};
+BEGIN_SIMPLE_DATADESC( PhysBlockHeader_t )
+ DEFINE_FIELD( nSaved, FIELD_INTEGER ),
+ // NOTE: We want to save the actual address here for remapping, so use an integer
+ DEFINE_FIELD( pWorldObject, FIELD_INTEGER ),
+END_DATADESC()
+
+#if defined(_STATIC_LINKED) && defined(CLIENT_DLL)
+const char *g_ppszPhysTypeNames[PIID_NUM_TYPES] =
+{
+ "Unknown",
+ "IPhysicsObject",
+ "IPhysicsFluidController",
+ "IPhysicsSpring",
+ "IPhysicsConstraintGroup",
+ "IPhysicsConstraint",
+ "IPhysicsShadowController",
+ "IPhysicsPlayerController",
+ "IPhysicsMotionController",
+ "IPhysicsVehicleController",
+};
+#endif
+
+//-----------------------------------------------------------------------------
+
+struct BBox_t
+{
+ Vector mins, maxs;
+};
+
+struct Sphere_t
+{
+ float radius;
+};
+
+
+struct PhysObjectHeader_t
+{
+ PhysObjectHeader_t()
+ {
+ memset( this, 0, sizeof(*this) );
+ }
+
+ PhysInterfaceId_t type;
+ EHANDLE hEntity;
+ string_t fieldName;
+ int nObjects;
+ string_t modelName;
+ BBox_t bbox;
+ Sphere_t sphere;
+ int iCollide;
+
+ DECLARE_SIMPLE_DATADESC();
+};
+
+BEGIN_SIMPLE_DATADESC( PhysObjectHeader_t )
+ DEFINE_FIELD( type, FIELD_INTEGER ),
+ DEFINE_FIELD( hEntity, FIELD_EHANDLE ),
+ DEFINE_FIELD( fieldName, FIELD_STRING ),
+ DEFINE_FIELD( nObjects, FIELD_INTEGER ),
+ DEFINE_FIELD( modelName, FIELD_STRING ),
+
+ // Silence, Classcheck!
+ // DEFINE_FIELD( bbox, BBox_t ),
+ // DEFINE_FIELD( sphere, Sphere_t ),
+
+ DEFINE_FIELD( bbox.mins, FIELD_VECTOR ),
+ DEFINE_FIELD( bbox.maxs, FIELD_VECTOR ),
+ DEFINE_FIELD( sphere.radius, FIELD_FLOAT ),
+ DEFINE_FIELD( iCollide, FIELD_INTEGER ),
+END_DATADESC()
+
+//-----------------------------------------------------------------------------
+// Purpose: The central manager of physics save/load
+//
+
+class CPhysSaveRestoreBlockHandler : public CDefSaveRestoreBlockHandler,
+ public IPhysSaveRestoreManager
+#if !defined( CLIENT_DLL )
+ , public IEntityListener
+#endif
+{
+ struct QueuedItem_t;
+public:
+ CPhysSaveRestoreBlockHandler()
+ {
+ m_QueuedSaves.SetLessFunc( SaveQueueFunc );
+ SetDefLessFunc( m_QueuedRestores );
+ SetDefLessFunc( m_PhysObjectModels );
+ SetDefLessFunc( m_PhysObjectCustomModels );
+ SetDefLessFunc( m_PhysCollideBBoxModels );
+ }
+
+ const char *GetBlockName()
+ {
+ return "Physics";
+ }
+
+ //---------------------------------
+
+ virtual void PreSave( CSaveRestoreData * )
+ {
+ m_blockHeader.Clear();
+ }
+
+ //---------------------------------
+
+ virtual void Save( ISave *pSave )
+ {
+ m_blockHeader.pWorldObject = g_PhysWorldObject;
+ m_blockHeader.nSaved = m_QueuedSaves.Count();
+
+ while ( m_QueuedSaves.Count() )
+ {
+ const QueuedItem_t &item = m_QueuedSaves.ElementAtHead();
+
+ CBaseEntity *pOwner = item.header.hEntity.Get();
+
+ if ( pOwner )
+ {
+ pSave->WriteAll( &item.header );
+ pSave->StartBlock(); // Need block here in case entity is NULL on load
+ if ( item.header.nObjects )
+ {
+ for ( int i = 0; i < item.header.nObjects; i++ )
+ {
+ // Starting a block here allows the implementation of any individual physics
+ // class save/load to change non-trivially while retaining the overall
+ // integrity of the savefile.
+ pSave->StartBlock();
+ SavePhysicsObject( pSave, pOwner, item.ppPhysObj[i], item.header.type );
+ pSave->EndBlock();
+ }
+ }
+ // else, it will simply be recreated on restore
+ pSave->EndBlock();
+ }
+ m_QueuedSaves.RemoveAtHead();
+ }
+ }
+
+ //---------------------------------
+
+ virtual void WriteSaveHeaders( ISave *pSave )
+ {
+ pSave->WriteShort( &PHYS_SAVE_RESTORE_VERSION );
+ pSave->WriteAll( &m_blockHeader );
+ }
+
+ //---------------------------------
+
+ virtual void PostSave()
+ {
+ m_QueuedSaves.Purge();
+ }
+
+ //---------------------------------
+
+ virtual void PreRestore()
+ {
+#if !defined( CLIENT_DLL )
+ gEntList.AddListenerEntity( this );
+#endif
+
+ // UNDONE: This never runs!!!!
+ if ( physenv )
+ {
+ physprerestoreparams_t params;
+ params.recreatedObjectCount = 0;
+ physenv->PreRestore( params );
+ }
+ }
+
+ //---------------------------------
+
+ virtual void ReadRestoreHeaders( IRestore *pRestore )
+ {
+ // No reason why any future version shouldn't try to retain backward compatability. The default here is to not do so.
+ short version = pRestore->ReadShort();
+ m_fDoLoad = ( version == PHYS_SAVE_RESTORE_VERSION );
+
+ pRestore->ReadAll( &m_blockHeader );
+ }
+
+ //---------------------------------
+
+ virtual void Restore( IRestore *pRestore, bool )
+ {
+ if ( m_fDoLoad )
+ {
+ if ( physenv )
+ {
+ physprerestoreparams_t params;
+ params.recreatedObjectCount = 1;
+ params.recreatedObjectList[0].pNewObject = g_PhysWorldObject;
+ params.recreatedObjectList[0].pOldObject = m_blockHeader.pWorldObject;
+ physenv->PreRestore( params );
+ }
+
+ PhysObjectHeader_t header;
+
+ while ( m_blockHeader.nSaved-- )
+ {
+ pRestore->ReadAll( &header );
+ pRestore->StartBlock();
+
+ if ( header.hEntity != NULL )
+ {
+ RestoreBlock( pRestore, header );
+ }
+
+ pRestore->EndBlock();
+ }
+ }
+ }
+
+ //---------------------------------
+
+ void RestoreBlock( IRestore *pRestore, const PhysObjectHeader_t &header )
+ {
+ CBaseEntity * pOwner = header.hEntity.Get();
+ unsigned short iQueued = m_QueuedRestores.Find( pOwner );
+
+ if ( iQueued != m_QueuedRestores.InvalidIndex() )
+ {
+ MDLCACHE_CRITICAL_SECTION();
+ if ( pOwner->ShouldSavePhysics() && header.nObjects > 0 )
+ {
+ QueuedItem_t *pItem = m_QueuedRestores[iQueued]->FindItem( header.fieldName );
+
+ if ( pItem )
+ {
+ int nObjects = MIN( header.nObjects, pItem->header.nObjects );
+ if ( pItem->header.type == PIID_IPHYSICSOBJECT && nObjects == 1 )
+ {
+ RestorePhysicsObjectAndModel( pRestore, header, pItem, nObjects );
+ }
+ else
+ {
+ void **ppPhysObj = pItem->ppPhysObj;
+
+ for ( int i = 0; i < nObjects; i++ )
+ {
+ pRestore->StartBlock();
+ RestorePhysicsObject( pRestore, header, ppPhysObj + i );
+ pRestore->EndBlock();
+ if ( header.type == PIID_IPHYSICSMOTIONCONTROLLER )
+ {
+ void *pObj = ppPhysObj[i];
+ IPhysicsMotionController *pController = (IPhysicsMotionController *)pObj;
+ if ( pController )
+ {
+ // If the entity is the motion callback handler, then automatically set it
+ // NOTE: This is usually the case
+ IMotionEvent *pEvent = dynamic_cast<IMotionEvent *>(pOwner);
+ if ( pEvent )
+ {
+ pController->SetEventHandler( pEvent );
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ else
+ pOwner->CreateVPhysics();
+ }
+ }
+
+
+ //---------------------------------
+
+ void RestorePhysicsObjectAndModel( IRestore *pRestore, const PhysObjectHeader_t &header, CPhysSaveRestoreBlockHandler::QueuedItem_t *pItem, int nObjects )
+ {
+ if ( nObjects == 1 )
+ {
+ pRestore->StartBlock();
+
+ CPhysCollide *pPhysCollide = NULL;
+ int modelIndex = -1;
+ bool fCustomCollide = false;
+
+ if ( header.modelName != NULL_STRING )
+ {
+ CBaseEntity *pGlobalEntity = header.hEntity;
+#if !defined( CLIENT_DLL )
+ if ( NULL_STRING != pGlobalEntity->m_iGlobalname )
+ {
+ modelIndex = pGlobalEntity->GetModelIndex();
+ }
+ else
+#endif
+ {
+ modelIndex = modelinfo->GetModelIndex( STRING( header.modelName ) );
+ pGlobalEntity = NULL;
+ }
+
+ if ( modelIndex != -1 )
+ {
+ vcollide_t *pCollide = modelinfo->GetVCollide( modelIndex );
+ if ( pCollide )
+ {
+ if ( pCollide->solidCount > 0 && pCollide->solids && header.iCollide < pCollide->solidCount )
+ pPhysCollide = pCollide->solids[header.iCollide];
+ }
+ }
+ }
+ else if ( header.bbox.mins != vec3_origin || header.bbox.maxs != vec3_origin )
+ {
+ pPhysCollide = PhysCreateBbox( header.bbox.mins, header.bbox.maxs );
+ fCustomCollide = true;
+ }
+ else if ( header.sphere.radius != 0 )
+ {
+ // HACKHACK: Handle spheres here!!!
+ if ( !(*pItem->ppPhysObj) )
+ {
+ RestorePhysicsObject( pRestore, header, pItem->ppPhysObj, NULL );
+ }
+ return;
+ }
+
+ if ( pPhysCollide )
+ {
+ if ( !(*pItem->ppPhysObj) )
+ {
+ RestorePhysicsObject( pRestore, header, pItem->ppPhysObj, pPhysCollide );
+ if ( (*pItem->ppPhysObj) )
+ {
+ IPhysicsObject *pObject = (IPhysicsObject *)(*pItem->ppPhysObj);
+ if ( !fCustomCollide )
+ {
+ AssociateModel( pObject, modelIndex );
+ }
+ else
+ {
+ AssociateModel( pObject, pPhysCollide );
+ }
+ }
+ else
+ DevMsg( "Failed to restore physics object\n" );
+ }
+ else
+ DevMsg( "Physics object pointer unexpectedly non-null before restore. Should be creating physics object in CreatePhysics()?\n" );
+ }
+ else
+ DevMsg( "Failed to reestablish collision model for object\n" );
+
+ pRestore->EndBlock();
+ }
+ else
+ DevMsg( "Don't know how to reconsitite models for physobj array \n" );
+ }
+
+ //---------------------------------
+
+ virtual void PostRestore()
+ {
+ if ( physenv )
+ physenv->PostRestore();
+
+ unsigned short i = m_QueuedRestores.FirstInorder();
+ while ( i != m_QueuedRestores.InvalidIndex() )
+ {
+ delete m_QueuedRestores[i];
+ i = m_QueuedRestores.NextInorder( i );
+ }
+
+ m_QueuedRestores.RemoveAll();
+#if !defined( CLIENT_DLL )
+ gEntList.RemoveListenerEntity( this );
+#endif
+ }
+
+ //---------------------------------
+
+ void QueueSave( CBaseEntity *pOwner, typedescription_t *pTypeDesc, void **ppPhysObj, PhysInterfaceId_t type )
+ {
+ if ( !pOwner )
+ return;
+
+ bool fOnlyNotingExistence = !pOwner->ShouldSavePhysics();
+
+ QueuedItem_t item;
+
+ item.ppPhysObj = ppPhysObj;
+ item.header.hEntity = pOwner;
+ item.header.type = type;
+ item.header.nObjects = ( !fOnlyNotingExistence ) ? pTypeDesc->fieldSize : 0;
+ item.header.fieldName = AllocPooledString( pTypeDesc->fieldName );
+ // A pooled string is used here because there is no way
+ // right now to save a non-string_t string and have it
+ // compressed in the save symbol tables. Furthermore,
+ // the field name would normally be in the string
+ // pool anyway. (toml 12-10-02)
+ item.header.modelName = NULL_STRING;
+ memset( &item.header.bbox, 0, sizeof( item.header.bbox ) );
+ item.header.sphere.radius = 0;
+
+ if ( !fOnlyNotingExistence && type == PIID_IPHYSICSOBJECT )
+ {
+ // Don't doing the box thing for things like wheels on cars
+ IPhysicsObject *pPhysObj = (IPhysicsObject *)(*ppPhysObj);
+
+ if ( pPhysObj )
+ {
+ item.header.modelName = GetModelName( pPhysObj );
+ item.header.iCollide = physcollision->CollideIndex( pPhysObj->GetCollide() );
+ if ( item.header.modelName == NULL_STRING )
+ {
+ BBox_t *pBBox = GetBBox( pPhysObj );
+ if ( pBBox != NULL )
+ {
+ item.header.bbox = *pBBox;
+ }
+ else
+ {
+ if ( pPhysObj && pPhysObj->GetSphereRadius() != 0 )
+ {
+ item.header.sphere.radius = pPhysObj->GetSphereRadius();
+ }
+ else
+ {
+ DevMsg( "Don't know how to save model for physics object (class \"%s\")\n", pOwner->GetClassname() );
+ }
+ }
+ }
+ }
+ }
+
+ m_QueuedSaves.Insert( item );
+ }
+
+ //---------------------------------
+
+ void QueueRestore( CBaseEntity *pOwner, typedescription_t *pTypeDesc, void **ppPhysObj, PhysInterfaceId_t type )
+ {
+ CEntityRestoreSet *pEntitySet = NULL;
+ unsigned short iEntitySet = m_QueuedRestores.Find( pOwner );
+
+ if ( iEntitySet != m_QueuedRestores.InvalidIndex() )
+ {
+ pEntitySet = m_QueuedRestores[iEntitySet];
+ }
+ else
+ {
+ pEntitySet = new CEntityRestoreSet;
+ m_QueuedRestores.Insert( pOwner, pEntitySet );
+ }
+
+ pEntitySet->Add( pOwner, pTypeDesc, ppPhysObj, type );
+
+ memset( ppPhysObj, 0, pTypeDesc->fieldSize * sizeof( void * ) );
+ }
+
+ //---------------------------------
+
+ void SavePhysicsObject( ISave *pSave, CBaseEntity *pOwner, void *pObject, PhysInterfaceId_t type )
+ {
+ if ( physenv )
+ {
+ if ( !pObject )
+ return;
+ physsaveparams_t params = { pSave, pObject, type };
+ physenv->Save( params );
+ }
+ }
+
+ //---------------------------------
+
+ void RestorePhysicsObject( IRestore *pRestore, const PhysObjectHeader_t &header, void **ppObject, const CPhysCollide *pCollide = NULL )
+ {
+ if ( physenv )
+ {
+ physrestoreparams_t params = { pRestore, ppObject, header.type, header.hEntity.Get(), STRING(header.modelName), pCollide, physenv, physgametrace };
+ physenv->Restore( params );
+ }
+ }
+#if !defined( CLIENT_DLL )
+ //-----------------------------------------------------
+ // IEntityListener methods
+ // This object is only a listener during restore
+ virtual void OnEntityCreated( CBaseEntity *pEntity )
+ {
+ }
+
+ //---------------------------------
+
+ virtual void OnEntityDeleted( CBaseEntity *pEntity )
+ {
+ unsigned short iEntitySet = m_QueuedRestores.Find( pEntity );
+
+ if ( iEntitySet != m_QueuedRestores.InvalidIndex() )
+ {
+ delete m_QueuedRestores[iEntitySet];
+ m_QueuedRestores.RemoveAt( iEntitySet );
+ }
+ }
+#endif
+
+ //-----------------------------------------------------
+ // IPhysSaveRestoreManager methods
+
+ virtual void NoteBBox( const Vector &mins, const Vector &maxs, CPhysCollide *pCollide )
+ {
+ if ( pCollide && m_PhysCollideBBoxModels.Find( pCollide ) == m_PhysCollideBBoxModels.InvalidIndex() )
+ {
+ BBox_t box;
+ box.mins = mins;
+ box.maxs = maxs;
+ m_PhysCollideBBoxModels.Insert( pCollide, box );
+ }
+ }
+
+ //---------------------------------
+
+ virtual void AssociateModel( IPhysicsObject *pObject, int modelIndex )
+ {
+ Assert( m_PhysObjectModels.Find( pObject ) == m_PhysObjectModels.InvalidIndex() );
+ m_PhysObjectModels.Insert( pObject, modelIndex );
+ }
+
+ //---------------------------------
+
+ virtual void AssociateModel( IPhysicsObject *pObject, const CPhysCollide *pModel )
+ {
+ Assert( m_PhysObjectCustomModels.Find( pObject ) == m_PhysObjectCustomModels.InvalidIndex() );
+ m_PhysObjectCustomModels.Insert( pObject, pModel );
+ }
+
+ //---------------------------------
+
+ virtual void ForgetModel( IPhysicsObject *pObject )
+ {
+ if ( !m_PhysObjectModels.Remove( pObject ) )
+ m_PhysObjectCustomModels.Remove( pObject );
+ }
+
+ //---------------------------------
+
+ virtual void ForgetAllModels()
+ {
+ m_PhysObjectModels.RemoveAll();
+ m_PhysObjectCustomModels.RemoveAll();
+ m_PhysCollideBBoxModels.RemoveAll();
+ }
+
+ //---------------------------------
+
+ string_t GetModelName( IPhysicsObject *pObject )
+ {
+ int i = m_PhysObjectModels.Find( pObject );
+ if ( i == m_PhysObjectModels.InvalidIndex() )
+ return NULL_STRING;
+ return AllocPooledString( modelinfo->GetModelName( modelinfo->GetModel( m_PhysObjectModels[i] ) ) );
+ }
+
+ //---------------------------------
+
+ BBox_t * GetBBox( IPhysicsObject *pObject )
+ {
+ int i = m_PhysObjectCustomModels.Find( pObject );
+ if ( i == m_PhysObjectCustomModels.InvalidIndex() )
+ return NULL;
+ i = m_PhysCollideBBoxModels.Find( m_PhysObjectCustomModels[i] );
+ if ( i == m_PhysCollideBBoxModels.InvalidIndex() )
+ return NULL;
+ return &(m_PhysCollideBBoxModels[i]);
+ }
+
+ //---------------------------------
+
+private:
+ struct QueuedItem_t
+ {
+ PhysObjectHeader_t header;
+ void ** ppPhysObj;
+ };
+
+ class CEntityRestoreSet : public CUtlVector<QueuedItem_t>
+ {
+ public:
+ int Add( CBaseEntity *pOwner, typedescription_t *pTypeDesc, void **ppPhysObj, PhysInterfaceId_t type )
+ {
+ int i = AddToTail();
+
+ Assert( ppPhysObj );
+ Assert( *ppPhysObj == NULL ); // expected field to have been cleared
+ Assert( pOwner );
+
+ QueuedItem_t &item = Element( i );
+
+ item.ppPhysObj = ppPhysObj;
+ item.header.hEntity = pOwner;
+ item.header.type = type;
+ item.header.nObjects = pTypeDesc->fieldSize;
+ item.header.fieldName = AllocPooledString( pTypeDesc->fieldName ); // See comment in CPhysSaveRestoreBlockHandler::QueueSave()
+
+ return i;
+ }
+
+ QueuedItem_t *FindItem( string_t itemFieldName )
+ {
+ // generally, the set is very small, usually one, so linear search is not too gruesome;
+ for ( int i = 0; i < Count(); i++ )
+ {
+ string_t testName = Element(i).header.fieldName;
+ Assert( ( testName == itemFieldName && strcmp( STRING( testName ), STRING( itemFieldName ) ) == 0 ) ||
+ ( testName != itemFieldName && strcmp( STRING( testName ), STRING( itemFieldName ) ) != 0 ) );
+
+ if ( testName == itemFieldName )
+ return &(Element(i));
+ }
+ return NULL;
+ }
+ };
+
+ //---------------------------------
+
+ static bool SaveQueueFunc( const QueuedItem_t &left, const QueuedItem_t &right )
+ {
+ if ( left.header.type == right.header.type )
+ return ( left.header.hEntity->entindex() > right.header.hEntity->entindex() );
+
+ return ( left.header.type > right.header.type );
+ }
+
+ //---------------------------------
+
+ CUtlPriorityQueue<QueuedItem_t> m_QueuedSaves;
+ CUtlMap<CBaseEntity *, CEntityRestoreSet *> m_QueuedRestores;
+ bool m_fDoLoad;
+
+ //---------------------------------
+
+ CUtlMap<IPhysicsObject *, int> m_PhysObjectModels;
+ CUtlMap<IPhysicsObject *, const CPhysCollide *> m_PhysObjectCustomModels;
+ CUtlMap<const CPhysCollide *, BBox_t> m_PhysCollideBBoxModels;
+
+ //---------------------------------
+
+ PhysBlockHeader_t m_blockHeader;
+};
+
+//-----------------------------------------------------------------------------
+
+CPhysSaveRestoreBlockHandler g_PhysSaveRestoreBlockHandler;
+
+IPhysSaveRestoreManager *g_pPhysSaveRestoreManager = &g_PhysSaveRestoreBlockHandler;
+
+//-------------------------------------
+
+ISaveRestoreBlockHandler *GetPhysSaveRestoreBlockHandler()
+{
+ return &g_PhysSaveRestoreBlockHandler;
+}
+
+static bool IsValidEntityPointer( void *ptr )
+{
+#if !defined( CLIENT_DLL )
+ return gEntList.IsEntityPtr( ptr );
+#else
+ // Walk entities looking for pointer
+ int c = ClientEntityList().GetHighestEntityIndex();
+ for ( int i = 0; i <= c; i++ )
+ {
+ CBaseEntity *e = ClientEntityList().GetBaseEntity( i );
+ if ( !e )
+ continue;
+
+ if ( e == ptr )
+ return true;
+ }
+ return false;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Classifies field and queues it up for physics save/restore.
+//
+
+class CPhysObjSaveRestoreOps : public CDefSaveRestoreOps
+{
+public:
+ virtual void Save( const SaveRestoreFieldInfo_t &fieldInfo, ISave *pSave )
+ {
+ CBaseEntity *pOwnerEntity = pSave->GetGameSaveRestoreInfo()->GetCurrentEntityContext();
+
+ bool bFoundEntity = true;
+
+ if ( IsValidEntityPointer(pOwnerEntity) == false )
+ {
+ bFoundEntity = false;
+
+#if defined( CLIENT_DLL )
+ pOwnerEntity = ClientEntityList().GetBaseEntityFromHandle( pOwnerEntity->GetRefEHandle() );
+
+ if ( pOwnerEntity )
+ {
+ bFoundEntity = true;
+ }
+#endif
+ }
+
+ AssertMsg( pOwnerEntity && bFoundEntity == true, "Physics save/load is only suitable for entities" );
+
+ if ( m_type == PIID_UNKNOWN )
+ {
+ AssertMsg( 0, "Unknown physics save/load type");
+ return;
+ }
+ g_PhysSaveRestoreBlockHandler.QueueSave( pOwnerEntity, fieldInfo.pTypeDesc, (void **)fieldInfo.pField, m_type );
+ }
+
+ virtual void Restore( const SaveRestoreFieldInfo_t &fieldInfo, IRestore *pRestore )
+ {
+ CBaseEntity *pOwnerEntity = pRestore->GetGameSaveRestoreInfo()->GetCurrentEntityContext();
+
+ bool bFoundEntity = true;
+
+ if ( IsValidEntityPointer(pOwnerEntity) == false )
+ {
+ bFoundEntity = false;
+
+#if defined( CLIENT_DLL )
+ pOwnerEntity = ClientEntityList().GetBaseEntityFromHandle( pOwnerEntity->GetRefEHandle() );
+
+ if ( pOwnerEntity )
+ {
+ bFoundEntity = true;
+ }
+#endif
+ }
+
+ AssertMsg( pOwnerEntity && bFoundEntity == true, "Physics save/load is only suitable for entities" );
+
+ if ( m_type == PIID_UNKNOWN )
+ {
+ AssertMsg( 0, "Unknown physics save/load type");
+ return;
+ }
+
+ g_PhysSaveRestoreBlockHandler.QueueRestore( pOwnerEntity, fieldInfo.pTypeDesc, (void **)fieldInfo.pField, m_type );
+ }
+
+ virtual void MakeEmpty( const SaveRestoreFieldInfo_t &fieldInfo )
+ {
+ memset( fieldInfo.pField, 0, fieldInfo.pTypeDesc->fieldSize * sizeof( void * ) );
+ }
+
+ virtual bool IsEmpty( const SaveRestoreFieldInfo_t &fieldInfo )
+ {
+ void **ppPhysObj = (void **)fieldInfo.pField;
+ int nObjects = fieldInfo.pTypeDesc->fieldSize;
+ for ( int i = 0; i < nObjects; i++ )
+ {
+ if ( ppPhysObj[i] != NULL )
+ return false;
+ }
+ return true;
+ }
+
+ PhysInterfaceId_t m_type;
+};
+
+//-----------------------------------------------------------------------------
+
+CPhysObjSaveRestoreOps g_PhysObjSaveRestoreOps[PIID_NUM_TYPES];
+
+//-------------------------------------
+
+ISaveRestoreOps *GetPhysObjSaveRestoreOps( PhysInterfaceId_t type )
+{
+ static bool inited;
+ if ( !inited )
+ {
+ inited = true;
+ for ( int i = 0; i < PIID_NUM_TYPES; i++ )
+ {
+ g_PhysObjSaveRestoreOps[i].m_type = (PhysInterfaceId_t)i;
+ }
+ }
+ return &g_PhysObjSaveRestoreOps[type];
+}
+
+//=============================================================================