Skip to content

Commit

Permalink
Fix ScriptHook_t initialization order
Browse files Browse the repository at this point in the history
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, with
the proper value for pHelper, 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.

Fixes #244.
  • Loading branch information
z33ky committed Sep 5, 2024
1 parent 471a840 commit 6a2a58c
Show file tree
Hide file tree
Showing 3 changed files with 22 additions and 19 deletions.
2 changes: 1 addition & 1 deletion sp/src/game/shared/baseentity_shared.h
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ inline HSCRIPT ToHScript(CBaseEntity* pEnt)
return (pEnt) ? pEnt->GetScriptInstance() : NULL;
}

template <> ScriptClassDesc_t* GetScriptDesc<CBaseEntity>(CBaseEntity*);
template <> ScriptClassDesc_t* GetScriptDesc<CBaseEntity>(CBaseEntity*, bool);
inline CBaseEntity* ToEnt(HSCRIPT hScript)
{
return (hScript) ? (CBaseEntity*)g_pScriptVM->GetInstanceValue(hScript, GetScriptDescForClass(CBaseEntity)) : NULL;
Expand Down
2 changes: 1 addition & 1 deletion sp/src/game/shared/vscript_shared.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ inline bool VScriptRunScript( const char *pszScriptName, bool bWarnMissing = fal
#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>( className * ); ScriptClassDesc_t *className::GetScriptDesc() { return ::GetScriptDesc( this ); }
#define _IMPLEMENT_ENT_SCRIPTDESC_ACCESSOR( className ) template <> ScriptClassDesc_t * GetScriptDesc<className>( className *, bool ); ScriptClassDesc_t *className::GetScriptDesc() { return ::GetScriptDesc( this ); }

// Only allow scripts to create entities during map initialization
bool IsEntityCreationAllowedInScripts( void );
Expand Down
37 changes: 20 additions & 17 deletions sp/src/public/vscript/ivscript.h
Original file line number Diff line number Diff line change
Expand Up @@ -697,33 +697,36 @@ static inline int ToConstantVariant(int value)
//
//-----------------------------------------------------------------------------

#define ALLOW_SCRIPT_ACCESS() template <typename T> friend ScriptClassDesc_t *GetScriptDesc(T *);
#define ALLOW_SCRIPT_ACCESS() template <typename T> friend ScriptClassDesc_t *GetScriptDesc(T *, bool);

#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>(baseClass*); \
template <> ScriptClassDesc_t* GetScriptDesc<className>(className*); \
ScriptClassDesc_t & g_##className##_ScriptDesc = *GetScriptDesc<className>(nullptr); \
template <> ScriptClassDesc_t* GetScriptDesc<className>(className*) \
template <> ScriptClassDesc_t* GetScriptDesc<baseClass>(baseClass*, bool); \
template <> ScriptClassDesc_t* GetScriptDesc<className>(className*, bool); \
ScriptClassDesc_t & g_##className##_ScriptDesc = *GetScriptDesc<className>(nullptr, true); \
template <> ScriptClassDesc_t* GetScriptDesc<className>(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; \
ScriptInitClassDescNamed( pDesc, className, GetScriptDescForClass( baseClass ), scriptName ); \
ScriptClassDesc_t *pInstanceHelperBase = pDesc->m_pBaseDesc; \
while ( pInstanceHelperBase ) \
{ \
pDesc->pHelper = pInstanceHelperBase->pHelper; \
break; \
if ( pInstanceHelperBase->pHelper ) \
{ \
pDesc->pHelper = pInstanceHelperBase->pHelper; \
break; \
} \
pInstanceHelperBase = pInstanceHelperBase->m_pBaseDesc; \
} \
pInstanceHelperBase = pInstanceHelperBase->m_pBaseDesc; \
}
} \
if (!init) return pDesc;


#define BEGIN_SCRIPTDESC_ROOT_NAMED( className, scriptName, description ) \
Expand Down Expand Up @@ -781,10 +784,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 <typename T> ScriptClassDesc_t *GetScriptDesc(T *);
template <typename T> ScriptClassDesc_t *GetScriptDesc(T *, bool = false);

struct ScriptNoBase_t;
template <> inline ScriptClassDesc_t *GetScriptDesc<ScriptNoBase_t>( ScriptNoBase_t *) { return NULL; }
template <> inline ScriptClassDesc_t *GetScriptDesc<ScriptNoBase_t>( ScriptNoBase_t *, bool ) { return NULL; }

#define GetScriptDescForClass( className ) GetScriptDesc( ( className *)NULL )

Expand Down

0 comments on commit 6a2a58c

Please sign in to comment.