//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============// // // Purpose: This file contains brand new VScript singletons and singletons replicated from API // documentation in other games. // // See vscript_funcs_shared.cpp for more information. // // $NoKeywords: $ //=============================================================================// #include "cbase.h" #include #include #include "ammodef.h" #include "tier1/utlcommon.h" #include "soundenvelope.h" #include "saverestore_utlvector.h" #include "stdstring.h" #ifndef CLIENT_DLL #include "ai_speech.h" #include "ai_memory.h" #include "ai_squad.h" #endif // !CLIENT_DLL #include "usermessages.h" #include "filesystem.h" #include "igameevents.h" #include "engine/ivdebugoverlay.h" #ifdef CLIENT_DLL #include "IEffects.h" #include "fx.h" #include "itempents.h" #include "c_te_legacytempents.h" #include "iefx.h" #include "dlight.h" #if !defined(NO_STEAM) #include "steam/steam_api.h" #endif #endif #include "vscript_singletons.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" extern IScriptManager *scriptmanager; #ifdef GAME_DLL extern void SendProxy_StringT_To_String(const SendProp*, const void*, const void*, DVariant*, int, int); extern void SendProxy_UtlVectorLength(const SendProp*, const void*, const void*, DVariant*, int, int); class CSendProxyRecipients; extern void* SendProxy_LengthTable(const SendProp*, const void*, const void* pData, CSendProxyRecipients*, int); #define DataTableProxy_EHandle SendProxy_EHandleToInt #define DataTableProxy_String SendProxy_StringToString #define DataTableProxy_TableLength SendProxy_LengthTable #define DataTableProxy_UtlVectorLength SendProxy_UtlVectorLength #else extern void RecvProxy_UtlVectorLength(const CRecvProxyData*, void*, void*); extern void DataTableRecvProxy_LengthProxy(const RecvProp*, void**, void*, int); #define DataTableProxy_EHandle RecvProxy_IntToEHandle #define DataTableProxy_String RecvProxy_StringToString #define DataTableProxy_TableLength DataTableRecvProxy_LengthProxy #define DataTableProxy_UtlVectorLength RecvProxy_UtlVectorLength #endif extern ISaveRestoreOps* GetPhysObjSaveRestoreOps( PhysInterfaceId_t ); extern ISaveRestoreOps* ActivityDataOps(); extern ISaveRestoreOps* GetSoundSaveRestoreOps(); extern ISaveRestoreOps* GetStdStringDataOps(); #ifdef GAME_DLL #define UTLVECTOR_DATAOPS( fieldType, dataType )\ CUtlVectorDataopsInstantiator< fieldType >::GetDataOps( (CUtlVector< dataType >*)0 ) #define IS_EHANDLE_UTLVECTOR( td )\ td->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_EHANDLE, CHandle< CBaseEntity > ) ||\ td->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_EHANDLE, CHandle< CBaseFlex > ) ||\ td->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_EHANDLE, CHandle< CBaseAnimating > ) ||\ td->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_EHANDLE, CHandle< CBaseCombatWeapon > ) ||\ td->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_EHANDLE, CHandle< CBasePlayer > ) ||\ td->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_EHANDLE, CHandle< CAI_BaseNPC > ) ||\ td->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_EHANDLE, CHandle< CSceneEntity > ) ||\ td->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_EHANDLE, CHandle< CSceneListManager > ) ||\ td->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_EHANDLE, CHandle< CRagdollBoogie > ) ||\ td->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_EHANDLE, CHandle< CFish > ) ||\ td->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_EHANDLE, CHandle< CVGuiScreen > ) class CSceneListManager; class CRagdollBoogie; class CFish; #ifdef _DEBUG class CStringTableSaveRestoreOps; extern CStringTableSaveRestoreOps g_VguiScreenStringOps; extern INetworkStringTable *g_pStringTableVguiScreen; extern ISaveRestoreOps *thinkcontextFuncs; class CAI_EnemiesListSaveRestoreOps; extern CAI_EnemiesListSaveRestoreOps g_AI_MemoryListSaveRestoreOps; class CConceptHistoriesDataOps; extern CConceptHistoriesDataOps g_ConceptHistoriesSaveDataOps; #endif #endif //============================================================================= // Net Prop Manager // Based on L4D2 API //============================================================================= class CScriptNetPropManager { private: #if GAME_DLL typedef SendProp NetProp; typedef SendTable NetTable; typedef ServerClass NetworkClass; NetworkClass *GetNetworkClass( CBaseEntity* p ) { return p->GetServerClass(); } NetTable *GetNetTable( NetworkClass* p ) { return p->m_pTable; } void NetworkStateChanged( CBaseEntity* p, int o ) { p->NetworkProp()->NetworkStateChanged( o ); } #else typedef RecvProp NetProp; typedef RecvTable NetTable; typedef ClientClass NetworkClass; NetworkClass *GetNetworkClass( CBaseEntity* p ) { return p->GetClientClass(); } NetTable *GetNetTable( NetworkClass* p ) { return p->m_pRecvTable; } void NetworkStateChanged( CBaseEntity*, int ) {} #endif int GetClassID( CBaseEntity *p ) { return GetNetworkClass( p )->m_ClassID; } int GetIntPropSize( NetProp *pProp ) { Assert( pProp->GetType() == DPT_Int ); #ifdef GAME_DLL extern void SendProxy_UInt8ToInt32( const SendProp*, const void*, const void*, DVariant*, int, int ); extern void SendProxy_UInt16ToInt32( const SendProp*, const void*, const void*, DVariant*, int, int ); extern void SendProxy_UInt32ToInt32( const SendProp*, const void*, const void*, DVariant*, int, int ); SendVarProxyFn proxy = pProp->GetProxyFn(); if ( proxy == SendProxy_Int8ToInt32 || proxy == SendProxy_UInt8ToInt32 ) return 8; if ( proxy == SendProxy_Int16ToInt32 || proxy == SendProxy_UInt16ToInt32 ) return 16; if ( proxy == SendProxy_Int32ToInt32 || proxy == SendProxy_UInt32ToInt32 ) return 32; return pProp->m_nBits; #else RecvVarProxyFn proxy = pProp->GetProxyFn(); if ( proxy == RecvProxy_Int32ToInt8 ) return 8; if ( proxy == RecvProxy_Int32ToInt16 ) return 16; if ( proxy == RecvProxy_Int32ToInt32 ) return 32; return 0; #endif } bool IsEHandle( NetProp *pProp ) { return ( pProp->GetProxyFn() == DataTableProxy_EHandle ); } bool IsUtlVector( NetProp *pProp ) { #ifdef GAME_DLL SendVarProxyFn proxy = pProp->GetProxyFn(); #else RecvVarProxyFn proxy = pProp->GetProxyFn(); #endif return ( proxy == DataTableProxy_UtlVectorLength ); } private: enum types { _INT1 = ( 1 << 0 ), _INT8 = ( 1 << 1 ), _INT16 = ( 1 << 2 ), _INT32 = ( 1 << 3 ), _FLOAT = ( 1 << 4 ), _VEC3 = ( 1 << 5 ), _VEC2 = ( 1 << 6 ), _EHANDLE = ( 1 << 7 ), _CLASSPTR = ( 1 << 8 ), _EDICT = ( 1 << 9 ), _CSTRING = ( 1 << 10 ), _STRING_T = ( 1 << 11 ), _ARRAY = ( 1 << 12 ), _DATATABLE = ( 1 << 13 ), _PHYS = ( 1 << 14 ), _STDSTRING = _CSTRING | _STRING_T, _DAR_EHANDLE = _EHANDLE | _ARRAY, _DAR_CLASSPTR = _CLASSPTR | _ARRAY, _DAR_INT = _INT32 | _ARRAY, _DAR_FLOAT = _FLOAT | _ARRAY, //_MAX = ( 1 << 15 ) }; // UNDONE: Special case for GetPropType() to be able to return the table/array itself #define INDEX_GET_TYPE 0 #define MASK_INT_SIZE( _size ) ( ( 1 << (_size - 1) ) | ( (1 << (_size - 1)) - 1 ) ) #define MASK_NEAREST_BYTE( _bits ) ( ( (1 << ALIGN_TO_NEAREST_BYTE(_bits)) - 1 ) & ~((1 << _bits) - 1) ) #define ALIGN_TO_NEAREST_BYTE( _bits ) ( (_bits + 7) & ~7 ) #define VARINFO_ARRAYSIZE_BITS 12 struct varinfo_t { int offset : 32; // actually a short union { int mask : 32; int stringsize : 32; }; enum types datatype : 16; // element size in bytes unsigned int elemsize : 8; unsigned int arraysize : VARINFO_ARRAYSIZE_BITS; // Following are only used in integer netprops to handle unsigned and size casting bool isUnsigned : 1; bool isNotNetworked : 1; int GetOffset( int index ) { return offset + index * elemsize; } }; // Wrapper to be able to set case sensitive comparator in node insertion class vardict_t : public CUtlDict< varinfo_t > { public: vardict_t() : CUtlDict< varinfo_t >( k_eDictCompareTypeCaseSensitive ) {} }; // NOTE: This is lazy and inefficient. // Simply map highest level class id to unique caches. CUtlVector< int > m_EntMap; CUtlVector< vardict_t > m_VarDicts; varinfo_t* CacheNew( CBaseEntity *pEnt, const char *szProp ) { int idx = m_EntMap.Find( GetClassID( pEnt ) ); if ( idx == m_EntMap.InvalidIndex() ) { // Vector indices are kept in parallel as a workaround for encapsulating maps idx = m_EntMap.AddToTail( GetClassID( pEnt ) ); m_VarDicts.AddToTail(); } vardict_t &dict = m_VarDicts.Element( idx ); idx = dict.Find( szProp ); if ( idx == dict.InvalidIndex() ) idx = dict.Insert( szProp ); varinfo_t *pInfo = &dict.Element( idx ); V_memset( pInfo, 0, sizeof( varinfo_t ) ); return pInfo; } varinfo_t* CacheFetch( CBaseEntity *pEnt, const char *szProp ) { int idx = m_EntMap.Find( GetClassID( pEnt ) ); if ( idx == m_EntMap.InvalidIndex() ) return NULL; vardict_t &dict = m_VarDicts.Element( idx ); idx = dict.Find( szProp ); if ( idx == dict.InvalidIndex() ) return NULL; varinfo_t *pInfo = &dict.Element( idx ); return pInfo; } public: ~CScriptNetPropManager() { PurgeCache(); } void PurgeCache() { m_EntMap.Purge(); m_VarDicts.Purge(); } private: typedescription_t *FindField( char *pBase, datamap_t *map, const char *szName, int *offset ) { if ( map->baseMap ) { typedescription_t* p = FindField( pBase, map->baseMap, szName, offset ); if ( p ) return p; } typedescription_t *pFields = map->dataDesc; int numFields = map->dataNumFields; for ( int i = 0; i < numFields; i++ ) { typedescription_t* td = &pFields[i]; int fieldType = td->fieldType; int fieldOffset = td->fieldOffset[ TD_OFFSET_NORMAL ]; if ( td->flags & (FTYPEDESC_FUNCTIONTABLE | FTYPEDESC_INPUT | FTYPEDESC_OUTPUT) ) continue; if ( fieldType == FIELD_VOID || fieldType == FIELD_FUNCTION ) continue; if ( !V_strcmp( td->fieldName, szName ) ) { *offset += fieldOffset; if ( td->flags & FTYPEDESC_PTR ) { // Follow the pointer char * const pRef = *(char**)( pBase + *offset ); Assert( pRef ); *offset = pRef - pBase; } return td; } } return NULL; } NetProp *FindProp( char *pBase, NetTable *pTable, const char *szName, int *offset ) { int numProps = pTable->GetNumProps(); for ( int i = 0; i < numProps; i++ ) { NetProp* pProp = pTable->GetProp(i); if ( pProp->IsInsideArray() ) continue; if ( !V_strcmp( pProp->GetName(), szName ) ) { *offset += pProp->GetOffset(); return pProp; } // Go into inherited fields but not member tables, they are looked up explicitly // This is only a problem with m_AnimOverlay if ( ( pProp->GetFlags() & SPROP_COLLAPSIBLE ) || ( pProp->GetType() == DPT_DataTable && pProp->GetOffset() == 0 ) ) { // Don't go into lengthproxy if ( pProp->GetDataTableProxyFn() == DataTableProxy_TableLength ) continue; NetProp *p = FindProp( pBase + pProp->GetOffset(), pProp->GetDataTable(), szName, offset ); if ( p ) { *offset += pProp->GetOffset(); return p; } } } return NULL; } typedescription_t *FindInDataMap( char * const pBase, datamap_t *map, const char *szFullProp, int *offset ) { *offset = 0; // Look for exact match typedescription_t *pField = FindField( pBase, map, szFullProp, offset ); if ( pField ) return pField; // Look for members const char *pszProp = szFullProp; const char *pszPropEnd = V_strnchr( pszProp, '.', 512 ); if ( !pszPropEnd ) return NULL; do { // this string comes from squirrel stringtable, it can be modified *((char*)pszPropEnd) = 0; pField = FindField( pBase, map, pszProp, offset ); *((char*)pszPropEnd) = '.'; pszProp = pszPropEnd + 1; if ( !pField || ( map = pField->td ) == NULL ) return NULL; // Look for exact match again, just in case pField = FindField( pBase, map, pszProp, offset ); if ( pField ) return pField; } while ( ( pszPropEnd = V_strnchr( pszProp, '.', 512 ) ) != NULL ); return FindField( pBase, map, pszProp, offset ); } NetProp *FindInNetTable( char * const pBase, NetTable *pTable, const char *szFullProp, int *offset ) { *offset = 0; // Look for exact match NetProp *pProp = FindProp( pBase, pTable, szFullProp, offset ); if ( pProp ) return pProp; // Look for members const char *pszProp = szFullProp; const char *pszPropEnd = V_strnchr( pszProp, '.', 512 ); if ( !pszPropEnd ) return NULL; do { // this string comes from squirrel stringtable, it can be modified *((char*)pszPropEnd) = 0; pProp = FindProp( pBase, pTable, pszProp, offset ); *((char*)pszPropEnd) = '.'; pszProp = pszPropEnd + 1; if ( !pProp || ( pTable = pProp->GetDataTable() ) == NULL ) return NULL; // Look for exact match again for fields such as m_Local{m_skybox3d.scale} pProp = FindProp( pBase, pTable, pszProp, offset ); if ( pProp ) return pProp; } while ( ( pszPropEnd = V_strnchr( pszProp, '.', 512 ) ) != NULL ); return FindProp( pBase, pTable, pszProp, offset ); } // Searches NetTable first to handle overwritten member network variables - see // CPlayerResource::m_iHealth and CBaseEntity::m_iHealth varinfo_t *GetVarInfo( CBaseEntity *pEnt, const char *szProp, int index ) { int offset = 0; NetTable *pTable = GetNetTable( GetNetworkClass( pEnt ) ); NetProp *pProp = FindInNetTable( (char*)pEnt, pTable, szProp, &offset ); if ( pProp ) { #define SetVarInfo()\ varinfo_t *pInfo = CacheNew( pEnt, szProp );\ pInfo->isNotNetworked = 0;\ pInfo->elemsize = pProp->GetElementStride();\ pInfo->arraysize = pProp->GetNumElements();\ pInfo->offset = offset; switch ( pProp->GetType() ) { case DPT_Int: { if ( IsUtlVector( pProp ) ) { return NULL; } if ( index < 0 || index >= pProp->GetNumElements() ) { Warning( "NetProp element index out of range! %s[%d]\n", szProp, index ); return NULL; } Assert( index == 0 || pProp->GetElementStride() > 0 ); if ( IsEHandle( pProp ) ) { Assert( pProp->GetElementStride() == sizeof(int) || pProp->GetElementStride() < 0 ); SetVarInfo(); pInfo->datatype = types::_EHANDLE; return pInfo; } else { const int size = GetIntPropSize( pProp ); #ifdef CLIENT_DLL // Client might be reading any amount of bits in a custom RecvProxy // Break and check the datamaps if ( size == 0 ) break; #endif Assert( size <= pProp->GetElementStride() || pProp->GetElementStride() < 0 ); SetVarInfo(); pInfo->mask = MASK_INT_SIZE( size ); pInfo->datatype = types::_INT32; return pInfo; } } case DPT_Float: { if ( index < 0 || index >= pProp->GetNumElements() ) { Warning( "NetProp element index out of range! %s[%d]\n", szProp, index ); return NULL; } Assert( index == 0 || pProp->GetElementStride() > 0 ); Assert( pProp->GetElementStride() == sizeof(float) || pProp->GetElementStride() < 0 ); SetVarInfo(); pInfo->datatype = types::_FLOAT; return pInfo; } case DPT_Vector: { if ( index < 0 || index >= pProp->GetNumElements() ) { Warning( "NetProp element index out of range! %s[%d]\n", szProp, index ); return NULL; } Assert( index == 0 || pProp->GetElementStride() > 0 ); Assert( pProp->GetElementStride() == sizeof(float)*3 || pProp->GetElementStride() < 0 ); SetVarInfo(); pInfo->datatype = types::_VEC3; return pInfo; } case DPT_VectorXY: { if ( index < 0 || index >= pProp->GetNumElements() ) { Warning( "NetProp element index out of range! %s[%d]\n", szProp, index ); return NULL; } Assert( index == 0 || pProp->GetElementStride() > 0 ); Assert( pProp->GetElementStride() == sizeof(float)*2 || pProp->GetElementStride() < 0 ); SetVarInfo(); pInfo->datatype = types::_VEC2; return pInfo; } case DPT_String: { if ( index < 0 || index >= pProp->GetNumElements() ) { Warning( "NetProp element index out of range! %s[%d]\n", szProp, index ); return NULL; } Assert( index == 0 || pProp->GetElementStride() > 0 ); SetVarInfo(); #ifdef GAME_DLL pInfo->stringsize = 0; #else pInfo->stringsize = pProp->m_StringBufferSize; #endif #ifdef GAME_DLL if ( pProp->GetProxyFn() == SendProxy_StringT_To_String ) { pInfo->datatype = types::_STRING_T; } else #endif { Assert( pProp->GetProxyFn() == DataTableProxy_String ); pInfo->datatype = types::_CSTRING; } return pInfo; } case DPT_DataTable: { NetTable* pArray = pProp->GetDataTable(); if ( V_strcmp( pProp->GetName(), pArray->GetName() ) != 0 ) { Warning( "DT is not an array! %s(%s)\n", pProp->GetName(), pArray->GetName() ); return NULL; } if ( index < 0 || index >= pArray->GetNumProps() ) { Warning( "NetProp element index out of range! %s[%d]\n", szProp, index ); return NULL; } pProp = pArray->GetProp( index ); switch ( pProp->GetType() ) { case DPT_Int: { if ( IsEHandle( pProp ) ) { varinfo_t *pInfo = CacheNew( pEnt, szProp ); pInfo->elemsize = sizeof(int); pInfo->arraysize = pArray->GetNumProps(); pInfo->offset = offset; pInfo->datatype = types::_EHANDLE; return pInfo; } else { const int size = GetIntPropSize( pProp ); #ifdef CLIENT_DLL // Client might be reading any amount of bits in a custom RecvProxy // Break and check the datamaps if ( size == 0 ) break; #endif varinfo_t *pInfo = CacheNew( pEnt, szProp ); if ( pArray->GetNumProps() > 1 ) { pInfo->elemsize = pArray->GetProp(1)->GetOffset() - pArray->GetProp(0)->GetOffset(); } else { // Doesn't matter for an array of a single element pInfo->elemsize = 0; } pInfo->arraysize = pArray->GetNumProps(); pInfo->offset = offset; pInfo->mask = MASK_INT_SIZE( size ); pInfo->datatype = types::_INT32; return pInfo; } } case DPT_Float: { varinfo_t *pInfo = CacheNew( pEnt, szProp ); pInfo->elemsize = sizeof(float); pInfo->arraysize = pArray->GetNumProps(); pInfo->offset = offset; pInfo->datatype = types::_FLOAT; return pInfo; } case DPT_Vector: { varinfo_t *pInfo = CacheNew( pEnt, szProp ); pInfo->elemsize = sizeof(float)*3; pInfo->arraysize = pArray->GetNumProps(); pInfo->offset = offset; pInfo->datatype = types::_VEC3; return pInfo; } case DPT_VectorXY: { varinfo_t *pInfo = CacheNew( pEnt, szProp ); pInfo->elemsize = sizeof(float)*2; pInfo->arraysize = pArray->GetNumProps(); pInfo->offset = offset; pInfo->datatype = types::_VEC2; return pInfo; } case DPT_DataTable: { AssertMsg( 0, "DT in DT" ); return NULL; } case DPT_Array: { AssertMsg( 0, "Array in DT" ); return NULL; } case DPT_String: { AssertMsg( 0, "String in DT" ); return NULL; } default: UNREACHABLE(); } #ifdef CLIENT_DLL // DPT_Int can break into here for datamap fallback break; #else UNREACHABLE(); #endif } // DPT_DataTable case DPT_Array: { Assert( pProp->GetArrayProp() ); NetProp *pArray = pProp->GetArrayProp(); offset += pArray->GetOffset(); if ( index < 0 || index >= pProp->GetNumElements() ) { Warning( "NetProp element index out of range! %s[%d]\n", szProp, index ); return NULL; } switch ( pArray->GetType() ) { case DPT_Int: { Assert( index == 0 || pProp->GetElementStride() > 0 ); if ( IsEHandle( pArray ) ) { SetVarInfo(); pInfo->datatype = types::_EHANDLE; return pInfo; } else { const int size = GetIntPropSize( pArray ); #ifdef CLIENT_DLL // Client might be reading any amount of bits in a custom RecvProxy // Break and check the datamaps if ( size == 0 ) break; #endif SetVarInfo(); pInfo->mask = MASK_INT_SIZE( size ); pInfo->datatype = types::_INT32; return pInfo; } } case DPT_Float: { SetVarInfo(); pInfo->datatype = types::_FLOAT; return pInfo; } case DPT_Vector: { SetVarInfo(); pInfo->datatype = types::_VEC3; return pInfo; } case DPT_VectorXY: { SetVarInfo(); pInfo->datatype = types::_VEC2; return pInfo; } case DPT_String: { AssertMsg( 0, "String array not implemented" ); return NULL; } case DPT_Array: case DPT_DataTable: AssertMsg( 0, "DT in array" ); default: UNREACHABLE(); } #ifdef CLIENT_DLL // DPT_Int can break into here for datamap fallback break; #else UNREACHABLE(); #endif } // DPT_Array default: UNREACHABLE(); } // ambigious int size on client, check the datamaps #undef SetVarInfo } datamap_t *map = pEnt->GetDataDescMap(); typedescription_t *pField = FindInDataMap( (char*)pEnt, map, szProp, &offset ); if ( pField ) { #ifdef CLIENT_DLL find_field: #endif if ( index < 0 || index >= pField->fieldSize ) { Warning( "NetProp element index out of range! %s[%d]\n", szProp, index ); return NULL; } #define SetVarInfo()\ varinfo_t *pInfo = CacheNew( pEnt, szProp );\ pInfo->isNotNetworked = 1;\ pInfo->elemsize = pField->fieldSizeInBytes / pField->fieldSize;\ pInfo->arraysize = pField->fieldSize;\ pInfo->offset = offset; switch ( pField->fieldType ) { case FIELD_INTEGER: case FIELD_MATERIALINDEX: case FIELD_MODELINDEX: case FIELD_COLOR32: case FIELD_TICK: case FIELD_BOOLEAN: case FIELD_CHARACTER: case FIELD_SHORT: { SetVarInfo(); pInfo->isUnsigned = ( pField->flags & SPROP_UNSIGNED ) != 0; pInfo->isNotNetworked = 1; switch ( pField->fieldType ) { case FIELD_INTEGER: case FIELD_MATERIALINDEX: case FIELD_MODELINDEX: case FIELD_COLOR32: case FIELD_TICK: pInfo->datatype = types::_INT32; break; case FIELD_BOOLEAN: pInfo->datatype = types::_INT1; break; case FIELD_CHARACTER: Assert( pField->fieldSizeInBytes == pField->fieldSize ); pInfo->stringsize = pField->fieldSizeInBytes; pInfo->datatype = types::_INT8; break; case FIELD_SHORT: pInfo->datatype = types::_INT16; break; default: UNREACHABLE(); } return pInfo; } case FIELD_FLOAT: case FIELD_TIME: { Assert( sizeof(float) == pField->fieldSizeInBytes / pField->fieldSize ); SetVarInfo(); pInfo->datatype = types::_FLOAT; return pInfo; } case FIELD_EHANDLE: { Assert( sizeof(int) == pField->fieldSizeInBytes / pField->fieldSize ); SetVarInfo(); pInfo->datatype = types::_EHANDLE; return pInfo; } #ifdef GAME_DLL case FIELD_CLASSPTR: { Assert( sizeof(int*) == pField->fieldSizeInBytes / pField->fieldSize ); SetVarInfo(); pInfo->datatype = types::_CLASSPTR; return pInfo; } case FIELD_EDICT: { Assert( sizeof(int*) == pField->fieldSizeInBytes / pField->fieldSize ); SetVarInfo(); pInfo->datatype = types::_EDICT; return pInfo; } #endif case FIELD_VECTOR: case FIELD_POSITION_VECTOR: { Assert( sizeof(float)*3 == pField->fieldSizeInBytes / pField->fieldSize ); SetVarInfo(); pInfo->datatype = types::_VEC3; return pInfo; } case FIELD_STRING: case FIELD_MODELNAME: case FIELD_SOUNDNAME: { SetVarInfo(); pInfo->stringsize = 0; pInfo->datatype = types::_STRING_T; return pInfo; } case FIELD_CUSTOM: { if ( pField->pSaveRestoreOps == GetPhysObjSaveRestoreOps( PIID_IPHYSICSOBJECT ) ) { SetVarInfo(); pInfo->datatype = types::_PHYS; return pInfo; } else if ( pField->pSaveRestoreOps == ActivityDataOps() ) { SetVarInfo(); pInfo->datatype = types::_INT32; return pInfo; } #ifdef GAME_DLL else if ( IS_EHANDLE_UTLVECTOR( pField ) ) { SetVarInfo(); pInfo->arraysize = ( 1 << VARINFO_ARRAYSIZE_BITS ) - 1; // dynamic, check on get pInfo->datatype = types::_DAR_EHANDLE; } else if ( pField->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_CLASSPTR, CBaseEntity* ) ) { SetVarInfo(); pInfo->arraysize = ( 1 << VARINFO_ARRAYSIZE_BITS ) - 1; // dynamic, check on get pInfo->datatype = types::_DAR_CLASSPTR; } else if ( pField->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_INTEGER, int ) ) { SetVarInfo(); pInfo->arraysize = ( 1 << VARINFO_ARRAYSIZE_BITS ) - 1; // dynamic, check on get pInfo->datatype = types::_DAR_INT; } else if ( pField->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_FLOAT, float ) || pField->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_TIME, float ) ) { SetVarInfo(); pInfo->arraysize = ( 1 << VARINFO_ARRAYSIZE_BITS ) - 1; // dynamic, check on get pInfo->datatype = types::_DAR_FLOAT; } // Only used by CAI_PlayerAlly::m_PendingConcept else if ( pField->pSaveRestoreOps == GetStdStringDataOps() ) { SetVarInfo(); pInfo->datatype = types::_STDSTRING; return pInfo; } #endif return NULL; } case FIELD_EMBEDDED: return NULL; default: AssertMsg( 0, "Unknown type %d\n", pField->fieldType ); return NULL; } UNREACHABLE(); #undef SetVarInfo } #ifdef CLIENT_DLL else { map = pEnt->GetPredDescMap(); pField = FindInDataMap( (char*)pEnt, map, szProp, &offset ); if ( pField ) { goto find_field; } } #endif return NULL; } public: // FIXME: Cannot get datatable/arrays at the moment bool HasProp( HSCRIPT hEnt, const char *szProp ) { CBaseEntity *pEnt = ToEnt( hEnt ); if ( !pEnt ) return false; varinfo_t *pInfo = CacheFetch( pEnt, szProp ); if ( !pInfo ) { pInfo = GetVarInfo( pEnt, szProp, INDEX_GET_TYPE ); if ( !pInfo ) return false; } return true; } // FIXME: Cannot get datatable/arrays at the moment const char *GetPropType( HSCRIPT hEnt, const char *szProp ) { CBaseEntity *pEnt = ToEnt( hEnt ); if ( !pEnt ) return NULL; varinfo_t *pInfo = CacheFetch( pEnt, szProp ); if ( !pInfo ) { pInfo = GetVarInfo( pEnt, szProp, INDEX_GET_TYPE ); if ( !pInfo ) return NULL; } switch ( pInfo->datatype ) { case types::_INT1: case types::_INT8: case types::_INT16: case types::_INT32: return "integer"; case types::_FLOAT: return "float"; case types::_VEC3: return "vector"; case types::_VEC2: return "vector2d"; case types::_CSTRING: case types::_STRING_T: case types::_STDSTRING: return "string"; case types::_EHANDLE: case types::_CLASSPTR: case types::_EDICT: return "entity"; case types::_PHYS: return "phys"; case types::_ARRAY: return "array"; case types::_DATATABLE: return "datatable"; } if ( pInfo->arraysize > 1 ) return "array"; return ""; } int GetPropArraySize( HSCRIPT hEnt, const char *szProp ) { CBaseEntity *pEnt = ToEnt( hEnt ); if ( !pEnt ) return -1; varinfo_t *pInfo = CacheFetch( pEnt, szProp ); if ( !pInfo ) { pInfo = GetVarInfo( pEnt, szProp, INDEX_GET_TYPE ); if ( !pInfo ) return -1; } #ifdef GAME_DLL switch ( pInfo->datatype ) { case types::_DAR_EHANDLE: { CUtlVector< EHANDLE > &vec = *(CUtlVector< EHANDLE >*)((char*)pEnt + pInfo->offset); if ( !vec.Base() ) return -1; return vec.Count(); } case types::_DAR_CLASSPTR: { CUtlVector< CBaseEntity* > &vec = *(CUtlVector< CBaseEntity* >*)((char*)pEnt + pInfo->offset); if ( !vec.Base() ) return -1; return vec.Count(); } case types::_DAR_INT: { CUtlVector< int > &vec = *(CUtlVector< int >*)((char*)pEnt + pInfo->offset); if ( !vec.Base() ) return -1; return vec.Count(); } case types::_DAR_FLOAT: { CUtlVector< float > &vec = *(CUtlVector< float >*)((char*)pEnt + pInfo->offset); if ( !vec.Base() ) return -1; return vec.Count(); } } #endif return pInfo->arraysize; } public: int GetPropIntArray( HSCRIPT hEnt, const char *szProp, int index ) { CBaseEntity *pEnt = ToEnt( hEnt ); if ( !pEnt ) return -1; varinfo_t *pInfo = CacheFetch( pEnt, szProp ); if ( !pInfo ) { pInfo = GetVarInfo( pEnt, szProp, index ); if ( !pInfo ) return -1; } if ( index < 0 || (unsigned int)index >= pInfo->arraysize ) return -1; if ( pInfo->isNotNetworked ) { switch ( pInfo->datatype ) { case types::_INT32: if ( pInfo->isUnsigned ) return *(unsigned int*)((char*)pEnt + pInfo->GetOffset( index )); return *(int*)((char*)pEnt + pInfo->GetOffset( index )); case types::_INT1: return *(bool*)((char*)pEnt + pInfo->GetOffset( index )); case types::_INT8: if ( pInfo->isUnsigned ) return *(unsigned char*)((char*)pEnt + pInfo->GetOffset( index )); return *(char*)((char*)pEnt + pInfo->GetOffset( index )); case types::_INT16: if ( pInfo->isUnsigned ) return *(unsigned short*)((char*)pEnt + pInfo->GetOffset( index )); return *(short*)((char*)pEnt + pInfo->GetOffset( index )); #ifdef GAME_DLL case types::_DAR_INT: { CUtlVector< int > &vec = *(CUtlVector< int >*)((char*)pEnt + pInfo->offset); if ( !vec.Base() ) return -1; if ( index >= vec.Count() ) return -1; return vec[ index ]; } #endif } } else { switch ( pInfo->datatype ) { case types::_INT32: return (*(int*)((char*)pEnt + pInfo->GetOffset( index ))) & pInfo->mask; } } return -1; } void SetPropIntArray( HSCRIPT hEnt, const char *szProp, int value, int index ) { CBaseEntity *pEnt = ToEnt( hEnt ); if ( !pEnt ) return; varinfo_t *pInfo = CacheFetch( pEnt, szProp ); if ( !pInfo ) { pInfo = GetVarInfo( pEnt, szProp, index ); if ( !pInfo ) return; } if ( index < 0 || (unsigned int)index >= pInfo->arraysize ) return; if ( pInfo->isNotNetworked ) { switch ( pInfo->datatype ) { case types::_INT32: if ( pInfo->isUnsigned ) { *(unsigned int*)((char*)pEnt + pInfo->GetOffset( index )) = value; NetworkStateChanged( pEnt, pInfo->GetOffset( index ) ); break; } *(int*)((char*)pEnt + pInfo->GetOffset( index )) = value; NetworkStateChanged( pEnt, pInfo->GetOffset( index ) ); break; case types::_INT1: *(bool*)((char*)pEnt + pInfo->GetOffset( index )) = value; NetworkStateChanged( pEnt, pInfo->GetOffset( index ) ); break; case types::_INT8: if ( pInfo->isUnsigned ) { *(unsigned char*)((char*)pEnt + pInfo->GetOffset( index )) = value; NetworkStateChanged( pEnt, pInfo->GetOffset( index ) ); break; } *(char*)((char*)pEnt + pInfo->GetOffset( index )) = value; NetworkStateChanged( pEnt, pInfo->GetOffset( index ) ); break; case types::_INT16: if ( pInfo->isUnsigned ) { *(unsigned short*)((char*)pEnt + pInfo->GetOffset( index )) = value; NetworkStateChanged( pEnt, pInfo->GetOffset( index ) ); break; } *(short*)((char*)pEnt + pInfo->GetOffset( index )) = value; NetworkStateChanged( pEnt, pInfo->GetOffset( index ) ); break; #ifdef GAME_DLL case types::_DAR_INT: { CUtlVector< int > &vec = *(CUtlVector< int >*)((char*)pEnt + pInfo->offset); if ( !vec.Base() ) return; if ( index >= vec.Count() ) return; vec[ index ] = value; NetworkStateChanged( pEnt, pInfo->offset ); break; } #endif } } else { switch ( pInfo->datatype ) { case types::_INT32: { int *dest = (int*)((char*)pEnt + pInfo->GetOffset( index )); *dest = (*dest & ~pInfo->mask) | (value & pInfo->mask); NetworkStateChanged( pEnt, pInfo->GetOffset( index ) ); break; } } } } float GetPropFloatArray( HSCRIPT hEnt, const char *szProp, int index ) { CBaseEntity *pEnt = ToEnt( hEnt ); if ( !pEnt ) return -1; varinfo_t *pInfo = CacheFetch( pEnt, szProp ); if ( !pInfo ) { pInfo = GetVarInfo( pEnt, szProp, index ); if ( !pInfo ) return -1; } unsigned int arraysize = pInfo->arraysize; if ( pInfo->datatype == types::_VEC3 ) arraysize *= 3; if ( index < 0 || (unsigned int)index >= arraysize ) return -1; switch ( pInfo->datatype ) { case types::_FLOAT: return *(float*)((char*)pEnt + pInfo->GetOffset( index )); case types::_VEC3: return ((float*)((char*)pEnt + pInfo->GetOffset( index / 3 )))[ index % 3 ]; #ifdef GAME_DLL case types::_DAR_FLOAT: { CUtlVector< float > &vec = *(CUtlVector< float >*)((char*)pEnt + pInfo->offset); if ( !vec.Base() ) return -1; if ( index >= vec.Count() ) return -1; return vec[ index ]; } #endif } return -1; } void SetPropFloatArray( HSCRIPT hEnt, const char *szProp, float value, int index ) { CBaseEntity *pEnt = ToEnt( hEnt ); if ( !pEnt ) return; varinfo_t *pInfo = CacheFetch( pEnt, szProp ); if ( !pInfo ) { pInfo = GetVarInfo( pEnt, szProp, index ); if ( !pInfo ) return; } unsigned int arraysize = pInfo->arraysize; if ( pInfo->datatype == types::_VEC3 ) arraysize *= 3; if ( index < 0 || (unsigned int)index >= arraysize ) return; switch ( pInfo->datatype ) { case types::_FLOAT: *(float*)((char*)pEnt + pInfo->GetOffset( index )) = value; NetworkStateChanged( pEnt, pInfo->GetOffset( index ) ); break; case types::_VEC3: ((float*)((char*)pEnt + pInfo->GetOffset( index / 3 )))[ index % 3 ] = value; NetworkStateChanged( pEnt, pInfo->GetOffset( index / 3 ) ); break; #ifdef GAME_DLL case types::_DAR_FLOAT: { CUtlVector< float > &vec = *(CUtlVector< float >*)((char*)pEnt + pInfo->offset); if ( !vec.Base() ) return; if ( index >= vec.Count() ) return; vec[ index ] = value; NetworkStateChanged( pEnt, pInfo->offset ); break; } #endif } } HSCRIPT GetPropEntityArray( HSCRIPT hEnt, const char *szProp, int index ) { CBaseEntity *pEnt = ToEnt( hEnt ); if ( !pEnt ) return NULL; varinfo_t *pInfo = CacheFetch( pEnt, szProp ); if ( !pInfo ) { pInfo = GetVarInfo( pEnt, szProp, index ); if ( !pInfo ) return NULL; } if ( index < 0 || (unsigned int)index >= pInfo->arraysize ) return NULL; switch ( pInfo->datatype ) { case types::_EHANDLE: { EHANDLE &iEHandle = *(EHANDLE*)((char*)pEnt + pInfo->GetOffset( index )); return ToHScript( iEHandle ); } #ifdef GAME_DLL case types::_CLASSPTR: { CBaseEntity* ptr = *(CBaseEntity**)((char*)pEnt + pInfo->GetOffset( index )); return ToHScript( ptr ); } case types::_EDICT: { edict_t* ptr = *(edict_t**)((char*)pEnt + pInfo->GetOffset( index )); return ToHScript( GetContainingEntity( ptr ) ); } case types::_DAR_EHANDLE: { CUtlVector< EHANDLE > &vec = *(CUtlVector< EHANDLE >*)((char*)pEnt + pInfo->offset); if ( !vec.Base() ) return NULL; if ( index >= vec.Count() ) return NULL; return ToHScript( vec[ index ] ); } case types::_DAR_CLASSPTR: { CUtlVector< CBaseEntity* > &vec = *(CUtlVector< CBaseEntity* >*)((char*)pEnt + pInfo->offset); if ( !vec.Base() ) return NULL; if ( index >= vec.Count() ) return NULL; return ToHScript( vec[ index ] ); } #endif case types::_PHYS: { IPhysicsObject* ptr = *(IPhysicsObject**)((char*)pEnt + pInfo->GetOffset( index )); return ptr ? g_pScriptVM->RegisterInstance( ptr ) : NULL; } } return NULL; } void SetPropEntityArray( HSCRIPT hEnt, const char *szProp, HSCRIPT value, int index ) { CBaseEntity *pEnt = ToEnt( hEnt ); if ( !pEnt ) return; varinfo_t *pInfo = CacheFetch( pEnt, szProp ); if ( !pInfo ) { pInfo = GetVarInfo( pEnt, szProp, index ); if ( !pInfo ) return; } if ( index < 0 || (unsigned int)index >= pInfo->arraysize ) return; switch ( pInfo->datatype ) { case types::_EHANDLE: *(EHANDLE*)((char*)pEnt + pInfo->GetOffset( index )) = ToEnt( value ); NetworkStateChanged( pEnt, pInfo->GetOffset( index ) ); break; #ifdef GAME_DLL case types::_CLASSPTR: *(CBaseEntity**)((char*)pEnt + pInfo->GetOffset( index )) = ToEnt( value ); NetworkStateChanged( pEnt, pInfo->GetOffset( index ) ); break; case types::_EDICT: { CBaseEntity* ptr = ToEnt( value ); *(edict_t**)((char*)pEnt + pInfo->GetOffset( index )) = ptr ? ptr->edict() : NULL; NetworkStateChanged( pEnt, pInfo->GetOffset( index ) ); break; } case types::_DAR_EHANDLE: { CUtlVector< EHANDLE > &vec = *(CUtlVector< EHANDLE >*)((char*)pEnt + pInfo->offset); if ( !vec.Base() ) return; if ( index >= vec.Count() ) return; vec[ index ] = ToEnt( value ); NetworkStateChanged( pEnt, pInfo->offset ); break; } case types::_DAR_CLASSPTR: { CUtlVector< CBaseEntity* > &vec = *(CUtlVector< CBaseEntity* >*)((char*)pEnt + pInfo->offset); if ( !vec.Base() ) return; if ( index >= vec.Count() ) return; vec[ index ] = ToEnt( value ); NetworkStateChanged( pEnt, pInfo->offset ); break; } #endif } } const Vector &GetPropVectorArray( HSCRIPT hEnt, const char *szProp, int index ) { CBaseEntity *pEnt = ToEnt( hEnt ); if ( !pEnt ) return vec3_invalid; varinfo_t *pInfo = CacheFetch( pEnt, szProp ); if ( !pInfo ) { pInfo = GetVarInfo( pEnt, szProp, index ); if ( !pInfo ) return vec3_invalid; } if ( index < 0 || (unsigned int)index >= pInfo->arraysize ) return vec3_invalid; switch ( pInfo->datatype ) { case types::_VEC3: return *(Vector*)((char*)pEnt + pInfo->GetOffset( index )); } return vec3_invalid; } void SetPropVectorArray( HSCRIPT hEnt, const char *szProp, const Vector &value, int index ) { CBaseEntity *pEnt = ToEnt( hEnt ); if ( !pEnt ) return; varinfo_t *pInfo = CacheFetch( pEnt, szProp ); if ( !pInfo ) { pInfo = GetVarInfo( pEnt, szProp, index ); if ( !pInfo ) return; } if ( index < 0 || (unsigned int)index >= pInfo->arraysize ) return; switch ( pInfo->datatype ) { case types::_VEC3: *(Vector*)((char*)pEnt + pInfo->GetOffset( index )) = value; NetworkStateChanged( pEnt, pInfo->GetOffset( index ) ); break; } } const char *GetPropStringArray( HSCRIPT hEnt, const char *szProp, int index ) { CBaseEntity *pEnt = ToEnt( hEnt ); if ( !pEnt ) return NULL; varinfo_t *pInfo = CacheFetch( pEnt, szProp ); if ( !pInfo ) { pInfo = GetVarInfo( pEnt, szProp, index ); if ( !pInfo ) return NULL; } if ( index < 0 || (unsigned int)index >= pInfo->arraysize ) return NULL; switch ( pInfo->datatype ) { case types::_CSTRING: return (const char*)((char*)pEnt + pInfo->GetOffset( index )); case types::_STRING_T: // Identical to _CSTRING on client return STRING( *(string_t*)((char*)pEnt + pInfo->GetOffset( index )) ); case types::_INT8: { if ( !pInfo->stringsize ) return NULL; char * const pVar = ((char*)pEnt + pInfo->GetOffset( index )); // Is this null terminated? int i = 0; char *c = pVar; while ( *(c++) && i++ < pInfo->stringsize ); if ( i >= pInfo->stringsize ) { // Not a null terminated string, don't talk to me ever again pInfo->stringsize = 0; return NULL; } return pVar; } #ifdef GAME_DLL case types::_STDSTRING: return ( (std::string*)((char*)pEnt + pInfo->GetOffset( index )) )->c_str(); #endif } return NULL; } void SetPropStringArray( HSCRIPT hEnt, const char *szProp, const char *value, int index ) { CBaseEntity *pEnt = ToEnt( hEnt ); if ( !pEnt ) return; varinfo_t *pInfo = CacheFetch( pEnt, szProp ); if ( !pInfo ) { pInfo = GetVarInfo( pEnt, szProp, index ); if ( !pInfo ) return; } if ( index < 0 || (unsigned int)index >= pInfo->arraysize ) return; switch ( pInfo->datatype ) { case types::_CSTRING: case types::_INT8: { if ( pInfo->stringsize ) { V_strncpy( (char*)pEnt + pInfo->GetOffset( index ), value, pInfo->stringsize ); NetworkStateChanged( pEnt, pInfo->GetOffset( index ) ); break; } } case types::_STRING_T: { extern string_t FindPooledString( const char* ); extern string_t AllocPooledString( const char* ); string_t src = FindPooledString( value ); if ( src == NULL_STRING ) src = AllocPooledString( value ); #ifdef GAME_DLL *(string_t*)((char*)pEnt + pInfo->GetOffset( index )) = src; #else V_strcpy( (char*)pEnt + pInfo->GetOffset( index ), src ); #endif NetworkStateChanged( pEnt, pInfo->GetOffset( index ) ); break; } #ifdef GAME_DLL case types::_STDSTRING: { ( (std::string*)((char*)pEnt + pInfo->GetOffset( index )) )->assign( value, V_strlen(value) ); NetworkStateChanged( pEnt, pInfo->GetOffset( index ) ); break; } #endif } } #define GetProp( type, name )\ type GetProp##name( HSCRIPT hEnt, const char* szProp )\ {\ return GetProp##name##Array( hEnt, szProp, 0 );\ } #define SetProp( type, name )\ void SetProp##name( HSCRIPT hEnt, const char* szProp, type value )\ {\ SetProp##name##Array( hEnt, szProp, value, 0 );\ } GetProp( int, Int ); SetProp( int, Int ); GetProp( float, Float ); SetProp( float, Float ); GetProp( HSCRIPT, Entity ); SetProp( HSCRIPT, Entity ); GetProp( const Vector&, Vector ); SetProp( const Vector&, Vector ); GetProp( const char*, String ); SetProp( const char*, String ); #undef GetProp #undef SetProp #ifdef _DEBUG private: CUtlBuffer m_output; CUtlString m_indent; int m_indent_level; void IndentStart() { m_indent = ""; m_indent_level = 0; } void Indent1() { m_indent_level++; m_indent.Append("\t"); } void Indent0() { m_indent_level--; m_indent = m_indent.Slice( 0, m_indent_level ); } void PrintVec3( float *pVar ) { if ( *(Vector*)pVar != vec3_invalid ) { Print( "[%f %f %f]", pVar[0], pVar[1], pVar[2] ); } else { Print("vec3_invalid"); } } void PrintVec2( float *pVar ) { Print( "[%f %f]", pVar[0], pVar[1] ); } void PrintEntity( EHANDLE* pVar ) { CBaseEntity* ent = *pVar; if ( ent ) { Print("[%d]%s", ent->entindex(), ent->GetDebugName()); } else { Print("null"); } } #ifdef GAME_DLL void PrintEntity( CBaseEntity* pVar ) { CBaseEntity* ent = pVar; if ( ent ) { Print("[%d]%s", ent->entindex(), ent->GetDebugName()); } else { Print("null"); } } void PrintEntity( edict_t* pVar ) { CBaseEntity* ent = GetContainingEntity( pVar ); if ( ent ) { Print("[%d]%s", ent->entindex(), ent->GetDebugName()); } else { Print("null"); } } #endif #ifdef GAME_DLL void PrintString( string_t pVar ) { if ( STRING(pVar) ) { Print("\"%s\"", STRING(pVar)); } else { Print("null"); } } #endif void PrintString( const char *pVar ) { if ( pVar ) { Print("\"%s\"", pVar); } else { Print("null"); } } void PrintPropType( NetProp *pProp ) { switch ( pProp->GetType() ) { case DPT_Int: if ( IsUtlVector( pProp ) ) { Print("UtlVector"); } else if ( IsEHandle( pProp ) ) { Print( "entity" ); } else { Print( "int" ); } break; #ifdef SUPPORTS_INT64 case DPT_Int64: AssertMsg( 0, "not implemented" ); Print( "int64" ); break; #endif case DPT_Float: Print( "float" ); break; case DPT_Vector: Print( "vec3" ); break; case DPT_VectorXY: Print( "vec2" ); break; case DPT_String: { #ifdef GAME_DLL if ( pProp->GetProxyFn() == SendProxy_StringT_To_String ) { Print("string_t"); } else #endif { #ifdef CLIENT_DLL Print("string[%d]", pProp->m_StringBufferSize); #else Print("string"); #endif } break; } case DPT_Array: case DPT_DataTable: break; default: UNREACHABLE(); } } void PrintProp_r( char *pVar, NetProp *pProp ) { switch ( pProp->GetType() ) { case DPT_Int: { if ( IsUtlVector( pProp ) ) { } else if ( IsEHandle( pProp ) ) { PrintEntity( (EHANDLE*)pVar ); } else { #ifdef GAME_DLL // Is this value larger than networked size? AssertMsg( (*(int*)pVar & MASK_NEAREST_BYTE( pProp->m_nBits )) == 0, "%s(%i) %d bits doesn't fit networked %d bits", pProp->GetName(), *(int*)pVar & MASK_NEAREST_BYTE( pProp->m_nBits ), ALIGN_TO_NEAREST_BYTE(pProp->m_nBits), pProp->m_nBits ); #endif int size = GetIntPropSize( pProp ); if ( size ) { Print( "%i", *(int*)pVar & MASK_INT_SIZE( size ) ); } else { Print( " 0x%08x", *(int*)pVar ); } } break; } #ifdef SUPPORTS_INT64 case DPT_Int64: { Print( "%lli", *(int64*)pVar ); break; } #endif case DPT_Float: { Assert( pProp->GetElementStride() == sizeof(float) || pProp->GetElementStride() < 0 ); if ( *(float*)pVar == FLT_MAX ) { Print("FLT_MAX"); } else { Print("%f", *(float*)pVar); } break; } case DPT_Vector: { PrintVec3( (float*)pVar ); break; } case DPT_VectorXY: { PrintVec2( (float*)pVar ); break; } case DPT_String: { #ifdef GAME_DLL if ( pProp->GetProxyFn() == SendProxy_StringT_To_String ) { PrintString( *(string_t*)pVar ); } else #endif { Assert( pProp->GetProxyFn() == DataTableProxy_String ); PrintString( (char*)pVar ); } break; } case DPT_DataTable: { NetTable* pArray = pProp->GetDataTable(); Assert( pArray->GetNumProps() ); if ( V_strcmp( pProp->GetName(), pArray->GetName() ) != 0 ) { Print( " -> (%s)\n", pArray->GetName() ); DumpNetTable_r( pVar, pArray ); break; } // Double check that each element is the same size // Array indexing ints gets element size from this int diff1 = pArray->GetProp(1)->GetOffset() - pArray->GetProp(0)->GetOffset(); for ( int k = 0; k < pArray->GetNumProps()-1; k++ ) { int diff2 = pArray->GetProp(k+1)->GetOffset() - pArray->GetProp(k)->GetOffset(); Assert( diff1 == diff2 ); } Print(" <"); PrintPropType( pArray->GetProp(0) ); Print(" array> #%d", pArray->GetNumProps()); Print("\n%s[", m_indent.Get()); Indent1(); for ( int j = 0; j < pArray->GetNumProps(); j++ ) { Print("\n%s", m_indent.Get()); PrintProp_r( pVar + pArray->GetProp(j)->GetOffset(), pArray->GetProp(j) ); } Indent0(); Print( "\n%s]", m_indent.Get() ); break; } case DPT_Array: { Assert( pProp->GetArrayProp() ); NetProp *pArray = pProp->GetArrayProp(); pVar += pArray->GetOffset(); int numElements = pProp->GetNumElements(); int elementStride = pProp->GetElementStride(); Print(" <"); PrintPropType( pArray ); Print(" array> #%d", numElements); Print("\n%s[", m_indent.Get()); Indent1(); for ( int j = 0; j < numElements; j++ ) { Print("\n%s", m_indent.Get()); PrintProp_r( pVar + j * elementStride, pArray ); } Indent0(); Print( "\n%s]", m_indent.Get() ); break; } default: UNREACHABLE(); } } void DumpNetTable_r( void *pEnt, NetTable *pTable ) { Print("%s{\n", m_indent.Get()); Indent1(); int numProps = pTable->GetNumProps(); for ( int i = 0; i < numProps; i++ ) { NetProp* pProp = pTable->GetProp(i); char* pVar = (char*)pEnt + pProp->GetOffset(); if ( pProp->IsInsideArray() ) continue; Print( "%s%s", m_indent.Get(), pProp->GetName() ); if ( pProp->GetOffset() == 0 ) Print("<0>"); if ( pProp->GetType() != DPT_DataTable ) Print(" <"); PrintPropType( pProp ); if ( pProp->GetType() != DPT_DataTable ) Print("> "); PrintProp_r( pVar, pProp ); Print("\n"); } Indent0(); Print("%s}", m_indent.Get()); } void PrintFieldType( char *pVar, typedescription_t *td ) { switch ( td->fieldType ) { case FIELD_INTEGER: case FIELD_MATERIALINDEX: case FIELD_MODELINDEX: case FIELD_TICK: Print( "int" ); break; case FIELD_SHORT: Print( "short" ); break; case FIELD_CHARACTER: Print( "char" ); break; case FIELD_BOOLEAN: Print( "bool" ); break; case FIELD_COLOR32: Print( "clr32" ); break; case FIELD_FLOAT: case FIELD_TIME: Print( "float" ); break; case FIELD_VECTOR: case FIELD_POSITION_VECTOR: Print( "vec3" ); break; case FIELD_VECTOR2D: Print( "vec2" ); break; case FIELD_STRING: case FIELD_MODELNAME: case FIELD_SOUNDNAME: Print( "string" ); break; case FIELD_EHANDLE: #ifdef GAME_DLL case FIELD_CLASSPTR: case FIELD_EDICT: #endif Print( "entity" ); break; case FIELD_VMATRIX: Print( "VMatrix" ); break; case FIELD_VMATRIX_WORLDSPACE: Print( "VMatrix WORLDSPACE" ); break; case FIELD_MATRIX3X4_WORLDSPACE: Print( "matrix3x4 WORLDSPACE" ); break; case FIELD_INTERVAL: Print( "interval_t" ); break; case FIELD_CUSTOM: PrintCustomFieldType( pVar, td ); break; case FIELD_EMBEDDED: if ( td->fieldSize > 1 ) Print( "DT" ); break; default: Print( "unknown %d", td->fieldType ); } } void PrintCustomFieldType( char *pVar, typedescription_t *td ) { Assert( td->fieldType == FIELD_CUSTOM ); const char *g_ppszPhysTypeNames[PIID_NUM_TYPES] = { "Unknown Phys", "IPhysicsObject", "IPhysicsFluidController", "IPhysicsSpring", "IPhysicsConstraintGroup", "IPhysicsConstraint", "IPhysicsShadowController", "IPhysicsPlayerController", "IPhysicsMotionController", "IPhysicsVehicleController", }; for ( int i = 0; i < PIID_NUM_TYPES; i++ ) { if ( td->pSaveRestoreOps == GetPhysObjSaveRestoreOps( (PhysInterfaceId_t)i ) ) { Print("%s", g_ppszPhysTypeNames[i]); return; } } if ( td->pSaveRestoreOps == ActivityDataOps() ) { Print("int"); } else if ( td->pSaveRestoreOps == GetSoundSaveRestoreOps() ) { Print("CSoundPatch"); } else if ( td->pSaveRestoreOps == GetStdStringDataOps() ) { Print("stdstring"); } #ifdef GAME_DLL else if ( IS_EHANDLE_UTLVECTOR( td ) ) { CUtlVector< EHANDLE > &vec = *(CUtlVector< EHANDLE >*)pVar; if ( vec.Base() ) Print("entity utlvector #%d", vec.Count()); else Print("entity utlvector"); } else if ( td->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_INTEGER, int ) ) { CUtlVector< int > &vec = *(CUtlVector< int >*)pVar; if ( vec.Base() ) Print("int utlvector #%d", vec.Count()); else Print("int utlvector"); } else if ( td->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_FLOAT, float ) || td->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_TIME, float ) ) { CUtlVector< float > &vec = *(CUtlVector< float >*)pVar; if ( vec.Base() ) Print("float utlvector #%d", vec.Count()); else Print("float utlvector"); } else if ( td->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_STRING, string_t ) ) { CUtlVector< string_t > &vec = *(CUtlVector< string_t >*)pVar; if ( vec.Base() ) Print("string utlvector #%d", vec.Count()); else Print("string utlvector"); } else if ( td->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_CLASSPTR, CBaseEntity* ) ) { CUtlVector< CBaseEntity* > &vec = *(CUtlVector< CBaseEntity* >*)pVar; if ( vec.Base() ) Print("entity utlvector #%d", vec.Count()); else Print("entity utlvector"); } else if ( td->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_VECTOR, Vector ) ) { AssertMsg( 0, "Implement me" ); CUtlVector< Vector > &vec = *(CUtlVector< Vector >*)pVar; if ( vec.Base() ) Print("Vector utlvector #%d", vec.Count()); else Print("Vector utlvector"); } else if ( !V_strcmp( td->fieldName, "m_pIk" ) ) { Print("IK"); } else if ( td->pSaveRestoreOps == thinkcontextFuncs ) { Print("thinkfunc"); } else if ( td->pSaveRestoreOps == (ISaveRestoreOps*)(&g_AI_MemoryListSaveRestoreOps) ) { Print("AI memory map"); } else if ( td->pSaveRestoreOps == (ISaveRestoreOps*)(&g_VguiScreenStringOps)) { Print("string (vgui screen)"); } else if ( td->pSaveRestoreOps == (ISaveRestoreOps*)(&g_ConceptHistoriesSaveDataOps) ) { Print("concept histories"); } #endif // GAME_DLL else { Print("custom"); } } void PrintCustomField( char *pVar, typedescription_t *td ) { Assert( td->fieldType == FIELD_CUSTOM ); for ( int i = 0; i < PIID_NUM_TYPES; i++ ) { if ( td->pSaveRestoreOps == GetPhysObjSaveRestoreOps( (PhysInterfaceId_t)i ) ) { Print("0x%x", pVar); return; } } if ( td->pSaveRestoreOps == ActivityDataOps() ) { Print("%i", *(int*)pVar); } else if ( td->pSaveRestoreOps == GetSoundSaveRestoreOps() ) { if ( *pVar ) { CSoundPatch *pSound = *(CSoundPatch**)pVar; PrintString( CSoundEnvelopeController::GetController().SoundGetName( pSound ) ); } else { Print( "null" ); } } else if ( td->pSaveRestoreOps == GetStdStringDataOps() ) { Print("%s", ((std::string*)pVar)->c_str()); } #ifdef GAME_DLL else if ( IS_EHANDLE_UTLVECTOR( td ) ) { CUtlVector< EHANDLE > &vec = *(CUtlVector< EHANDLE >*)pVar; if ( !vec.Base() ) { Print("null"); return; } Print("\n%s[", m_indent.Get()); Indent1(); FOR_EACH_VEC( vec, i ) { Print("\n%s", m_indent.Get()); PrintEntity( vec[i] ); } Indent0(); Print("\n%s]", m_indent.Get()); } else if ( td->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_INTEGER, int ) ) { CUtlVector< int > &vec = *(CUtlVector< int >*)pVar; if ( !vec.Base() ) { Print("null"); return; } Print("\n%s[", m_indent.Get()); Indent1(); FOR_EACH_VEC( vec, i ) { Print("\n%s", m_indent.Get()); Print( "%i", vec[i] ); } Indent0(); Print("\n%s]", m_indent.Get()); } else if ( td->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_FLOAT, float ) || td->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_TIME, float ) ) { CUtlVector< float > &vec = *(CUtlVector< float >*)pVar; if ( !vec.Base() ) { Print("null"); return; } Print("\n%s[", m_indent.Get()); Indent1(); FOR_EACH_VEC( vec, i ) { Print("\n%s", m_indent.Get()); Print( "%f", vec[i] ); } Indent0(); Print("\n%s]", m_indent.Get()); } else if ( td->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_STRING, string_t ) ) { CUtlVector< string_t > &vec = *(CUtlVector< string_t >*)pVar; if ( !vec.Base() ) { Print("null"); return; } Print("\n%s[", m_indent.Get()); Indent1(); FOR_EACH_VEC( vec, i ) { Print("\n%s", m_indent.Get()); PrintString( vec[i] ); } Indent0(); Print("\n%s]", m_indent.Get()); } else if ( td->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_CLASSPTR, CBaseEntity* ) ) { CUtlVector< CBaseEntity* > &vec = *(CUtlVector< CBaseEntity* >*)pVar; if ( !vec.Base() ) { Print("null"); return; } Print("\n%s[", m_indent.Get()); Indent1(); FOR_EACH_VEC( vec, i ) { Print("\n%s", m_indent.Get()); PrintEntity( vec[i] ); } Indent0(); Print("\n%s]", m_indent.Get()); } else if ( td->pSaveRestoreOps == (ISaveRestoreOps*)(&g_VguiScreenStringOps) ) { const char *pString = g_pStringTableVguiScreen->GetString( *(int*)pVar ); PrintString( (char*)pString ); } #endif // GAME_DLL else { Print("0x%x", pVar); } } void PrintField_r( char *pVar, typedescription_t *td ) { switch ( td->fieldType ) { case FIELD_INTEGER: case FIELD_MATERIALINDEX: case FIELD_MODELINDEX: case FIELD_TICK: if ( td->flags & SPROP_UNSIGNED ) { Print("%u", *(unsigned int*)pVar); } else { Print("%i", *(int*)pVar); } break; case FIELD_COLOR32: Print("0x%08x", *(int*)pVar); break; case FIELD_BOOLEAN: Print("%i", *(bool*)pVar & 1); break; case FIELD_CHARACTER: if ( *pVar < 0x20 ) { Print("%i (0x%x)", *pVar, *pVar); } else { Print("%i '%c'", *pVar, *pVar); } break; case FIELD_SHORT: if ( td->flags & SPROP_UNSIGNED ) { Print("%u", *(unsigned short*)pVar); } else { Print("%i", *(short*)pVar); } break; case FIELD_FLOAT: case FIELD_TIME: if ( *(float*)pVar == FLT_MAX ) { Print("FLT_MAX"); } else { Print("%f", *(float*)pVar); } break; case FIELD_VECTOR: case FIELD_POSITION_VECTOR: PrintVec3( (float*)pVar ); break; case FIELD_VECTOR2D: PrintVec2( (float*)pVar ); break; case FIELD_STRING: case FIELD_MODELNAME: case FIELD_SOUNDNAME: #ifdef GAME_DLL PrintString( *(string_t*)pVar ); #else PrintString( *(char**)pVar ); #endif break; case FIELD_EHANDLE: PrintEntity( (EHANDLE*)pVar ); break; #ifdef GAME_DLL case FIELD_CLASSPTR: PrintEntity( *(CBaseEntity**)pVar ); break; case FIELD_EDICT: PrintEntity( *(edict_t**)pVar ); break; #endif case FIELD_EMBEDDED: Print(" -> (%s)\n", td->td->dataClassName); DumpDataFields_r( pVar, td->td ); break; case FIELD_CUSTOM: PrintCustomField( pVar, td ); break; default: Print( "", td->fieldType ); } } void DumpDataFields_r( void *pEnt, datamap_t *map ) { Print("%s{\n", m_indent.Get()); Indent1(); if ( map->baseMap ) { Print("%sbaseclass -> (%s)\n", m_indent.Get(), map->baseMap->dataClassName); DumpDataFields_r( pEnt, map->baseMap ); Print("\n"); } typedescription_t *pFields = map->dataDesc; int numFields = map->dataNumFields; for ( int i = 0; i < numFields; i++ ) { typedescription_t* td = &pFields[i]; if ( td->flags & (FTYPEDESC_FUNCTIONTABLE | FTYPEDESC_INPUT | FTYPEDESC_OUTPUT) ) continue; if ( td->fieldType == FIELD_VOID || td->fieldType == FIELD_FUNCTION ) continue; char *pVar = (char*)pEnt + td->fieldOffset[ TD_OFFSET_NORMAL ]; if ( td->flags & FTYPEDESC_PTR ) { AssertMsg( *(char**)pVar, "NULL ptr ref" ); pVar = *(char**)pVar; } Print( "%s%s", m_indent.Get(), td->fieldName ); if ( td->fieldSize == 1 ) { if ( td->fieldType != FIELD_EMBEDDED ) Print(" <"); PrintFieldType( pVar, td ); if ( td->fieldType != FIELD_EMBEDDED ) Print("> "); PrintField_r( pVar, td ); } else { Print(" <"); PrintFieldType( pVar, td ); Print(" array> #%d", td->fieldSize); Print("\n%s[", m_indent.Get()); Indent1(); for ( int j = 0; j < td->fieldSize; j++ ) { Print("\n%s", m_indent.Get()); PrintField_r( pVar + j * td->fieldSizeInBytes / td->fieldSize, td ); } Indent0(); Print("\n%s]", m_indent.Get()); } Print("\n"); } Indent0(); Print("%s}", m_indent.Get()); } void Print( const char *fmt, ... ) { char buf[2048]; va_list va; va_start( va, fmt ); V_vsnprintf( buf, sizeof(buf) - 1, fmt, va ); va_end( va ); m_output.PutString( buf ); } public: void Dump( HSCRIPT hEnt, const char* filename ) { CBaseEntity *pEnt = ToEnt( hEnt ); if ( !pEnt ) return; if ( !filename || !*filename ) return; m_output.SetBufferType( true, false ); IndentStart(); Print( "\n" ); Print( "(%s)\n", GetNetTable( GetNetworkClass(pEnt) )->GetName() ); DumpNetTable_r( pEnt, GetNetTable( GetNetworkClass(pEnt) ) ); Print( "\n\n" ); Print( "\n" ); Print( "(%s)\n", pEnt->GetDataDescMap()->dataClassName ); DumpDataFields_r( pEnt, pEnt->GetDataDescMap() ); Print( "\n\n" ); #ifdef CLIENT_DLL Print( "\n" ); Print( "(%s)\n", pEnt->GetPredDescMap()->dataClassName ); DumpDataFields_r( pEnt, pEnt->GetPredDescMap() ); Print( "\n\n" ); #endif const char *pszFile = V_GetFileName( filename ); filesystem->WriteFile( pszFile, "MOD", m_output ); m_indent.Purge(); m_output.Purge(); } #endif // _DEBUG } g_ScriptNetPropManager; BEGIN_SCRIPTDESC_ROOT_NAMED( CScriptNetPropManager, "CNetPropManager", SCRIPT_SINGLETON "Allows reading and updating the network properties and data fields of an entity." ) DEFINE_SCRIPTFUNC( GetPropArraySize, "Returns the size of an array." ) DEFINE_SCRIPTFUNC( GetPropEntity, "Reads an entity." ) DEFINE_SCRIPTFUNC( GetPropEntityArray, "Reads an entity from an array." ) DEFINE_SCRIPTFUNC( GetPropFloat, "Reads a float." ) DEFINE_SCRIPTFUNC( GetPropFloatArray, "Reads a float from an array." ) DEFINE_SCRIPTFUNC( GetPropInt, "Reads an integer." ) DEFINE_SCRIPTFUNC( GetPropIntArray, "Reads an integer from an array." ) DEFINE_SCRIPTFUNC( GetPropString, "Reads a string." ) DEFINE_SCRIPTFUNC( GetPropStringArray, "Reads a string from an array." ) DEFINE_SCRIPTFUNC( GetPropVector, "Reads a 3D vector." ) DEFINE_SCRIPTFUNC( GetPropVectorArray, "Reads a 3D vector from an array." ) DEFINE_SCRIPTFUNC( GetPropType, "Returns the netprop type as a string." ) DEFINE_SCRIPTFUNC( HasProp, "Checks if netprop/datafield exists." ) DEFINE_SCRIPTFUNC( SetPropEntity, "Sets an entity." ) DEFINE_SCRIPTFUNC( SetPropEntityArray, "Sets an entity in an array." ) DEFINE_SCRIPTFUNC( SetPropFloat, "Sets to the specified float." ) DEFINE_SCRIPTFUNC( SetPropFloatArray, "Sets a float in an array." ) DEFINE_SCRIPTFUNC( SetPropInt, "Sets to the specified integer." ) DEFINE_SCRIPTFUNC( SetPropIntArray, "Sets an integer in an array." ) DEFINE_SCRIPTFUNC( SetPropString, "Sets to the specified string." ) DEFINE_SCRIPTFUNC( SetPropStringArray, "Sets a string in an array." ) DEFINE_SCRIPTFUNC( SetPropVector, "Sets to the specified vector." ) DEFINE_SCRIPTFUNC( SetPropVectorArray, "Sets a 3D vector in an array." ) #ifdef _DEBUG DEFINE_SCRIPTFUNC( Dump, "Dump all readable netprop and datafield values of this entity. Pass in file name to write into." ); #endif END_SCRIPTDESC(); //============================================================================= // Localization Interface // Unique to Mapbase //============================================================================= class CScriptLocalize { public: const char *GetTokenAsUTF8( const char *pszToken ) { const char *pText = g_pVGuiLocalize->FindAsUTF8( pszToken ); if ( pText ) { return pText; } return NULL; } void AddStringAsUTF8( const char *pszToken, const char *pszString ) { wchar_t wpszString[256]; g_pVGuiLocalize->ConvertANSIToUnicode( pszString, wpszString, sizeof(wpszString) ); // TODO: This is a fake file name! Should "fileName" mean anything? g_pVGuiLocalize->AddString( pszToken, wpszString, "resource/vscript_localization.txt" ); } private: } g_ScriptLocalize; BEGIN_SCRIPTDESC_ROOT_NAMED( CScriptLocalize, "CLocalize", SCRIPT_SINGLETON "Accesses functions related to localization strings." ) DEFINE_SCRIPTFUNC( GetTokenAsUTF8, "Gets the current language's token as a UTF-8 string (not Unicode)." ) DEFINE_SCRIPTFUNC( AddStringAsUTF8, "Adds a new localized token as a UTF-8 string (not Unicode)." ) END_SCRIPTDESC(); //============================================================================= // Game Event Listener // Based on Source 2 API // // NOTE: In Source 2 vscript (Lua) event listener contexts are tables that are // passed to the callback function as the call environment. // In mapbase implementation these are string identifiers because unlike Lua, // Squirrel has closure methods such as 'bindenv' which can bind functions to specified environments. //============================================================================= // Define to use the older code that loads all events manually independent from the game event manager. // Otherwise access event descriptors directly from engine. //#define USE_OLD_EVENT_DESCRIPTORS 1 class CScriptGameEventListener : public IGameEventListener2, public CAutoGameSystem { public: CScriptGameEventListener() : m_bActive(false) { #ifdef _DEBUG m_nEventTick = 0; #endif } ~CScriptGameEventListener() { StopListeningForEvent(); } int ListenToGameEvent( const char* szEvent, HSCRIPT hFunc, const char* szContext ); void StopListeningForEvent(); public: static bool StopListeningToGameEvent( int listener ); static void StopListeningToAllGameEvents( const char* szContext ); public: void FireGameEvent( IGameEvent *event ); void LevelShutdownPreEntity(); private: //int m_index; HSCRIPT m_hCallback; unsigned int m_iContextHash; bool m_bActive; #ifdef _DEBUG int m_nEventTick; #endif static StringHashFunctor Hash; static inline unsigned int HashContext( const char* c ) { return c ? Hash(c) : 0; } inline int GetIndex() { Assert( sizeof(CScriptGameEventListener*) == sizeof(int) ); return reinterpret_cast(this); } public: enum // event data types, dependant on engine definitions { TYPE_LOCAL = 0, TYPE_STRING = 1, TYPE_FLOAT = 2, TYPE_LONG = 3, TYPE_SHORT = 4, TYPE_BYTE = 5, TYPE_BOOL = 6 }; static void WriteEventData( IGameEvent *event, HSCRIPT hTable ); #ifdef USE_OLD_EVENT_DESCRIPTORS static void LoadAllEvents(); static void LoadEventsFromFile( const char *filename, const char *pathID = NULL ); static CUtlMap< unsigned int, KeyValues* > s_GameEvents; static CUtlVector< KeyValues* > s_LoadedFiles; #endif public: //static int g_nIndexCounter; static CUtlVectorAutoPurge< CScriptGameEventListener* > s_Listeners; #if _DEBUG static void DumpEventListeners(); #endif }; CUtlVectorAutoPurge< CScriptGameEventListener* > CScriptGameEventListener::s_Listeners; StringHashFunctor CScriptGameEventListener::Hash; #ifdef USE_OLD_EVENT_DESCRIPTORS CUtlMap< unsigned int, KeyValues* > CScriptGameEventListener::s_GameEvents( DefLessFunc(unsigned int) ); CUtlVector< KeyValues* > CScriptGameEventListener::s_LoadedFiles; #endif #if _DEBUG #ifdef CLIENT_DLL CON_COMMAND_F( cl_dump_script_game_event_listeners, "Dump all game event listeners created from script.", FCVAR_CHEAT ) { CScriptGameEventListener::DumpEventListeners(); } #else CON_COMMAND_F( dump_script_game_event_listeners, "Dump all game event listeners created from script.", FCVAR_CHEAT ) { CScriptGameEventListener::DumpEventListeners(); } #endif #endif #ifdef USE_OLD_EVENT_DESCRIPTORS //----------------------------------------------------------------------------- // Executed in LevelInitPreEntity //----------------------------------------------------------------------------- void CScriptGameEventListener::LoadAllEvents() { // Listed in the same order they are loaded in GameEventManager const char *filenames[] = { "resource/serverevents.res", "resource/gameevents.res", "resource/mapbaseevents.res", "resource/modevents.res" }; const char *pathlist[] = { "GAME", "MOD" }; // Destroy old KeyValues if ( s_LoadedFiles.Count() ) { for ( int i = s_LoadedFiles.Count(); i--; ) s_LoadedFiles[i]->deleteThis(); s_LoadedFiles.Purge(); s_GameEvents.Purge(); } for ( int j = 0; j < ARRAYSIZE(pathlist); ++j ) for ( int i = 0; i < ARRAYSIZE(filenames); ++i ) { LoadEventsFromFile( filenames[i], pathlist[j] ); } } //----------------------------------------------------------------------------- // Load event files into a lookup array to be able to return the event data to the VM. //----------------------------------------------------------------------------- void CScriptGameEventListener::LoadEventsFromFile( const char *filename, const char *pathID ) { KeyValues *pKV = new KeyValues("GameEvents"); if ( !pKV->LoadFromFile( filesystem, filename, pathID ) ) { // CGMsg( 1, CON_GROUP_VSCRIPT, "CScriptGameEventListener::LoadEventsFromFile: Failed to load file [%s]%s\n", pathID, filename ); pKV->deleteThis(); return; } int count = 0; for ( KeyValues *key = pKV->GetFirstSubKey(); key; key = key->GetNextKey() ) { for ( KeyValues *sub = key->GetFirstSubKey(); sub; sub = sub->GetNextKey() ) { if ( sub->GetDataType() == KeyValues::TYPE_STRING ) { const char *szVal = sub->GetString(); if ( !V_stricmp( szVal, "string" ) ) { sub->SetInt( NULL, TYPE_STRING ); } else if ( !V_stricmp( szVal, "bool" ) ) { sub->SetInt( NULL, TYPE_BOOL ); } else if ( !V_stricmp( szVal, "byte" ) ) { sub->SetInt( NULL, TYPE_BYTE ); } else if ( !V_stricmp( szVal, "short" ) ) { sub->SetInt( NULL, TYPE_SHORT ); } else if ( !V_stricmp( szVal, "long" ) ) { sub->SetInt( NULL, TYPE_LONG ); } else if ( !V_stricmp( szVal, "float" ) ) { sub->SetInt( NULL, TYPE_FLOAT ); } } // none : value is not networked // string : a zero terminated string // bool : unsigned int, 1 bit // byte : unsigned int, 8 bit // short : signed int, 16 bit // long : signed int, 32 bit // float : float, 32 bit } // Store event subkeys // Replace key so modevents can overwrite gameevents. // It does not check for hash collisions, however. s_GameEvents.InsertOrReplace( Hash( key->GetName() ), key ); ++count; } // Store files (allocated KV) s_LoadedFiles.AddToTail( pKV ); CGMsg( 2, CON_GROUP_VSCRIPT, "CScriptGameEventListener::LoadEventsFromFile: Loaded [%s]%s (%i)\n", pathID, filename, count ); } #endif #if _DEBUG void CScriptGameEventListener::DumpEventListeners() { CGMsg( 0, CON_GROUP_VSCRIPT, "--- Script game event listener dump start\n" ); CGMsg( 0, CON_GROUP_VSCRIPT, "# ID CONTEXT\n" ); FOR_EACH_VEC( s_Listeners, i ) { CGMsg( 0, CON_GROUP_VSCRIPT, " %d : %d : %u\n", i, s_Listeners[i]->GetIndex(), s_Listeners[i]->m_iContextHash ); } CGMsg( 0, CON_GROUP_VSCRIPT, "--- Script game event listener dump end\n" ); } #endif void CScriptGameEventListener::LevelShutdownPreEntity() { s_Listeners.FindAndFastRemove(this); delete this; } void CScriptGameEventListener::FireGameEvent( IGameEvent *event ) { #ifdef _DEBUG m_nEventTick = gpGlobals->tickcount; #endif ScriptVariant_t hTable; g_pScriptVM->CreateTable( hTable ); WriteEventData( event, hTable ); g_pScriptVM->SetValue( hTable, "game_event_listener", GetIndex() ); // g_pScriptVM->SetValue( hTable, "game_event_name", event->GetName() ); g_pScriptVM->ExecuteFunction( m_hCallback, &hTable, 1, NULL, NULL, true ); g_pScriptVM->ReleaseScript( hTable ); } struct CGameEventDescriptor { byte _0[36]; KeyValues *m_pEventKeys; //byte _1[22]; }; class CGameEvent__// : public IGameEvent { public: virtual ~CGameEvent__(); // [0] CGameEventDescriptor *m_pDescriptor; // 0x04 //KeyValues *m_pEventData; // 0x08 }; //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- void CScriptGameEventListener::WriteEventData( IGameEvent *event, HSCRIPT hTable ) { #ifdef USE_OLD_EVENT_DESCRIPTORS int i = s_GameEvents.Find( Hash( event->GetName() ) ); if ( i == s_GameEvents.InvalidIndex() ) return; KeyValues *pKV = s_GameEvents[i]; #endif #if defined(_DEBUG) && !defined(USE_OLD_EVENT_DESCRIPTORS) try { #endif #if !defined(USE_OLD_EVENT_DESCRIPTORS) KeyValues *pKV = reinterpret_cast< CGameEvent__* >(event)->m_pDescriptor->m_pEventKeys; #endif for ( KeyValues *sub = pKV->GetFirstSubKey(); sub; sub = sub->GetNextKey() ) { const char *szKey = sub->GetName(); switch ( sub->GetInt() ) { case TYPE_LOCAL: case TYPE_STRING: g_pScriptVM->SetValue( hTable, szKey, event->GetString( szKey ) ); break; case TYPE_FLOAT: g_pScriptVM->SetValue( hTable, szKey, event->GetFloat ( szKey ) ); break; case TYPE_BOOL: g_pScriptVM->SetValue( hTable, szKey, event->GetBool ( szKey ) ); break; default: g_pScriptVM->SetValue( hTable, szKey, event->GetInt ( szKey ) ); } } #if defined(_DEBUG) && !defined(USE_OLD_EVENT_DESCRIPTORS) // Access a bunch of KeyValues functions to validate it is the correct address. // This may not always throw an exception when it is incorrect, but eventually it will. } catch (...) { // CGameEvent or CGameEventDescriptor offsets did not match! // This should mean these were modified in engine.dll. // // Implement this utility yourself by adding a function to get event descriptor keys // either on CGameEventManager or on CGameEvent interfaces. // On CGameEventManager downcast IGameEvent input to CGameEvent, then return event->descriptor->keys // On CGameEvent return (member) descriptor->keys // // Finally assign it to pKV above. Warning("CScriptGameEventListener::WriteEventData internal error\n"); Assert(0); } #endif } //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- int CScriptGameEventListener::ListenToGameEvent( const char* szEvent, HSCRIPT hFunc, const char* szContext ) { bool bValid; if ( gameeventmanager && hFunc ) #ifdef CLIENT_DLL bValid = gameeventmanager->AddListener( this, szEvent, false ); #else bValid = gameeventmanager->AddListener( this, szEvent, true ); #endif else bValid = false; if ( bValid ) { m_iContextHash = HashContext( szContext ); m_hCallback = hFunc; m_bActive = true; s_Listeners.AddToTail( this ); return GetIndex(); } else { delete this; return 0; } } //----------------------------------------------------------------------------- // Free stuff. Called from the destructor, does not remove itself from the listener list. //----------------------------------------------------------------------------- void CScriptGameEventListener::StopListeningForEvent() { if ( !m_bActive ) return; if ( g_pScriptVM ) g_pScriptVM->ReleaseScript( m_hCallback ); m_hCallback = NULL; m_bActive = false; if ( gameeventmanager ) gameeventmanager->RemoveListener( this ); #ifdef _DEBUG // Event listeners are iterated forwards in the game event manager, // removing while iterating will cause it to skip one listener. // // Fix this in engine without altering any behaviour by // changing event exeuction order to tail->head, // changing listener removal to tail->head, // changing listener addition to head if ( m_nEventTick == gpGlobals->tickcount ) { Warning("CScriptGameEventListener stopped in the same frame it was fired. This will break other event listeners!\n"); } #endif } //----------------------------------------------------------------------------- // Stop the specified event listener. //----------------------------------------------------------------------------- bool CScriptGameEventListener::StopListeningToGameEvent( int listener ) { CScriptGameEventListener *p = reinterpret_cast(listener); // INT_TO_POINTER bool bRemoved = s_Listeners.FindAndFastRemove(p); if ( bRemoved ) { delete p; } return bRemoved; } //----------------------------------------------------------------------------- // Stops listening to all events within a context. //----------------------------------------------------------------------------- void CScriptGameEventListener::StopListeningToAllGameEvents( const char* szContext ) { unsigned int hash = HashContext( szContext ); for ( int i = s_Listeners.Count(); i--; ) { CScriptGameEventListener *pCur = s_Listeners[i]; if ( pCur->m_iContextHash == hash ) { s_Listeners.FastRemove(i); delete pCur; } } } //============================================================================= //============================================================================= static int ListenToGameEvent( const char* szEvent, HSCRIPT hFunc, const char* szContext ) { CScriptGameEventListener *p = new CScriptGameEventListener(); return p->ListenToGameEvent( szEvent, hFunc, szContext ); } //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- static void FireGameEvent( const char* szEvent, HSCRIPT hTable ) { IGameEvent *event = gameeventmanager->CreateEvent( szEvent ); if ( event ) { ScriptVariant_t key, val; int nIterator = -1; while ( ( nIterator = g_pScriptVM->GetKeyValue( hTable, nIterator, &key, &val ) ) != -1 ) { switch ( val.m_type ) { case FIELD_FLOAT: event->SetFloat ( key.m_pszString, val.m_float ); break; case FIELD_INTEGER: event->SetInt ( key.m_pszString, val.m_int ); break; case FIELD_BOOLEAN: event->SetBool ( key.m_pszString, val.m_bool ); break; case FIELD_CSTRING: event->SetString( key.m_pszString, val.m_pszString ); break; } g_pScriptVM->ReleaseValue(key); g_pScriptVM->ReleaseValue(val); } #ifdef CLIENT_DLL gameeventmanager->FireEventClientSide(event); #else gameeventmanager->FireEvent(event); #endif } } #ifndef CLIENT_DLL //----------------------------------------------------------------------------- // Copy of FireGameEvent, server only with no broadcast to clients. //----------------------------------------------------------------------------- static void FireGameEventLocal( const char* szEvent, HSCRIPT hTable ) { IGameEvent *event = gameeventmanager->CreateEvent( szEvent ); if ( event ) { ScriptVariant_t key, val; int nIterator = -1; while ( ( nIterator = g_pScriptVM->GetKeyValue( hTable, nIterator, &key, &val ) ) != -1 ) { switch ( val.m_type ) { case FIELD_FLOAT: event->SetFloat ( key.m_pszString, val.m_float ); break; case FIELD_INTEGER: event->SetInt ( key.m_pszString, val.m_int ); break; case FIELD_BOOLEAN: event->SetBool ( key.m_pszString, val.m_bool ); break; case FIELD_CSTRING: event->SetString( key.m_pszString, val.m_pszString ); break; } g_pScriptVM->ReleaseValue(key); g_pScriptVM->ReleaseValue(val); } gameeventmanager->FireEvent(event,true); } } #endif // !CLIENT_DLL static ScriptHook_t g_Hook_OnSave; static ScriptHook_t g_Hook_OnRestore; //============================================================================= // Save/Restore Utility // Based on L4D2 API //============================================================================= class CScriptSaveRestoreUtil : public CAutoGameSystem { public: static void SaveTable( const char *szId, HSCRIPT hTable ); static void RestoreTable( const char *szId, HSCRIPT hTable ); static void ClearSavedTable( const char *szId ); public: // IGameSystem void OnSave() { if ( g_pScriptVM ) { if ( GetScriptHookManager().IsEventHooked( "OnSave" ) ) g_Hook_OnSave.Call( NULL, NULL, NULL ); // Legacy hook HSCRIPT hFunc = g_pScriptVM->LookupFunction( "OnSave" ); if ( hFunc ) { g_pScriptVM->Call( hFunc ); g_pScriptVM->ReleaseScript( hFunc ); } } } #ifdef CLIENT_DLL // On the client, OnRestore() is called before VScript is actually restored, so this has to be called manually from VScript save/restore instead void OnVMRestore() #else void OnRestore() #endif { if ( g_pScriptVM ) { if ( GetScriptHookManager().IsEventHooked( "OnRestore" ) ) g_Hook_OnRestore.Call( NULL, NULL, NULL ); // Legacy hook HSCRIPT hFunc = g_pScriptVM->LookupFunction( "OnRestore" ); if ( hFunc ) { g_pScriptVM->Call( hFunc ); g_pScriptVM->ReleaseScript( hFunc ); } } } void Shutdown() { FOR_EACH_MAP_FAST( m_Lookup, i ) m_Lookup[i]->deleteThis(); m_Lookup.Purge(); } private: static StringHashFunctor Hash; static CUtlMap< unsigned int, KeyValues* > m_Lookup; } g_ScriptSaveRestoreUtil; #ifdef CLIENT_DLL void VScriptSaveRestoreUtil_OnVMRestore() { g_ScriptSaveRestoreUtil.OnVMRestore(); } #endif CUtlMap< unsigned int, KeyValues* > CScriptSaveRestoreUtil::m_Lookup( DefLessFunc(unsigned int) ); StringHashFunctor CScriptSaveRestoreUtil::Hash; //----------------------------------------------------------------------------- // Store a table with primitive values that will persist across level transitions and save loads. // Case sensitive //----------------------------------------------------------------------------- void CScriptSaveRestoreUtil::SaveTable( const char *szId, HSCRIPT hTable ) { KeyValues *pKV; unsigned int hash = Hash(szId); int idx = m_Lookup.Find( hash ); if ( idx == m_Lookup.InvalidIndex() ) { pKV = new KeyValues("ScriptSavedTable"); m_Lookup.Insert( hash, pKV ); } else { pKV = m_Lookup[idx]; pKV->Clear(); } ScriptVariant_t key, val; int nIterator = -1; while ( ( nIterator = g_pScriptVM->GetKeyValue( hTable, nIterator, &key, &val ) ) != -1 ) { switch ( val.m_type ) { case FIELD_FLOAT: pKV->SetFloat ( key.m_pszString, val.m_float ); break; case FIELD_INTEGER: pKV->SetInt ( key.m_pszString, val.m_int ); break; case FIELD_BOOLEAN: pKV->SetBool ( key.m_pszString, val.m_bool ); break; case FIELD_CSTRING: pKV->SetString( key.m_pszString, val.m_pszString ); break; } g_pScriptVM->ReleaseValue(key); g_pScriptVM->ReleaseValue(val); } } //----------------------------------------------------------------------------- // Retrieves a table from storage. Write into input table. //----------------------------------------------------------------------------- void CScriptSaveRestoreUtil::RestoreTable( const char *szId, HSCRIPT hTable ) { int idx = m_Lookup.Find( Hash(szId) ); if ( idx == m_Lookup.InvalidIndex() ) { // DevWarning( 2, "RestoreTable could not find saved table with context '%s'\n", szId ); return; } KeyValues *pKV = m_Lookup[idx]; FOR_EACH_SUBKEY( pKV, key ) { switch ( key->GetDataType() ) { case KeyValues::TYPE_STRING: g_pScriptVM->SetValue( hTable, key->GetName(), key->GetString() ); break; case KeyValues::TYPE_INT: g_pScriptVM->SetValue( hTable, key->GetName(), key->GetInt() ); break; case KeyValues::TYPE_FLOAT: g_pScriptVM->SetValue( hTable, key->GetName(), key->GetFloat() ); break; } } } //----------------------------------------------------------------------------- // Remove a saved table. //----------------------------------------------------------------------------- void CScriptSaveRestoreUtil::ClearSavedTable( const char *szId ) { int idx = m_Lookup.Find( Hash(szId) ); if ( idx != m_Lookup.InvalidIndex() ) { m_Lookup[idx]->deleteThis(); m_Lookup.RemoveAt( idx ); } else { // DevWarning( 2, "ClearSavedTable could not find saved table with context '%s'\n", szId ); } } //============================================================================= // Read/Write to File // Based on L4D2/Source 2 API //============================================================================= #define SCRIPT_MAX_FILE_READ_SIZE (64 * 1024 * 1024) // 64MB #define SCRIPT_MAX_FILE_WRITE_SIZE (64 * 1024 * 1024) // 64MB #define SCRIPT_RW_PATH_ID "MOD" #define SCRIPT_RW_FULL_PATH_FMT "vscript_io/%s" class CScriptReadWriteFile : public CAutoGameSystem { // A singleton class with all static members is used to be able to free the read string on level shutdown, // and register script funcs directly. Same reason applies to CScriptSaveRestoreUtil public: static bool FileWrite( const char *szFile, const char *szInput ); static const char *FileRead( const char *szFile ); static bool FileExists( const char *szFile ); // NOTE: These two functions are new with Mapbase and have no Valve equivalent static bool KeyValuesWrite( const char *szFile, HSCRIPT hInput ); static HSCRIPT KeyValuesRead( const char *szFile ); void LevelShutdownPostEntity() { if ( m_pszReturnReadFile ) { delete[] m_pszReturnReadFile; m_pszReturnReadFile = NULL; } } private: static char *m_pszReturnReadFile; } g_ScriptReadWrite; char *CScriptReadWriteFile::m_pszReturnReadFile = NULL; //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- bool CScriptReadWriteFile::FileWrite( const char *szFile, const char *szInput ) { size_t len = strlen(szInput); if ( len > SCRIPT_MAX_FILE_WRITE_SIZE ) { DevWarning( 2, "Input is too large for a ScriptFileWrite ( %s / %d MB )\n", V_pretifymem(len,2,true), (SCRIPT_MAX_FILE_WRITE_SIZE >> 20) ); return false; } char pszFullName[MAX_PATH]; V_snprintf( pszFullName, sizeof(pszFullName), SCRIPT_RW_FULL_PATH_FMT, szFile ); if ( !V_RemoveDotSlashes( pszFullName, CORRECT_PATH_SEPARATOR, true ) ) { DevWarning( 2, "Invalid file location : %s\n", szFile ); return false; } CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER ); buf.PutString(szInput); int nSize = V_strlen(pszFullName) + 1; char *pszDir = (char*)stackalloc(nSize); V_memcpy( pszDir, pszFullName, nSize ); V_StripFilename( pszDir ); g_pFullFileSystem->CreateDirHierarchy( pszDir, SCRIPT_RW_PATH_ID ); bool res = g_pFullFileSystem->WriteFile( pszFullName, SCRIPT_RW_PATH_ID, buf ); buf.Purge(); return res; } //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- const char *CScriptReadWriteFile::FileRead( const char *szFile ) { char pszFullName[MAX_PATH]; V_snprintf( pszFullName, sizeof(pszFullName), SCRIPT_RW_FULL_PATH_FMT, szFile ); if ( !V_RemoveDotSlashes( pszFullName, CORRECT_PATH_SEPARATOR, true ) ) { DevWarning( 2, "Invalid file location : %s\n", szFile ); return NULL; } unsigned int size = g_pFullFileSystem->Size( pszFullName, SCRIPT_RW_PATH_ID ); if ( size >= SCRIPT_MAX_FILE_READ_SIZE ) { DevWarning( 2, "File '%s' (from '%s') is too large for a ScriptFileRead ( %s / %u bytes )\n", pszFullName, szFile, V_pretifymem(size,2,true), SCRIPT_MAX_FILE_READ_SIZE ); return NULL; } FileHandle_t file = g_pFullFileSystem->Open( pszFullName, "rb", SCRIPT_RW_PATH_ID ); if ( !file ) { return NULL; } // Close the previous buffer if (m_pszReturnReadFile) g_pFullFileSystem->FreeOptimalReadBuffer( m_pszReturnReadFile ); unsigned bufSize = g_pFullFileSystem->GetOptimalReadSize( file, size + 2 ); m_pszReturnReadFile = (char*)g_pFullFileSystem->AllocOptimalReadBuffer( file, bufSize ); bool bRetOK = ( g_pFullFileSystem->ReadEx( m_pszReturnReadFile, bufSize, size, file ) != 0 ); g_pFullFileSystem->Close( file ); // close file after reading if ( bRetOK ) { m_pszReturnReadFile[size] = 0; // null terminate file as EOF //buffer[size+1] = 0; // double NULL terminating in case this is a unicode file return m_pszReturnReadFile; } else { return NULL; } } //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- bool CScriptReadWriteFile::FileExists( const char *szFile ) { char pszFullName[MAX_PATH]; V_snprintf( pszFullName, sizeof(pszFullName), SCRIPT_RW_FULL_PATH_FMT, szFile ); if ( !V_RemoveDotSlashes( pszFullName, CORRECT_PATH_SEPARATOR, true ) ) { DevWarning( 2, "Invalid file location : %s\n", szFile ); return NULL; } return g_pFullFileSystem->FileExists( pszFullName, SCRIPT_RW_PATH_ID ); } //----------------------------------------------------------------------------- // Get the checksum of any file. Can be used to check the existence or validity of a file. // Returns unsigned int as hex string. //----------------------------------------------------------------------------- /* const char *CScriptReadWriteFile::CRC32_Checksum( const char *szFilename ) { CUtlBuffer buf( 0, 0, CUtlBuffer::READ_ONLY ); if ( !g_pFullFileSystem->ReadFile( szFilename, NULL, buf ) ) return NULL; // first time calling, allocate if ( !m_pszReturnCRC32 ) m_pszReturnCRC32 = new char[9]; // 'FFFFFFFF\0' V_snprintf( const_cast(m_pszReturnCRC32), 9, "%X", CRC32_ProcessSingleBuffer( buf.Base(), buf.Size()-1 ) ); buf.Purge(); return m_pszReturnCRC32; } */ //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- bool CScriptReadWriteFile::KeyValuesWrite( const char *szFile, HSCRIPT hInput ) { KeyValues *pKV = scriptmanager->GetKeyValuesFromScriptKV( g_pScriptVM, hInput ); if (!pKV) { return false; } CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER ); pKV->RecursiveSaveToFile( buf, 0 ); if ( buf.Size() > SCRIPT_MAX_FILE_WRITE_SIZE ) { DevWarning( 2, "Input is too large for a ScriptKeyValuesWrite ( %s / %d MB )\n", V_pretifymem(buf.Size(),2,true), (SCRIPT_MAX_FILE_WRITE_SIZE >> 20) ); buf.Purge(); return false; } char pszFullName[MAX_PATH]; V_snprintf( pszFullName, sizeof(pszFullName), SCRIPT_RW_FULL_PATH_FMT, szFile ); if ( !V_RemoveDotSlashes( pszFullName, CORRECT_PATH_SEPARATOR, true ) ) { DevWarning( 2, "Invalid file location : %s\n", szFile ); buf.Purge(); return false; } int nSize = V_strlen(pszFullName) + 1; char *pszDir = (char*)stackalloc(nSize); V_memcpy( pszDir, pszFullName, nSize ); V_StripFilename( pszDir ); g_pFullFileSystem->CreateDirHierarchy( pszDir, SCRIPT_RW_PATH_ID ); bool res = g_pFullFileSystem->WriteFile( pszFullName, SCRIPT_RW_PATH_ID, buf ); buf.Purge(); return res; } //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- HSCRIPT CScriptReadWriteFile::KeyValuesRead( const char *szFile ) { char pszFullName[MAX_PATH]; V_snprintf( pszFullName, sizeof(pszFullName), SCRIPT_RW_FULL_PATH_FMT, szFile ); if ( !V_RemoveDotSlashes( pszFullName, CORRECT_PATH_SEPARATOR, true ) ) { DevWarning( 2, "Invalid file location : %s\n", szFile ); return NULL; } unsigned int size = g_pFullFileSystem->Size( pszFullName, SCRIPT_RW_PATH_ID ); if ( size >= SCRIPT_MAX_FILE_READ_SIZE ) { DevWarning( 2, "File '%s' (from '%s') is too large for a ScriptKeyValuesRead ( %s / %u bytes )\n", pszFullName, szFile, V_pretifymem(size,2,true), SCRIPT_MAX_FILE_READ_SIZE ); return NULL; } KeyValues *pKV = new KeyValues( szFile ); if ( !pKV->LoadFromFile( g_pFullFileSystem, pszFullName, SCRIPT_RW_PATH_ID ) ) { pKV->deleteThis(); return NULL; } HSCRIPT hScript = scriptmanager->CreateScriptKeyValues( g_pScriptVM, pKV, true ); // bAllowDestruct is supposed to automatically remove the involved KV return hScript; } //============================================================================= // Network message helper // (Unique to mapbase) // // Uses usermessages for server to client, UserCmd for client to server communication. // The custom message name is hashed and sent as word with the message. //============================================================================= static CNetMsgScriptHelper scriptnetmsg; CNetMsgScriptHelper *g_ScriptNetMsg = &scriptnetmsg; #ifdef GAME_DLL #define m_MsgIn_() m_MsgIn-> #define DLL_LOC_STR "[Server]" #else #define m_MsgIn_() m_MsgIn. #define DLL_LOC_STR "[Client]" #endif #ifdef GAME_DLL #define SCRIPT_NETMSG_WRITE_FUNC #else #define SCRIPT_NETMSG_WRITE_FUNC if ( m_bWriteIgnore ) { return; } #endif #ifdef _DEBUG #ifdef GAME_DLL ConVar script_net_debug("script_net_debug", "0"); #define DebugNetMsg( l, ... ) do { if (script_net_debug.GetInt() >= l) ConColorMsg( Color(100, 225, 255, 255), __VA_ARGS__ ); } while (0); #else ConVar script_net_debug("script_net_debug_client", "0"); #define DebugNetMsg( l, ... ) do { if (script_net_debug.GetInt() >= l) ConColorMsg( Color(100, 225, 175, 255), __VA_ARGS__ ); } while (0); #endif #define DebugWarning(...) Warning( __VA_ARGS__ ) #else #define DebugNetMsg(...) (void)(0) #define DebugWarning(...) (void)(0) #endif // Keep track of message names to print on failure #ifdef _DEBUG struct NetMsgHook_t { void Set( const char *s ) { hash = CNetMsgScriptHelper::Hash( s ); name = strdup(s); } ~NetMsgHook_t() { free( name ); } int hash; char *name; }; CUtlVector< NetMsgHook_t > g_NetMsgHooks; static const char *GetNetMsgName( int hash ) { FOR_EACH_VEC( g_NetMsgHooks, i ) { if ( g_NetMsgHooks[i].hash == hash ) return g_NetMsgHooks[i].name; } return 0; } static const char *HasNetMsgCollision( int hash, const char *ignore ) { FOR_EACH_VEC( g_NetMsgHooks, i ) { if ( g_NetMsgHooks[i].hash == hash && V_strcmp( g_NetMsgHooks[i].name, ignore ) != 0 ) { return g_NetMsgHooks[i].name; } } return 0; } #endif // _DEBUG inline int CNetMsgScriptHelper::Hash( const char *key ) { int hash = HashStringCaseless( key ); Assert( hash < (1 << SCRIPT_NETMSG_HEADER_BITS) ); return hash; } void CNetMsgScriptHelper::WriteToBuffer( bf_write *bf ) { #ifdef CLIENT_DLL Assert( m_nQueueCount < ( 1 << SCRIPT_NETMSG_QUEUE_BITS ) ); bf->WriteUBitLong( m_nQueueCount, SCRIPT_NETMSG_QUEUE_BITS ); DebugNetMsg( 2, DLL_LOC_STR " CNetMsgScriptHelper::WriteToBuffer() count(%d) size(%d)\n", m_nQueueCount, m_MsgOut.GetNumBitsWritten() + SCRIPT_NETMSG_QUEUE_BITS ); #endif bf->WriteBits( m_MsgOut.GetData(), m_MsgOut.GetNumBitsWritten() ); } //----------------------------------------------------------------------------- // Reset the current network message buffer //----------------------------------------------------------------------------- void CNetMsgScriptHelper::Reset() { m_MsgOut.StartWriting( m_MsgData, sizeof(m_MsgData), 0 ); #ifdef GAME_DLL m_filter.Reset(); #else m_iLastBit = 0; #endif } //----------------------------------------------------------------------------- // Create the storage for the receiver callback functions. // Functions are handled in the VM, the storage table is here. //----------------------------------------------------------------------------- void CNetMsgScriptHelper::InitPostVM() { ScriptVariant_t hHooks; g_pScriptVM->CreateTable( hHooks ); m_Hooks = (HSCRIPT)hHooks; } void CNetMsgScriptHelper::LevelShutdownPreVM() { Reset(); if ( m_Hooks ) g_pScriptVM->ReleaseScript( m_Hooks ); m_Hooks = NULL; #ifdef CLIENT_DLL m_bWriteReady = m_bWriteIgnore = false; m_MsgIn.Reset(); #else m_MsgIn = NULL; #endif #ifdef _DEBUG g_NetMsgHooks.Purge(); #endif } #ifdef CLIENT_DLL bool CNetMsgScriptHelper::Init() // IGameSystem { usermessages->HookMessage( "ScriptMsg", __MsgFunc_ScriptMsg ); return true; } //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- void CNetMsgScriptHelper::__MsgFunc_ScriptMsg( bf_read &msg ) { g_ScriptNetMsg->ReceiveMessage( msg ); } #endif // CLIENT_DLL //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- #ifdef GAME_DLL void CNetMsgScriptHelper::ReceiveMessage( bf_read *msg, CBaseEntity *pPlayer ) { m_MsgIn = msg; #else void CNetMsgScriptHelper::ReceiveMessage( bf_read &msg ) { m_MsgIn.StartReading( msg.m_pData, msg.m_nDataBytes ); #endif DebugNetMsg( 2, DLL_LOC_STR " %s()\n", __FUNCTION__ ); // Don't do anything if there's no VM here. This can happen if a message from the server goes to a VM-less client, or vice versa. if ( !g_pScriptVM ) { CGWarning( 0, CON_GROUP_VSCRIPT, DLL_LOC_STR " CNetMsgScriptHelper: No VM on receiving side\n" ); return; } #ifdef GAME_DLL int count = m_MsgIn_()ReadUBitLong( SCRIPT_NETMSG_QUEUE_BITS ); DebugNetMsg( 2, " msg count %d\n", count ); while ( count-- ) #endif { int hash = m_MsgIn_()ReadWord(); #ifdef _DEBUG const char *msgName = GetNetMsgName( hash ); DebugNetMsg( 2, " -- begin msg [%d]%s\n", hash, msgName ); #endif ScriptVariant_t hfn; if ( g_pScriptVM->GetValue( m_Hooks, hash, &hfn ) ) { #ifdef GAME_DLL if ( g_pScriptVM->Call( hfn, NULL, true, NULL, pPlayer->m_hScriptInstance ) == SCRIPT_ERROR ) #else if ( g_pScriptVM->ExecuteFunction( hfn, NULL, 0, NULL, NULL, true ) == SCRIPT_ERROR ) #endif { #ifdef _DEBUG DevWarning( 1, DLL_LOC_STR " NetMsg: invalid callback '%s'\n", GetNetMsgName( hash ) ); #else DevWarning( 1, DLL_LOC_STR " NetMsg: invalid callback [%d]\n", hash ); #endif } g_pScriptVM->ReleaseValue( hfn ); } else { DevWarning( 1, DLL_LOC_STR " NetMsg hook not found [%d]\n", hash ); } DebugNetMsg( 2, " -- end msg\n" ); } } //----------------------------------------------------------------------------- // Start writing new custom network message //----------------------------------------------------------------------------- void CNetMsgScriptHelper::Start( const char *msg ) { if ( !msg || !msg[0] ) { g_pScriptVM->RaiseException( DLL_LOC_STR "NetMsg: invalid message name" ); return; } DebugNetMsg( 1, DLL_LOC_STR " %s() [%d]%s\n", __FUNCTION__, Hash( msg ), msg ); #ifdef CLIENT_DLL // Client can write multiple messages in a frame before the usercmd is sent, // this queue system ensures client messages are written to the cmd all at once. // NOTE: All messages share the same buffer. if ( !m_bWriteReady ) { Reset(); m_nQueueCount = 0; m_bWriteIgnore = false; } else if ( m_nQueueCount == ((1< client // // Sends an exclusive usermessage. //----------------------------------------------------------------------------- void CNetMsgScriptHelper::Send( HSCRIPT player, bool bReliable ) { DebugNetMsg( 1, DLL_LOC_STR " %s() size(%d)\n", __FUNCTION__, GetNumBitsWritten() ); CBaseEntity *pPlayer = ToEnt(player); if ( pPlayer ) { m_filter.AddRecipient( (CBasePlayer*)pPlayer ); } if ( bReliable ) { m_filter.MakeReliable(); } Assert( usermessages->LookupUserMessage( "ScriptMsg" ) != -1 ); DoSendUserMsg( &m_filter, usermessages->LookupUserMessage( "ScriptMsg" ) ); } #else // CLIENT_DLL //----------------------------------------------------------------------------- // client -> server // // Mark UserCmd delta ready. //----------------------------------------------------------------------------- void CNetMsgScriptHelper::Send() { DebugNetMsg( 1, DLL_LOC_STR " %s() size(%d)\n", __FUNCTION__, m_bWriteIgnore ? 0 : GetNumBitsWritten() ); m_bWriteReady = true; } #endif //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- void CNetMsgScriptHelper::Receive( const char *msg, HSCRIPT func ) { if ( !msg || !msg[0] ) { g_pScriptVM->RaiseException( DLL_LOC_STR "NetMsg: invalid message name" ); return; } #ifdef _DEBUG int hash = Hash( msg ); const char *psz = HasNetMsgCollision( hash, msg ); AssertMsg3( !psz, DLL_LOC_STR " NetMsg hash collision! [%d] '%s', '%s'\n", hash, msg, psz ); NetMsgHook_t &hook = g_NetMsgHooks[ g_NetMsgHooks.AddToTail() ]; hook.Set( msg ); #endif if ( func ) { g_pScriptVM->SetValue( m_Hooks, Hash( msg ), func ); } else { g_pScriptVM->ClearValue( m_Hooks, Hash( msg ) ); } } #ifdef GAME_DLL void CNetMsgScriptHelper::DoSendUserMsg( CRecipientFilter *filter, int type ) { WriteToBuffer( engine->UserMessageBegin( filter, type ) ); engine->MessageEnd(); } void CNetMsgScriptHelper::DoSendEntityMsg( CBaseEntity *entity, bool reliable ) { WriteToBuffer( engine->EntityMessageBegin( entity->entindex(), entity->GetServerClass(), reliable ) ); engine->MessageEnd(); } //----------------------------------------------------------------------------- // Send a usermessage from the server to the client //----------------------------------------------------------------------------- void CNetMsgScriptHelper::SendUserMessage( HSCRIPT hPlayer, const char *msg, bool bReliable ) { int msg_type = usermessages->LookupUserMessage(msg); if ( msg_type == -1 ) { g_pScriptVM->RaiseException( UTIL_VarArgs("SendUserMessage: Unregistered message '%s'", msg) ); return; } CBaseEntity *pPlayer = ToEnt(hPlayer); if ( pPlayer ) { m_filter.AddRecipient( (CBasePlayer*)pPlayer ); } if ( bReliable ) { m_filter.MakeReliable(); } DoSendUserMsg( &m_filter, msg_type ); } //----------------------------------------------------------------------------- // Send a message from a server side entity to its client side counterpart //----------------------------------------------------------------------------- void CNetMsgScriptHelper::SendEntityMessage( HSCRIPT hEnt, bool bReliable ) { CBaseEntity *entity = ToEnt(hEnt); if ( !entity ) { g_pScriptVM->RaiseException("SendEntityMessage: invalid entity"); return; } DoSendEntityMsg( entity, bReliable ); } #else //----------------------------------------------------------------------------- // Dispatch a usermessage on client //----------------------------------------------------------------------------- void CNetMsgScriptHelper::DispatchUserMessage( const char *msg ) { bf_read buffer( m_MsgOut.GetData(), m_MsgOut.GetNumBytesWritten() ); usermessages->DispatchUserMessage( usermessages->LookupUserMessage(msg), buffer ); } #endif // GAME_DLL void CNetMsgScriptHelper::WriteInt( int iValue, int bits ) { SCRIPT_NETMSG_WRITE_FUNC m_MsgOut.WriteSBitLong( iValue, bits ); } void CNetMsgScriptHelper::WriteUInt( int iValue, int bits ) { SCRIPT_NETMSG_WRITE_FUNC m_MsgOut.WriteUBitLong( iValue, bits ); } void CNetMsgScriptHelper::WriteByte( int iValue ) { SCRIPT_NETMSG_WRITE_FUNC m_MsgOut.WriteByte( iValue ); } void CNetMsgScriptHelper::WriteChar( int iValue ) { SCRIPT_NETMSG_WRITE_FUNC m_MsgOut.WriteChar( iValue ); } void CNetMsgScriptHelper::WriteShort( int iValue ) { SCRIPT_NETMSG_WRITE_FUNC m_MsgOut.WriteShort( iValue ); } void CNetMsgScriptHelper::WriteWord( int iValue ) { SCRIPT_NETMSG_WRITE_FUNC m_MsgOut.WriteWord( iValue ); } void CNetMsgScriptHelper::WriteLong( int iValue ) { SCRIPT_NETMSG_WRITE_FUNC m_MsgOut.WriteLong( iValue ); } void CNetMsgScriptHelper::WriteFloat( float flValue ) { SCRIPT_NETMSG_WRITE_FUNC m_MsgOut.WriteFloat( flValue ); } void CNetMsgScriptHelper::WriteNormal( float flValue ) { SCRIPT_NETMSG_WRITE_FUNC m_MsgOut.WriteBitNormal( flValue ); } void CNetMsgScriptHelper::WriteAngle( float flValue ) { SCRIPT_NETMSG_WRITE_FUNC m_MsgOut.WriteBitAngle( flValue, 8 ); } void CNetMsgScriptHelper::WriteCoord( float flValue ) { SCRIPT_NETMSG_WRITE_FUNC m_MsgOut.WriteBitCoord( flValue ); } void CNetMsgScriptHelper::WriteVec3Coord( const Vector& rgflValue ) { SCRIPT_NETMSG_WRITE_FUNC m_MsgOut.WriteBitVec3Coord( rgflValue ); } void CNetMsgScriptHelper::WriteVec3Normal( const Vector& rgflValue ) { SCRIPT_NETMSG_WRITE_FUNC m_MsgOut.WriteBitVec3Normal( rgflValue ); } void CNetMsgScriptHelper::WriteAngles( const QAngle& rgflValue ) { SCRIPT_NETMSG_WRITE_FUNC m_MsgOut.WriteBitAngles( rgflValue ); } void CNetMsgScriptHelper::WriteString( const char *sz ) { SCRIPT_NETMSG_WRITE_FUNC // Larger strings can be written but cannot be read Assert( V_strlen(sz) < SCRIPT_NETMSG_STRING_SIZE ); m_MsgOut.WriteString( sz ); } void CNetMsgScriptHelper::WriteBool( bool bValue ) { SCRIPT_NETMSG_WRITE_FUNC m_MsgOut.WriteOneBit( bValue ? 1 : 0 ); } void CNetMsgScriptHelper::WriteEntity( HSCRIPT hEnt ) { SCRIPT_NETMSG_WRITE_FUNC CBaseEntity *p = ToEnt(hEnt); int i = p ? p->entindex() : 0; m_MsgOut.WriteUBitLong( i, MAX_EDICT_BITS ); } void CNetMsgScriptHelper::WriteEHandle( HSCRIPT hEnt ) { SCRIPT_NETMSG_WRITE_FUNC CBaseEntity *pEnt = ToEnt( hEnt ); long iEncodedEHandle; if ( pEnt ) { EHANDLE hEnt = pEnt; int iSerialNum = hEnt.GetSerialNumber() & (1 << NUM_NETWORKED_EHANDLE_SERIAL_NUMBER_BITS) - 1; iEncodedEHandle = hEnt.GetEntryIndex() | (iSerialNum << MAX_EDICT_BITS); } else { iEncodedEHandle = INVALID_NETWORKED_EHANDLE_VALUE; } m_MsgOut.WriteLong( iEncodedEHandle ); } int CNetMsgScriptHelper::ReadInt( int bits ) { return m_MsgIn_()ReadSBitLong(bits); } int CNetMsgScriptHelper::ReadUInt( int bits ) { return m_MsgIn_()ReadUBitLong(bits); } int CNetMsgScriptHelper::ReadByte() { return m_MsgIn_()ReadByte(); } int CNetMsgScriptHelper::ReadChar() { return m_MsgIn_()ReadChar(); } int CNetMsgScriptHelper::ReadShort() { return m_MsgIn_()ReadShort(); } int CNetMsgScriptHelper::ReadWord() { return m_MsgIn_()ReadWord(); } int CNetMsgScriptHelper::ReadLong() { return m_MsgIn_()ReadLong(); } float CNetMsgScriptHelper::ReadFloat() { return m_MsgIn_()ReadFloat(); } float CNetMsgScriptHelper::ReadNormal() { return m_MsgIn_()ReadBitNormal(); } float CNetMsgScriptHelper::ReadAngle() { return m_MsgIn_()ReadBitAngle( 8 ); } float CNetMsgScriptHelper::ReadCoord() { return m_MsgIn_()ReadBitCoord(); } const Vector& CNetMsgScriptHelper::ReadVec3Coord() { static Vector vec3; m_MsgIn_()ReadBitVec3Coord(vec3); return vec3; } const Vector& CNetMsgScriptHelper::ReadVec3Normal() { static Vector vec3; m_MsgIn_()ReadBitVec3Normal(vec3); return vec3; } const QAngle& CNetMsgScriptHelper::ReadAngles() { static QAngle vec3; m_MsgIn_()ReadBitAngles(vec3); return vec3; } const char* CNetMsgScriptHelper::ReadString() { static char buf[ SCRIPT_NETMSG_STRING_SIZE ]; m_MsgIn_()ReadString( buf, sizeof(buf) ); return buf; } bool CNetMsgScriptHelper::ReadBool() { return m_MsgIn_()ReadOneBit(); } HSCRIPT CNetMsgScriptHelper::ReadEntity() { int index = m_MsgIn_()ReadUBitLong( MAX_EDICT_BITS ); if ( !index ) return NULL; #ifdef GAME_DLL edict_t *e = INDEXENT(index); if ( e && !e->IsFree() ) { return ToHScript( GetContainingEntity(e) ); } #else // CLIENT_DLL if ( index < NUM_ENT_ENTRIES ) { return ToHScript( CBaseEntity::Instance(index) ); } #endif return NULL; } HSCRIPT CNetMsgScriptHelper::ReadEHandle() { int iEncodedEHandle = m_MsgIn_()ReadLong(); if ( iEncodedEHandle == INVALID_NETWORKED_EHANDLE_VALUE ) return NULL; int iEntry = iEncodedEHandle & ( (1 << MAX_EDICT_BITS) - 1 ); int iSerialNum = iEncodedEHandle >> MAX_EDICT_BITS; return ToHScript( EHANDLE( iEntry, iSerialNum ) ); } inline int CNetMsgScriptHelper::GetNumBitsWritten() { #ifdef GAME_DLL return m_MsgOut.GetNumBitsWritten() - SCRIPT_NETMSG_HEADER_BITS; #else return m_MsgOut.m_iCurBit - m_iLastBit - SCRIPT_NETMSG_HEADER_BITS; #endif } BEGIN_SCRIPTDESC_ROOT_NAMED( CNetMsgScriptHelper, "CNetMsg", SCRIPT_SINGLETON "Network messages" ) #ifdef GAME_DLL DEFINE_SCRIPTFUNC( SendUserMessage, "Send a usermessage from the server to the client" ) DEFINE_SCRIPTFUNC( SendEntityMessage, "Send a message from a server side entity to its client side counterpart" ) // TODO: multiplayer #else DEFINE_SCRIPTFUNC( DispatchUserMessage, "Dispatch a usermessage on client" ) #endif DEFINE_SCRIPTFUNC( Reset, "Reset the current network message buffer" ) DEFINE_SCRIPTFUNC( Start, "Start writing new custom network message" ) DEFINE_SCRIPTFUNC( Receive, "Set custom network message callback" ) DEFINE_SCRIPTFUNC_NAMED( Receive, "Recieve", SCRIPT_HIDE ) // This was a typo until v6.3 #ifdef GAME_DLL DEFINE_SCRIPTFUNC( Send, "Send a custom network message from the server to the client (max 252 bytes)" ) #else DEFINE_SCRIPTFUNC( Send, "Send a custom network message from the client to the server (max 2044 bytes)" ) #endif DEFINE_SCRIPTFUNC( WriteInt, "variable bit signed int" ) DEFINE_SCRIPTFUNC( WriteUInt, "variable bit unsigned int" ) DEFINE_SCRIPTFUNC( WriteByte, "8 bit unsigned char" ) DEFINE_SCRIPTFUNC( WriteChar, "8 bit char" ) DEFINE_SCRIPTFUNC( WriteShort, "16 bit short" ) DEFINE_SCRIPTFUNC( WriteWord, "16 bit unsigned short" ) DEFINE_SCRIPTFUNC( WriteLong, "32 bit long" ) DEFINE_SCRIPTFUNC( WriteFloat, "32 bit float" ) DEFINE_SCRIPTFUNC( WriteNormal, "12 bit" ) DEFINE_SCRIPTFUNC( WriteAngle, "8 bit unsigned char" ) DEFINE_SCRIPTFUNC( WriteCoord, "" ) DEFINE_SCRIPTFUNC( WriteVec3Coord, "" ) DEFINE_SCRIPTFUNC( WriteVec3Normal, "27 bit" ) DEFINE_SCRIPTFUNC( WriteAngles, "" ) DEFINE_SCRIPTFUNC( WriteString, "max 512 bytes at once" ) DEFINE_SCRIPTFUNC( WriteBool, "1 bit" ) DEFINE_SCRIPTFUNC( WriteEntity, "11 bit (entindex)" ) DEFINE_SCRIPTFUNC( WriteEHandle, "32 bit long" ) DEFINE_SCRIPTFUNC( ReadInt, "" ) DEFINE_SCRIPTFUNC( ReadUInt, "" ) DEFINE_SCRIPTFUNC( ReadByte, "" ) DEFINE_SCRIPTFUNC( ReadChar, "" ) DEFINE_SCRIPTFUNC( ReadShort, "" ) DEFINE_SCRIPTFUNC( ReadWord, "" ) DEFINE_SCRIPTFUNC( ReadLong, "" ) DEFINE_SCRIPTFUNC( ReadFloat, "" ) DEFINE_SCRIPTFUNC( ReadNormal, "" ) DEFINE_SCRIPTFUNC( ReadAngle, "" ) DEFINE_SCRIPTFUNC( ReadCoord, "" ) DEFINE_SCRIPTFUNC( ReadVec3Coord, "" ) DEFINE_SCRIPTFUNC( ReadVec3Normal, "" ) DEFINE_SCRIPTFUNC( ReadAngles, "" ) DEFINE_SCRIPTFUNC( ReadString, "" ) DEFINE_SCRIPTFUNC( ReadBool, "" ) DEFINE_SCRIPTFUNC( ReadEntity, "" ) DEFINE_SCRIPTFUNC( ReadEHandle, "" ) DEFINE_SCRIPTFUNC( GetNumBitsWritten, "" ) END_SCRIPTDESC(); #define RETURN_IF_CANNOT_DRAW_OVERLAY\ if (engine->IsPaused())\ return; class CDebugOverlayScriptHelper { public: void Box( const Vector &origin, const Vector &mins, const Vector &maxs, int r, int g, int b, int a, float flDuration ) { RETURN_IF_CANNOT_DRAW_OVERLAY debugoverlay->AddBoxOverlay(origin, mins, maxs, vec3_angle, r, g, b, a, flDuration); } void BoxDirection( const Vector &origin, const Vector &mins, const Vector &maxs, const Vector &forward, int r, int g, int b, int a, float flDuration ) { RETURN_IF_CANNOT_DRAW_OVERLAY QAngle f_angles = vec3_angle; f_angles.y = UTIL_VecToYaw(forward); debugoverlay->AddBoxOverlay(origin, mins, maxs, f_angles, r, g, b, a, flDuration); } void BoxAngles( const Vector &origin, const Vector &mins, const Vector &maxs, const QAngle &angles, int r, int g, int b, int a, float flDuration ) { RETURN_IF_CANNOT_DRAW_OVERLAY debugoverlay->AddBoxOverlay(origin, mins, maxs, angles, r, g, b, a, flDuration); } void SweptBox( const Vector& start, const Vector& end, const Vector& mins, const Vector& maxs, const QAngle & angles, int r, int g, int b, int a, float flDuration ) { RETURN_IF_CANNOT_DRAW_OVERLAY debugoverlay->AddSweptBoxOverlay(start, end, mins, maxs, angles, r, g, b, a, flDuration); } void EntityBounds( HSCRIPT pEntity, int r, int g, int b, int a, float flDuration ) { RETURN_IF_CANNOT_DRAW_OVERLAY CBaseEntity *pEnt = ToEnt(pEntity); if (!pEnt) return; const CCollisionProperty *pCollide = pEnt->CollisionProp(); debugoverlay->AddBoxOverlay(pCollide->GetCollisionOrigin(), pCollide->OBBMins(), pCollide->OBBMaxs(), pCollide->GetCollisionAngles(), r, g, b, a, flDuration); } void Line( const Vector &origin, const Vector &target, int r, int g, int b, bool noDepthTest, float flDuration ) { RETURN_IF_CANNOT_DRAW_OVERLAY debugoverlay->AddLineOverlay(origin, target, r, g, b, noDepthTest, flDuration); } void Triangle( const Vector &p1, const Vector &p2, const Vector &p3, int r, int g, int b, int a, bool noDepthTest, float duration ) { RETURN_IF_CANNOT_DRAW_OVERLAY debugoverlay->AddTriangleOverlay(p1, p2, p3, r, g, b, a, noDepthTest, duration); } void EntityText( int entityID, int text_offset, const char *text, float flDuration, int r, int g, int b, int a ) { RETURN_IF_CANNOT_DRAW_OVERLAY debugoverlay->AddEntityTextOverlay(entityID, text_offset, flDuration, (int)clamp(r * 255.f, 0.f, 255.f), (int)clamp(g * 255.f, 0.f, 255.f), (int)clamp(b * 255.f, 0.f, 255.f), (int)clamp(a * 255.f, 0.f, 255.f), text); } void EntityTextAtPosition( const Vector &origin, int text_offset, const char *text, float flDuration, int r, int g, int b, int a ) { RETURN_IF_CANNOT_DRAW_OVERLAY debugoverlay->AddTextOverlayRGB(origin, text_offset, flDuration, r, g, b, a, "%s", text); } void Grid( const Vector &vPosition ) { RETURN_IF_CANNOT_DRAW_OVERLAY debugoverlay->AddGridOverlay(vPosition); } void Text( const Vector &origin, const char *text, float flDuration ) { RETURN_IF_CANNOT_DRAW_OVERLAY debugoverlay->AddTextOverlay(origin, flDuration, "%s", text); } void ScreenText( float fXpos, float fYpos, const char *text, int r, int g, int b, int a, float flDuration ) { RETURN_IF_CANNOT_DRAW_OVERLAY debugoverlay->AddScreenTextOverlay(fXpos, fYpos, flDuration, r, g, b, a, text); } void Cross3D( const Vector &position, float size, int r, int g, int b, bool noDepthTest, float flDuration ) { RETURN_IF_CANNOT_DRAW_OVERLAY Line( position + Vector(size,0,0), position - Vector(size,0,0), r, g, b, noDepthTest, flDuration ); Line( position + Vector(0,size,0), position - Vector(0,size,0), r, g, b, noDepthTest, flDuration ); Line( position + Vector(0,0,size), position - Vector(0,0,size), r, g, b, noDepthTest, flDuration ); } void Cross3DOriented( const Vector &position, const QAngle &angles, float size, int r, int g, int b, bool noDepthTest, float flDuration ) { RETURN_IF_CANNOT_DRAW_OVERLAY Vector forward, right, up; AngleVectors( angles, &forward, &right, &up ); forward *= size; right *= size; up *= size; Line( position + right, position - right, r, g, b, noDepthTest, flDuration ); Line( position + forward, position - forward, r, g, b, noDepthTest, flDuration ); Line( position + up, position - up, r, g, b, noDepthTest, flDuration ); } void DrawTickMarkedLine( const Vector &startPos, const Vector &endPos, float tickDist, int tickTextDist, int r, int g, int b, bool noDepthTest, float flDuration ) { RETURN_IF_CANNOT_DRAW_OVERLAY Vector lineDir = (endPos - startPos); float lineDist = VectorNormalize(lineDir); int numTicks = lineDist / tickDist; Vector upVec = Vector(0,0,4); Vector sideDir; Vector tickPos = startPos; int tickTextCnt = 0; CrossProduct(lineDir, upVec, sideDir); Line(startPos, endPos, r, g, b, noDepthTest, flDuration); for (int i = 0; i 0 ) { Triangle( p5, p4, p3, r, g, b, a, noDepthTest, flDuration ); Triangle( p1, p7, p6, r, g, b, a, noDepthTest, flDuration ); Triangle( p6, p2, p1, r, g, b, a, noDepthTest, flDuration ); Triangle( p3, p4, p5, r, g, b, a, noDepthTest, flDuration ); Triangle( p6, p7, p1, r, g, b, a, noDepthTest, flDuration ); Triangle( p1, p2, p6, r, g, b, a, noDepthTest, flDuration ); } } void YawArrow( const Vector &startPos, float yaw, float length, float width, int r, int g, int b, int a, bool noDepthTest, float flDuration ) { RETURN_IF_CANNOT_DRAW_OVERLAY Vector forward = UTIL_YawToVector( yaw ); HorzArrow( startPos, startPos + forward * length, width, r, g, b, a, noDepthTest, flDuration ); } void VertArrow( const Vector &startPos, const Vector &endPos, float width, int r, int g, int b, int a, bool noDepthTest, float flDuration ) { RETURN_IF_CANNOT_DRAW_OVERLAY Vector lineDir = (endPos - startPos); VectorNormalize( lineDir ); Vector upVec; Vector sideDir; float radius = width / 2.0; VectorVectors( lineDir, sideDir, upVec ); Vector p1 = startPos - upVec * radius; Vector p2 = endPos - lineDir * width - upVec * radius; Vector p3 = endPos - lineDir * width - upVec * width; Vector p4 = endPos; Vector p5 = endPos - lineDir * width + upVec * width; Vector p6 = endPos - lineDir * width + upVec * radius; Vector p7 = startPos + upVec * radius; Line(p1, p2, r,g,b,noDepthTest,flDuration); Line(p2, p3, r,g,b,noDepthTest,flDuration); Line(p3, p4, r,g,b,noDepthTest,flDuration); Line(p4, p5, r,g,b,noDepthTest,flDuration); Line(p5, p6, r,g,b,noDepthTest,flDuration); Line(p6, p7, r,g,b,noDepthTest,flDuration); if ( a > 0 ) { Triangle( p5, p4, p3, r, g, b, a, noDepthTest, flDuration ); Triangle( p1, p7, p6, r, g, b, a, noDepthTest, flDuration ); Triangle( p6, p2, p1, r, g, b, a, noDepthTest, flDuration ); Triangle( p3, p4, p5, r, g, b, a, noDepthTest, flDuration ); Triangle( p6, p7, p1, r, g, b, a, noDepthTest, flDuration ); Triangle( p1, p2, p6, r, g, b, a, noDepthTest, flDuration ); } } void Axis( const Vector &position, const QAngle &angles, float size, bool noDepthTest, float flDuration ) { RETURN_IF_CANNOT_DRAW_OVERLAY Vector xvec, yvec, zvec; AngleVectors( angles, &xvec, &yvec, &zvec ); xvec = position + (size * xvec); yvec = position - (size * yvec); zvec = position + (size * zvec); Line( position, xvec, 255, 0, 0, noDepthTest, flDuration ); Line( position, yvec, 0, 255, 0, noDepthTest, flDuration ); Line( position, zvec, 0, 0, 255, noDepthTest, flDuration ); } void Sphere( const Vector ¢er, float radius, int r, int g, int b, bool noDepthTest, float flDuration ) { RETURN_IF_CANNOT_DRAW_OVERLAY Vector edge, lastEdge; float axisSize = radius; Line( center + Vector( 0, 0, -axisSize ), center + Vector( 0, 0, axisSize ), r, g, b, noDepthTest, flDuration ); Line( center + Vector( 0, -axisSize, 0 ), center + Vector( 0, axisSize, 0 ), r, g, b, noDepthTest, flDuration ); Line( center + Vector( -axisSize, 0, 0 ), center + Vector( axisSize, 0, 0 ), r, g, b, noDepthTest, flDuration ); lastEdge = Vector( radius + center.x, center.y, center.z ); float angle; for( angle=0.0f; angle <= 360.0f; angle += 22.5f ) { edge.x = radius * cosf( angle / 180.0f * M_PI ) + center.x; edge.y = center.y; edge.z = radius * sinf( angle / 180.0f * M_PI ) + center.z; Line( edge, lastEdge, r, g, b, noDepthTest, flDuration ); lastEdge = edge; } lastEdge = Vector( center.x, radius + center.y, center.z ); for( angle=0.0f; angle <= 360.0f; angle += 22.5f ) { edge.x = center.x; edge.y = radius * cosf( angle / 180.0f * M_PI ) + center.y; edge.z = radius * sinf( angle / 180.0f * M_PI ) + center.z; Line( edge, lastEdge, r, g, b, noDepthTest, flDuration ); lastEdge = edge; } lastEdge = Vector( center.x, radius + center.y, center.z ); for( angle=0.0f; angle <= 360.0f; angle += 22.5f ) { edge.x = radius * cosf( angle / 180.0f * M_PI ) + center.x; edge.y = radius * sinf( angle / 180.0f * M_PI ) + center.y; edge.z = center.z; Line( edge, lastEdge, r, g, b, noDepthTest, flDuration ); lastEdge = edge; } } void CircleOriented( const Vector &position, const QAngle &angles, float radius, int r, int g, int b, int a, bool bNoDepthTest, float flDuration ) { RETURN_IF_CANNOT_DRAW_OVERLAY matrix3x4_t xform; AngleMatrix(angles, position, xform); Vector xAxis, yAxis; MatrixGetColumn(xform, 2, xAxis); MatrixGetColumn(xform, 1, yAxis); Circle(position, xAxis, yAxis, radius, r, g, b, a, bNoDepthTest, flDuration); } void Circle( const Vector &position, const Vector &xAxis, const Vector &yAxis, float radius, int r, int g, int b, int a, bool bNoDepthTest, float flDuration ) { RETURN_IF_CANNOT_DRAW_OVERLAY const unsigned int nSegments = 16; const float flRadStep = (M_PI*2.0f) / (float) nSegments; Vector vecLastPosition; Vector vecStart = position + xAxis * radius; Vector vecPosition = vecStart; for ( int i = 1; i <= nSegments; i++ ) { vecLastPosition = vecPosition; float flSin, flCos; SinCos( flRadStep*i, &flSin, &flCos ); vecPosition = position + (xAxis * flCos * radius) + (yAxis * flSin * radius); Line( vecLastPosition, vecPosition, r, g, b, bNoDepthTest, flDuration ); if ( a && i > 1 ) { debugoverlay->AddTriangleOverlay( vecStart, vecLastPosition, vecPosition, r, g, b, a, bNoDepthTest, flDuration ); } } } #ifndef CLIENT_DLL void SetDebugBits( HSCRIPT hEntity, int bit ) // DebugOverlayBits_t { CBaseEntity *pEnt = ToEnt(hEntity); if (!pEnt) return; if (pEnt->m_debugOverlays & bit) { pEnt->m_debugOverlays &= ~bit; } else { pEnt->m_debugOverlays |= bit; #ifdef AI_MONITOR_FOR_OSCILLATION if (pEnt->IsNPC()) { pEnt->MyNPCPointer()->m_ScheduleHistory.RemoveAll(); } #endif//AI_MONITOR_FOR_OSCILLATION } } #endif void ClearAllOverlays() { #ifndef CLIENT_DLL // Clear all entities of their debug overlays for (CBaseEntity *pEntity = gEntList.FirstEnt(); pEntity; pEntity = gEntList.NextEnt(pEntity)) { pEntity->m_debugOverlays = 0; } #endif debugoverlay->ClearAllOverlays(); } private: } g_ScriptDebugOverlay; BEGIN_SCRIPTDESC_ROOT( CDebugOverlayScriptHelper, SCRIPT_SINGLETON "CDebugOverlayScriptHelper" ) DEFINE_SCRIPTFUNC( Box, "Draws a world-space axis-aligned box. Specify bounds in world space." ) DEFINE_SCRIPTFUNC( BoxDirection, "Draw box oriented to a Vector direction" ) DEFINE_SCRIPTFUNC( BoxAngles, "Draws an oriented box at the origin. Specify bounds in local space." ) DEFINE_SCRIPTFUNC( SweptBox, "Draws a swept box. Specify endpoints in world space and the bounds in local space." ) DEFINE_SCRIPTFUNC( EntityBounds, "Draws bounds of an entity" ) DEFINE_SCRIPTFUNC( Line, "Draws a line between two points" ) DEFINE_SCRIPTFUNC( Triangle, "Draws a filled triangle. Specify vertices in world space." ) DEFINE_SCRIPTFUNC( EntityText, "Draws text on an entity" ) DEFINE_SCRIPTFUNC( EntityTextAtPosition, "Draw entity text overlay at a specific position" ) DEFINE_SCRIPTFUNC( Grid, "Add grid overlay" ) DEFINE_SCRIPTFUNC( Text, "Draws 2D text. Specify origin in world space." ) DEFINE_SCRIPTFUNC( ScreenText, "Draws 2D text. Specify coordinates in screen space." ) DEFINE_SCRIPTFUNC( Cross3D, "Draws a world-aligned cross. Specify origin in world space." ) DEFINE_SCRIPTFUNC( Cross3DOriented, "Draws an oriented cross. Specify origin in world space." ) DEFINE_SCRIPTFUNC( DrawTickMarkedLine, "Draws a dashed line. Specify endpoints in world space." ) DEFINE_SCRIPTFUNC( HorzArrow, "Draws a horizontal arrow. Specify endpoints in world space." ) DEFINE_SCRIPTFUNC( YawArrow, "Draws a arrow associated with a specific yaw. Specify endpoints in world space." ) DEFINE_SCRIPTFUNC( VertArrow, "Draws a vertical arrow. Specify endpoints in world space." ) DEFINE_SCRIPTFUNC( Axis, "Draws an axis. Specify origin + orientation in world space." ) DEFINE_SCRIPTFUNC( Sphere, "Draws a wireframe sphere. Specify center in world space." ) DEFINE_SCRIPTFUNC( CircleOriented, "Draws a circle oriented. Specify center in world space." ) DEFINE_SCRIPTFUNC( Circle, "Draws a circle. Specify center in world space." ) #ifndef CLIENT_DLL DEFINE_SCRIPTFUNC( SetDebugBits, "Set debug bits on entity" ) #endif DEFINE_SCRIPTFUNC( ClearAllOverlays, "Clear all debug overlays at once" ) END_SCRIPTDESC(); //============================================================================= // ConVars //============================================================================= class CScriptConCommand : public ConCommand, public ICommandCallback, public ICommandCompletionCallback { typedef ConCommand BaseClass; public: ~CScriptConCommand() { Unregister(); } CScriptConCommand( const char *name, HSCRIPT fn, const char *helpString, int flags, ConCommand *pLinked = NULL ) : BaseClass( name, this, helpString, flags, 0 ), m_pLinked(pLinked), m_hCallback(fn), m_hCompletionCallback(NULL) { m_nCmdNameLen = V_strlen(name) + 1; Assert( m_nCmdNameLen - 1 <= 128 ); } void CommandCallback( const CCommand &command ) { int count = command.ArgC(); ScriptVariant_t *vArgv = (ScriptVariant_t*)stackalloc( sizeof(ScriptVariant_t) * count ); for ( int i = 0; i < count; ++i ) { vArgv[i] = command[i]; } ScriptVariant_t ret; if ( g_pScriptVM->ExecuteFunction( m_hCallback, vArgv, count, &ret, NULL, true ) == SCRIPT_ERROR ) { DevWarning( 1, "CScriptConCommand: invalid callback for '%s'\n", command[0] ); } if ( m_pLinked && (ret.m_type == FIELD_BOOLEAN) && ret.m_bool ) { m_pLinked->Dispatch( command ); } } int CommandCompletionCallback( const char *partial, CUtlVector< CUtlString > &commands ) { Assert( g_pScriptVM ); Assert( m_hCompletionCallback ); ScriptVariant_t hArray; g_pScriptVM->CreateArray( hArray ); // split command name from partial, pass both separately to the script function char *cmdname = (char*)stackalloc( m_nCmdNameLen ); V_memcpy( cmdname, partial, m_nCmdNameLen - 1 ); cmdname[ m_nCmdNameLen - 1 ] = 0; char argPartial[256]; V_StrRight( partial, V_strlen(partial) - m_nCmdNameLen, argPartial, sizeof(argPartial) ); ScriptVariant_t args[3] = { cmdname, argPartial, hArray }; if ( g_pScriptVM->ExecuteFunction( m_hCompletionCallback, args, 3, NULL, NULL, true ) == SCRIPT_ERROR ) { DevWarning( 1, "CScriptConCommand: invalid command completion callback for '%s'\n", cmdname ); g_pScriptVM->ReleaseScript( hArray ); return 0; } int count = 0; ScriptVariant_t val; int it = -1; while ( ( it = g_pScriptVM->GetKeyValue( hArray, it, NULL, &val ) ) != -1 ) { if ( val.m_type == FIELD_CSTRING ) { CUtlString &s = commands.Element( commands.AddToTail() ); int len = V_strlen( val.m_pszString ); if ( len <= COMMAND_COMPLETION_ITEM_LENGTH - 1 ) { s.Set( val.m_pszString ); } else { s.SetDirect( val.m_pszString, COMMAND_COMPLETION_ITEM_LENGTH - 1 ); } ++count; } g_pScriptVM->ReleaseValue(val); if ( count == COMMAND_COMPLETION_MAXITEMS ) break; } g_pScriptVM->ReleaseScript( hArray ); return count; } void SetCompletionCallback( HSCRIPT fn ) { if ( m_hCompletionCallback ) g_pScriptVM->ReleaseScript( m_hCompletionCallback ); if (fn) { if ( !BaseClass::IsRegistered() ) return; BaseClass::m_pCommandCompletionCallback = this; BaseClass::m_bHasCompletionCallback = true; m_hCompletionCallback = fn; } else { BaseClass::m_pCommandCompletionCallback = NULL; BaseClass::m_bHasCompletionCallback = false; m_hCompletionCallback = NULL; } } void SetCallback( HSCRIPT fn ) { if (fn) { if ( !BaseClass::IsRegistered() ) Register(); if ( m_hCallback ) g_pScriptVM->ReleaseScript( m_hCallback ); m_hCallback = fn; } else { Unregister(); } } inline void Unregister() { if ( g_pCVar && BaseClass::IsRegistered() ) g_pCVar->UnregisterConCommand( this ); if ( g_pScriptVM ) { if ( m_hCallback ) { g_pScriptVM->ReleaseScript( m_hCallback ); m_hCallback = NULL; } SetCompletionCallback( NULL ); } } inline void Register() { if ( g_pCVar ) g_pCVar->RegisterConCommand( this ); } HSCRIPT m_hCallback; ConCommand *m_pLinked; HSCRIPT m_hCompletionCallback; int m_nCmdNameLen; }; class CScriptConVar : public ConVar { typedef ConVar BaseClass; public: ~CScriptConVar() { Unregister(); } CScriptConVar( const char *pName, const char *pDefaultValue, const char *pHelpString, int flags/*, float fMin, float fMax*/ ) : BaseClass( pName, pDefaultValue, flags, pHelpString ), m_hCallback(NULL) {} void SetChangeCallback( HSCRIPT fn ) { void ScriptConVarCallback( IConVar*, const char*, float ); if ( m_hCallback ) g_pScriptVM->ReleaseScript( m_hCallback ); if (fn) { m_hCallback = fn; BaseClass::InstallChangeCallback( (FnChangeCallback_t)ScriptConVarCallback ); } else { m_hCallback = NULL; BaseClass::InstallChangeCallback( NULL ); } } inline void Unregister() { if ( g_pCVar && BaseClass::IsRegistered() ) g_pCVar->UnregisterConCommand( this ); if ( g_pScriptVM ) { SetChangeCallback( NULL ); } } HSCRIPT m_hCallback; }; static CUtlMap< unsigned int, bool > g_ConVarsBlocked( DefLessFunc(unsigned int) ); static CUtlMap< unsigned int, bool > g_ConCommandsOverridable( DefLessFunc(unsigned int) ); static CUtlMap< unsigned int, CScriptConCommand* > g_ScriptConCommands( DefLessFunc(unsigned int) ); static CUtlMap< unsigned int, CScriptConVar* > g_ScriptConVars( DefLessFunc(unsigned int) ); class CScriptConvarAccessor : public CAutoGameSystem { public: static inline unsigned int Hash( const char*sz ){ return HashStringCaseless(sz); } public: inline void AddOverridable( const char *name ) { g_ConCommandsOverridable.InsertOrReplace( Hash(name), true ); } inline bool IsOverridable( unsigned int hash ) { int idx = g_ConCommandsOverridable.Find( hash ); return ( idx != g_ConCommandsOverridable.InvalidIndex() ); } inline void AddBlockedConVar( const char *name ) { g_ConVarsBlocked.InsertOrReplace( Hash(name), true ); } inline bool IsBlockedConvar( const char *name ) { int idx = g_ConVarsBlocked.Find( Hash(name) ); return ( idx != g_ConVarsBlocked.InvalidIndex() ); } public: void RegisterCommand( const char *name, HSCRIPT fn, const char *helpString, int flags ); void SetCompletionCallback( const char *name, HSCRIPT fn ); void UnregisterCommand( const char *name ); void RegisterConvar( const char *name, const char *pDefaultValue, const char *helpString, int flags ); void SetChangeCallback( const char *name, HSCRIPT fn ); HSCRIPT GetCommandClient() { #ifdef GAME_DLL return ToHScript( UTIL_GetCommandClient() ); #else return ToHScript( C_BasePlayer::GetLocalPlayer() ); #endif } #ifdef GAME_DLL const char *GetClientConvarValue( int index, const char* cvar ) { return engine->GetClientConVarValue( index, cvar ); } #endif public: bool Init(); void LevelShutdownPostEntity() { g_ScriptConCommands.PurgeAndDeleteElements(); g_ScriptConVars.PurgeAndDeleteElements(); } public: float GetFloat( const char *pszConVar ) { ConVarRef cvar( pszConVar ); if ( cvar.IsFlagSet( FCVAR_SERVER_CANNOT_QUERY ) ) return NULL; return cvar.GetFloat(); } int GetInt( const char *pszConVar ) { ConVarRef cvar( pszConVar ); if ( cvar.IsFlagSet( FCVAR_SERVER_CANNOT_QUERY ) ) return NULL; return cvar.GetInt(); } bool GetBool( const char *pszConVar ) { ConVarRef cvar( pszConVar ); if ( cvar.IsFlagSet( FCVAR_SERVER_CANNOT_QUERY ) ) return NULL; return cvar.GetBool(); } const char *GetStr( const char *pszConVar ) { ConVarRef cvar( pszConVar ); if ( cvar.IsFlagSet( FCVAR_SERVER_CANNOT_QUERY ) ) return NULL; return cvar.GetString(); } const char *GetDefaultValue( const char *pszConVar ) { ConVarRef cvar( pszConVar ); return cvar.GetDefault(); } bool IsFlagSet( const char *pszConVar, int nFlags ) { ConVarRef cvar( pszConVar ); return cvar.IsFlagSet( nFlags ); } void SetFloat( const char *pszConVar, float value ) { SetValue( pszConVar, value ); } void SetInt( const char *pszConVar, int value ) { SetValue( pszConVar, value ); } void SetBool( const char *pszConVar, bool value ) { SetValue( pszConVar, value ); } void SetStr( const char *pszConVar, const char *value ) { SetValue( pszConVar, value ); } template void SetValue( const char *pszConVar, T value ) { ConVarRef cvar( pszConVar ); if ( !cvar.IsValid() ) return; if ( cvar.IsFlagSet( FCVAR_NOT_CONNECTED | FCVAR_SERVER_CANNOT_QUERY ) ) return; if ( IsBlockedConvar( pszConVar ) ) return; cvar.SetValue( value ); } } g_ScriptConvarAccessor; void CScriptConvarAccessor::RegisterCommand( const char *name, HSCRIPT fn, const char *helpString, int flags ) { unsigned int hash = Hash(name); int idx = g_ScriptConCommands.Find(hash); if ( idx == g_ScriptConCommands.InvalidIndex() ) { ConCommandBase *pBase = g_pCVar->FindCommandBase(name); if ( pBase && ( !pBase->IsCommand() || !IsOverridable(hash) ) ) { DevWarning( 1, "CScriptConvarAccessor::RegisterCommand unable to register blocked ConCommand: %s\n", name ); return; } if ( !fn ) return; CScriptConCommand *p = new CScriptConCommand( name, fn, helpString, flags, static_cast< ConCommand* >(pBase) ); g_ScriptConCommands.Insert( hash, p ); } else { CScriptConCommand *pCmd = g_ScriptConCommands[idx]; pCmd->SetCallback( fn ); //CGMsg( 1, CON_GROUP_VSCRIPT, "CScriptConvarAccessor::RegisterCommand replacing command already registered: %s\n", name ); } } void CScriptConvarAccessor::SetCompletionCallback( const char *name, HSCRIPT fn ) { unsigned int hash = Hash(name); int idx = g_ScriptConCommands.Find(hash); if ( idx != g_ScriptConCommands.InvalidIndex() ) { g_ScriptConCommands[idx]->SetCompletionCallback( fn ); } } void CScriptConvarAccessor::UnregisterCommand( const char *name ) { unsigned int hash = Hash(name); int idx = g_ScriptConCommands.Find(hash); if ( idx != g_ScriptConCommands.InvalidIndex() ) { g_ScriptConCommands[idx]->Unregister(); } } void CScriptConvarAccessor::RegisterConvar( const char *name, const char *pDefaultValue, const char *helpString, int flags ) { Assert( g_pCVar ); unsigned int hash = Hash(name); int idx = g_ScriptConVars.Find(hash); if ( idx == g_ScriptConVars.InvalidIndex() ) { if ( g_pCVar->FindCommandBase(name) ) { DevWarning( 1, "CScriptConvarAccessor::RegisterConvar unable to register blocked ConCommand: %s\n", name ); return; } CScriptConVar *p = new CScriptConVar( name, pDefaultValue, helpString, flags ); g_ScriptConVars.Insert( hash, p ); } else { //CGMsg( 1, CON_GROUP_VSCRIPT, "CScriptConvarAccessor::RegisterConvar convar %s already registered\n", name ); } } void CScriptConvarAccessor::SetChangeCallback( const char *name, HSCRIPT fn ) { unsigned int hash = Hash(name); int idx = g_ScriptConVars.Find(hash); if ( idx != g_ScriptConVars.InvalidIndex() ) { g_ScriptConVars[idx]->SetChangeCallback( fn ); } } void ScriptConVarCallback( IConVar *var, const char* pszOldValue, float flOldValue ) { ConVar *cvar = (ConVar*)var; const char *name = cvar->GetName(); unsigned int hash = CScriptConvarAccessor::Hash( name ); int idx = g_ScriptConVars.Find(hash); if ( idx != g_ScriptConVars.InvalidIndex() ) { Assert( g_ScriptConVars[idx]->m_hCallback ); ScriptVariant_t args[5] = { name, pszOldValue, flOldValue, cvar->GetString(), cvar->GetFloat() }; if ( g_pScriptVM->ExecuteFunction( g_ScriptConVars[idx]->m_hCallback, args, 5, NULL, NULL, true ) == SCRIPT_ERROR ) { DevWarning( 1, "CScriptConVar: invalid change callback for '%s'\n", name ); } } } bool CScriptConvarAccessor::Init() { static bool bExecOnce = false; if ( bExecOnce ) return true; bExecOnce = true; AddOverridable( "+attack" ); AddOverridable( "+attack2" ); AddOverridable( "+attack3" ); AddOverridable( "+forward" ); AddOverridable( "+back" ); AddOverridable( "+moveleft" ); AddOverridable( "+moveright" ); AddOverridable( "+use" ); AddOverridable( "+jump" ); AddOverridable( "+zoom" ); AddOverridable( "+reload" ); AddOverridable( "+speed" ); AddOverridable( "+walk" ); AddOverridable( "+duck" ); AddOverridable( "+strafe" ); AddOverridable( "+alt1" ); AddOverridable( "+alt2" ); AddOverridable( "+grenade1" ); AddOverridable( "+grenade2" ); AddOverridable( "+showscores" ); AddOverridable( "+voicerecord" ); AddOverridable( "-attack" ); AddOverridable( "-attack2" ); AddOverridable( "-attack3" ); AddOverridable( "-forward" ); AddOverridable( "-back" ); AddOverridable( "-moveleft" ); AddOverridable( "-moveright" ); AddOverridable( "-use" ); AddOverridable( "-jump" ); AddOverridable( "-zoom" ); AddOverridable( "-reload" ); AddOverridable( "-speed" ); AddOverridable( "-walk" ); AddOverridable( "-duck" ); AddOverridable( "-strafe" ); AddOverridable( "-alt1" ); AddOverridable( "-alt2" ); AddOverridable( "-grenade1" ); AddOverridable( "-grenade2" ); AddOverridable( "-showscores" ); AddOverridable( "-voicerecord" ); AddOverridable( "toggle_duck" ); AddOverridable( "impulse" ); AddOverridable( "use" ); AddOverridable( "lastinv" ); AddOverridable( "invnext" ); AddOverridable( "invprev" ); AddOverridable( "phys_swap" ); AddOverridable( "slot0" ); AddOverridable( "slot1" ); AddOverridable( "slot2" ); AddOverridable( "slot3" ); AddOverridable( "slot4" ); AddOverridable( "slot5" ); AddOverridable( "slot6" ); AddOverridable( "slot7" ); AddOverridable( "slot8" ); AddOverridable( "slot9" ); AddOverridable( "slot10" ); AddOverridable( "save" ); AddOverridable( "load" ); AddOverridable( "say" ); AddOverridable( "say_team" ); AddBlockedConVar( "con_enable" ); AddBlockedConVar( "cl_allowdownload" ); AddBlockedConVar( "cl_allowupload" ); AddBlockedConVar( "cl_downloadfilter" ); return true; } BEGIN_SCRIPTDESC_ROOT_NAMED( CScriptConvarAccessor, "CConvars", SCRIPT_SINGLETON "Provides an interface to convars." ) DEFINE_SCRIPTFUNC( RegisterConvar, "register a new console variable." ) DEFINE_SCRIPTFUNC( RegisterCommand, "register a console command." ) DEFINE_SCRIPTFUNC( SetCompletionCallback, "callback is called with 3 parameters (cmd, partial, commands), user strings must be appended to 'commands' array" ) DEFINE_SCRIPTFUNC( SetChangeCallback, "callback is called with 5 parameters (var, szOldValue, flOldValue, szNewValue, flNewValue)" ) DEFINE_SCRIPTFUNC( UnregisterCommand, "unregister a console command." ) DEFINE_SCRIPTFUNC( GetCommandClient, "returns the player who issued this console command." ) #ifdef GAME_DLL DEFINE_SCRIPTFUNC( GetClientConvarValue, "Get a convar keyvalue for a specified client" ) #endif DEFINE_SCRIPTFUNC( GetFloat, "Returns the convar as a float. May return null if no such convar." ) DEFINE_SCRIPTFUNC( GetInt, "Returns the convar as an int. May return null if no such convar." ) DEFINE_SCRIPTFUNC( GetBool, "Returns the convar as a bool. May return null if no such convar." ) DEFINE_SCRIPTFUNC( GetStr, "Returns the convar as a string. May return null if no such convar." ) DEFINE_SCRIPTFUNC( GetDefaultValue, "Returns the convar's default value as a string. May return null if no such convar." ) DEFINE_SCRIPTFUNC( IsFlagSet, "Returns the convar's flags. May return null if no such convar." ) DEFINE_SCRIPTFUNC( SetFloat, "Sets the value of the convar as a float." ) DEFINE_SCRIPTFUNC( SetInt, "Sets the value of the convar as an int." ) DEFINE_SCRIPTFUNC( SetBool, "Sets the value of the convar as a bool." ) DEFINE_SCRIPTFUNC( SetStr, "Sets the value of the convar as a string." ) END_SCRIPTDESC(); //============================================================================= // Effects // (Unique to mapbase) // // At the moment only clientside until a filtering method on server is finalised. // // TEs most of the time call IEffects (g_pEffects) or ITempEnts (tempents) on client, // but they also record for tools recording mode. // // On client no TE is suppressed. // TE flags are found at tempent.h // // TODO: //============================================================================= #ifdef CLIENT_DLL class CEffectsScriptHelper { public: void DynamicLight( int index, const Vector& origin, int r, int g, int b, int exponent, float radius, float die, float decay, int style = 0, int flags = 0 ) { //te->DynamicLight( filter, delay, &origin, r, g, b, exponent, radius, die, decay ); dlight_t *dl = effects->CL_AllocDlight( index ); dl->origin = origin; dl->color.r = r; dl->color.g = g; dl->color.b = b; dl->color.exponent = exponent; dl->radius = radius; dl->die = gpGlobals->curtime + die; dl->decay = decay; dl->style = style; dl->flags = flags; } void Explosion( const Vector& pos, float scale, int radius, int magnitude, int flags ) { C_RecipientFilter filter; filter.AddAllPlayers(); // framerate, modelindex, normal and materialtype are unused // radius for ragdolls extern short g_sModelIndexFireball; te->Explosion( filter, 0.0f, &pos, g_sModelIndexFireball, scale, 15, flags, radius, magnitude, &vec3_origin ); } // void FXExplosion( const Vector& pos, const Vector& normal, int materialType = 'C' ) // { // // just the particles // // materialtype only for debris. can be 'C','W' or anything else. // FX_Explosion( const_cast(pos), const_cast(normal), materialType ); // } // void ConcussiveExplosion( const Vector& pos, const Vector& normal ) // { // FX_ConcussiveExplosion( const_cast(pos), const_cast(normal) ); // } // void MicroExplosion( const Vector& pos, const Vector& normal ) // { // FX_MicroExplosion( const_cast(pos), const_cast(normal) ); // } // void MuzzleFlash( int type, HSCRIPT hEntity, int attachment, bool firstPerson ) // { // C_BaseEntity *p = ToEnt(hEntity); // ClientEntityHandle_t ent = p ? (ClientEntityList().EntIndexToHandle)( p->entindex() ) : NULL;; // tempents->MuzzleFlash( type, ent, attachment, firstPerson ); // } void Sparks( const Vector& pos, int nMagnitude, int nTrailLength, const Vector& pDir ) { //te->Sparks( filter, delay, &pos, nMagnitude, nTrailLength, &pDir ); //g_pEffects->Sparks( pos, nMagnitude, nTrailLength, &pDir ); FX_ElectricSpark( pos, nMagnitude, nTrailLength, &pDir ); } void MetalSparks( const Vector& pos, const Vector& dir ) { //g_pEffects->MetalSparks( pos, dir ); FX_MetalSpark( pos, dir, dir ); } // void Smoke( const Vector& pos, float scale, int framerate) // { // extern short g_sModelIndexSmoke; // //te->Smoke( filter, 0.0, &pos, g_sModelIndexSmoke, scale * 10.0f, framerate ); // g_pEffects->Smoke( pos, g_sModelIndexSmoke, scale, framerate ); // } void Dust( const Vector &pos, const Vector &dir, float size, float speed ) { //te->Dust( filter, delay, pos, dir, size, speed ); //g_pEffects->Dust( pos, dir, size, speed ); FX_Dust( pos, dir, size, speed ); } void Bubbles( const Vector &mins, const Vector &maxs, float height, int modelindex, int count, float speed ) { //int bubbles = modelinfo->GetModelIndex( "sprites/bubble.vmt" ); //te->Bubbles( filter, delay, &mins, &maxs, height, modelindex, count, speed ); tempents->Bubbles( mins, maxs, height, modelindex, count, speed ); } // void Fizz( const Vector& mins, const Vector& maxs, int modelIndex, int density, int current/*, int flags*/ ) // { // //te->Fizz( filter, delay, ent, modelindex, density, current ); // //tempents->FizzEffect( ToEnt(ent), modelindex, density, current ); // } void Sprite( const Vector &pos, const Vector &dir, float scale, int modelIndex, int rendermode, int renderfx, int brightness, float life, int flags ) { //te->Sprite( filter, delay, &pos, modelindex, size, brightness ); float a = (1.0 / 255.0) * brightness; tempents->TempSprite( pos, dir, scale, modelIndex, rendermode, renderfx, a, life, flags ); } // void PhysicsProp( float delay, int modelindex, int skin, const Vector& pos, const QAngle &angles, // const Vector& vel, int flags, int effects ) // { // //te->PhysicsProp( filter, delay, modelindex, skin, pos, angles, vel, flags, effects ); // tempents->PhysicsProp( modelindex, skin, pos, angles, vel, flags, effects ); // } void ClientProjectile( const Vector& vecOrigin, const Vector& vecVelocity, const Vector& vecAccel, int modelindex, int lifetime, HSCRIPT pOwner, const char *pszImpactEffect = NULL, const char *pszParticleEffect = NULL ) { //te->ClientProjectile( filter, delay, &vecOrigin, &vecVelocity, modelindex, lifetime, ToEnt(pOwner) ); if ( pszImpactEffect && !(*pszImpactEffect) ) pszImpactEffect = NULL; if ( pszParticleEffect && !(*pszParticleEffect) ) pszParticleEffect = NULL; tempents->ClientProjectile( vecOrigin, vecVelocity, vecAccel, modelindex, lifetime, ToEnt(pOwner), pszImpactEffect, pszParticleEffect ); } } g_ScriptEffectsHelper; BEGIN_SCRIPTDESC_ROOT_NAMED( CEffectsScriptHelper, "CEffects", SCRIPT_SINGLETON "" ) DEFINE_SCRIPTFUNC( DynamicLight, "" ) DEFINE_SCRIPTFUNC( Explosion, "" ) DEFINE_SCRIPTFUNC( Sparks, "" ) DEFINE_SCRIPTFUNC( MetalSparks, "" ) DEFINE_SCRIPTFUNC( Dust, "" ) DEFINE_SCRIPTFUNC( Bubbles, "" ) DEFINE_SCRIPTFUNC( Sprite, "" ) DEFINE_SCRIPTFUNC( ClientProjectile, "" ) END_SCRIPTDESC(); //============================================================================= //============================================================================= extern CGlowObjectManager g_GlowObjectManager; class CScriptGlowObjectManager : public CAutoGameSystem { public: CUtlVector m_RegisteredObjects; void LevelShutdownPostEntity() { FOR_EACH_VEC( m_RegisteredObjects, i ) g_GlowObjectManager.UnregisterGlowObject( m_RegisteredObjects[i] ); m_RegisteredObjects.Purge(); } public: int Register( HSCRIPT hEntity, int r, int g, int b, int a, bool bRenderWhenOccluded, bool bRenderWhenUnoccluded ) { Vector vGlowColor; vGlowColor.x = r * ( 1.0f / 255.0f ); vGlowColor.y = g * ( 1.0f / 255.0f ); vGlowColor.z = b * ( 1.0f / 255.0f ); float flGlowAlpha = a * ( 1.0f / 255.0f ); int idx = g_GlowObjectManager.RegisterGlowObject( ToEnt(hEntity), vGlowColor, flGlowAlpha, bRenderWhenOccluded, bRenderWhenUnoccluded, -1 ); m_RegisteredObjects.AddToTail( idx ); return idx; } void Unregister( int nGlowObjectHandle ) { if ( (nGlowObjectHandle < 0) || (nGlowObjectHandle >= g_GlowObjectManager.m_GlowObjectDefinitions.Count()) ) return; g_GlowObjectManager.UnregisterGlowObject( nGlowObjectHandle ); m_RegisteredObjects.FindAndFastRemove( nGlowObjectHandle ); } void SetEntity( int nGlowObjectHandle, HSCRIPT hEntity ) { g_GlowObjectManager.SetEntity( nGlowObjectHandle, ToEnt(hEntity) ); } void SetColor( int nGlowObjectHandle, int r, int g, int b ) { Vector vGlowColor; vGlowColor.x = r * ( 1.0f / 255.0f ); vGlowColor.y = g * ( 1.0f / 255.0f ); vGlowColor.z = b * ( 1.0f / 255.0f ); g_GlowObjectManager.SetColor( nGlowObjectHandle, vGlowColor ); } void SetAlpha( int nGlowObjectHandle, int a ) { float flGlowAlpha = a * ( 1.0f / 255.0f ); g_GlowObjectManager.SetAlpha( nGlowObjectHandle, flGlowAlpha ); } void SetRenderFlags( int nGlowObjectHandle, bool bRenderWhenOccluded, bool bRenderWhenUnoccluded ) { g_GlowObjectManager.SetRenderFlags( nGlowObjectHandle, bRenderWhenOccluded, bRenderWhenUnoccluded ); } } g_ScriptGlowObjectManager; BEGIN_SCRIPTDESC_ROOT_NAMED( CScriptGlowObjectManager, "CGlowObjectManager", SCRIPT_SINGLETON "" ) DEFINE_SCRIPTFUNC( Register, "( HSCRIPT hEntity, int r, int g, int b, int a, bool bRenderWhenOccluded, bool bRenderWhenUnoccluded )" ) DEFINE_SCRIPTFUNC( Unregister, "" ) DEFINE_SCRIPTFUNC( SetEntity, "" ) DEFINE_SCRIPTFUNC( SetColor, "" ) DEFINE_SCRIPTFUNC( SetAlpha, "" ) DEFINE_SCRIPTFUNC( SetRenderFlags, "" ) END_SCRIPTDESC(); //============================================================================= //============================================================================= #if !defined(NO_STEAM) class CScriptSteamAPI { public: const char *GetSteam2ID() { if ( !steamapicontext || !steamapicontext->SteamUser() ) return NULL; CSteamID id = steamapicontext->SteamUser()->GetSteamID(); uint32 accountID = id.GetAccountID(); uint32 steamInstanceID = 0; uint32 high32bits = accountID % 2; uint32 low32bits = accountID / 2; static char ret[48]; V_snprintf( ret, sizeof(ret), "STEAM_%u:%u:%u", steamInstanceID, high32bits, low32bits ); return ret; } int GetSecondsSinceComputerActive() { if ( !steamapicontext || !steamapicontext->SteamUtils() ) return 0; return steamapicontext->SteamUtils()->GetSecondsSinceComputerActive(); } int GetCurrentBatteryPower() { if ( !steamapicontext || !steamapicontext->SteamUtils() ) return 0; return steamapicontext->SteamUtils()->GetCurrentBatteryPower(); } #if 0 const char *GetIPCountry() { if ( !steamapicontext || !steamapicontext->SteamUtils() ) return NULL; const char *get = steamapicontext->SteamUtils()->GetIPCountry(); if ( !get ) return NULL; static char ret[3]; V_strncpy( ret, get, 3 ); return ret; } #endif const char *GetCurrentGameLanguage() { if ( !steamapicontext || !steamapicontext->SteamApps() ) return NULL; const char *lang = steamapicontext->SteamApps()->GetCurrentGameLanguage(); if ( !lang ) return NULL; static char ret[16]; V_strncpy( ret, lang, sizeof(ret) ); return ret; } const char *GetCurrentBetaName() { if ( !steamapicontext || !steamapicontext->SteamApps() ) return NULL; static char ret[16]; steamapicontext->SteamApps()->GetCurrentBetaName( ret, sizeof( ret ) ); return ret; } #if 0 bool IsSubscribedApp( int nAppID ) { if ( !steamapicontext || !steamapicontext->SteamApps() ) return false; return steamapicontext->SteamApps()->BIsSubscribedApp( nAppID ); } #endif bool IsAppInstalled( int nAppID ) { if ( !steamapicontext || !steamapicontext->SteamApps() ) return false; return steamapicontext->SteamApps()->BIsAppInstalled( nAppID ); } } g_ScriptSteamAPI; BEGIN_SCRIPTDESC_ROOT_NAMED( CScriptSteamAPI, "CSteamAPI", SCRIPT_SINGLETON "" ) DEFINE_SCRIPTFUNC( GetSteam2ID, "" ) //DEFINE_SCRIPTFUNC( IsVACBanned, "" ) DEFINE_SCRIPTFUNC( GetSecondsSinceComputerActive, "Returns the number of seconds since the user last moved the mouse." ) DEFINE_SCRIPTFUNC( GetCurrentBatteryPower, "Return the amount of battery power left in the current system in % [0..100], 255 for being on AC power" ) //DEFINE_SCRIPTFUNC( GetIPCountry, "Returns the 2 digit ISO 3166-1-alpha-2 format country code this client is running in (as looked up via an IP-to-location database)" ) DEFINE_SCRIPTFUNC( GetCurrentGameLanguage, "Gets the current language that the user has set as API language code. This falls back to the Steam UI language if the user hasn't explicitly picked a language for the title." ) DEFINE_SCRIPTFUNC( GetCurrentBetaName, "Gets the name of the user's current beta branch. In Source SDK Base 2013 Singleplayer, this will usually return 'upcoming'." ) //DEFINE_SCRIPTFUNC( IsSubscribedApp, "Returns true if the user is subscribed to the specified app ID." ) DEFINE_SCRIPTFUNC( IsAppInstalled, "Returns true if the user has the specified app ID installed on their computer." ) END_SCRIPTDESC(); #endif // !NO_STEAM #endif // CLIENT_DLL void RegisterScriptSingletons() { ScriptRegisterFunctionNamed( g_pScriptVM, CScriptSaveRestoreUtil::SaveTable, "SaveTable", "Store a table with primitive values that will persist across level transitions and save loads." ); ScriptRegisterFunctionNamed( g_pScriptVM, CScriptSaveRestoreUtil::RestoreTable, "RestoreTable", "Retrieves a table from storage. Write into input table." ); ScriptRegisterFunctionNamed( g_pScriptVM, CScriptSaveRestoreUtil::ClearSavedTable, "ClearSavedTable", "Removes the table with the given context." ); ScriptRegisterSimpleHook( g_pScriptVM, g_Hook_OnSave, "OnSave", FIELD_VOID, "Called when the game is saved." ); ScriptRegisterSimpleHook( g_pScriptVM, g_Hook_OnRestore, "OnRestore", FIELD_VOID, "Called when the game is restored." ); ScriptRegisterFunctionNamed( g_pScriptVM, CScriptReadWriteFile::FileWrite, "StringToFile", "Stores the string into the file" ); ScriptRegisterFunctionNamed( g_pScriptVM, CScriptReadWriteFile::FileRead, "FileToString", "Returns the string from the file, null if no file or file is too big." ); ScriptRegisterFunctionNamed( g_pScriptVM, CScriptReadWriteFile::FileExists, "FileExists", "Returns true if the file exists." ); ScriptRegisterFunctionNamed( g_pScriptVM, CScriptReadWriteFile::KeyValuesWrite, "KeyValuesToFile", "Stores the CScriptKeyValues into the file" ); ScriptRegisterFunctionNamed( g_pScriptVM, CScriptReadWriteFile::KeyValuesRead, "FileToKeyValues", "Returns the CScriptKeyValues from the file, null if no file or file is too big." ); ScriptRegisterFunction( g_pScriptVM, ListenToGameEvent, "Register as a listener for a game event from script." ); ScriptRegisterFunctionNamed( g_pScriptVM, CScriptGameEventListener::StopListeningToGameEvent, "StopListeningToGameEvent", "Stop the specified event listener." ); ScriptRegisterFunctionNamed( g_pScriptVM, CScriptGameEventListener::StopListeningToAllGameEvents, "StopListeningToAllGameEvents", "Stop listening to all game events within a specific context." ); ScriptRegisterFunction( g_pScriptVM, FireGameEvent, "Fire a game event." ); #ifndef CLIENT_DLL ScriptRegisterFunction( g_pScriptVM, FireGameEventLocal, "Fire a game event without broadcasting to the client." ); #endif g_pScriptVM->RegisterInstance( &g_ScriptNetPropManager, "NetProps" ); g_pScriptVM->RegisterInstance( &g_ScriptLocalize, "Localize" ); g_pScriptVM->RegisterInstance( g_ScriptNetMsg, "NetMsg" ); g_pScriptVM->RegisterInstance( &g_ScriptDebugOverlay, "debugoverlay" ); g_pScriptVM->RegisterInstance( &g_ScriptConvarAccessor, "Convars" ); #ifdef CLIENT_DLL g_pScriptVM->RegisterInstance( &g_ScriptEffectsHelper, "effects" ); g_pScriptVM->RegisterInstance( &g_ScriptGlowObjectManager, "GlowObjectManager" ); #if !defined(NO_STEAM) g_pScriptVM->RegisterInstance( &g_ScriptSteamAPI, "steam" ); #endif #endif // Singletons not unique to VScript (not declared or defined here) g_pScriptVM->RegisterInstance( GameRules(), "GameRules" ); g_pScriptVM->RegisterInstance( GetAmmoDef(), "AmmoDef" ); #ifndef CLIENT_DLL g_pScriptVM->RegisterInstance( &g_AI_SquadManager, "Squads" ); #endif #ifdef USE_OLD_EVENT_DESCRIPTORS CScriptGameEventListener::LoadAllEvents(); #endif g_ScriptNetMsg->InitPostVM(); }