From 6052072fd1cbf9bf272cbf58a8a188a7fc0f7b43 Mon Sep 17 00:00:00 2001 From: FortyTwoFortyTwo Date: Sun, 20 Oct 2024 17:03:29 +0100 Subject: [PATCH] Add forward and native on script vm initialized --- README.md | 14 +++++++++----- scripting/include/vscript.inc | 19 +++++++++++++++++-- scripting/vscript.sp | 8 +++++++- scripting/vscript/list.sp | 25 +++++++++++++++++++++++++ scripting/vscript_test.sp | 27 +++++++++++++++++++-------- 5 files changed, 77 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index bca9781..94592e0 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ All builds can be found [here](https://github.com/FortyTwoFortyTwo/VScript/actio Compiles and executes a script code with params and returns, helpful when `RunScriptCode` input does not support receiving returns. ```sp -public void OnPluginStart() +public void OnAllPluginsLoaded() { HSCRIPT script = VScript_CompileScript("printl(\"Wow a message!\"); return 4242; function PrintMessage(param) { printl(param) }"); @@ -44,7 +44,7 @@ This allows to directly call or detour a function without needing to manually ge ```sp Handle g_SDKCallGetAngles; -public void OnPluginStart() +public void OnAllPluginsLoaded() { VScriptFunction func = VScript_GetClassFunction("CBaseEntity", "GetAngles"); g_SDKCallGetAngles = func.CreateSDKCall(); @@ -77,18 +77,22 @@ Creates a new native function where scripts can make use of it. Does nothing by ```sp VScriptFunction g_NewFunction; -public void OnPluginStart() +public void OnAllPluginsLoaded() { // Create a new function, or get an existing one if name already exists g_NewFunction = VScript_CreateGlobalFunction("NewFunction"); g_NewFunction.SetParam(1, FIELD_FLOAT); g_NewFunction.Return = FIELD_INTEGER; g_NewFunction.SetFunctionEmpty(); + + // If plugin were to be lateloaded and that script vm is already initialized, just manually call it. + if (VScript_IsScriptVMInitialized()) + VScript_OnScriptVMInitialized(); } -public void OnMapStart() +public void VScript_OnScriptVMInitialized() { - // Global function need to be registered everytime g_pScriptVM has been reset, which usually happens on mapchange + // Global function need to be registered everytime g_pScriptVM has been reset, which happens right before this forward g_NewFunction.Register(); } ``` diff --git a/scripting/include/vscript.inc b/scripting/include/vscript.inc index 8d2f407..b7aef0b 100644 --- a/scripting/include/vscript.inc +++ b/scripting/include/vscript.inc @@ -225,7 +225,7 @@ methodmap VScriptFunction < Address // @param from A function to copy from. public native void CopyFrom(VScriptFunction from); - // Register this as a global function until when g_pScriptVM has been reset. + // Register this as a global function until when g_pScriptVM has been reset. This should be called inside VScript_OnScriptVMInitialized forward. public native void Register(); // Creates an SDKCall with parameters auto filled @@ -302,7 +302,7 @@ methodmap VScriptClass < Address // @return Address of VScriptFunction. public native VScriptFunction CreateFunction(); - // Register this class as an instance. This does not require calling VScript_ResetScriptVM unless if modifications were made afterward. + // Register this class as an instance. This should be used inside VScript_OnScriptVMInitialized forward. // // @param instance Name of an instance in script. // @return Created HSCRIPT instance. @@ -410,6 +410,20 @@ methodmap VScriptExecute < Handle public native void GetReturnVector(float buffer[3]); } + +/** + * Called when g_pScriptVM has been fully initialized, this is where VScriptClass.RegisterInstance and VScriptFunction.Register should be called + * @note This forward does not get called on plugin lateload, use VScript_IsScriptVMInitialized to determine whenever to manually call this forward + */ +forward void VScript_OnScriptVMInitialized(); + +/** + * Returns whenever g_pScriptVM has been initialized, useful for plugin start to determine whenever to call VScript_ResetScriptVM or VScript_OnScriptVMInitialized if this were to return true + * + * @return True if script vm is initialized, false otherwise + */ +native bool VScript_IsScriptVMInitialized(); + /** * Deletes g_pScriptVM and creates a new one. This should be used when VScriptClass or VScriptFunction has been modified, including adding new functions to class */ @@ -675,6 +689,7 @@ public void __pl_vscript_SetNTVOptional() MarkNativeAsOptional("VScriptExecute.GetReturnString"); MarkNativeAsOptional("VScriptExecute.GetReturnVector"); + MarkNativeAsOptional("VScript_IsScriptVMInitialized"); MarkNativeAsOptional("VScript_ResetScriptVM"); MarkNativeAsOptional("VScript_CompileScript"); MarkNativeAsOptional("VScript_CompileScriptFile"); diff --git a/scripting/vscript.sp b/scripting/vscript.sp index 48b2ba7..845b46c 100644 --- a/scripting/vscript.sp +++ b/scripting/vscript.sp @@ -2,7 +2,7 @@ #include "include/vscript.inc" -#define PLUGIN_VERSION "1.9.0" +#define PLUGIN_VERSION "1.9.1" #define PLUGIN_VERSION_REVISION "manual" char g_sOperatingSystem[16]; @@ -120,6 +120,7 @@ public APLRes AskPluginLoad2(Handle hMyself, bool bLate, char[] sError, int iLen CreateNative("VScriptExecute.GetReturnString", Native_Execute_GetReturnString); CreateNative("VScriptExecute.GetReturnVector", Native_Execute_GetReturnVector); + CreateNative("VScript_IsScriptVMInitialized", Native_IsScriptVMInitialized); CreateNative("VScript_ResetScriptVM", Native_ResetScriptVM); CreateNative("VScript_CompileScript", Native_CompileScript); CreateNative("VScript_CompileScriptFile", Native_CompileScriptFile); @@ -763,6 +764,11 @@ public any Native_Execute_GetReturnVector(Handle hPlugin, int iNumParams) return 0; } +public any Native_IsScriptVMInitialized(Handle hPlugin, int iNumParams) +{ + return GetScriptVM() != Address_Null; +} + public any Native_ResetScriptVM(Handle hPlugin, int iNumParams) { if (!g_bAllowResetScriptVM) diff --git a/scripting/vscript/list.sp b/scripting/vscript/list.sp index f5c8bb9..8f0fcd1 100644 --- a/scripting/vscript/list.sp +++ b/scripting/vscript/list.sp @@ -1,8 +1,13 @@ +static int g_iInitializing; +static GlobalForward g_fOnScriptVMInitialized; + static ArrayList g_aGlobalFunctions; static ArrayList g_aClasses; void List_LoadGamedata(GameData hGameData) { + g_fOnScriptVMInitialized = new GlobalForward("VScript_OnScriptVMInitialized", ET_Ignore); + DynamicDetour hDetour; hDetour = VTable_CreateDetour(hGameData, "IScriptVM", "Init", ReturnType_Bool); @@ -13,6 +18,9 @@ void List_LoadGamedata(GameData hGameData) hDetour = VTable_CreateDetour(hGameData, "IScriptVM", "RegisterClass", ReturnType_Bool, HookParamType_Int); hDetour.Enable(Hook_Post, List_RegisterClass); + + hDetour = VTable_CreateDetour(hGameData, "IGameSystem", "LevelInitPreEntity", ReturnType_Bool); + hDetour.Enable(Hook_Post, List_LevelInitPreEntity); } void List_LoadDefaults() @@ -26,10 +34,12 @@ void List_LoadDefaults() HSCRIPT pScriptVM = GetScriptVM(); // Create new vscriptvm and set back, so we can collect all of the default stuffs + g_iInitializing = -1; // Don't want to call forward from this SetScriptVM(view_as(Address_Null)); GameSystem_ServerInit(); GameSystem_ServerTerm(); SetScriptVM(pScriptVM); + g_iInitializing = 0; int iEntity = INVALID_ENT_REFERENCE; while ((iEntity = FindEntityByClassname(iEntity, "*")) != INVALID_ENT_REFERENCE) @@ -38,6 +48,9 @@ void List_LoadDefaults() MRESReturn List_Init(Address pScriptVM, DHookReturn hReturn) { + if (g_iInitializing == 0) + g_iInitializing = 1; + g_aGlobalFunctions.Clear(); g_aClasses.Clear(); return MRES_Ignored; @@ -63,6 +76,18 @@ MRESReturn List_RegisterClass(Address pScriptVM, DHookReturn hReturn, DHookParam return MRES_Ignored; } +MRESReturn List_LevelInitPreEntity(Address pGameSystem, DHookReturn hReturn) +{ + if (g_iInitializing == 1) + { + g_iInitializing = 0; + Call_StartForward(g_fOnScriptVMInitialized); + Call_Finish(); + } + + return MRES_Ignored; +} + void List_AddEntityScriptDesc(int iEntity) { VScriptClass pClass = Entity_GetScriptDesc(iEntity); diff --git a/scripting/vscript_test.sp b/scripting/vscript_test.sp index eb64124..5fe19b4 100644 --- a/scripting/vscript_test.sp +++ b/scripting/vscript_test.sp @@ -24,19 +24,15 @@ public Plugin myinfo = url = "https://github.com/FortyTwoFortyTwo/VScript", }; -public void OnMapStart() +public void OnAllPluginsLoaded() { - VScriptFunction pFunction; - VScriptExecute hExecute; - int iValue; - char sBuffer[256]; - float vecResult[3]; + // Best to do it in OnAllPluginsLoaded, to ensure that vscript plugin is fully loaded /* * Test member call with bunch of params, this first because of resetting g_pScriptVM */ - pFunction = VScript_CreateClassFunction("CBaseEntity", "BunchOfParams"); + VScriptFunction pFunction = VScript_CreateClassFunction("CBaseEntity", "BunchOfParams"); pFunction.SetParam(1, FIELD_INTEGER); pFunction.SetParam(2, FIELD_FLOAT); pFunction.SetParam(3, FIELD_BOOLEAN); @@ -45,8 +41,22 @@ public void OnMapStart() pFunction.Return = FIELD_FLOAT; pFunction.SetFunctionEmpty(); - VScript_ResetScriptVM(); + // If script vm is already initialized, force reset it as we can use modified CBaseEntity. + // If were only calling VScriptClass.RegisterInstance or VScriptFunction.Register, only need to manually call VScript_OnScriptVMInitialized() without resetting it. + if (VScript_IsScriptVMInitialized()) + VScript_ResetScriptVM(); +} + +public void VScript_OnScriptVMInitialized() +{ + VScriptFunction pFunction; + VScriptExecute hExecute; + int iValue; + char sBuffer[256]; + float vecResult[3]; + + // BunchOfParams created at OnAllPluginsLoaded RunScript("function BunchOfParams(entity, param1, param2, param3, param4, param5) { return entity.BunchOfParams(param1, param2, param3, param4, param5) }"); // Setup VScript Call @@ -63,6 +73,7 @@ public void OnMapStart() AssertInt(FIELD_VOID, hExecute.ReturnType); // Now detour the newly created function + pFunction = VScript_GetClassFunction("CBaseEntity", "BunchOfParams"); pFunction.CreateDetour().Enable(Hook_Pre, Detour_BunchOfParams); // Test again