From 79e95fdd57983066f3ad98e85aea63359d424d20 Mon Sep 17 00:00:00 2001 From: Blixibon Date: Wed, 10 Sep 2025 16:28:55 -0500 Subject: [PATCH] Add various missing functions from TF2 SDK --- src/game/server/netpropmanager.cpp | 5 +- src/game/server/netpropmanager.h | 12 +- src/game/server/vscript_server.nut | 14 ++ src/game/shared/basecombatweapon_shared.cpp | 4 +- .../shared/mapbase/vscript_funcs_shared.cpp | 2 +- .../shared/mapbase/vscript_singletons.cpp | 108 +++++++++++++++ src/vscript/vscript_squirrel.cpp | 126 +++++++++++++++++- 7 files changed, 261 insertions(+), 10 deletions(-) diff --git a/src/game/server/netpropmanager.cpp b/src/game/server/netpropmanager.cpp index 69b976d8f4a..d6d58fb6a64 100644 --- a/src/game/server/netpropmanager.cpp +++ b/src/game/server/netpropmanager.cpp @@ -16,8 +16,6 @@ // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" -#ifndef MAPBASE_VSCRIPT - extern void SendProxy_StringT_To_String( const SendProp *pProp, const void *pStruct, const void *pVarData, DVariant *pOut, int iElement, int objectID ); extern ISaveRestoreOps* ActivityDataOps(); @@ -27,6 +25,7 @@ const char *ArrayElementNameForIdx( size_t i ) return DT_ArrayElementNameForIdx( i ); } +#ifndef MAPBASE_VSCRIPT //----------------------------------------------------------------------------- CNetPropManager::~CNetPropManager() { @@ -1076,6 +1075,7 @@ bool CNetPropManager::GetPropInfo( HSCRIPT hEnt, const char *pszProperty, int el return true; } +#endif //----------------------------------------------------------------------------- @@ -1390,4 +1390,3 @@ void CNetPropManager::GetTable( HSCRIPT hEnt, int iPropType, HSCRIPT hTable ) CollectNestedDataMaps( pDataMap, pBaseEntity, 0, hTable ); } } -#endif diff --git a/src/game/server/netpropmanager.h b/src/game/server/netpropmanager.h index cf9843e3d50..8e0d63d114a 100644 --- a/src/game/server/netpropmanager.h +++ b/src/game/server/netpropmanager.h @@ -13,13 +13,13 @@ #pragma once #endif -#ifndef MAPBASE_VSCRIPT #include "dt_send.h" #include "datamap.h" // Gets and sets SendTable/DataMap netprops and caches results class CNetPropManager { +#ifndef MAPBASE_VSCRIPT public: ~CNetPropManager(); @@ -72,6 +72,7 @@ class CNetPropManager // Searches a ServerClass's SendTable and datamap and returns pertinent prop info inline PropInfo_t GetEntityPropInfo( CBaseEntity *pBaseEntity, const char *pstrProperty, int element ); +#endif // Gets the value of a SendProp and stores it in a table inline void StoreSendPropValue( SendProp *pSendProp, CBaseEntity *pBaseEntity, int iOffset, int iElement, HSCRIPT hTable ); @@ -85,6 +86,13 @@ class CNetPropManager // Iterates through the DataMap and stores prop names in a table inline void CollectNestedDataMaps( datamap_t *pMap, CBaseEntity *pBaseEntity, int iOffset, HSCRIPT hTable ); +#ifdef MAPBASE_VSCRIPT + +public: + // Fills in a passed table with all SendProps or DataMaps for the provided entity + void GetTable( HSCRIPT hEnt, int iPropType, HSCRIPT hTable ); + +#else private: @@ -184,8 +192,8 @@ class CNetPropManager // Fills in a passed table with property info for the provided entity bool GetPropInfo( HSCRIPT hEnt, const char *pstrProperty, int element, HSCRIPT hTable ); -}; #endif +}; #endif // NETPROPMANAGER_H diff --git a/src/game/server/vscript_server.nut b/src/game/server/vscript_server.nut index 9e2d84fe178..0d304bb702b 100644 --- a/src/game/server/vscript_server.nut +++ b/src/game/server/vscript_server.nut @@ -83,6 +83,20 @@ function PrecacheOther( a, b = "" ) return PrecacheOther( a, b ) } +function PrecacheEntityFromTable( a, b = null ) +{ + if ( b == null ) + { + // Table only + return DoPrecacheEntityFromTable( a.classname, a ) + } + else + { + // Classname + table + return DoPrecacheEntityFromTable( a, b ) + } +} + function __ReplaceClosures( script, scope ) { if ( !scope ) diff --git a/src/game/shared/basecombatweapon_shared.cpp b/src/game/shared/basecombatweapon_shared.cpp index 4eaaa4814ab..f64a98546cb 100644 --- a/src/game/shared/basecombatweapon_shared.cpp +++ b/src/game/shared/basecombatweapon_shared.cpp @@ -3200,8 +3200,8 @@ BEGIN_ENT_SCRIPTDESC( CBaseCombatWeapon, BASECOMBATWEAPON_DERIVED_FROM, "The bas DEFINE_SCRIPTFUNC( GetWeight, "Get the weapon's weight." ) DEFINE_SCRIPTFUNC( GetPrintName, "" ) - DEFINE_SCRIPTFUNC_CL( GetSlot, "" ) - DEFINE_SCRIPTFUNC_CL( GetPosition, "" ) + DEFINE_SCRIPTFUNC( GetSlot, "" ) + DEFINE_SCRIPTFUNC( GetPosition, "" ) DEFINE_SCRIPTFUNC( CanBePickedUpByNPCs, "Check if the weapon can be picked up by NPCs." ) diff --git a/src/game/shared/mapbase/vscript_funcs_shared.cpp b/src/game/shared/mapbase/vscript_funcs_shared.cpp index dbdba0e8d72..5c13ced4484 100644 --- a/src/game/shared/mapbase/vscript_funcs_shared.cpp +++ b/src/game/shared/mapbase/vscript_funcs_shared.cpp @@ -975,7 +975,7 @@ void RegisterSharedScriptFunctions() #ifndef CLIENT_DLL ScriptRegisterFunction( g_pScriptVM, AddThinkToEnt, "This will put a think function onto an entity, or pass null to remove it. This is NOT chained, so be careful." ); - ScriptRegisterFunction( g_pScriptVM, PrecacheEntityFromTable, "Precache an entity from KeyValues in a table." ); + ScriptRegisterFunctionNamed( g_pScriptVM, PrecacheEntityFromTable, "DoPrecacheEntityFromTable", SCRIPT_ALIAS( "PrecacheEntityFromTable", "Precache an entity from KeyValues in a table." ) ); ScriptRegisterFunction( g_pScriptVM, SpawnEntityFromTable, "Native function for entity spawning." ); #endif // !CLIENT_DLL ScriptRegisterFunction( g_pScriptVM, EntIndexToHScript, "Returns the script handle for the given entity index." ); diff --git a/src/game/shared/mapbase/vscript_singletons.cpp b/src/game/shared/mapbase/vscript_singletons.cpp index a7fe8c42464..07cbf14d6d2 100644 --- a/src/game/shared/mapbase/vscript_singletons.cpp +++ b/src/game/shared/mapbase/vscript_singletons.cpp @@ -41,6 +41,8 @@ #if !defined(NO_STEAM) #include "steam/steam_api.h" #endif +#else +#include "netpropmanager.h" #endif #include "vscript_singletons.h" @@ -103,6 +105,18 @@ extern ISaveRestoreOps* GetStdStringDataOps(); #endif #endif +#ifndef CLIENT_DLL +// This is a smaller version of the stock net prop manager which has functions we need +CNetPropManager g_ScriptNetPropManagerStub; +#endif + +// Copied from netpropmanager.cpp +const char *s_pszBannedNetProps[] +{ + "EntityQuality", + "AccountID", +}; + //============================================================================= // Net Prop Manager // Based on L4D2 API @@ -461,6 +475,16 @@ class CScriptNetPropManager // CPlayerResource::m_iHealth and CBaseEntity::m_iHealth varinfo_t *GetVarInfo( CBaseEntity *pEnt, const char *szProp, int index ) { + // Copied from netpropmanager.cpp + for ( int i = 0; i < ARRAYSIZE( s_pszBannedNetProps ); i++ ) + { + if ( V_stristr( szProp, s_pszBannedNetProps[ i ] ) != NULL) + { + // Replace any banned properties with a dummy string. + szProp = "Y6WP5EH4I45F2LMKSDY2"; + } + } + int offset = 0; NetTable *pTable = GetNetTable( GetNetworkClass( pEnt ) ); NetProp *pProp = FindInNetTable( (char*)pEnt, pTable, szProp, &offset ); @@ -1615,6 +1639,16 @@ class CScriptNetPropManager } } + bool GetPropBoolArray( HSCRIPT hEnt, const char *szProp, int index ) + { + return (bool)GetPropIntArray( hEnt, szProp, index ); + } + + void SetPropBoolArray( HSCRIPT hEnt, const char *szProp, bool value, int index ) + { + SetPropIntArray( hEnt, szProp, (int)value, index ); + } + #define GetProp( type, name )\ type GetProp##name( HSCRIPT hEnt, const char* szProp )\ {\ @@ -1629,6 +1663,8 @@ class CScriptNetPropManager GetProp( int, Int ); SetProp( int, Int ); + GetProp( bool, Bool ); + SetProp( bool, Bool ); GetProp( float, Float ); SetProp( float, Float ); GetProp( HSCRIPT, Entity ); @@ -1641,6 +1677,62 @@ class CScriptNetPropManager #undef GetProp #undef SetProp + //----------------------------------------------------------------------------- + // Adapted from netpropmanager.cpp + //----------------------------------------------------------------------------- + bool GetPropInfo( HSCRIPT hEnt, const char *szProp, int index, HSCRIPT hTable ) + { + CBaseEntity *pEnt = ToEnt( hEnt ); + if ( !pEnt ) + return false; + + varinfo_t *pInfo = CacheFetch( pEnt, szProp ); + if ( !pInfo ) + { + pInfo = GetVarInfo( pEnt, szProp, INDEX_GET_TYPE ); + + if ( !pInfo ) + return false; + } + + g_pScriptVM->SetValue( hTable, "is_sendprop", !pInfo->isNotNetworked ); + g_pScriptVM->SetValue( hTable, "type", pInfo->datatype ); + + int size = 0; + if ( pInfo->datatype == types::_STRING_T || pInfo->datatype == types::_CSTRING || pInfo->datatype == types::_INT8 ) + { + size = pInfo->stringsize; + } + else + { + // Revert MASK_INT_SIZE to get original size + int bits = pInfo->mask; + while (bits > 0) + { + bits >>= 1; + size++; + } + } + + // TODO: pInfo->arraysize stores both GetNumElements() and GetNumProps() + g_pScriptVM->SetValue( hTable, "bits", size ); + g_pScriptVM->SetValue( hTable, "elements", pInfo->arraysize ); + g_pScriptVM->SetValue( hTable, "offset", pInfo->GetOffset( index ) ); + g_pScriptVM->SetValue( hTable, "length", pInfo->arraysize ); + g_pScriptVM->SetValue( hTable, "array_props", pInfo->elemsize ); + g_pScriptVM->SetValue( hTable, "flags", pInfo->isUnsigned ? SPROP_UNSIGNED : 0 ); // TODO: Proper flag storage? + + return true; + } + +#ifndef CLIENT_DLL + void GetTable( HSCRIPT hEnt, int iPropType, HSCRIPT hTable ) + { + // To avoid copying a bunch of code, we keep a piece of the original net prop manager around for this + g_ScriptNetPropManagerStub.GetTable( hEnt, iPropType, hTable ); + } +#endif + #ifdef _DEBUG private: CUtlBuffer m_output; @@ -2541,9 +2633,19 @@ BEGIN_SCRIPTDESC_ROOT_NAMED( CScriptNetPropManager, "CNetPropManager", SCRIPT_SI DEFINE_SCRIPTFUNC( SetPropStringArray, "Sets a string in an array." ) DEFINE_SCRIPTFUNC( SetPropVector, "Sets to the specified vector." ) DEFINE_SCRIPTFUNC( SetPropVectorArray, "Sets a 3D vector in an array." ) + DEFINE_SCRIPTFUNC( GetPropInfo, "Fills in a passed table with property info for the provided entity." ) +#ifndef CLIENT_DLL + DEFINE_SCRIPTFUNC( GetTable, "Fills in a passed table with all props of a specified type for the provided entity (set prop_type to 0 for SendTable or 1 for DataMap)." ) +#endif #ifdef _DEBUG DEFINE_SCRIPTFUNC( Dump, "Dump all readable netprop and datafield values of this entity. Pass in file name to write into." ); #endif + + // TF2 SDK compatibility + DEFINE_SCRIPTFUNC( GetPropBool, SCRIPT_HIDE ) + DEFINE_SCRIPTFUNC( GetPropBoolArray, SCRIPT_HIDE ) + DEFINE_SCRIPTFUNC( SetPropBool, SCRIPT_HIDE ) + DEFINE_SCRIPTFUNC( SetPropBoolArray, SCRIPT_HIDE ) END_SCRIPTDESC(); //============================================================================= @@ -5016,6 +5118,11 @@ class CScriptConvarAccessor : public CAutoGameSystem GameRules()->SaveConvar( cvar ); } + bool IsConVarOnAllowList( const char *pszConVar ) + { + return !IsBlockedConvar( pszConVar ); + } + } g_ScriptConvarAccessor; @@ -5233,6 +5340,7 @@ BEGIN_SCRIPTDESC_ROOT_NAMED( CScriptConvarAccessor, "CConvars", SCRIPT_SINGLETON DEFINE_SCRIPTFUNC( SetBool, "Sets the value of the convar as a bool." ) DEFINE_SCRIPTFUNC( SetStr, "Sets the value of the convar as a string." ) DEFINE_SCRIPTFUNC_NAMED( SetVariant, "SetValue", "Sets the value of the convar with any applicable type." ) + DEFINE_SCRIPTFUNC( IsConVarOnAllowList, "Checks if the cvar is allowed to be changed." ) END_SCRIPTDESC(); diff --git a/src/vscript/vscript_squirrel.cpp b/src/vscript/vscript_squirrel.cpp index 5d476a108ea..49dc6b79454 100644 --- a/src/vscript/vscript_squirrel.cpp +++ b/src/vscript/vscript_squirrel.cpp @@ -15,7 +15,7 @@ #include "squirrel.h" #include "sqstdaux.h" -//#include "sqstdblob.h" +#include "sqstdblob.h" //#include "sqstdsystem.h" #include "sqstdtime.h" //#include "sqstdio.h" @@ -983,6 +983,117 @@ namespace SQVector return 1; } + SQInteger Forward(HSQUIRRELVM vm) + { + Vector* v1 = nullptr; + + if (sq_gettop(vm) != 1 || + SQ_FAILED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v1, TYPETAG_VECTOR))) + { + return sq_throwerror(vm, "Expected (Vector)"); + } + + sq_getclass(vm, 1); + sq_createinstance(vm, -1); + SQUserPointer p; + sq_getinstanceup(vm, -1, &p, 0); + new(p) Vector(); + AngleVectors( *((QAngle*)v1), (Vector*)p ); + sq_remove(vm, -2); + + return 1; + } + + SQInteger Left(HSQUIRRELVM vm) + { + Vector* v1 = nullptr; + + if (sq_gettop(vm) != 1 || + SQ_FAILED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v1, TYPETAG_VECTOR))) + { + return sq_throwerror(vm, "Expected (Vector)"); + } + + sq_getclass(vm, 1); + sq_createinstance(vm, -1); + SQUserPointer p; + sq_getinstanceup(vm, -1, &p, 0); + new(p) Vector(); + AngleVectors( *((QAngle*)v1), NULL, (Vector*)p, NULL ); // Despite being named "Left", docs suggest this returns right vector in live TF2 + sq_remove(vm, -2); + + return 1; + } + + SQInteger Up(HSQUIRRELVM vm) + { + Vector* v1 = nullptr; + + if (sq_gettop(vm) != 1 || + SQ_FAILED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v1, TYPETAG_VECTOR))) + { + return sq_throwerror(vm, "Expected (Vector)"); + } + + sq_getclass(vm, 1); + sq_createinstance(vm, -1); + SQUserPointer p; + sq_getinstanceup(vm, -1, &p, 0); + new(p) Vector(); + AngleVectors( *((QAngle*)v1), NULL, NULL, (Vector*)p ); + sq_remove(vm, -2); + + return 1; + } + + SQInteger Pitch(HSQUIRRELVM vm) + { + Vector* v1 = nullptr; + + if (sq_gettop(vm) != 1 || + SQ_FAILED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v1, TYPETAG_VECTOR))) + { + return sq_throwerror(vm, "Expected (Vector)"); + } + + sq_pushfloat(vm, v1->x); + return 1; + } + + SQInteger Yaw(HSQUIRRELVM vm) + { + Vector* v1 = nullptr; + + if (sq_gettop(vm) != 1 || + SQ_FAILED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v1, TYPETAG_VECTOR))) + { + return sq_throwerror(vm, "Expected (Vector)"); + } + + sq_pushfloat(vm, v1->y); + return 1; + } + + SQInteger Roll(HSQUIRRELVM vm) + { + Vector* v1 = nullptr; + + if (sq_gettop(vm) != 1 || + SQ_FAILED(sq_getinstanceup(vm, 1, (SQUserPointer*)&v1, TYPETAG_VECTOR))) + { + return sq_throwerror(vm, "Expected (Vector)"); + } + + sq_pushfloat(vm, v1->z); + return 1; + } + + SQInteger ToQuat(HSQUIRRELVM vm) + { + // TODO + return sq_throwerror(vm, "ToQuat() is currently unsupported in Mapbase"); + } + SQInteger ToString(HSQUIRRELVM vm) { Vector* v1 = nullptr; @@ -1082,6 +1193,15 @@ namespace SQVector {_SC("_typeof"), TypeOf, 1, _SC(".")}, {_SC("_nexti"), Nexti, 2, _SC("..")}, + // TF2 SDK compatibility + {_SC("Forward"), Forward, 1, _SC("...")}, + {_SC("Left"), Left, 1, _SC("...")}, + {_SC("Up"), Up, 1, _SC("...")}, + {_SC("Pitch"), Pitch, 1, _SC("...")}, + {_SC("Yaw"), Yaw, 1, _SC("...")}, + {_SC("Roll"), Roll, 1, _SC("...")}, + {_SC("ToQuat"), ToQuat, 1, _SC("...")}, + {nullptr,(SQFUNCTION)0,0,nullptr} }; @@ -2068,9 +2188,11 @@ bool SquirrelVM::Init() // that also depends on compile errors not showing up and relies on IFilesystem with // a path prefix. // - //sqstd_register_bloblib(vm_); //sqstd_register_iolib(vm_); //sqstd_register_systemlib(vm_); + + // bloblib is used in the TF2 SDK + sqstd_register_bloblib(vm_); // There is no vulnerability in getting time. sqstd_register_timelib(vm_);