From 6297d2b5527e2c9d40320a4a0adb8ef970bc45f8 Mon Sep 17 00:00:00 2001 From: Alexander 'z33ky' Hirsch <1zeeky@gmail.com> Date: Thu, 5 Sep 2024 22:31:10 +0200 Subject: [PATCH] Fix ScriptHook_t initialization order When a ScriptClassDesc_t for is initialized (SCRIPTDESC), it recursively invokes its parents initializers in order to obtain their pHelper member. Initialization is only done once, so repeated initialization is skipped. Initialization includes assignment of a vector of ScriptHook_t's (DEFINE_SCRIPTFUNC/BEGIN_SCRIPTHOOK), which must be initialized beforehand. Both of these use (static) globals; Within a translation unit, initialization order is defined to be the same as the order of declaration. So within a translation unit we must define all ScriptHook_t's before the ScriptClassDesc_t using them. A problem occurs with the parent initialization though, since there is no defined order between translation units, meaning initialization of a ScriptClassDesc_t can happen before its ScriptHook_t's, despite being the correct order within its translation unit. On MSVC it seems this issue is benign. On GCC/Linux however the initialization of a ScriptHook_t essentially cleared whatever happened during the initialization of the ScriptClassDesc_t, meaning many hooks simply didn't work. This situation is remedied by delaying the initialization of the ScriptClassDesc_t's ScriptHook_t vector to only when the constructor of it is invoked from its translation unit. This is accomplished simply by adding a boolean parameter to the function (GetScriptDesc()) that is true in the global constructor invocation, and false by default (including when doing parent ScriptClassDesc_t initialization). When false, a valid ScriptClassDesc_t pointer is still returned, which is all that is needed for the initialization of the child ScriptClassDesc_t. The value of the returned pointer is a fixed memory location, and does not change due to the delayed initialization. The script-helper must be initialized eagerly though, for the search of a base-class helper. This also changes the SCRIPTDESC slightly to accommodate the eager initialization of helper instance pointer. Fixes #244. --- sp/src/game/client/c_baseentity.cpp | 3 +- sp/src/game/client/mapbase/vscript_vgui.cpp | 8 +-- sp/src/game/client/vscript_client.cpp | 3 +- sp/src/game/server/baseentity.cpp | 3 +- sp/src/game/shared/baseentity_shared.h | 2 +- .../shared/mapbase/vscript_funcs_shared.cpp | 7 +-- sp/src/game/shared/vscript_shared.h | 16 +++-- sp/src/public/vscript/ivscript.h | 59 +++++++++++-------- sp/src/vscript/vscript_bindings_base.cpp | 3 +- sp/src/vscript/vscript_bindings_math.cpp | 3 +- 10 files changed, 56 insertions(+), 51 deletions(-) diff --git a/sp/src/game/client/c_baseentity.cpp b/sp/src/game/client/c_baseentity.cpp index 1d3de004b03..135e12bb136 100644 --- a/sp/src/game/client/c_baseentity.cpp +++ b/sp/src/game/client/c_baseentity.cpp @@ -433,8 +433,7 @@ ScriptHook_t C_BaseEntity::g_Hook_UpdateOnRemove; ScriptHook_t C_BaseEntity::g_Hook_ModifyEmitSoundParams; #endif -BEGIN_ENT_SCRIPTDESC_ROOT( C_BaseEntity, "Root class of all client-side entities" ) - DEFINE_SCRIPT_INSTANCE_HELPER( &g_BaseEntityScriptInstanceHelper ) +BEGIN_ENT_SCRIPTDESC_ROOT_WITH_HELPER( C_BaseEntity, "Root class of all client-side entities", &g_BaseEntityScriptInstanceHelper ) DEFINE_SCRIPTFUNC_NAMED( GetAbsOrigin, "GetOrigin", "" ) DEFINE_SCRIPTFUNC_NAMED( ScriptGetForward, "GetForwardVector", "Get the forward vector of the entity" ) #ifdef MAPBASE_VSCRIPT diff --git a/sp/src/game/client/mapbase/vscript_vgui.cpp b/sp/src/game/client/mapbase/vscript_vgui.cpp index 805d3305140..c679d6e2588 100644 --- a/sp/src/game/client/mapbase/vscript_vgui.cpp +++ b/sp/src/game/client/mapbase/vscript_vgui.cpp @@ -1031,7 +1031,7 @@ void CScriptSurface::DrawColoredTextRect( int font, int x, int y, int w, int h, #define __base() this->_base #define BEGIN_SCRIPTDESC_VGUI( panelClass )\ - BEGIN_SCRIPTDESC_NAMED( CScript_##panelClass##_Helper, IScriptVGUIObject, #panelClass, "" )\ + BEGIN_SCRIPTDESC_NAMED_WITH_HELPER( CScript_##panelClass##_Helper, IScriptVGUIObject, #panelClass, "", VGUI_SCRIPT_INSTANCE_HELPER )\ DEFINE_VGUI_SCRIPTFUNC_##panelClass() // @@ -1235,9 +1235,9 @@ class CScriptVGUIScriptInstanceHelper : public IScriptInstanceHelper static CScriptVGUIScriptInstanceHelper g_ScriptVGUIScriptInstanceHelper; -#define DEFINE_VGUI_SCRIPT_INSTANCE_HELPER() DEFINE_SCRIPT_INSTANCE_HELPER( &g_ScriptVGUIScriptInstanceHelper ) +#define VGUI_SCRIPT_INSTANCE_HELPER &g_ScriptVGUIScriptInstanceHelper #else -#define DEFINE_VGUI_SCRIPT_INSTANCE_HELPER() +#define VGUI_SCRIPT_INSTANCE_HELPER NULL #endif @@ -1866,8 +1866,6 @@ CLASS_HELPER_INTERFACE_ROOT( Panel ) }; #define DEFINE_VGUI_SCRIPTFUNC_Panel()\ - DEFINE_VGUI_SCRIPT_INSTANCE_HELPER()\ -\ DEFINE_SCRIPTFUNC( Destroy, "" )\ DEFINE_SCRIPTFUNC( MakeReadyForUse, "" )\ DEFINE_SCRIPTFUNC( GetName, "" )\ diff --git a/sp/src/game/client/vscript_client.cpp b/sp/src/game/client/vscript_client.cpp index 7bece925a43..f99eef6341f 100644 --- a/sp/src/game/client/vscript_client.cpp +++ b/sp/src/game/client/vscript_client.cpp @@ -234,8 +234,7 @@ class CMaterialProxyScriptInstanceHelper : public IScriptInstanceHelper CMaterialProxyScriptInstanceHelper g_MaterialProxyScriptInstanceHelper; -BEGIN_SCRIPTDESC_ROOT_NAMED( CScriptMaterialProxy, "CScriptMaterialProxy", "Material proxy for VScript" ) - DEFINE_SCRIPT_INSTANCE_HELPER( &g_MaterialProxyScriptInstanceHelper ) +BEGIN_SCRIPTDESC_ROOT_NAMED_WITH_HELPER( CScriptMaterialProxy, "CScriptMaterialProxy", "Material proxy for VScript", &g_MaterialProxyScriptInstanceHelper ) DEFINE_SCRIPTFUNC( GetVarString, "Gets a material var's string value" ) DEFINE_SCRIPTFUNC( GetVarInt, "Gets a material var's int value" ) DEFINE_SCRIPTFUNC( GetVarFloat, "Gets a material var's float value" ) diff --git a/sp/src/game/server/baseentity.cpp b/sp/src/game/server/baseentity.cpp index 9df5cc09df1..71d0c695e8c 100644 --- a/sp/src/game/server/baseentity.cpp +++ b/sp/src/game/server/baseentity.cpp @@ -2257,8 +2257,7 @@ ScriptHook_t CBaseEntity::g_Hook_ModifyEmitSoundParams; ScriptHook_t CBaseEntity::g_Hook_ModifySentenceParams; #endif -BEGIN_ENT_SCRIPTDESC_ROOT( CBaseEntity, "Root class of all server-side entities" ) - DEFINE_SCRIPT_INSTANCE_HELPER( &g_BaseEntityScriptInstanceHelper ) +BEGIN_ENT_SCRIPTDESC_ROOT_WITH_HELPER( CBaseEntity, "Root class of all server-side entities", &g_BaseEntityScriptInstanceHelper ) DEFINE_SCRIPTFUNC_NAMED( ConnectOutputToScript, "ConnectOutput", "Adds an I/O connection that will call the named function when the specified output fires" ) DEFINE_SCRIPTFUNC_NAMED( DisconnectOutputFromScript, "DisconnectOutput", "Removes a connected script function from an I/O event." ) diff --git a/sp/src/game/shared/baseentity_shared.h b/sp/src/game/shared/baseentity_shared.h index 85a0ffd89a1..4891b0bc237 100644 --- a/sp/src/game/shared/baseentity_shared.h +++ b/sp/src/game/shared/baseentity_shared.h @@ -255,7 +255,7 @@ inline HSCRIPT ToHScript(CBaseEntity* pEnt) return (pEnt) ? pEnt->GetScriptInstance() : NULL; } -template <> ScriptClassDesc_t* GetScriptDesc(CBaseEntity*); +template <> ScriptClassDesc_t* GetScriptDesc(CBaseEntity*, bool); inline CBaseEntity* ToEnt(HSCRIPT hScript) { return (hScript) ? (CBaseEntity*)g_pScriptVM->GetInstanceValue(hScript, GetScriptDescForClass(CBaseEntity)) : NULL; diff --git a/sp/src/game/shared/mapbase/vscript_funcs_shared.cpp b/sp/src/game/shared/mapbase/vscript_funcs_shared.cpp index 4c6d4817943..0a1c3c69e59 100644 --- a/sp/src/game/shared/mapbase/vscript_funcs_shared.cpp +++ b/sp/src/game/shared/mapbase/vscript_funcs_shared.cpp @@ -376,8 +376,7 @@ END_SCRIPTDESC(); CPlaneTInstanceHelper g_PlaneTInstanceHelper; -BEGIN_SCRIPTDESC_ROOT( cplane_t, "" ) - DEFINE_SCRIPT_INSTANCE_HELPER( &g_PlaneTInstanceHelper ) +BEGIN_SCRIPTDESC_ROOT_WITH_HELPER( cplane_t, "", &g_PlaneTInstanceHelper ) END_SCRIPTDESC(); static HSCRIPT ScriptTraceLineComplex( const Vector &vecStart, const Vector &vecEnd, HSCRIPT entIgnore, int iMask, int iCollisionGroup ) @@ -509,9 +508,7 @@ static void DestroyFireBulletsInfo( HSCRIPT hBulletsInfo ) //----------------------------------------------------------------------------- CAnimEventTInstanceHelper g_AnimEventTInstanceHelper; -BEGIN_SCRIPTDESC_ROOT( scriptanimevent_t, "" ) - DEFINE_SCRIPT_INSTANCE_HELPER( &g_AnimEventTInstanceHelper ) - +BEGIN_SCRIPTDESC_ROOT_WITH_HELPER( scriptanimevent_t, "", &g_AnimEventTInstanceHelper ) DEFINE_SCRIPTFUNC( GetEvent, "" ) DEFINE_SCRIPTFUNC( SetEvent, "" ) diff --git a/sp/src/game/shared/vscript_shared.h b/sp/src/game/shared/vscript_shared.h index 508342208d4..0447ab8bba4 100644 --- a/sp/src/game/shared/vscript_shared.h +++ b/sp/src/game/shared/vscript_shared.h @@ -21,12 +21,16 @@ inline bool VScriptRunScript( const char *pszScriptName, bool bWarnMissing = fal #define DECLARE_ENT_SCRIPTDESC() ALLOW_SCRIPT_ACCESS(); virtual ScriptClassDesc_t *GetScriptDesc() -#define BEGIN_ENT_SCRIPTDESC( className, baseClass, description ) _IMPLEMENT_ENT_SCRIPTDESC_ACCESSOR( className ); BEGIN_SCRIPTDESC( className, baseClass, description ) -#define BEGIN_ENT_SCRIPTDESC_ROOT( className, description ) _IMPLEMENT_ENT_SCRIPTDESC_ACCESSOR( className ); BEGIN_SCRIPTDESC_ROOT( className, description ) -#define BEGIN_ENT_SCRIPTDESC_NAMED( className, baseClass, scriptName, description ) _IMPLEMENT_ENT_SCRIPTDESC_ACCESSOR( className ); BEGIN_SCRIPTDESC_NAMED( className, baseClass, scriptName, description ) -#define BEGIN_ENT_SCRIPTDESC_ROOT_NAMED( className, scriptName, description ) _IMPLEMENT_ENT_SCRIPTDESC_ACCESSOR( className ); BEGIN_SCRIPTDESC_ROOT_NAMED( className, scriptName, description ) - -#define _IMPLEMENT_ENT_SCRIPTDESC_ACCESSOR( className ) template <> ScriptClassDesc_t * GetScriptDesc( className * ); ScriptClassDesc_t *className::GetScriptDesc() { return ::GetScriptDesc( this ); } +#define BEGIN_ENT_SCRIPTDESC( className, baseClass, description ) _IMPLEMENT_ENT_SCRIPTDESC_ACCESSOR( className ); BEGIN_SCRIPTDESC( className, baseClass, description ) +#define BEGIN_ENT_SCRIPTDESC_WITH_HELPER( className, baseClass, description, helper ) _IMPLEMENT_ENT_SCRIPTDESC_ACCESSOR( className ); BEGIN_SCRIPTDESC_WITH_HELPER( className, baseClass, description, helper ) +#define BEGIN_ENT_SCRIPTDESC_ROOT( className, description ) _IMPLEMENT_ENT_SCRIPTDESC_ACCESSOR( className ); BEGIN_SCRIPTDESC_ROOT( className, description ) +#define BEGIN_ENT_SCRIPTDESC_ROOT_WITH_HELPER( className, description, helper ) _IMPLEMENT_ENT_SCRIPTDESC_ACCESSOR( className ); BEGIN_SCRIPTDESC_ROOT_WITH_HELPER( className, description, helper ) +#define BEGIN_ENT_SCRIPTDESC_NAMED( className, baseClass, scriptName, description ) _IMPLEMENT_ENT_SCRIPTDESC_ACCESSOR( className ); BEGIN_SCRIPTDESC_NAMED( className, baseClass, scriptName, description ) +#define BEGIN_ENT_SCRIPTDESC_NAMED_WITH_HELPER( className, baseClass, scriptName, description, helper ) _IMPLEMENT_ENT_SCRIPTDESC_ACCESSOR( className ); BEGIN_SCRIPTDESC_NAMED_WITH_HELPER( className, baseClass, scriptName, description, helper ) +#define BEGIN_ENT_SCRIPTDESC_ROOT_NAMED( className, scriptName, description ) _IMPLEMENT_ENT_SCRIPTDESC_ACCESSOR( className ); BEGIN_SCRIPTDESC_ROOT_NAMED( className, scriptName, description ) +#define BEGIN_ENT_SCRIPTDESC_ROOT_NAMED_WITH_HELPER( className, scriptName, description, helper ) _IMPLEMENT_ENT_SCRIPTDESC_ACCESSOR( className ); BEGIN_SCRIPTDESC_ROOT_NAMED_WITH_HELPER( className, scriptName, description, helper ) + +#define _IMPLEMENT_ENT_SCRIPTDESC_ACCESSOR( className ) template <> ScriptClassDesc_t * GetScriptDesc( className *, bool ); ScriptClassDesc_t *className::GetScriptDesc() { return ::GetScriptDesc( this ); } // Only allow scripts to create entities during map initialization bool IsEntityCreationAllowedInScripts( void ); diff --git a/sp/src/public/vscript/ivscript.h b/sp/src/public/vscript/ivscript.h index 58f981e037e..79942ceb5b2 100644 --- a/sp/src/public/vscript/ivscript.h +++ b/sp/src/public/vscript/ivscript.h @@ -697,37 +697,48 @@ static inline int ToConstantVariant(int value) // //----------------------------------------------------------------------------- -#define ALLOW_SCRIPT_ACCESS() template friend ScriptClassDesc_t *GetScriptDesc(T *); - -#define BEGIN_SCRIPTDESC( className, baseClass, description ) BEGIN_SCRIPTDESC_NAMED( className, baseClass, #className, description ) -#define BEGIN_SCRIPTDESC_ROOT( className, description ) BEGIN_SCRIPTDESC_ROOT_NAMED( className, #className, description ) - -#define BEGIN_SCRIPTDESC_NAMED( className, baseClass, scriptName, description ) \ - template <> ScriptClassDesc_t* GetScriptDesc(baseClass*); \ - template <> ScriptClassDesc_t* GetScriptDesc(className*); \ - ScriptClassDesc_t & g_##className##_ScriptDesc = *GetScriptDesc(nullptr); \ - template <> ScriptClassDesc_t* GetScriptDesc(className*) \ +#define ALLOW_SCRIPT_ACCESS() template friend ScriptClassDesc_t *GetScriptDesc(T *, bool); + +#define BEGIN_SCRIPTDESC( className, baseClass, description ) BEGIN_SCRIPTDESC_WITH_HELPER( className, baseClass, description, NULL ) +#define BEGIN_SCRIPTDESC_WITH_HELPER( className, baseClass, description, helper ) BEGIN_SCRIPTDESC_NAMED_WITH_HELPER( className, baseClass, #className, description, helper ) +#define BEGIN_SCRIPTDESC_ROOT( className, description ) BEGIN_SCRIPTDESC_ROOT_WITH_HELPER( className, description, NULL ) +#define BEGIN_SCRIPTDESC_ROOT_WITH_HELPER( className, description, helper ) BEGIN_SCRIPTDESC_ROOT_NAMED_WITH_HELPER( className, #className, description, helper ) + +#define BEGIN_SCRIPTDESC_NAMED_WITH_HELPER( className, baseClass, scriptName, description, helper ) \ + template <> ScriptClassDesc_t* GetScriptDesc(baseClass*, bool); \ + template <> ScriptClassDesc_t* GetScriptDesc(className*, bool); \ + ScriptClassDesc_t & g_##className##_ScriptDesc = *GetScriptDesc(nullptr, true); \ + template <> ScriptClassDesc_t* GetScriptDesc(className*, bool init) \ { \ static ScriptClassDesc_t g_##className##_ScriptDesc; \ typedef className _className; \ ScriptClassDesc_t *pDesc = &g_##className##_ScriptDesc; \ - if (pDesc->m_pszClassname) return pDesc; \ - pDesc->m_pszDescription = description; \ - ScriptInitClassDescNamed( pDesc, className, GetScriptDescForClass( baseClass ), scriptName ); \ - ScriptClassDesc_t *pInstanceHelperBase = pDesc->m_pBaseDesc; \ - while ( pInstanceHelperBase ) \ + if (!pDesc->m_pszClassname) \ { \ - if ( pInstanceHelperBase->pHelper ) \ + pDesc->m_pszDescription = description; \ + ScriptClassDesc_t *pBaseDesc = GetScriptDescForClass( baseClass ); \ + ScriptInitClassDescNamed( pDesc, className, pBaseDesc, scriptName ); \ + pDesc->pHelper = helper; \ + if ( !pDesc->pHelper ) \ { \ - pDesc->pHelper = pInstanceHelperBase->pHelper; \ - break; \ + while ( pBaseDesc ) \ + { \ + if ( pBaseDesc->pHelper ) \ + { \ + pDesc->pHelper = pBaseDesc->pHelper; \ + break; \ + } \ + pBaseDesc = pBaseDesc->m_pBaseDesc; \ + } \ } \ - pInstanceHelperBase = pInstanceHelperBase->m_pBaseDesc; \ - } + } \ + if (!init) return pDesc; #define BEGIN_SCRIPTDESC_ROOT_NAMED( className, scriptName, description ) \ - BEGIN_SCRIPTDESC_NAMED( className, ScriptNoBase_t, scriptName, description ) + BEGIN_SCRIPTDESC_ROOT_NAMED_WITH_HELPER( className, scriptName, description, NULL ) +#define BEGIN_SCRIPTDESC_ROOT_NAMED_WITH_HELPER( className, scriptName, description, helper ) \ + BEGIN_SCRIPTDESC_NAMED_WITH_HELPER( className, ScriptNoBase_t, scriptName, description, helper ) #define END_SCRIPTDESC() \ return pDesc; \ @@ -736,7 +747,7 @@ static inline int ToConstantVariant(int value) #define DEFINE_SCRIPTFUNC( func, description ) DEFINE_SCRIPTFUNC_NAMED( func, #func, description ) #define DEFINE_SCRIPTFUNC_NAMED( func, scriptName, description ) ScriptAddFunctionToClassDescNamed( pDesc, _className, func, scriptName, description ); #define DEFINE_SCRIPT_CONSTRUCTOR() ScriptAddConstructorToClassDesc( pDesc, _className ); -#define DEFINE_SCRIPT_INSTANCE_HELPER( p ) pDesc->pHelper = (p); +#define DEFINE_SCRIPT_INSTANCE_HELPER( p ) MUST_USE_BEGIN_SCRIPTDESC_WITH_HELPER_INSTEAD #ifdef MAPBASE_VSCRIPT // Use this for hooks which have no parameters @@ -781,10 +792,10 @@ static inline int ToConstantVariant(int value) do { ScriptMemberDesc_t *pBinding = &((pDesc)->m_Members[(pDesc)->m_Members.AddToTail()]); pBinding->m_pszScriptName = varName; pBinding->m_pszDescription = description; pBinding->m_ReturnType = returnType; } while (0); #endif -template ScriptClassDesc_t *GetScriptDesc(T *); +template ScriptClassDesc_t *GetScriptDesc(T *, bool = false); struct ScriptNoBase_t; -template <> inline ScriptClassDesc_t *GetScriptDesc( ScriptNoBase_t *) { return NULL; } +template <> inline ScriptClassDesc_t *GetScriptDesc( ScriptNoBase_t *, bool ) { return NULL; } #define GetScriptDescForClass( className ) GetScriptDesc( ( className *)NULL ) diff --git a/sp/src/vscript/vscript_bindings_base.cpp b/sp/src/vscript/vscript_bindings_base.cpp index 99b6194aa46..3d6fb7c08c2 100644 --- a/sp/src/vscript/vscript_bindings_base.cpp +++ b/sp/src/vscript/vscript_bindings_base.cpp @@ -387,10 +387,9 @@ CScriptKeyValues::~CScriptKeyValues( ) //============================================================================= CScriptColorInstanceHelper g_ColorScriptInstanceHelper; -BEGIN_SCRIPTDESC_ROOT( Color, "" ) +BEGIN_SCRIPTDESC_ROOT_WITH_HELPER( Color, "", &g_ColorScriptInstanceHelper ) DEFINE_SCRIPT_CONSTRUCTOR() - DEFINE_SCRIPT_INSTANCE_HELPER( &g_ColorScriptInstanceHelper ) DEFINE_SCRIPTFUNC( SetColor, "Sets the color." ) diff --git a/sp/src/vscript/vscript_bindings_math.cpp b/sp/src/vscript/vscript_bindings_math.cpp index cb1567d5492..e5690bd07ac 100644 --- a/sp/src/vscript/vscript_bindings_math.cpp +++ b/sp/src/vscript/vscript_bindings_math.cpp @@ -205,10 +205,9 @@ void ScriptMatrixSetTranslation( const Vector& vecset, HSCRIPT hMat1 ) //============================================================================= CScriptQuaternionInstanceHelper g_QuaternionScriptInstanceHelper; -BEGIN_SCRIPTDESC_ROOT_NAMED( Quaternion, "Quaternion", "A quaternion." ) +BEGIN_SCRIPTDESC_ROOT_NAMED_WITH_HELPER( Quaternion, "Quaternion", "A quaternion.", &g_QuaternionScriptInstanceHelper ) DEFINE_SCRIPT_CONSTRUCTOR() - DEFINE_SCRIPT_INSTANCE_HELPER( &g_QuaternionScriptInstanceHelper ) DEFINE_SCRIPTFUNC_NAMED( ScriptInit, "Init", "Creates a quaternion with the given values." ) DEFINE_MEMBERVAR( "x", FIELD_FLOAT, "The quaternion's i axis component." )