//========= Copyright Valve Corporation, All rights reserved. ==============================// // // Purpose: Gets and sets SendTable/DataMap networked properties and caches results. // // Code contributions by and used with the permission of L4D2 modders: // Neil Rao (neilrao42@gmail.com) // Raymond Nondorf (rayman1103@aol.com) //==========================================================================================// #include "cbase.h" //#include "../../engine/server.h" #include "netpropmanager.h" #include "stdstring.h" #include "dt_utlvector_common.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" extern void SendProxy_StringT_To_String( const SendProp *pProp, const void *pStruct, const void *pVarData, DVariant *pOut, int iElement, int objectID ); extern ISaveRestoreOps* ActivityDataOps(); const char *ArrayElementNameForIdx( size_t i ) { i = Clamp( i, 0, MAX_ARRAY_ELEMENTS ); return DT_ArrayElementNameForIdx( i ); } //----------------------------------------------------------------------------- CNetPropManager::~CNetPropManager() { m_PropCache.PurgeAndDeleteElements(); } //----------------------------------------------------------------------------- SendProp *CNetPropManager::SearchSendTable( SendTable *pSendTable, const char *pszProperty ) const { // Iterate through the send table and find the prop that we are looking for for ( int nPropIdx = 0; nPropIdx < pSendTable->GetNumProps(); nPropIdx++ ) { SendProp *pSendProp = pSendTable->GetProp( nPropIdx ); const char *pszPropName = pSendProp->GetName(); // If we found the property, return the prop if ( pszPropName && V_strcmp( pszPropName, pszProperty ) == 0 ) { // Skip the prop if it's inside an array, since we want the array itself if ( pSendProp->IsInsideArray() ) continue; else return pSendProp; } // Search nested tables SendTable *pInternalSendTable = pSendProp->GetDataTable(); if ( pInternalSendTable && pSendProp->GetOffset() == 0 ) { pSendProp = SearchSendTable( pInternalSendTable, pszProperty ); if ( pSendProp ) return pSendProp; } } return NULL; } //----------------------------------------------------------------------------- inline typedescription_t *CNetPropManager::SearchDataMap( datamap_t *pMap, const char *pszProperty ) const { while ( pMap ) { for ( int field = 0; field < pMap->dataNumFields; field++ ) { typedescription_t *pTypeDesc = &pMap->dataDesc[field]; const char *fieldName = pTypeDesc->fieldName; if ( !fieldName ) continue; if ( V_strcmp( pszProperty, fieldName ) == 0 ) return pTypeDesc; if ( pTypeDesc->td && pTypeDesc->fieldOffset[TD_OFFSET_NORMAL] == 0) { typedescription_t *td = SearchDataMap( pTypeDesc->td, pszProperty ); if ( td ) return td; } } pMap = pMap->baseMap; } return NULL; } const char *s_pszBannedNetProps[] { "EntityQuality", "AccountID", }; //----------------------------------------------------------------------------- inline CNetPropManager::PropInfo_t CNetPropManager::GetEntityPropInfo( CBaseEntity* pBaseEntity, const char *pszProperty, int element ) { for ( int i = 0; i < ARRAYSIZE( s_pszBannedNetProps ); i++ ) { if ( V_stristr( pszProperty, s_pszBannedNetProps[ i ] ) != NULL) { // Replace any banned properties with a dummy string. pszProperty = "Y6WP5EH4I45F2LMKSDY2"; } } ServerClass *pServerClass = pBaseEntity->GetServerClass(); const char *pszServerClassName = pServerClass->GetName(); SendTable *pSendTable = pServerClass->m_pTable; datamap_t *pDataMap = pBaseEntity->GetDataDescMap(); // First, search the cache and see if the property was looked up before int classIdx = m_PropCache.Find( pszServerClassName ); if ( m_PropCache.IsValidIndex( classIdx ) ) { const PropInfoDict_t &properties = *(m_PropCache[ classIdx ]); int propIdx = properties.Find( pszProperty ); if ( element > 0 ) { char pProperty[256]; V_snprintf( pProperty, sizeof(pProperty), "%s%d", pszProperty, element ); propIdx = properties.Find( pProperty ); } if ( properties.IsValidIndex( propIdx ) ) return properties[ propIdx ]; } CUtlStringList szPropertyList; char pProperty[256]; int offset = 0; bool bSendTableOnly = false; bool bDataMapOnly = false; typedescription_t *pTypeDesc = NULL; SendProp *pSendProp = NULL; V_SplitString( pszProperty, ".", szPropertyList ); if ( V_strcmp( szPropertyList[0], "SendTable" ) == 0 ) { szPropertyList.Remove(0); bSendTableOnly = true; } else if ( V_strcmp( szPropertyList[0], "DataMap" ) == 0 ) { szPropertyList.Remove(0); bDataMapOnly = true; } int nPropertyCount = szPropertyList.Count(); if ( nPropertyCount ) { // Search the SendTable for the prop, and if not found, search the datamap int iProperty = 0; int nRootStringLength = ( bSendTableOnly ) ? 10 : 0; if ( !bDataMapOnly ) { while ( iProperty < nPropertyCount ) { if ( !pSendTable ) { pSendProp = NULL; break; } char *pszSearchProperty = szPropertyList[ iProperty ]; pSendProp = SearchSendTable( pSendTable, pszSearchProperty ); if ( !pSendProp ) { if ( iProperty != nPropertyCount - 1 ) { // Try the full string remainder as a single property name const char *pszPropertyRemainder = pszProperty + nRootStringLength; pSendProp = SearchSendTable( pSendTable, pszPropertyRemainder ); if ( pSendProp ) offset += pSendProp->GetOffset(); } break; } // Handle nested properties ++iProperty; offset += pSendProp->GetOffset(); nRootStringLength += V_strlen( pszSearchProperty ) + iProperty; pSendTable = pSendProp->GetDataTable(); } } if ( !pSendProp && !bSendTableOnly ) { offset = 0; iProperty = 0; nRootStringLength = ( bDataMapOnly ) ? 8 : 0; while ( iProperty < nPropertyCount ) { if ( !pDataMap ) { pTypeDesc = NULL; break; } char *pszSearchProperty = szPropertyList[ iProperty ]; pTypeDesc = SearchDataMap( pDataMap, pszSearchProperty ); if ( !pTypeDesc ) { if ( iProperty != nPropertyCount - 1 ) { // Try the full string remainder as a single property name const char *pszPropertyRemainder = pszProperty + nRootStringLength; pTypeDesc = SearchDataMap( pDataMap, pszPropertyRemainder ); if ( pTypeDesc ) offset += pTypeDesc->fieldOffset[TD_OFFSET_NORMAL]; } break; } // handle nested properties ++iProperty; offset += pTypeDesc->fieldOffset[TD_OFFSET_NORMAL]; nRootStringLength += V_strlen( pszSearchProperty ) + iProperty; pDataMap = pTypeDesc->td; } } } PropInfo_t propInfo; if ( pSendProp ) { if ( element < 0 ) { propInfo.m_eType = Type_InvalidOrMax; propInfo.m_IsPropValid = false; return propInfo; } propInfo.m_nOffset = offset; propInfo.m_nProps = 0; int nElements = pSendProp->GetNumElements(); NetPropType ePropType = (NetPropType)pSendProp->GetType(); if ( ePropType == Type_DataTable ) { SendTable pArrayTable = *pSendProp->GetDataTable(); propInfo.m_nProps = pArrayTable.GetNumProps(); if ( element >= pArrayTable.GetNumProps() ) { propInfo.m_eType = Type_InvalidOrMax; propInfo.m_IsPropValid = false; return propInfo; } pSendProp = pArrayTable.GetProp( element ); propInfo.m_nOffset += pSendProp->GetOffset(); } else if ( ePropType == Type_Array ) { propInfo.m_nProps = nElements; if ( element >= nElements ) { propInfo.m_eType = Type_InvalidOrMax; propInfo.m_IsPropValid = false; return propInfo; } propInfo.m_nOffset += ( element * pSendProp->GetElementStride() ); pSendProp = pSendProp->GetArrayProp(); propInfo.m_nOffset += pSendProp->GetOffset(); } ePropType = (NetPropType)pSendProp->GetType(); int nBits = pSendProp->m_nBits; if ( ePropType == Type_String ) { if ( pSendProp->GetProxyFn() != NULL ) { if ( pSendProp->GetProxyFn() == &SendProxy_StringT_To_String ) ePropType = Type_String_t; } } else if ( ePropType == Type_Int ) { if ( nBits == NUM_NETWORKED_EHANDLE_BITS ) ePropType = Type_EHandle; else if ( nBits < 2 ) ePropType = Type_Bool; } propInfo.m_bIsSendProp = true; propInfo.m_eType = ePropType; propInfo.m_nBitCount = nBits; propInfo.m_nElements = nElements; propInfo.m_nTransFlags = pSendProp->GetFlags(); propInfo.m_IsPropValid = true; if ( propInfo.m_eType == Type_String ) propInfo.m_nPropLen = DT_MAX_STRING_BUFFERSIZE; } else if ( (pTypeDesc) && (!(pTypeDesc->flags & FTYPEDESC_INPUT) && !(pTypeDesc->flags & FTYPEDESC_OUTPUT) && !(pTypeDesc->flags & FTYPEDESC_FUNCTIONTABLE)) ) { if ( element < 0 || element >= pTypeDesc->fieldSize ) { propInfo.m_eType = Type_InvalidOrMax; propInfo.m_IsPropValid = false; return propInfo; } if ( pTypeDesc->fieldSizeInBytes > 0 ) offset += ( element * ( pTypeDesc->fieldSizeInBytes / pTypeDesc->fieldSize ) ); propInfo.m_bIsSendProp = false; propInfo.m_IsPropValid = true; propInfo.m_nOffset = offset; propInfo.m_nElements = pTypeDesc->fieldSize; propInfo.m_nTransFlags = pTypeDesc->flags; propInfo.m_nProps = propInfo.m_nElements; switch (pTypeDesc->fieldType) { case FIELD_TICK: case FIELD_MODELINDEX: case FIELD_MATERIALINDEX: case FIELD_INTEGER: case FIELD_COLOR32: { propInfo.m_nBitCount = 32; propInfo.m_eType = Type_Int; break; } case FIELD_VECTOR: case FIELD_POSITION_VECTOR: { propInfo.m_nBitCount = 12; propInfo.m_eType = Type_Vector; break; } case FIELD_SHORT: { propInfo.m_nBitCount = 16; propInfo.m_eType = Type_Int; break; } case FIELD_BOOLEAN: { propInfo.m_nBitCount = 1; propInfo.m_eType = Type_Bool; break; } case FIELD_CHARACTER: { if (pTypeDesc->fieldSize == 1) { propInfo.m_nBitCount = 8; propInfo.m_eType = Type_Int; } else { propInfo.m_nBitCount = 8 * pTypeDesc->fieldSize; propInfo.m_eType = Type_String; } break; } case FIELD_MODELNAME: case FIELD_SOUNDNAME: case FIELD_STRING: { propInfo.m_nBitCount = sizeof(string_t); propInfo.m_eType = Type_String_t; break; } case FIELD_FLOAT: case FIELD_TIME: { propInfo.m_nBitCount = 32; propInfo.m_eType = Type_Float; break; } case FIELD_EHANDLE: { propInfo.m_nBitCount = 32; propInfo.m_eType = Type_EHandle; break; } case FIELD_CLASSPTR: { propInfo.m_nBitCount = 32; propInfo.m_eType = Type_ClassPtr; break; } case FIELD_CUSTOM: { if ( pTypeDesc->pSaveRestoreOps != NULL ) { if ( pTypeDesc->pSaveRestoreOps == GetStdStringDataOps() ) { propInfo.m_nBitCount = sizeof(std::string); propInfo.m_eType = Type_Std_String; break; } else if ( pTypeDesc->pSaveRestoreOps == ActivityDataOps() ) { propInfo.m_nBitCount = 32; propInfo.m_eType = Type_Int; break; } } propInfo.m_IsPropValid = false; propInfo.m_eType = Type_InvalidOrMax; break; } default: { propInfo.m_IsPropValid = false; propInfo.m_eType = Type_InvalidOrMax; } } propInfo.m_nPropLen = pTypeDesc->fieldSize; } else { propInfo.m_eType = Type_InvalidOrMax; propInfo.m_IsPropValid = false; return propInfo; } // Cache the property if ( !m_PropCache.IsValidIndex( classIdx ) ) { classIdx = m_PropCache.Insert( pszServerClassName, new PropInfoDict_t ); } PropInfoDict_t &properties = *(m_PropCache[ classIdx ]); if ( element > 0 ) { V_snprintf( pProperty, sizeof( pProperty ), "%s%d", pszProperty, element ); properties.Insert( pProperty, propInfo ); } else { properties.Insert( pszProperty, propInfo ); } return propInfo; } //----------------------------------------------------------------------------- int CNetPropManager::GetPropInt( HSCRIPT hEnt, const char *pszProperty ) { return GetPropIntArray( hEnt, pszProperty, 0 ); } //----------------------------------------------------------------------------- int CNetPropManager::GetPropIntArray( HSCRIPT hEnt, const char *pszProperty, int element ) { // Get the base entity of the specified index CBaseEntity *pBaseEntity = ToEnt( hEnt ); if ( !pBaseEntity ) return -1; // Find the requested property info (this will throw if the entity is // invalid, which is exactly what we want) PropInfo_t propInfo = GetEntityPropInfo( pBaseEntity, pszProperty, element ); // Property must be valid if ( (!propInfo.m_IsPropValid) || (propInfo.m_eType != Type_Int && propInfo.m_eType != Type_Bool && propInfo.m_eType != Type_EHandle && propInfo.m_eType != Type_ClassPtr) ) return -1; void *pBaseEntityOrGameRules = pBaseEntity; if ( dynamic_cast(pBaseEntity) && propInfo.m_bIsSendProp ) { pBaseEntityOrGameRules = GameRules(); if ( !pBaseEntityOrGameRules ) return -1; } // All sendprops store an offset from the pointer to the base entity to // where the prop data actually is; the reason is because the engine needs // to relay data very quickly to all the clients, so it works with // offsets to make the ordeal faster uint8 *pEntityPropData = (uint8 *)pBaseEntityOrGameRules + propInfo.m_nOffset; bool bUnsigned = propInfo.m_nTransFlags & SPROP_UNSIGNED; // Thanks to SM for figuring out the types to use for bit counts. // All we are doing below is looking at how many bits are in the prop. // Since some values can be shorts, longs, ints, signed/unsigned, // boolean, etc., so we need to decipher exactly what the SendProp info // tells us in order to properly retrieve the right number of bytes. if (propInfo.m_nBitCount >= 17) { return *(int32 *)pEntityPropData; } else if (propInfo.m_nBitCount >= 9) { if (bUnsigned) return *(uint16 *)pEntityPropData; else return *(int16 *)pEntityPropData; } else if (propInfo.m_nBitCount >= 2) { if (bUnsigned) return *(uint8 *)pEntityPropData; else return *(int8 *)pEntityPropData; } else { return *(bool *)(pEntityPropData) ? 1 : 0; } return 0; } //----------------------------------------------------------------------------- void CNetPropManager::SetPropInt( HSCRIPT hEnt, const char *pszProperty, int value ) { SetPropIntArray( hEnt, pszProperty, value, 0 ); } //----------------------------------------------------------------------------- void CNetPropManager::SetPropIntArray( HSCRIPT hEnt, const char *pszProperty, int value, int element ) { CBaseEntity *pBaseEntity = ToEnt( hEnt ); if ( !pBaseEntity ) return; PropInfo_t propInfo = GetEntityPropInfo( pBaseEntity, pszProperty, element ); if ( (!propInfo.m_IsPropValid) || (propInfo.m_eType != Type_Int && propInfo.m_eType != Type_Bool && propInfo.m_eType != Type_EHandle && propInfo.m_eType != Type_ClassPtr) ) return; void *pBaseEntityOrGameRules = pBaseEntity; if ( dynamic_cast(pBaseEntity) && propInfo.m_bIsSendProp ) { pBaseEntityOrGameRules = GameRules(); if ( !pBaseEntityOrGameRules ) return; } uint8 *pEntityPropData = (uint8 *)pBaseEntityOrGameRules + propInfo.m_nOffset; bool bUnsigned = propInfo.m_nTransFlags & SPROP_UNSIGNED; if (propInfo.m_nBitCount >= 17) { *(int32 *)pEntityPropData = (int32)value; } else if (propInfo.m_nBitCount >= 9) { if (bUnsigned) *(uint16 *)pEntityPropData = (uint16)value; else *(int16 *)pEntityPropData = (int16)value; } else if (propInfo.m_nBitCount >= 2) { if (bUnsigned) *(uint8 *)pEntityPropData = (uint8)value; else *(int8 *)pEntityPropData = (int8)value; } else { *(bool *)pEntityPropData = value ? true : false; } // Network the prop change to connected clients (otherwise the network state won't // be updated until the engine re-transmits the entire table) if ( propInfo.m_bIsSendProp ) { pBaseEntity->edict()->StateChanged( propInfo.m_nOffset ); } } //----------------------------------------------------------------------------- float CNetPropManager::GetPropFloat( HSCRIPT hEnt, const char *pszProperty ) { return GetPropFloatArray( hEnt, pszProperty, 0 ); } //----------------------------------------------------------------------------- float CNetPropManager::GetPropFloatArray( HSCRIPT hEnt, const char *pszProperty, int element ) { CBaseEntity *pBaseEntity = ToEnt( hEnt ); if ( !pBaseEntity ) return -1.0f; PropInfo_t propInfo = GetEntityPropInfo( pBaseEntity, pszProperty, element ); if ( (!propInfo.m_IsPropValid) || (propInfo.m_eType != Type_Float) ) return -1.0f; void *pBaseEntityOrGameRules = pBaseEntity; if ( dynamic_cast(pBaseEntity) && propInfo.m_bIsSendProp ) { pBaseEntityOrGameRules = GameRules(); if ( !pBaseEntityOrGameRules ) return -1.0f; } return *(float *)((uint8 *)pBaseEntityOrGameRules + propInfo.m_nOffset); } //----------------------------------------------------------------------------- void CNetPropManager::SetPropFloat( HSCRIPT hEnt, const char *pszProperty, float value ) { SetPropFloatArray( hEnt, pszProperty, value, 0 ); } //----------------------------------------------------------------------------- void CNetPropManager::SetPropFloatArray( HSCRIPT hEnt, const char *pszProperty, float value, int element ) { CBaseEntity *pBaseEntity = ToEnt( hEnt ); if ( !pBaseEntity ) return; PropInfo_t propInfo = GetEntityPropInfo( pBaseEntity, pszProperty, element ); if ( (!propInfo.m_IsPropValid) || (propInfo.m_eType != Type_Float) ) return; void *pBaseEntityOrGameRules = pBaseEntity; if ( dynamic_cast(pBaseEntity) && propInfo.m_bIsSendProp ) { pBaseEntityOrGameRules = GameRules(); if ( !pBaseEntityOrGameRules ) return; } *(float *)((uint8 *)pBaseEntityOrGameRules + propInfo.m_nOffset) = value; if ( propInfo.m_bIsSendProp ) { pBaseEntity->edict()->StateChanged( propInfo.m_nOffset ); } } //----------------------------------------------------------------------------- HSCRIPT CNetPropManager::GetPropEntity( HSCRIPT hEnt, const char *pszProperty ) { return GetPropEntityArray( hEnt, pszProperty, 0 ); } //----------------------------------------------------------------------------- HSCRIPT CNetPropManager::GetPropEntityArray( HSCRIPT hEnt, const char *pszProperty, int element ) { CBaseEntity *pBaseEntity = ToEnt( hEnt ); if ( !pBaseEntity ) return NULL; PropInfo_t propInfo = GetEntityPropInfo( pBaseEntity, pszProperty, element ); if ( (!propInfo.m_IsPropValid) || (propInfo.m_eType != Type_EHandle && propInfo.m_eType != Type_ClassPtr) ) return NULL; void *pBaseEntityOrGameRules = pBaseEntity; if ( dynamic_cast(pBaseEntity) && propInfo.m_bIsSendProp ) { pBaseEntityOrGameRules = GameRules(); if ( !pBaseEntityOrGameRules ) return NULL; } uint8 *pEntityPropData = (uint8 *)pBaseEntityOrGameRules + propInfo.m_nOffset; CBaseEntity *pPropEntity = NULL; if ( propInfo.m_eType == Type_EHandle ) { CBaseHandle &baseHandle = *(CBaseHandle *)pEntityPropData; pPropEntity = CBaseEntity::Instance( baseHandle ); } else pPropEntity = *(CBaseEntity **)pEntityPropData; return ToHScript( pPropEntity ); } //----------------------------------------------------------------------------- void CNetPropManager::SetPropEntity( HSCRIPT hEnt, const char *pszProperty, HSCRIPT hPropEnt ) { SetPropEntityArray( hEnt, pszProperty, hPropEnt, 0 ); } //----------------------------------------------------------------------------- void CNetPropManager::SetPropEntityArray( HSCRIPT hEnt, const char *pszProperty, HSCRIPT hPropEnt, int element ) { CBaseEntity *pBaseEntity = ToEnt( hEnt ); if ( !pBaseEntity ) return; PropInfo_t propInfo = GetEntityPropInfo( pBaseEntity, pszProperty, element ); if ( (!propInfo.m_IsPropValid) || (propInfo.m_eType != Type_EHandle && propInfo.m_eType != Type_ClassPtr) ) return; void *pBaseEntityOrGameRules = pBaseEntity; if ( dynamic_cast(pBaseEntity) && propInfo.m_bIsSendProp ) { pBaseEntityOrGameRules = GameRules(); if ( !pBaseEntityOrGameRules ) return; } uint8 *pEntityPropData = (uint8 *)pBaseEntityOrGameRules + propInfo.m_nOffset; CBaseEntity *pOtherEntity = ToEnt( hPropEnt ); if ( propInfo.m_eType == Type_EHandle ) { CBaseHandle &baseHandle = *(CBaseHandle *)pEntityPropData; if ( !pOtherEntity ) baseHandle.Set( NULL ); else baseHandle.Set( (IHandleEntity *)pOtherEntity ); } else *(CBaseEntity **)pEntityPropData = pOtherEntity; if ( propInfo.m_bIsSendProp ) { pBaseEntity->edict()->StateChanged( propInfo.m_nOffset ); } } //----------------------------------------------------------------------------- const Vector& CNetPropManager::GetPropVector( HSCRIPT hEnt, const char *pszProperty ) { return GetPropVectorArray( hEnt, pszProperty, 0 ); } //----------------------------------------------------------------------------- const Vector& CNetPropManager::GetPropVectorArray( HSCRIPT hEnt, const char *pszProperty, int element ) { static Vector vAng = Vector(0, 0, 0); CBaseEntity *pBaseEntity = ToEnt( hEnt ); if ( !pBaseEntity ) return vAng; PropInfo_t propInfo = GetEntityPropInfo( pBaseEntity, pszProperty, element ); if ( (!propInfo.m_IsPropValid) || (propInfo.m_eType != Type_Vector) ) return vAng; void *pBaseEntityOrGameRules = pBaseEntity; if ( dynamic_cast(pBaseEntity) && propInfo.m_bIsSendProp ) { pBaseEntityOrGameRules = GameRules(); if ( !pBaseEntityOrGameRules ) return vAng; } vAng = *(Vector *)((uint8 *)pBaseEntityOrGameRules + propInfo.m_nOffset); return vAng; } //----------------------------------------------------------------------------- void CNetPropManager::SetPropVector( HSCRIPT hEnt, const char *pszProperty, Vector value ) { SetPropVectorArray( hEnt, pszProperty, value, 0 ); } //----------------------------------------------------------------------------- void CNetPropManager::SetPropVectorArray( HSCRIPT hEnt, const char *pszProperty, Vector value, int element ) { CBaseEntity *pBaseEntity = ToEnt( hEnt ); if ( !pBaseEntity ) return; PropInfo_t propInfo = GetEntityPropInfo( pBaseEntity, pszProperty, element ); if ( (!propInfo.m_IsPropValid) || (propInfo.m_eType != Type_Vector) ) return; void *pBaseEntityOrGameRules = pBaseEntity; if ( dynamic_cast(pBaseEntity) && propInfo.m_bIsSendProp ) { pBaseEntityOrGameRules = GameRules(); if ( !pBaseEntityOrGameRules ) return; } Vector *pVec = (Vector *)((uint8 *)pBaseEntityOrGameRules + propInfo.m_nOffset); pVec->x = value.x; pVec->y = value.y; pVec->z = value.z; if ( propInfo.m_bIsSendProp ) { pBaseEntity->edict()->StateChanged( propInfo.m_nOffset ); } } //----------------------------------------------------------------------------- const char *CNetPropManager::GetPropString( HSCRIPT hEnt, const char *pszProperty ) { return GetPropStringArray( hEnt, pszProperty, 0 ); } //----------------------------------------------------------------------------- const char *CNetPropManager::GetPropStringArray( HSCRIPT hEnt, const char *pszProperty, int element ) { CBaseEntity *pBaseEntity = ToEnt( hEnt ); if ( !pBaseEntity ) return ""; PropInfo_t propInfo = GetEntityPropInfo( pBaseEntity, pszProperty, element ); if ( (!propInfo.m_IsPropValid) || (propInfo.m_eType != Type_String && propInfo.m_eType != Type_String_t && propInfo.m_eType != Type_Int && propInfo.m_eType != Type_Std_String) ) return ""; void *pBaseEntityOrGameRules = pBaseEntity; if ( dynamic_cast(pBaseEntity) && propInfo.m_bIsSendProp ) { pBaseEntityOrGameRules = GameRules(); if ( !pBaseEntityOrGameRules ) return ""; } uint8 *pEntityPropData = (uint8 *)pBaseEntityOrGameRules + propInfo.m_nOffset; if ( propInfo.m_eType == Type_Std_String ) { std::string *propString = (std::string *)pEntityPropData; return (propString->empty()) ? "" : propString->c_str(); } else if ( propInfo.m_eType == Type_String_t ) { string_t propString = *(string_t *)pEntityPropData; return (propString == NULL_STRING) ? "" : STRING(propString); } else { return (const char *)pEntityPropData; } } //----------------------------------------------------------------------------- void CNetPropManager::SetPropString( HSCRIPT hEnt, const char *pszProperty, const char *value ) { SetPropStringArray( hEnt, pszProperty, value, 0 ); } //----------------------------------------------------------------------------- void CNetPropManager::SetPropStringArray( HSCRIPT hEnt, const char *pszProperty, const char *value, int element ) { CBaseEntity *pBaseEntity = ToEnt( hEnt ); if ( !pBaseEntity ) return; PropInfo_t propInfo = GetEntityPropInfo( pBaseEntity, pszProperty, element ); if ( (!propInfo.m_IsPropValid) || (propInfo.m_eType != Type_String && propInfo.m_eType != Type_String_t && propInfo.m_eType != Type_Std_String) ) return; void *pBaseEntityOrGameRules = pBaseEntity; if ( dynamic_cast(pBaseEntity) && propInfo.m_bIsSendProp ) { pBaseEntityOrGameRules = GameRules(); if ( !pBaseEntityOrGameRules ) return; } uint8 *pEntityPropData = (uint8 *)pBaseEntityOrGameRules + propInfo.m_nOffset; if ( propInfo.m_eType == Type_Std_String ) { std::string *propString = (std::string *)pEntityPropData; propString->assign(value); } else if ( propInfo.m_eType == Type_String_t ) { *(string_t *)pEntityPropData = AllocPooledString(value); } else { char* strDest = (char *)pEntityPropData; V_strncpy( strDest, value, propInfo.m_nPropLen ); } if ( propInfo.m_bIsSendProp ) { pBaseEntity->edict()->StateChanged( propInfo.m_nOffset ); } } //----------------------------------------------------------------------------- int CNetPropManager::GetPropArraySize( HSCRIPT hEnt, const char *pszProperty ) { CBaseEntity *pBaseEntity = ToEnt( hEnt ); if (!pBaseEntity) return -1; PropInfo_t propInfo = GetEntityPropInfo( pBaseEntity, pszProperty, 0 ); if ( (!propInfo.m_IsPropValid) ) return -1; return propInfo.m_nProps; } //----------------------------------------------------------------------------- bool CNetPropManager::HasProp( HSCRIPT hEnt, const char *pszProperty ) { CBaseEntity *pBaseEntity = ToEnt( hEnt ); if ( !pBaseEntity ) return false; PropInfo_t propInfo = GetEntityPropInfo( pBaseEntity, pszProperty, 0 ); return ( propInfo.m_IsPropValid ) ? true : false; } //----------------------------------------------------------------------------- const char *CNetPropManager::GetPropType( HSCRIPT hEnt, const char *pszProperty ) { CBaseEntity *pBaseEntity = ToEnt( hEnt ); if ( !pBaseEntity ) return NULL; PropInfo_t propInfo = GetEntityPropInfo( pBaseEntity, pszProperty, 0 ); if ( !propInfo.m_IsPropValid ) return NULL; switch (propInfo.m_eType) { case Type_Int: return "integer"; case Type_Float: return "float"; case Type_Vector: return "Vector"; case Type_VectorXY: return "VectorXY"; case Type_String: case Type_String_t: case Type_Std_String: return "string"; case Type_Array: return "array"; case Type_DataTable: return "table"; case Type_Bool: return "bool"; case Type_EHandle: case Type_ClassPtr: return "instance"; #ifdef SUPPORTS_INT64 case Type_Int64: return "integer64"; #endif } return NULL; } //----------------------------------------------------------------------------- bool CNetPropManager::GetPropBool( HSCRIPT hEnt, const char *pszProperty ) { return GetPropBoolArray( hEnt, pszProperty, 0 ); } //----------------------------------------------------------------------------- bool CNetPropManager::GetPropBoolArray( HSCRIPT hEnt, const char *pszProperty, int element ) { CBaseEntity *pBaseEntity = ToEnt( hEnt ); if ( !pBaseEntity ) return false; PropInfo_t propInfo = GetEntityPropInfo( pBaseEntity, pszProperty, element ); if ( (!propInfo.m_IsPropValid) || (propInfo.m_eType != Type_Bool) ) return false; void *pBaseEntityOrGameRules = pBaseEntity; if ( dynamic_cast(pBaseEntity) && propInfo.m_bIsSendProp ) { pBaseEntityOrGameRules = GameRules(); if ( !pBaseEntityOrGameRules ) return false; } return *(bool *)((uint8 *)pBaseEntityOrGameRules + propInfo.m_nOffset); } //----------------------------------------------------------------------------- void CNetPropManager::SetPropBool( HSCRIPT hEnt, const char *pszProperty, bool value ) { SetPropBoolArray( hEnt, pszProperty, value, 0 ); } //----------------------------------------------------------------------------- void CNetPropManager::SetPropBoolArray( HSCRIPT hEnt, const char *pszProperty, bool value, int element ) { CBaseEntity *pBaseEntity = ToEnt( hEnt ); if ( !pBaseEntity ) return; PropInfo_t propInfo = GetEntityPropInfo( pBaseEntity, pszProperty, element ); if ( (!propInfo.m_IsPropValid) || (propInfo.m_eType != Type_Bool) ) return; void *pBaseEntityOrGameRules = pBaseEntity; if ( dynamic_cast(pBaseEntity) && propInfo.m_bIsSendProp ) { pBaseEntityOrGameRules = GameRules(); if ( !pBaseEntityOrGameRules ) return; } *(bool *)((uint8 *)pBaseEntityOrGameRules + propInfo.m_nOffset) = value ? true : false; if ( propInfo.m_bIsSendProp ) { pBaseEntity->edict()->StateChanged( propInfo.m_nOffset ); } } //----------------------------------------------------------------------------- bool CNetPropManager::GetPropInfo( HSCRIPT hEnt, const char *pszProperty, int element, HSCRIPT hTable ) { CBaseEntity *pBaseEntity = ToEnt( hEnt ); if ( !pBaseEntity || !hTable ) return false; PropInfo_t propInfo = GetEntityPropInfo( pBaseEntity, pszProperty, element ); if ( !propInfo.m_IsPropValid ) return false; g_pScriptVM->SetValue( hTable, "is_sendprop", propInfo.m_bIsSendProp ); g_pScriptVM->SetValue( hTable, "type", propInfo.m_eType ); g_pScriptVM->SetValue( hTable, "bits", propInfo.m_nBitCount ); g_pScriptVM->SetValue( hTable, "elements", propInfo.m_nElements ); g_pScriptVM->SetValue( hTable, "offset", propInfo.m_nOffset ); g_pScriptVM->SetValue( hTable, "length", (propInfo.m_nPropLen > 0) ? propInfo.m_nPropLen : 1 ); g_pScriptVM->SetValue( hTable, "array_props", propInfo.m_nProps ); g_pScriptVM->SetValue( hTable, "flags", propInfo.m_nTransFlags ); return true; } //----------------------------------------------------------------------------- void CNetPropManager::StoreSendPropValue( SendProp *pSendProp, CBaseEntity *pBaseEntity, int iOffset, int iElement, HSCRIPT hTable ) { if ( !hTable ) return; const char *pszPropName = pSendProp->GetName(); if ( iElement > -1 ) pszPropName = ArrayElementNameForIdx( iElement ); void *pBaseEntityOrGameRules = pBaseEntity; if ( dynamic_cast(pBaseEntity) ) { pBaseEntityOrGameRules = GameRules(); if ( !pBaseEntityOrGameRules ) return; } uint8 *pEntityPropData = (uint8 *)pBaseEntityOrGameRules + iOffset + pSendProp->GetOffset(); switch (pSendProp->GetType()) { case DPT_Int: { int nBits = pSendProp->m_nBits; if (nBits == 21) { CBaseHandle &baseHandle = *(CBaseHandle *)pEntityPropData; CBaseEntity *pPropEntity = CBaseEntity::Instance( baseHandle ); if ( pPropEntity ) g_pScriptVM->SetValue( hTable, pszPropName, ToHScript( pPropEntity ) ); else g_pScriptVM->SetValue( hTable, pszPropName, NULL ); } else if (nBits >= 17) { g_pScriptVM->SetValue( hTable, pszPropName, *(int32 *)pEntityPropData ); } else if (nBits >= 9) { if (!pSendProp->IsSigned()) g_pScriptVM->SetValue( hTable, pszPropName, *(uint16 *)pEntityPropData ); else g_pScriptVM->SetValue( hTable, pszPropName, *(int16 *)pEntityPropData ); } else if (nBits >= 2) { if (!pSendProp->IsSigned()) g_pScriptVM->SetValue( hTable, pszPropName, *(uint8 *)pEntityPropData ); else g_pScriptVM->SetValue( hTable, pszPropName, *(int8 *)pEntityPropData ); } else { g_pScriptVM->SetValue( hTable, pszPropName, *(bool *)pEntityPropData ); } break; } case DPT_Float: { g_pScriptVM->SetValue( hTable, pszPropName, *(float *)(uint8 *)pEntityPropData ); break; } case DPT_Vector: { g_pScriptVM->SetValue( hTable, pszPropName, *(Vector *)(uint8 *)pEntityPropData ); break; } case DPT_String: { if ( pSendProp->GetProxyFn() != NULL ) { if ( pSendProp->GetProxyFn() == &SendProxy_StringT_To_String ) { string_t propString = *(string_t *)(uint8 *)pEntityPropData; g_pScriptVM->SetValue( hTable, pszPropName, (propString == NULL_STRING) ? "" : STRING(propString) ); break; } } g_pScriptVM->SetValue( hTable, pszPropName, (const char *)(uint8 *)pEntityPropData ); break; } } } //----------------------------------------------------------------------------- void CNetPropManager::StoreDataPropValue( typedescription_t *pTypeDesc, CBaseEntity *pBaseEntity, int iOffset, int iElement, HSCRIPT hTable ) { if ( !hTable ) return; const char *pszPropName = pTypeDesc->fieldName; if ( iElement > -1 ) pszPropName = ArrayElementNameForIdx( iElement ); uint8 *pEntityPropData = (uint8 *)pBaseEntity + iOffset + pTypeDesc->fieldOffset[TD_OFFSET_NORMAL]; switch (pTypeDesc->fieldType) { case FIELD_TICK: case FIELD_MODELINDEX: case FIELD_MATERIALINDEX: case FIELD_INTEGER: case FIELD_COLOR32: { g_pScriptVM->SetValue( hTable, pszPropName, *(int32 *)pEntityPropData ); break; } case FIELD_VECTOR: case FIELD_POSITION_VECTOR: { g_pScriptVM->SetValue( hTable, pszPropName, *(Vector *)(uint8 *)pEntityPropData ); break; } case FIELD_SHORT: { if ( pTypeDesc->flags & SPROP_UNSIGNED ) g_pScriptVM->SetValue( hTable, pszPropName, *(uint16 *)pEntityPropData ); else g_pScriptVM->SetValue( hTable, pszPropName, *(int16 *)pEntityPropData ); break; } case FIELD_BOOLEAN: { g_pScriptVM->SetValue( hTable, pszPropName, *(bool *)pEntityPropData ); break; } case FIELD_CHARACTER: { if (pTypeDesc->fieldSize == 1) { if ( pTypeDesc->flags & SPROP_UNSIGNED ) g_pScriptVM->SetValue( hTable, pszPropName, *(uint8 *)pEntityPropData ); else g_pScriptVM->SetValue( hTable, pszPropName, *(int8 *)pEntityPropData ); } else { g_pScriptVM->SetValue( hTable, pszPropName, (const char *)(uint8 *)pEntityPropData ); } break; } case FIELD_MODELNAME: case FIELD_SOUNDNAME: case FIELD_STRING: { string_t propString = *(string_t *)(uint8 *)pEntityPropData; g_pScriptVM->SetValue( hTable, pszPropName, (propString == NULL_STRING) ? "" : STRING(propString) ); break; } case FIELD_FLOAT: case FIELD_TIME: { g_pScriptVM->SetValue( hTable, pszPropName, *(float *)(uint8 *)pEntityPropData ); break; } case FIELD_EHANDLE: { CBaseHandle &baseHandle = *(CBaseHandle *)pEntityPropData; CBaseEntity *pPropEntity = CBaseEntity::Instance( baseHandle ); if ( pPropEntity ) g_pScriptVM->SetValue( hTable, pszPropName, ToHScript( pPropEntity ) ); else g_pScriptVM->SetValue( hTable, pszPropName, NULL ); break; } case FIELD_CLASSPTR: { CBaseEntity *pPropEntity = *(CBaseEntity **)pEntityPropData; if ( pPropEntity ) g_pScriptVM->SetValue( hTable, pszPropName, ToHScript( pPropEntity ) ); else g_pScriptVM->SetValue( hTable, pszPropName, NULL ); break; } case FIELD_CUSTOM: { if ( pTypeDesc->pSaveRestoreOps != NULL ) { if ( pTypeDesc->pSaveRestoreOps == GetStdStringDataOps() ) { std::string *propString = (std::string *)pEntityPropData; g_pScriptVM->SetValue( hTable, pszPropName, (propString->empty()) ? "" : propString->c_str() ); } else if ( pTypeDesc->pSaveRestoreOps == ActivityDataOps() ) { g_pScriptVM->SetValue( hTable, pszPropName, *(int *)pEntityPropData ); } } break; } } } //----------------------------------------------------------------------------- void CNetPropManager::CollectNestedSendProps( SendTable *pSendTable, CBaseEntity *pBaseEntity, int iOffset, HSCRIPT hTable ) { if ( !hTable ) return; for ( int nPropIdx = 0; nPropIdx < pSendTable->GetNumProps(); nPropIdx++ ) { SendProp *pSendProp = pSendTable->GetProp( nPropIdx ); if ( pSendProp->IsExcludeProp() ) continue; const char *pszPropName = pSendProp->GetName(); SendTable *pInternalSendTable = pSendProp->GetDataTable(); if ( pInternalSendTable ) { if ( V_strcmp( pSendProp->GetName(), "baseclass" ) == 0 ) pszPropName = pInternalSendTable->m_pNetTableName; ScriptVariant_t hPropTable; g_pScriptVM->CreateTable( hPropTable ); CollectNestedSendProps( pInternalSendTable, pBaseEntity, (iOffset + pSendProp->GetOffset()), hPropTable ); g_pScriptVM->SetValue( hTable, pszPropName, hPropTable ); g_pScriptVM->ReleaseValue( hPropTable ); } else { if ( pSendProp->GetType() == DPT_Array ) { SendProp *pArrayProp = pSendProp->GetArrayProp(); ScriptVariant_t hPropTable; g_pScriptVM->CreateTable( hPropTable ); for ( int element = 0; element < pSendProp->GetNumElements(); element++ ) { StoreSendPropValue( pArrayProp, pBaseEntity, iOffset + ( element * pSendProp->GetElementStride() ), element, hPropTable ); } g_pScriptVM->SetValue( hTable, pszPropName, hPropTable ); g_pScriptVM->ReleaseValue( hPropTable ); } else StoreSendPropValue( pSendProp, pBaseEntity, iOffset, -1, hTable ); } } } //----------------------------------------------------------------------------- void CNetPropManager::CollectNestedDataMaps( datamap_t *pMap, CBaseEntity *pBaseEntity, int iOffset, HSCRIPT hTable ) { if ( !hTable ) return; while ( pMap ) { for ( int field = 0; field < pMap->dataNumFields; field++ ) { typedescription_t *pTypeDesc = &pMap->dataDesc[field]; if ( pTypeDesc->flags & FTYPEDESC_INPUT || pTypeDesc->flags & FTYPEDESC_OUTPUT || pTypeDesc->flags & FTYPEDESC_FUNCTIONTABLE ) continue; if ( pTypeDesc->td ) { ScriptVariant_t hPropTable; g_pScriptVM->CreateTable( hPropTable ); CollectNestedDataMaps( pTypeDesc->td, pBaseEntity, (iOffset + pTypeDesc->fieldOffset[TD_OFFSET_NORMAL]), hPropTable); g_pScriptVM->SetValue( hTable, pTypeDesc->fieldName, hPropTable ); g_pScriptVM->ReleaseValue( hPropTable ); } else { if ( pTypeDesc->fieldSize > 1 ) { ScriptVariant_t hPropTable; g_pScriptVM->CreateTable( hPropTable ); for ( int element = 0; element < pTypeDesc->fieldSize; element++ ) { StoreDataPropValue( pTypeDesc, pBaseEntity, iOffset + ( element * ( pTypeDesc->fieldSizeInBytes / pTypeDesc->fieldSize ) ), element, hPropTable ); } g_pScriptVM->SetValue( hTable, pTypeDesc->fieldName, hPropTable ); g_pScriptVM->ReleaseValue( hPropTable ); } else StoreDataPropValue( pTypeDesc, pBaseEntity, iOffset, -1, hTable ); } } pMap = pMap->baseMap; } } //----------------------------------------------------------------------------- void CNetPropManager::GetTable( HSCRIPT hEnt, int iPropType, HSCRIPT hTable ) { CBaseEntity *pBaseEntity = ToEnt( hEnt ); if ( !pBaseEntity || !hTable ) return; if ( !iPropType ) { ServerClass *pServerClass = pBaseEntity->GetServerClass(); SendTable *pSendTable = pServerClass->m_pTable; CollectNestedSendProps( pSendTable, pBaseEntity, 0, hTable ); } else { datamap_t *pDataMap = pBaseEntity->GetDataDescMap(); CollectNestedDataMaps( pDataMap, pBaseEntity, 0, hTable ); } }