From 0417fd998286d2a5b5055b687f8fc2eb731e6a78 Mon Sep 17 00:00:00 2001 From: samisalreadytaken <46823719+samisalreadytaken@users.noreply.github.com> Date: Wed, 27 May 2026 16:42:11 +0300 Subject: [PATCH 1/8] Warn on missing NetProp fields --- sp/src/game/shared/mapbase/vscript_singletons.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/sp/src/game/shared/mapbase/vscript_singletons.cpp b/sp/src/game/shared/mapbase/vscript_singletons.cpp index 81459ebfcf8..c21ad31e626 100644 --- a/sp/src/game/shared/mapbase/vscript_singletons.cpp +++ b/sp/src/game/shared/mapbase/vscript_singletons.cpp @@ -458,7 +458,7 @@ class CScriptNetPropManager // 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 ) + varinfo_t *GetVarInfo( CBaseEntity *pEnt, const char *szProp, int index, bool bDontWarnOnMissing = false ) { int offset = 0; NetTable *pTable = GetNetTable( GetNetworkClass( pEnt ) ); @@ -956,6 +956,10 @@ class CScriptNetPropManager } } #endif + + if ( !bDontWarnOnMissing ) + Warning( "NetProp field (%s)->'%s' does not exist!\n", pEnt->GetClassname(), szProp ); + return NULL; } @@ -970,7 +974,7 @@ class CScriptNetPropManager varinfo_t *pInfo = CacheFetch( pEnt, szProp ); if ( !pInfo ) { - pInfo = GetVarInfo( pEnt, szProp, INDEX_GET_TYPE ); + pInfo = GetVarInfo( pEnt, szProp, INDEX_GET_TYPE, true ); if ( !pInfo ) return false; From c8a330a892760f01afdc7db158e7d24e8c1684a8 Mon Sep 17 00:00:00 2001 From: samisalreadytaken <46823719+samisalreadytaken@users.noreply.github.com> Date: Wed, 27 May 2026 16:44:27 +0300 Subject: [PATCH 2/8] Convert IS_EHANDLE_UTLVECTOR macro to function --- .../shared/mapbase/vscript_singletons.cpp | 38 +++++++++++++------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/sp/src/game/shared/mapbase/vscript_singletons.cpp b/sp/src/game/shared/mapbase/vscript_singletons.cpp index c21ad31e626..31efbd0675a 100644 --- a/sp/src/game/shared/mapbase/vscript_singletons.cpp +++ b/sp/src/game/shared/mapbase/vscript_singletons.cpp @@ -74,22 +74,36 @@ 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; + + bool IS_EHANDLE_UTLVECTOR( const typedescription_t *td ) + { + // Different entity handles are compiled as unique types, thus return unique SaveRestoreOps + return ( td->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_EHANDLE, CHandle< CBaseEntity > ) || + // CSceneEntity::m_hActorList + td->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_EHANDLE, CHandle< CBaseFlex > ) || + // CTriggerSoundscape::m_spectators + td->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_EHANDLE, CHandle< CBasePlayer > ) || + // CAI_GoalEntity::m_actors + td->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_EHANDLE, CHandle< CAI_BaseNPC > ) || + // CAntlionTemplateMaker::m_Children + //td->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_EHANDLE, CHandle< CNPC_Antlion > ) || + // CPointRagdollBoogie::m_Boogies + td->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_EHANDLE, CHandle< CRagdollBoogie > ) || + // CFishPool::m_fishes + td->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_EHANDLE, CHandle< CFish > ) || + // CBaseViewModel::m_hScreens + td->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_EHANDLE, CHandle< CVGuiScreen > ) || + // CSceneManager::{m_ActiveScenes, m_hNotifySceneCompletion} + td->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_EHANDLE, CHandle< CSceneEntity > ) || + // CSceneManager::m_hListManagers, CSceneListManager::m_hListManagers + td->pSaveRestoreOps == UTLVECTOR_DATAOPS( FIELD_EHANDLE, CHandle< CSceneListManager > ) + ); + } + #ifdef _DEBUG class CStringTableSaveRestoreOps; extern CStringTableSaveRestoreOps g_VguiScreenStringOps; From 69291e06ee809b4ed76f9868549c564d920f4369 Mon Sep 17 00:00:00 2001 From: samisalreadytaken <46823719+samisalreadytaken@users.noreply.github.com> Date: Wed, 27 May 2026 16:49:54 +0300 Subject: [PATCH 3/8] Fix clientside color32 NetProp fields --- sp/src/game/shared/mapbase/vscript_singletons.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sp/src/game/shared/mapbase/vscript_singletons.cpp b/sp/src/game/shared/mapbase/vscript_singletons.cpp index 31efbd0675a..7f4343ed9fc 100644 --- a/sp/src/game/shared/mapbase/vscript_singletons.cpp +++ b/sp/src/game/shared/mapbase/vscript_singletons.cpp @@ -174,7 +174,7 @@ class CScriptNetPropManager return 8; if ( proxy == RecvProxy_Int32ToInt16 ) return 16; - if ( proxy == RecvProxy_Int32ToInt32 ) + if ( proxy == RecvProxy_Int32ToInt32 || proxy == RecvProxy_IntToColor32 ) return 32; return 0; From 168daceb378c3e4746d9a202c029a2508f4b5e98 Mon Sep 17 00:00:00 2001 From: samisalreadytaken <46823719+samisalreadytaken@users.noreply.github.com> Date: Wed, 27 May 2026 16:52:48 +0300 Subject: [PATCH 4/8] Simplify bounds check on NetProps and add more assertions --- .../shared/mapbase/vscript_singletons.cpp | 83 ++++++++++++++----- 1 file changed, 63 insertions(+), 20 deletions(-) diff --git a/sp/src/game/shared/mapbase/vscript_singletons.cpp b/sp/src/game/shared/mapbase/vscript_singletons.cpp index 7f4343ed9fc..441430c1fda 100644 --- a/sp/src/game/shared/mapbase/vscript_singletons.cpp +++ b/sp/src/game/shared/mapbase/vscript_singletons.cpp @@ -232,7 +232,14 @@ class CScriptNetPropManager #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 + #define VARINFO_ELEMSIZE_BITS 8 + #define VARINFO_ARRAYSIZE_BITS 16 + #define VARINFO_ELEMSIZE_MAX ( ( 1 << VARINFO_ELEMSIZE_BITS ) - 1 ) + #define VARINFO_ARRAYSIZE_MAX ( ( 1 << VARINFO_ARRAYSIZE_BITS ) - 1 ) + + // Else either clamp 'arraysize' assignments to 0x7fffffff + // or change unsigned boundary checks + COMPILE_TIME_ASSERT( VARINFO_ARRAYSIZE_BITS < 32 ); struct varinfo_t { @@ -246,13 +253,13 @@ class CScriptNetPropManager enum types datatype : 16; - // element size in bytes - unsigned int elemsize : 8; unsigned int arraysize : VARINFO_ARRAYSIZE_BITS; + // element size in bytes + unsigned int elemsize : VARINFO_ELEMSIZE_BITS; // Following are only used in integer netprops to handle unsigned and size casting - bool isUnsigned : 1; - bool isNotNetworked : 1; + unsigned int isUnsigned : 1; + unsigned int isNotNetworked : 1; int GetOffset( int index ) { @@ -483,7 +490,9 @@ class CScriptNetPropManager #define SetVarInfo()\ varinfo_t *pInfo = CacheNew( pEnt, szProp );\ pInfo->isNotNetworked = 0;\ + Assert( pProp->GetElementStride() <= VARINFO_ELEMSIZE_MAX );\ pInfo->elemsize = pProp->GetElementStride();\ + Assert( pProp->GetNumElements() > 0 && pProp->GetNumElements() <= VARINFO_ARRAYSIZE_MAX );\ pInfo->arraysize = pProp->GetNumElements();\ pInfo->offset = offset; @@ -629,6 +638,7 @@ class CScriptNetPropManager { varinfo_t *pInfo = CacheNew( pEnt, szProp ); pInfo->elemsize = sizeof(int); + Assert( pArray->GetNumProps() > 0 && pArray->GetNumProps() <= VARINFO_ARRAYSIZE_MAX ); pInfo->arraysize = pArray->GetNumProps(); pInfo->offset = offset; pInfo->datatype = types::_EHANDLE; @@ -655,6 +665,7 @@ class CScriptNetPropManager pInfo->elemsize = 0; } + Assert( pArray->GetNumProps() > 0 && pArray->GetNumProps() <= VARINFO_ARRAYSIZE_MAX ); pInfo->arraysize = pArray->GetNumProps(); pInfo->offset = offset; pInfo->mask = MASK_INT_SIZE( size ); @@ -666,6 +677,7 @@ class CScriptNetPropManager { varinfo_t *pInfo = CacheNew( pEnt, szProp ); pInfo->elemsize = sizeof(float); + Assert( pArray->GetNumProps() > 0 && pArray->GetNumProps() <= VARINFO_ARRAYSIZE_MAX ); pInfo->arraysize = pArray->GetNumProps(); pInfo->offset = offset; pInfo->datatype = types::_FLOAT; @@ -675,6 +687,7 @@ class CScriptNetPropManager { varinfo_t *pInfo = CacheNew( pEnt, szProp ); pInfo->elemsize = sizeof(float)*3; + Assert( pArray->GetNumProps() > 0 && pArray->GetNumProps() <= VARINFO_ARRAYSIZE_MAX ); pInfo->arraysize = pArray->GetNumProps(); pInfo->offset = offset; pInfo->datatype = types::_VEC3; @@ -684,6 +697,7 @@ class CScriptNetPropManager { varinfo_t *pInfo = CacheNew( pEnt, szProp ); pInfo->elemsize = sizeof(float)*2; + Assert( pArray->GetNumProps() > 0 && pArray->GetNumProps() <= VARINFO_ARRAYSIZE_MAX ); pInfo->arraysize = pArray->GetNumProps(); pInfo->offset = offset; pInfo->datatype = types::_VEC2; @@ -809,7 +823,9 @@ class CScriptNetPropManager #define SetVarInfo()\ varinfo_t *pInfo = CacheNew( pEnt, szProp );\ pInfo->isNotNetworked = 1;\ + Assert( pField->fieldSizeInBytes / pField->fieldSize <= VARINFO_ELEMSIZE_MAX );\ pInfo->elemsize = pField->fieldSizeInBytes / pField->fieldSize;\ + Assert( pField->fieldSize > 0 && pField->fieldSize <= VARINFO_ARRAYSIZE_MAX );\ pInfo->arraysize = pField->fieldSize;\ pInfo->offset = offset; @@ -918,26 +934,26 @@ class CScriptNetPropManager else if ( IS_EHANDLE_UTLVECTOR( pField ) ) { SetVarInfo(); - pInfo->arraysize = ( 1 << VARINFO_ARRAYSIZE_BITS ) - 1; // dynamic, check on get + pInfo->arraysize = VARINFO_ARRAYSIZE_MAX; 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->arraysize = VARINFO_ARRAYSIZE_MAX; 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->arraysize = VARINFO_ARRAYSIZE_MAX; 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->arraysize = VARINFO_ARRAYSIZE_MAX; pInfo->datatype = types::_DAR_FLOAT; } // Only used by CAI_PlayerAlly::m_PendingConcept @@ -1114,7 +1130,7 @@ class CScriptNetPropManager return -1; } - if ( index < 0 || (unsigned int)index >= pInfo->arraysize ) + if ( (unsigned int)index >= pInfo->arraysize ) return -1; if ( pInfo->isNotNetworked ) @@ -1175,7 +1191,7 @@ class CScriptNetPropManager return; } - if ( index < 0 || (unsigned int)index >= pInfo->arraysize ) + if ( (unsigned int)index >= pInfo->arraysize ) return; if ( pInfo->isNotNetworked ) @@ -1266,7 +1282,7 @@ class CScriptNetPropManager if ( pInfo->datatype == types::_VEC3 ) arraysize *= 3; - if ( index < 0 || (unsigned int)index >= arraysize ) + if ( (unsigned int)index >= arraysize ) return -1; switch ( pInfo->datatype ) @@ -1311,7 +1327,7 @@ class CScriptNetPropManager if ( pInfo->datatype == types::_VEC3 ) arraysize *= 3; - if ( index < 0 || (unsigned int)index >= arraysize ) + if ( (unsigned int)index >= arraysize ) return; switch ( pInfo->datatype ) @@ -1355,7 +1371,7 @@ class CScriptNetPropManager return NULL; } - if ( index < 0 || (unsigned int)index >= pInfo->arraysize ) + if ( (unsigned int)index >= pInfo->arraysize ) return NULL; switch ( pInfo->datatype ) @@ -1420,7 +1436,7 @@ class CScriptNetPropManager return; } - if ( index < 0 || (unsigned int)index >= pInfo->arraysize ) + if ( (unsigned int)index >= pInfo->arraysize ) return; switch ( pInfo->datatype ) @@ -1482,7 +1498,7 @@ class CScriptNetPropManager return vec3_invalid; } - if ( index < 0 || (unsigned int)index >= pInfo->arraysize ) + if ( (unsigned int)index >= pInfo->arraysize ) return vec3_invalid; switch ( pInfo->datatype ) @@ -1509,7 +1525,7 @@ class CScriptNetPropManager return; } - if ( index < 0 || (unsigned int)index >= pInfo->arraysize ) + if ( (unsigned int)index >= pInfo->arraysize ) return; switch ( pInfo->datatype ) @@ -1536,7 +1552,7 @@ class CScriptNetPropManager return NULL; } - if ( index < 0 || (unsigned int)index >= pInfo->arraysize ) + if ( (unsigned int)index >= pInfo->arraysize ) return NULL; switch ( pInfo->datatype ) @@ -1590,7 +1606,7 @@ class CScriptNetPropManager return; } - if ( index < 0 || (unsigned int)index >= pInfo->arraysize ) + if ( (unsigned int)index >= pInfo->arraysize ) return; switch ( pInfo->datatype ) @@ -1902,7 +1918,6 @@ class CScriptNetPropManager case DPT_DataTable: { NetTable* pArray = pProp->GetDataTable(); - Assert( pArray->GetNumProps() ); if ( V_strcmp( pProp->GetName(), pArray->GetName() ) != 0 ) { @@ -1911,6 +1926,8 @@ class CScriptNetPropManager break; } + Assert( pArray->GetNumProps() > 0 && pArray->GetNumProps() <= VARINFO_ARRAYSIZE_MAX ); + // 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(); @@ -1943,6 +1960,9 @@ class CScriptNetPropManager NetProp *pArray = pProp->GetArrayProp(); pVar += pArray->GetOffset(); + Assert( pProp->GetNumElements() > 0 && pProp->GetNumElements() <= VARINFO_ARRAYSIZE_MAX ); + Assert( pProp->GetElementStride() <= VARINFO_ELEMSIZE_MAX ); + int numElements = pProp->GetNumElements(); int elementStride = pProp->GetElementStride(); @@ -2228,13 +2248,17 @@ class CScriptNetPropManager Print("null"); return; } + + Assert( vec.Count() >= 0 && vec.Count() <= VARINFO_ARRAYSIZE_MAX ); 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()); } @@ -2246,13 +2270,17 @@ class CScriptNetPropManager Print("null"); return; } + + Assert( vec.Count() >= 0 && vec.Count() <= VARINFO_ARRAYSIZE_MAX ); 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()); } @@ -2265,13 +2293,17 @@ class CScriptNetPropManager Print("null"); return; } + + Assert( vec.Count() >= 0 && vec.Count() <= VARINFO_ARRAYSIZE_MAX ); 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()); } @@ -2283,13 +2315,17 @@ class CScriptNetPropManager Print("null"); return; } + + Assert( vec.Count() >= 0 && vec.Count() <= VARINFO_ARRAYSIZE_MAX ); 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()); } @@ -2301,13 +2337,17 @@ class CScriptNetPropManager Print("null"); return; } + + Assert( vec.Count() >= 0 && vec.Count() <= VARINFO_ARRAYSIZE_MAX ); 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()); } @@ -2462,6 +2502,9 @@ class CScriptNetPropManager } else { + Assert( td->fieldSize > 0 && td->fieldSize <= VARINFO_ARRAYSIZE_MAX ); + Assert( td->fieldSizeInBytes / td->fieldSize <= VARINFO_ELEMSIZE_MAX ); + Print(" <"); PrintFieldType( pVar, td ); Print(" array> #%d", td->fieldSize); From b31fb17cbb1614226ded9badf68f18feb5ff7646 Mon Sep 17 00:00:00 2001 From: samisalreadytaken <46823719+samisalreadytaken@users.noreply.github.com> Date: Wed, 27 May 2026 17:01:30 +0300 Subject: [PATCH 5/8] Add gamerules exception on NetProps --- .../shared/mapbase/vscript_singletons.cpp | 129 ++++++++++++------ 1 file changed, 84 insertions(+), 45 deletions(-) diff --git a/sp/src/game/shared/mapbase/vscript_singletons.cpp b/sp/src/game/shared/mapbase/vscript_singletons.cpp index 441430c1fda..f1a3bc0ef12 100644 --- a/sp/src/game/shared/mapbase/vscript_singletons.cpp +++ b/sp/src/game/shared/mapbase/vscript_singletons.cpp @@ -261,6 +261,8 @@ class CScriptNetPropManager unsigned int isUnsigned : 1; unsigned int isNotNetworked : 1; + unsigned int isGameRules : 1; + int GetOffset( int index ) { return offset + index * elemsize; @@ -279,7 +281,7 @@ class CScriptNetPropManager CUtlVector< int > m_EntMap; CUtlVector< vardict_t > m_VarDicts; - varinfo_t* CacheNew( CBaseEntity *pEnt, const char *szProp ) + varinfo_t *CacheNew( CBaseEntity *pEnt, const char *szProp, bool bNetworked ) { int idx = m_EntMap.Find( GetClassID( pEnt ) ); if ( idx == m_EntMap.InvalidIndex() ) @@ -297,6 +299,13 @@ class CScriptNetPropManager varinfo_t *pInfo = &dict.Element( idx ); V_memset( pInfo, 0, sizeof( varinfo_t ) ); + + pInfo->isNotNetworked = !bNetworked; + + // see Recv/SendProxy_HL2GameRules + if ( bNetworked && dynamic_cast< CGameRulesProxy* >( pEnt ) ) + pInfo->isGameRules = 1; + return pInfo; } @@ -488,8 +497,7 @@ class CScriptNetPropManager { #define SetVarInfo()\ - varinfo_t *pInfo = CacheNew( pEnt, szProp );\ - pInfo->isNotNetworked = 0;\ + varinfo_t *pInfo = CacheNew( pEnt, szProp, true );\ Assert( pProp->GetElementStride() <= VARINFO_ELEMSIZE_MAX );\ pInfo->elemsize = pProp->GetElementStride();\ Assert( pProp->GetNumElements() > 0 && pProp->GetNumElements() <= VARINFO_ARRAYSIZE_MAX );\ @@ -636,7 +644,7 @@ class CScriptNetPropManager { if ( IsEHandle( pProp ) ) { - varinfo_t *pInfo = CacheNew( pEnt, szProp ); + varinfo_t *pInfo = CacheNew( pEnt, szProp, true ); pInfo->elemsize = sizeof(int); Assert( pArray->GetNumProps() > 0 && pArray->GetNumProps() <= VARINFO_ARRAYSIZE_MAX ); pInfo->arraysize = pArray->GetNumProps(); @@ -653,7 +661,7 @@ class CScriptNetPropManager if ( size == 0 ) break; #endif - varinfo_t *pInfo = CacheNew( pEnt, szProp ); + varinfo_t *pInfo = CacheNew( pEnt, szProp, true ); if ( pArray->GetNumProps() > 1 ) { @@ -675,7 +683,7 @@ class CScriptNetPropManager } case DPT_Float: { - varinfo_t *pInfo = CacheNew( pEnt, szProp ); + varinfo_t *pInfo = CacheNew( pEnt, szProp, true ); pInfo->elemsize = sizeof(float); Assert( pArray->GetNumProps() > 0 && pArray->GetNumProps() <= VARINFO_ARRAYSIZE_MAX ); pInfo->arraysize = pArray->GetNumProps(); @@ -685,7 +693,7 @@ class CScriptNetPropManager } case DPT_Vector: { - varinfo_t *pInfo = CacheNew( pEnt, szProp ); + varinfo_t *pInfo = CacheNew( pEnt, szProp, true ); pInfo->elemsize = sizeof(float)*3; Assert( pArray->GetNumProps() > 0 && pArray->GetNumProps() <= VARINFO_ARRAYSIZE_MAX ); pInfo->arraysize = pArray->GetNumProps(); @@ -695,7 +703,7 @@ class CScriptNetPropManager } case DPT_VectorXY: { - varinfo_t *pInfo = CacheNew( pEnt, szProp ); + varinfo_t *pInfo = CacheNew( pEnt, szProp, true ); pInfo->elemsize = sizeof(float)*2; Assert( pArray->GetNumProps() > 0 && pArray->GetNumProps() <= VARINFO_ARRAYSIZE_MAX ); pInfo->arraysize = pArray->GetNumProps(); @@ -821,8 +829,7 @@ class CScriptNetPropManager } #define SetVarInfo()\ - varinfo_t *pInfo = CacheNew( pEnt, szProp );\ - pInfo->isNotNetworked = 1;\ + varinfo_t *pInfo = CacheNew( pEnt, szProp, false );\ Assert( pField->fieldSizeInBytes / pField->fieldSize <= VARINFO_ELEMSIZE_MAX );\ pInfo->elemsize = pField->fieldSizeInBytes / pField->fieldSize;\ Assert( pField->fieldSize > 0 && pField->fieldSize <= VARINFO_ARRAYSIZE_MAX );\ @@ -842,7 +849,6 @@ class CScriptNetPropManager { SetVarInfo(); pInfo->isUnsigned = ( pField->flags & SPROP_UNSIGNED ) != 0; - pInfo->isNotNetworked = 1; switch ( pField->fieldType ) { case FIELD_INTEGER: @@ -993,6 +999,14 @@ class CScriptNetPropManager return NULL; } + static char *GetBase( CBaseEntity *pEnt, const varinfo_t *pInfo ) + { + if ( pInfo->isGameRules ) + return (char*)GameRules(); + + return (char*)pEnt; + } + public: // FIXME: Cannot get datatable/arrays at the moment bool HasProp( HSCRIPT hEnt, const char *szProp ) @@ -1078,33 +1092,36 @@ class CScriptNetPropManager if ( !pInfo ) return -1; } + #ifdef GAME_DLL + char *pBase = GetBase( pEnt, pInfo ); + switch ( pInfo->datatype ) { case types::_DAR_EHANDLE: { - CUtlVector< EHANDLE > &vec = *(CUtlVector< EHANDLE >*)((char*)pEnt + pInfo->offset); + CUtlVector< EHANDLE > &vec = *(CUtlVector< EHANDLE >*)(pBase + pInfo->offset); if ( !vec.Base() ) return -1; return vec.Count(); } case types::_DAR_CLASSPTR: { - CUtlVector< CBaseEntity* > &vec = *(CUtlVector< CBaseEntity* >*)((char*)pEnt + pInfo->offset); + CUtlVector< CBaseEntity* > &vec = *(CUtlVector< CBaseEntity* >*)(pBase + pInfo->offset); if ( !vec.Base() ) return -1; return vec.Count(); } case types::_DAR_INT: { - CUtlVector< int > &vec = *(CUtlVector< int >*)((char*)pEnt + pInfo->offset); + CUtlVector< int > &vec = *(CUtlVector< int >*)(pBase + pInfo->offset); if ( !vec.Base() ) return -1; return vec.Count(); } case types::_DAR_FLOAT: { - CUtlVector< float > &vec = *(CUtlVector< float >*)((char*)pEnt + pInfo->offset); + CUtlVector< float > &vec = *(CUtlVector< float >*)(pBase + pInfo->offset); if ( !vec.Base() ) return -1; return vec.Count(); @@ -1166,10 +1183,12 @@ class CScriptNetPropManager } else { + char *pBase = GetBase( pEnt, pInfo ); + switch ( pInfo->datatype ) { case types::_INT32: - return (*(int*)((char*)pEnt + pInfo->GetOffset( index ))) & pInfo->mask; + return (*(int*)(pBase + pInfo->GetOffset( index ))) & pInfo->mask; } } @@ -1249,11 +1268,13 @@ class CScriptNetPropManager } else { + char *pBase = GetBase( pEnt, pInfo ); + switch ( pInfo->datatype ) { case types::_INT32: { - int *dest = (int*)((char*)pEnt + pInfo->GetOffset( index )); + int *dest = (int*)(pBase + pInfo->GetOffset( index )); *dest = (*dest & ~pInfo->mask) | (value & pInfo->mask); NetworkStateChanged( pEnt, pInfo->GetOffset( index ) ); break; @@ -1285,16 +1306,18 @@ class CScriptNetPropManager if ( (unsigned int)index >= arraysize ) return -1; + char *pBase = GetBase( pEnt, pInfo ); + switch ( pInfo->datatype ) { case types::_FLOAT: - return *(float*)((char*)pEnt + pInfo->GetOffset( index )); + return *(float*)(pBase + pInfo->GetOffset( index )); case types::_VEC3: - return ((float*)((char*)pEnt + pInfo->GetOffset( index / 3 )))[ index % 3 ]; + return ((float*)(pBase + pInfo->GetOffset( index / 3 )))[ index % 3 ]; #ifdef GAME_DLL case types::_DAR_FLOAT: { - CUtlVector< float > &vec = *(CUtlVector< float >*)((char*)pEnt + pInfo->offset); + CUtlVector< float > &vec = *(CUtlVector< float >*)(pBase + pInfo->offset); if ( !vec.Base() ) return -1; if ( index >= vec.Count() ) @@ -1330,20 +1353,22 @@ class CScriptNetPropManager if ( (unsigned int)index >= arraysize ) return; + char *pBase = GetBase( pEnt, pInfo ); + switch ( pInfo->datatype ) { case types::_FLOAT: - *(float*)((char*)pEnt + pInfo->GetOffset( index )) = value; + *(float*)(pBase + pInfo->GetOffset( index )) = value; NetworkStateChanged( pEnt, pInfo->GetOffset( index ) ); break; case types::_VEC3: - ((float*)((char*)pEnt + pInfo->GetOffset( index / 3 )))[ index % 3 ] = value; + ((float*)(pBase + 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); + CUtlVector< float > &vec = *(CUtlVector< float >*)(pBase + pInfo->offset); if ( !vec.Base() ) return; if ( index >= vec.Count() ) @@ -1374,27 +1399,29 @@ class CScriptNetPropManager if ( (unsigned int)index >= pInfo->arraysize ) return NULL; + char *pBase = GetBase( pEnt, pInfo ); + switch ( pInfo->datatype ) { case types::_EHANDLE: { - EHANDLE &iEHandle = *(EHANDLE*)((char*)pEnt + pInfo->GetOffset( index )); + EHANDLE &iEHandle = *(EHANDLE*)(pBase + pInfo->GetOffset( index )); return ToHScript( iEHandle ); } #ifdef GAME_DLL case types::_CLASSPTR: { - CBaseEntity* ptr = *(CBaseEntity**)((char*)pEnt + pInfo->GetOffset( index )); + CBaseEntity *ptr = *(CBaseEntity**)(pBase + pInfo->GetOffset( index )); return ToHScript( ptr ); } case types::_EDICT: { - edict_t* ptr = *(edict_t**)((char*)pEnt + pInfo->GetOffset( index )); + edict_t *ptr = *(edict_t**)(pBase + pInfo->GetOffset( index )); return ToHScript( GetContainingEntity( ptr ) ); } case types::_DAR_EHANDLE: { - CUtlVector< EHANDLE > &vec = *(CUtlVector< EHANDLE >*)((char*)pEnt + pInfo->offset); + CUtlVector< EHANDLE > &vec = *(CUtlVector< EHANDLE >*)(pBase + pInfo->offset); if ( !vec.Base() ) return NULL; if ( index >= vec.Count() ) @@ -1403,7 +1430,7 @@ class CScriptNetPropManager } case types::_DAR_CLASSPTR: { - CUtlVector< CBaseEntity* > &vec = *(CUtlVector< CBaseEntity* >*)((char*)pEnt + pInfo->offset); + CUtlVector< CBaseEntity* > &vec = *(CUtlVector< CBaseEntity* >*)(pBase + pInfo->offset); if ( !vec.Base() ) return NULL; if ( index >= vec.Count() ) @@ -1413,7 +1440,7 @@ class CScriptNetPropManager #endif case types::_PHYS: { - IPhysicsObject* ptr = *(IPhysicsObject**)((char*)pEnt + pInfo->GetOffset( index )); + IPhysicsObject *ptr = *(IPhysicsObject**)(pBase + pInfo->GetOffset( index )); return ptr ? g_pScriptVM->RegisterInstance( ptr ) : NULL; } } @@ -1439,27 +1466,29 @@ class CScriptNetPropManager if ( (unsigned int)index >= pInfo->arraysize ) return; + char *pBase = GetBase( pEnt, pInfo ); + switch ( pInfo->datatype ) { case types::_EHANDLE: - *(EHANDLE*)((char*)pEnt + pInfo->GetOffset( index )) = ToEnt( value ); + *(EHANDLE*)(pBase + 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 ); + *(CBaseEntity**)(pBase + 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; + *(edict_t**)(pBase + 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); + CUtlVector< EHANDLE > &vec = *(CUtlVector< EHANDLE >*)(pBase + pInfo->offset); if ( !vec.Base() ) return; if ( index >= vec.Count() ) @@ -1470,7 +1499,7 @@ class CScriptNetPropManager } case types::_DAR_CLASSPTR: { - CUtlVector< CBaseEntity* > &vec = *(CUtlVector< CBaseEntity* >*)((char*)pEnt + pInfo->offset); + CUtlVector< CBaseEntity* > &vec = *(CUtlVector< CBaseEntity* >*)(pBase + pInfo->offset); if ( !vec.Base() ) return; if ( index >= vec.Count() ) @@ -1501,10 +1530,12 @@ class CScriptNetPropManager if ( (unsigned int)index >= pInfo->arraysize ) return vec3_invalid; + char *pBase = GetBase( pEnt, pInfo ); + switch ( pInfo->datatype ) { case types::_VEC3: - return *(Vector*)((char*)pEnt + pInfo->GetOffset( index )); + return *(Vector*)(pBase + pInfo->GetOffset( index )); } return vec3_invalid; @@ -1528,10 +1559,12 @@ class CScriptNetPropManager if ( (unsigned int)index >= pInfo->arraysize ) return; + char *pBase = GetBase( pEnt, pInfo ); + switch ( pInfo->datatype ) { case types::_VEC3: - *(Vector*)((char*)pEnt + pInfo->GetOffset( index )) = value; + *(Vector*)(pBase + pInfo->GetOffset( index )) = value; NetworkStateChanged( pEnt, pInfo->GetOffset( index ) ); break; } @@ -1555,18 +1588,20 @@ class CScriptNetPropManager if ( (unsigned int)index >= pInfo->arraysize ) return NULL; + char *pBase = GetBase( pEnt, pInfo ); + switch ( pInfo->datatype ) { case types::_CSTRING: - return (const char*)((char*)pEnt + pInfo->GetOffset( index )); + return (const char*)(pBase + pInfo->GetOffset( index )); case types::_STRING_T: // Identical to _CSTRING on client - return STRING( *(string_t*)((char*)pEnt + pInfo->GetOffset( index )) ); + return STRING( *(string_t*)(pBase + pInfo->GetOffset( index )) ); case types::_INT8: { if ( !pInfo->stringsize ) return NULL; - char * const pVar = ((char*)pEnt + pInfo->GetOffset( index )); + char * const pVar = pBase + pInfo->GetOffset( index ); // Is this null terminated? int i = 0; @@ -1584,7 +1619,7 @@ class CScriptNetPropManager } #ifdef GAME_DLL case types::_STDSTRING: - return ( (std::string*)((char*)pEnt + pInfo->GetOffset( index )) )->c_str(); + return ( (std::string*)(pBase + pInfo->GetOffset( index )) )->c_str(); #endif } @@ -1609,6 +1644,8 @@ class CScriptNetPropManager if ( (unsigned int)index >= pInfo->arraysize ) return; + char *pBase = GetBase( pEnt, pInfo ); + switch ( pInfo->datatype ) { case types::_CSTRING: @@ -1616,7 +1653,7 @@ class CScriptNetPropManager { if ( pInfo->stringsize ) { - V_strncpy( (char*)pEnt + pInfo->GetOffset( index ), value, pInfo->stringsize ); + V_strncpy( pBase + pInfo->GetOffset( index ), value, pInfo->stringsize ); NetworkStateChanged( pEnt, pInfo->GetOffset( index ) ); break; } @@ -1630,9 +1667,9 @@ class CScriptNetPropManager if ( src == NULL_STRING ) src = AllocPooledString( value ); #ifdef GAME_DLL - *(string_t*)((char*)pEnt + pInfo->GetOffset( index )) = src; + *(string_t*)(pBase + pInfo->GetOffset( index )) = src; #else - V_strcpy( (char*)pEnt + pInfo->GetOffset( index ), src ); + V_strcpy( pBase + pInfo->GetOffset( index ), src ); #endif NetworkStateChanged( pEnt, pInfo->GetOffset( index ) ); break; @@ -1640,7 +1677,7 @@ class CScriptNetPropManager #ifdef GAME_DLL case types::_STDSTRING: { - ( (std::string*)((char*)pEnt + pInfo->GetOffset( index )) )->assign( value, V_strlen(value) ); + ( (std::string*)(pBase + pInfo->GetOffset( index )) )->assign( value, V_strlen(value) ); NetworkStateChanged( pEnt, pInfo->GetOffset( index ) ); break; } @@ -2553,9 +2590,11 @@ class CScriptNetPropManager m_output.SetBufferType( true, false ); IndentStart(); + void *pNetBase = dynamic_cast< CGameRulesProxy* >( pEnt ) ? (void*)GameRules() : (void*)pEnt; + Print( "\n" ); Print( "(%s)\n", GetNetTable( GetNetworkClass(pEnt) )->GetName() ); - DumpNetTable_r( pEnt, GetNetTable( GetNetworkClass(pEnt) ) ); + DumpNetTable_r( pNetBase, GetNetTable( GetNetworkClass(pEnt) ) ); Print( "\n\n" ); Print( "\n" ); From 2d01391879527d6cb9345ffe40be055ee07f8494 Mon Sep 17 00:00:00 2001 From: samisalreadytaken <46823719+samisalreadytaken@users.noreply.github.com> Date: Tue, 9 Jun 2026 19:54:29 +0300 Subject: [PATCH 6/8] Add support for reading output values in NetProps --- sp/src/game/server/entityoutput.h | 30 ++++ sp/src/game/server/variant_t.h | 5 + .../shared/mapbase/vscript_singletons.cpp | 151 +++++++++++++++++- 3 files changed, 184 insertions(+), 2 deletions(-) diff --git a/sp/src/game/server/entityoutput.h b/sp/src/game/server/entityoutput.h index 424ae761fe2..5eb95ba65c1 100644 --- a/sp/src/game/server/entityoutput.h +++ b/sp/src/game/server/entityoutput.h @@ -85,8 +85,16 @@ class CBaseEntityOutput void SetActionList(CEventAction *newlist) { m_ActionList = newlist; } #endif +#ifdef MAPBASE_VSCRIPT + // Value is accessed in CScriptNetPropManager +public: +#else protected: +#endif variant_t m_Value; +#ifdef MAPBASE_VSCRIPT +protected: +#endif CEventAction *m_ActionList; DECLARE_SIMPLE_DATADESC(); @@ -104,6 +112,14 @@ template< class Type, fieldtype_t fieldType > class CEntityOutputTemplate : public CBaseEntityOutput { public: +#ifdef MAPBASE_VSCRIPT + // Initialise type to allow script access + CEntityOutputTemplate() : CBaseEntityOutput() + { + m_Value.fieldType = fieldType; + } +#endif + // // Sets an initial value without firing the output. // @@ -138,6 +154,13 @@ template<> class CEntityOutputTemplate : public CBaseEntityOutput { public: +#ifdef MAPBASE_VSCRIPT + CEntityOutputTemplate() : CBaseEntityOutput() + { + m_Value.fieldType = FIELD_VECTOR; + } +#endif + void Init( const Vector &value ) { m_Value.SetVector3D( value ); @@ -183,6 +206,13 @@ template<> class CEntityOutputTemplate : public CBaseEntityOutput { public: +#ifdef MAPBASE_VSCRIPT + CEntityOutputTemplate() : CBaseEntityOutput() + { + m_Value.fieldType = FIELD_POSITION_VECTOR; + } +#endif + void Init( const Vector &value ) { m_Value.SetPositionVector3D( value ); diff --git a/sp/src/game/server/variant_t.h b/sp/src/game/server/variant_t.h index fc460bfec2e..1741cc303f4 100644 --- a/sp/src/game/server/variant_t.h +++ b/sp/src/game/server/variant_t.h @@ -27,6 +27,11 @@ struct ScriptVariant_t; // class variant_t { +#ifdef MAPBASE_VSCRIPT + // Offsets are read in CScriptNetPropManager +public: +#endif + union { bool bVal; diff --git a/sp/src/game/shared/mapbase/vscript_singletons.cpp b/sp/src/game/shared/mapbase/vscript_singletons.cpp index f1a3bc0ef12..5a111673669 100644 --- a/sp/src/game/shared/mapbase/vscript_singletons.cpp +++ b/sp/src/game/shared/mapbase/vscript_singletons.cpp @@ -355,7 +355,7 @@ class CScriptNetPropManager int fieldType = td->fieldType; int fieldOffset = td->fieldOffset[ TD_OFFSET_NORMAL ]; - if ( td->flags & (FTYPEDESC_FUNCTIONTABLE | FTYPEDESC_INPUT | FTYPEDESC_OUTPUT) ) + if ( td->flags & (FTYPEDESC_FUNCTIONTABLE | FTYPEDESC_INPUT) ) continue; if ( fieldType == FIELD_VOID || fieldType == FIELD_FUNCTION ) @@ -924,6 +924,63 @@ class CScriptNetPropManager } case FIELD_CUSTOM: { +#ifdef GAME_DLL + if ( pField->flags & FTYPEDESC_OUTPUT ) + { + CBaseEntityOutput *pOutput = (CBaseEntityOutput*)( (char*)pEnt + offset ); + offset += offsetof( CBaseEntityOutput, m_Value ); + enum types datatype; + + switch ( pOutput->ValueFieldType() ) + { + case FIELD_INTEGER: + case FIELD_COLOR32: + { + datatype = types::_INT32; + offset += offsetof( variant_t, iVal ); + break; + } + case FIELD_FLOAT: + { + datatype = types::_FLOAT; + offset += offsetof( variant_t, flVal ); + break; + } + case FIELD_VECTOR: + case FIELD_POSITION_VECTOR: + { + datatype = types::_VEC3; + offset += offsetof( variant_t, vecVal ); + break; + } + case FIELD_STRING: + { + datatype = types::_STRING_T; + offset += offsetof( variant_t, iszVal ); + break; + } + case FIELD_EHANDLE: + { + datatype = types::_EHANDLE; + offset += offsetof( variant_t, eVal ); + break; + } + default: + AssertMsg( + pOutput->ValueFieldType() == FIELD_INPUT || + pOutput->ValueFieldType() == FIELD_VOID, + "not implemented" ); + return NULL; + } + + varinfo_t *pInfo = CacheNew( pEnt, szProp, false ); + pInfo->arraysize = 1; + pInfo->offset = offset; + pInfo->datatype = datatype; + return pInfo; + } +#endif + if ( pField->pSaveRestoreOps == GetPhysObjSaveRestoreOps( PIID_IPHYSICSOBJECT ) ) { SetVarInfo(); @@ -2130,6 +2187,47 @@ class CScriptNetPropManager { Assert( td->fieldType == FIELD_CUSTOM ); +#ifdef GAME_DLL + if ( td->flags & FTYPEDESC_OUTPUT ) + { + CBaseEntityOutput *pOutput = (CBaseEntityOutput*)pVar; + Print( "output " ); + + switch ( pOutput->ValueFieldType() ) + { + case FIELD_INTEGER: + Print( "int" ); + break; + case FIELD_COLOR32: + Print( "clr32" ); + break; + case FIELD_FLOAT: + Print( "float" ); + break; + case FIELD_VECTOR: + case FIELD_POSITION_VECTOR: + Print( "vec3" ); + break; + case FIELD_STRING: + Print( "string" ); + break; + case FIELD_EHANDLE: + Print( "entity" ); + break; + case FIELD_INPUT: + Print( "variant" ); + break; + case FIELD_VOID: + m_output.SeekPut( CUtlBuffer::SEEK_CURRENT, -1 ); + break; + default: + Print( "unknown %d", pOutput->ValueFieldType() ); + } + + return; + } +#endif + const char *g_ppszPhysTypeNames[PIID_NUM_TYPES] = { "Unknown Phys", @@ -2247,6 +2345,55 @@ class CScriptNetPropManager { Assert( td->fieldType == FIELD_CUSTOM ); +#ifdef GAME_DLL + if ( td->flags & FTYPEDESC_OUTPUT ) + { + CBaseEntityOutput *pOutput = (CBaseEntityOutput*)pVar; + pVar += offsetof( CBaseEntityOutput, m_Value ); + + switch ( pOutput->ValueFieldType() ) + { + case FIELD_INTEGER: + { + Print( "%i", ((variant_t*)pVar)->iVal ); + break; + } + case FIELD_COLOR32: + { + Print( "0x%08x", ((variant_t*)pVar)->iVal ); + break; + } + case FIELD_FLOAT: + { + Print( "%f", ((variant_t*)pVar)->flVal ); + break; + } + case FIELD_VECTOR: + case FIELD_POSITION_VECTOR: + { + PrintVec3( ((variant_t*)pVar)->vecVal ); + break; + } + case FIELD_STRING: + { + PrintString( ((variant_t*)pVar)->String() ); + break; + } + case FIELD_EHANDLE: + { + PrintEntity( &((variant_t*)pVar)->eVal ); + break; + } + case FIELD_INPUT: + case FIELD_VOID: + default: + break; + } + + return; + } +#endif + for ( int i = 0; i < PIID_NUM_TYPES; i++ ) { if ( td->pSaveRestoreOps == GetPhysObjSaveRestoreOps( (PhysInterfaceId_t)i ) ) @@ -2512,7 +2659,7 @@ class CScriptNetPropManager { typedescription_t* td = &pFields[i]; - if ( td->flags & (FTYPEDESC_FUNCTIONTABLE | FTYPEDESC_INPUT | FTYPEDESC_OUTPUT) ) + if ( td->flags & (FTYPEDESC_FUNCTIONTABLE | FTYPEDESC_INPUT) ) continue; if ( td->fieldType == FIELD_VOID || td->fieldType == FIELD_FUNCTION ) From dbbb73c3b6dc3af1b132721decb2e8e60d3f1cdc Mon Sep 17 00:00:00 2001 From: samisalreadytaken <46823719+samisalreadytaken@users.noreply.github.com> Date: Thu, 11 Jun 2026 18:45:55 +0300 Subject: [PATCH 7/8] Change CScriptNetPropManager cache to enable reading more values --- .../shared/mapbase/vscript_singletons.cpp | 147 ++++++++++++------ 1 file changed, 96 insertions(+), 51 deletions(-) diff --git a/sp/src/game/shared/mapbase/vscript_singletons.cpp b/sp/src/game/shared/mapbase/vscript_singletons.cpp index 5a111673669..f692afb0ee5 100644 --- a/sp/src/game/shared/mapbase/vscript_singletons.cpp +++ b/sp/src/game/shared/mapbase/vscript_singletons.cpp @@ -128,27 +128,22 @@ class CScriptNetPropManager typedef SendTable NetTable; typedef ServerClass NetworkClass; - NetworkClass *GetNetworkClass( CBaseEntity* p ) { return p->GetServerClass(); } - NetTable *GetNetTable( NetworkClass* p ) { return p->m_pTable; } + static NetworkClass *GetNetworkClass( CBaseEntity *p ) { return p->GetServerClass(); } + static NetTable *GetNetTable( NetworkClass *p ) { return p->m_pTable; } - void NetworkStateChanged( CBaseEntity* p, int o ) { p->NetworkProp()->NetworkStateChanged( o ); } + static 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; } + static NetworkClass *GetNetworkClass( CBaseEntity *p ) { return p->GetClientClass(); } + static NetTable *GetNetTable( NetworkClass *p ) { return p->m_pRecvTable; } - void NetworkStateChanged( CBaseEntity*, int ) {} + static void NetworkStateChanged( CBaseEntity*, int ) {} #endif - int GetClassID( CBaseEntity *p ) - { - return GetNetworkClass( p )->m_ClassID; - } - - int GetIntPropSize( NetProp *pProp ) + static int GetIntPropSize( NetProp *pProp ) { Assert( pProp->GetType() == DPT_Int ); @@ -181,12 +176,12 @@ class CScriptNetPropManager #endif } - bool IsEHandle( NetProp *pProp ) + static bool IsEHandle( NetProp *pProp ) { return ( pProp->GetProxyFn() == DataTableProxy_EHandle ); } - bool IsUtlVector( NetProp *pProp ) + static bool IsUtlVector( NetProp *pProp ) { #ifdef GAME_DLL SendVarProxyFn proxy = pProp->GetProxyFn(); @@ -257,10 +252,9 @@ class CScriptNetPropManager // element size in bytes unsigned int elemsize : VARINFO_ELEMSIZE_BITS; - // Following are only used in integer netprops to handle unsigned and size casting + // Only used in integer netprops to handle unsigned and size casting unsigned int isUnsigned : 1; unsigned int isNotNetworked : 1; - unsigned int isGameRules : 1; int GetOffset( int index ) @@ -269,35 +263,87 @@ class CScriptNetPropManager } }; + struct varsource_t + { + void *source; + varinfo_t info; + }; + + struct vardictelem_t + { + CCopyableUtlVector< varsource_t > sources; + }; + // Wrapper to be able to set case sensitive comparator in node insertion - class vardict_t : public CUtlDict< varinfo_t > + class vardict_t : public CUtlDict< vardictelem_t > { public: - vardict_t() : CUtlDict< varinfo_t >( k_eDictCompareTypeCaseSensitive ) {} + vardict_t() : CUtlDict< vardictelem_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; + // Different entities can have unique network tables while sharing data maps, + // or unique data maps while sharing network tables (e.g. server only entities). + // The cache needs to be able to differentiate the source of the variable info + // while being quick to access. + // + // While storing the origin table (e.g. DT_BaseEntity for DT_HL2_Player->m_vecOrigin) + // keeps the cache size small, it would have runtime impact from + // checking base tables and pointer chasing. + // Instead, store the highest level table identifiers even if they are identical to + // existing variables. (e.g. + // storing DT_HL2_Player and DT_PhysicsProp for the variable DT_BaseEntity->m_vecOrigin) + // + // Fetching varinfo from cache now does one sorted string lookup (RB tree) + // and 2 pointer comparisons for each class type that was fetched for that variable name + // (practically a very low number because NetProps is used more often for unique variables than not). + // + // This is a quick, not well thought out solution + vardict_t m_VarDicts; - varinfo_t *CacheNew( CBaseEntity *pEnt, const char *szProp, bool bNetworked ) + static varsource_t *CacheFind( CBaseEntity *pEnt, vardictelem_t *pElem ) { - int idx = m_EntMap.Find( GetClassID( pEnt ) ); - if ( idx == m_EntMap.InvalidIndex() ) + NetTable *pNetTable = GetNetTable( GetNetworkClass( pEnt ) ); + datamap_t *pDataMap = pEnt->GetDataDescMap(); +#ifdef CLIENT_DLL + datamap_t *pPredMap = pEnt->GetPredDescMap(); +#endif + + FOR_EACH_VEC( pElem->sources, i ) { - // Vector indices are kept in parallel as a workaround for encapsulating maps - idx = m_EntMap.AddToTail( GetClassID( pEnt ) ); - m_VarDicts.AddToTail(); + varsource_t *pSrc = &pElem->sources.Element( i ); + + if ( pSrc->source == pNetTable ) + return pSrc; + + if ( pSrc->source == pDataMap ) + return pSrc; +#ifdef CLIENT_DLL + if ( pSrc->source == pPredMap ) + return pSrc; +#endif } - vardict_t &dict = m_VarDicts.Element( idx ); + return NULL; + } - idx = dict.Find( szProp ); - if ( idx == dict.InvalidIndex() ) - idx = dict.Insert( szProp ); + varinfo_t *CacheNew( CBaseEntity *pEnt, const char *szProp, void *pSource, bool bNetworked ) + { + int idx = m_VarDicts.Find( szProp ); + if ( idx == m_VarDicts.InvalidIndex() ) + idx = m_VarDicts.Insert( szProp ); + + vardictelem_t *pElem = &m_VarDicts.Element( idx ); + varsource_t *pSrc = CacheFind( pEnt, pElem ); - varinfo_t *pInfo = &dict.Element( idx ); + if ( !pSrc ) + { + idx = pElem->sources.AddToTail(); + pSrc = &pElem->sources.Element( idx ); + } + + pSrc->source = pSource; + + varinfo_t *pInfo = &pSrc->info; V_memset( pInfo, 0, sizeof( varinfo_t ) ); pInfo->isNotNetworked = !bNetworked; @@ -311,17 +357,17 @@ class CScriptNetPropManager varinfo_t* CacheFetch( CBaseEntity *pEnt, const char *szProp ) { - int idx = m_EntMap.Find( GetClassID( pEnt ) ); - if ( idx == m_EntMap.InvalidIndex() ) - return NULL; + int idx = m_VarDicts.Find( szProp ); + if ( idx != m_VarDicts.InvalidIndex() ) + { + vardictelem_t *pElem = &m_VarDicts.Element( idx ); + varsource_t *pSrc = CacheFind( pEnt, pElem ); - vardict_t &dict = m_VarDicts.Element( idx ); - idx = dict.Find( szProp ); - if ( idx == dict.InvalidIndex() ) - return NULL; + if ( pSrc ) + return &pSrc->info; + } - varinfo_t *pInfo = &dict.Element( idx ); - return pInfo; + return NULL; } public: @@ -332,7 +378,6 @@ class CScriptNetPropManager void PurgeCache() { - m_EntMap.Purge(); m_VarDicts.Purge(); } @@ -497,7 +542,7 @@ class CScriptNetPropManager { #define SetVarInfo()\ - varinfo_t *pInfo = CacheNew( pEnt, szProp, true );\ + varinfo_t *pInfo = CacheNew( pEnt, szProp, (void*)pTable, true );\ Assert( pProp->GetElementStride() <= VARINFO_ELEMSIZE_MAX );\ pInfo->elemsize = pProp->GetElementStride();\ Assert( pProp->GetNumElements() > 0 && pProp->GetNumElements() <= VARINFO_ARRAYSIZE_MAX );\ @@ -644,7 +689,7 @@ class CScriptNetPropManager { if ( IsEHandle( pProp ) ) { - varinfo_t *pInfo = CacheNew( pEnt, szProp, true ); + varinfo_t *pInfo = CacheNew( pEnt, szProp, (void*)pTable, true ); pInfo->elemsize = sizeof(int); Assert( pArray->GetNumProps() > 0 && pArray->GetNumProps() <= VARINFO_ARRAYSIZE_MAX ); pInfo->arraysize = pArray->GetNumProps(); @@ -661,7 +706,7 @@ class CScriptNetPropManager if ( size == 0 ) break; #endif - varinfo_t *pInfo = CacheNew( pEnt, szProp, true ); + varinfo_t *pInfo = CacheNew( pEnt, szProp, (void*)pTable, true ); if ( pArray->GetNumProps() > 1 ) { @@ -683,7 +728,7 @@ class CScriptNetPropManager } case DPT_Float: { - varinfo_t *pInfo = CacheNew( pEnt, szProp, true ); + varinfo_t *pInfo = CacheNew( pEnt, szProp, (void*)pTable, true ); pInfo->elemsize = sizeof(float); Assert( pArray->GetNumProps() > 0 && pArray->GetNumProps() <= VARINFO_ARRAYSIZE_MAX ); pInfo->arraysize = pArray->GetNumProps(); @@ -693,7 +738,7 @@ class CScriptNetPropManager } case DPT_Vector: { - varinfo_t *pInfo = CacheNew( pEnt, szProp, true ); + varinfo_t *pInfo = CacheNew( pEnt, szProp, (void*)pTable, true ); pInfo->elemsize = sizeof(float)*3; Assert( pArray->GetNumProps() > 0 && pArray->GetNumProps() <= VARINFO_ARRAYSIZE_MAX ); pInfo->arraysize = pArray->GetNumProps(); @@ -703,7 +748,7 @@ class CScriptNetPropManager } case DPT_VectorXY: { - varinfo_t *pInfo = CacheNew( pEnt, szProp, true ); + varinfo_t *pInfo = CacheNew( pEnt, szProp, (void*)pTable, true ); pInfo->elemsize = sizeof(float)*2; Assert( pArray->GetNumProps() > 0 && pArray->GetNumProps() <= VARINFO_ARRAYSIZE_MAX ); pInfo->arraysize = pArray->GetNumProps(); @@ -829,7 +874,7 @@ class CScriptNetPropManager } #define SetVarInfo()\ - varinfo_t *pInfo = CacheNew( pEnt, szProp, false );\ + varinfo_t *pInfo = CacheNew( pEnt, szProp, (void*)map, false );\ Assert( pField->fieldSizeInBytes / pField->fieldSize <= VARINFO_ELEMSIZE_MAX );\ pInfo->elemsize = pField->fieldSizeInBytes / pField->fieldSize;\ Assert( pField->fieldSize > 0 && pField->fieldSize <= VARINFO_ARRAYSIZE_MAX );\ @@ -973,7 +1018,7 @@ class CScriptNetPropManager return NULL; } - varinfo_t *pInfo = CacheNew( pEnt, szProp, false ); + varinfo_t *pInfo = CacheNew( pEnt, szProp, (void*)map, false ); pInfo->arraysize = 1; pInfo->offset = offset; pInfo->datatype = datatype; From a28402ddf71514da33d23af206d936c8dc5dceca Mon Sep 17 00:00:00 2001 From: samisalreadytaken <46823719+samisalreadytaken@users.noreply.github.com> Date: Fri, 12 Jun 2026 21:30:16 +0300 Subject: [PATCH 8/8] Fallback to data maps for strings on NetProps --- .../shared/mapbase/vscript_singletons.cpp | 26 +++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/sp/src/game/shared/mapbase/vscript_singletons.cpp b/sp/src/game/shared/mapbase/vscript_singletons.cpp index f692afb0ee5..737950001d5 100644 --- a/sp/src/game/shared/mapbase/vscript_singletons.cpp +++ b/sp/src/game/shared/mapbase/vscript_singletons.cpp @@ -536,8 +536,11 @@ class CScriptNetPropManager varinfo_t *GetVarInfo( CBaseEntity *pEnt, const char *szProp, int index, bool bDontWarnOnMissing = false ) { int offset = 0; + datamap_t *map; + typedescription_t *pField; NetTable *pTable = GetNetTable( GetNetworkClass( pEnt ) ); NetProp *pProp = FindInNetTable( (char*)pEnt, pTable, szProp, &offset ); + if ( pProp ) { @@ -646,6 +649,19 @@ class CScriptNetPropManager Assert( index == 0 || pProp->GetElementStride() > 0 ); +#ifdef GAME_DLL + // If this networked string is not pooled, we don't know how large the buffer is + // Check the data maps to see if this variable has a size there + // Otherwise it's going to be read-only + if ( pProp->GetProxyFn() != SendProxy_StringT_To_String ) + { + map = pEnt->GetDataDescMap(); + pField = FindInDataMap( (char*)pEnt, map, szProp, &offset ); + if ( pField && pField->fieldType == FIELD_CHARACTER ) + goto find_field; + } +#endif + SetVarInfo(); #ifdef GAME_DLL pInfo->stringsize = 0; @@ -663,6 +679,7 @@ class CScriptNetPropManager Assert( pProp->GetProxyFn() == DataTableProxy_String ); pInfo->datatype = types::_CSTRING; } + return pInfo; } case DPT_DataTable: @@ -860,13 +877,11 @@ class CScriptNetPropManager #undef SetVarInfo } - datamap_t *map = pEnt->GetDataDescMap(); - typedescription_t *pField = FindInDataMap( (char*)pEnt, map, szProp, &offset ); + map = pEnt->GetDataDescMap(); + 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 ); @@ -1757,8 +1772,9 @@ class CScriptNetPropManager { V_strncpy( pBase + pInfo->GetOffset( index ), value, pInfo->stringsize ); NetworkStateChanged( pEnt, pInfo->GetOffset( index ) ); - break; } + + break; } case types::_STRING_T: {