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:
{