using NetHostBeef; using System; using System.Interop; using GlitchyEngine.Core; using GlitchyEngine.Scripting.Classes; using static GlitchyEngine.Scripting.ScriptGlue; namespace GlitchyEngine.Scripting; public struct ScriptFunctionPointers { public function void(UUID entityId) OnCreate; public function void(UUID entityId, float deltaTime) OnUpdate; public function void(UUID entityId, bool callEntity) OnDestroy; public function void(UUID entityId, Collision2D collision) OnCollisionEnter2D; } static class CoreClrHelper { static HostFxr.InitializeForDotnetCommandLineFn HostFxr_Init; static HostFxr.GetRuntimeDelegateFn HostFxr_GetDelegate; static HostFxr.CloseFn HostFxr_Close; static CoreClr.LoadAssemblyAndGetFunctionPointerFn LoadAssemblyAndGetFunctionPointerFn; static CoreClr.GetFunctionPointerFn GetFunctionPointerFn; private function void LoadScriptAssemblyFunc(uint8* appData, int64 appLength, uint8* pdbData, int64 pdbLength); //static function void(char8* path) LoadScriptAssembly; static LoadScriptAssemblyFunc _loadScriptAssembly; static function void() _unloadScriptAssembly; static function void() _waitUntilOldAssemblyDead; // public static unsafe void GetScriptClasses(void** outBuffer, long* length) private function void GetScriptClassesFunc(void** outBuffer, int64* length); static GetScriptClassesFunc _getScriptClasses; static function void() _freeScriptClassNames; //public static unsafe ScriptFunctions CreateScriptInstance(UUID entityId, byte* scriptClassName) private function void CreateScriptInstanceFunc(UUID entityId, char8* scriptClassName); static CreateScriptInstanceFunc _createScriptInstance; private function void ThrowExceptionFunc(char8* message); static ThrowExceptionFunc _throwException; private function void RegisterComponentTypeFunc(char8* fullComponentTypeName, function EngineResult(UUID entityId) addComponent, function EngineResult(UUID entityId) hasComponent, function EngineResult(UUID entityId) removeComponent); static RegisterComponentTypeFunc _registerComponentType; public static ScriptFunctionPointers _entityScriptFunctions; public static void Init(StringView coreAssemblyPath) { LoadHostFxr(); InitAndStartRuntime(coreAssemblyPath); PrepareFunction(); } static void LoadHostFxr() { char_t[256] buffer = ?; c_size bufferSize = buffer.Count; int rc = NetHost.get_hostfxr_path(&buffer, &bufferSize, null); Log.EngineLogger.AssertDebug(rc == 0, "Failed to get HostFxr path."); // Load hostfxr and get desired exports void* lib = NetHostHelper.LoadLibrary(&buffer); HostFxr_Init = NetHostHelper.GetExport(lib, "hostfxr_initialize_for_dotnet_command_line"); HostFxr_GetDelegate = NetHostHelper.GetExport(lib, "hostfxr_get_runtime_delegate"); HostFxr_Close = NetHostHelper.GetExport(lib, "hostfxr_close"); Log.EngineLogger.AssertDebug(HostFxr_Init != null && HostFxr_GetDelegate != null && HostFxr_Close != null, "Retrieving at least one HostFxr function failed."); } static void InitAndStartRuntime(StringView coreAssembly) { char_t* ptr = NetHostHelper.GetScopedRawPtr!(coreAssembly); char_t*[2] args = char_t*[]( ptr, null ); HostFxr.Handle cxt = null; int rc = HostFxr_Init(1, &args, null, &cxt); if (rc != 0 || cxt == null) { Log.EngineLogger.Error($"Failed to initialize HostFxr: {rc}"); HostFxr_Close(cxt); } Log.EngineLogger.Assert(rc == 0, "Failed to initialize HostFxr."); rc = HostFxr_GetDelegate(cxt, .LoadAssemblyAndGetFunctionPointer, (void**)&LoadAssemblyAndGetFunctionPointerFn); Log.EngineLogger.Assert(rc == 0, scope $"Get delegate failed: {rc}. (LoadAssemblyAndGetFunctionPointer)"); rc = HostFxr_GetDelegate(cxt, .GetFunctionPointer, (void**)&GetFunctionPointerFn); Log.EngineLogger.Assert(rc == 0, scope $"Get delegate failed: {rc}. (GetFunctionPointer)"); HostFxr_Close(cxt); } static void PrepareFunction() { GetFunctionPointerUnmanagedCallersOnly("GlitchyEngine.ScriptGlue, ScriptCore", "LoadScriptAssembly", out _loadScriptAssembly); GetFunctionPointerUnmanagedCallersOnly("GlitchyEngine.ScriptGlue, ScriptCore", "UnloadScriptAssembly", out _unloadScriptAssembly); GetFunctionPointerUnmanagedCallersOnly("GlitchyEngine.ScriptGlue, ScriptCore", "WaitUntilOldAssemblyDead", out _waitUntilOldAssemblyDead); GetFunctionPointerUnmanagedCallersOnly("GlitchyEngine.ScriptGlue, ScriptCore", "GetScriptClasses", out _getScriptClasses); GetFunctionPointerUnmanagedCallersOnly("GlitchyEngine.ScriptGlue, ScriptCore", "FreeScriptClassNames", out _freeScriptClassNames); GetFunctionPointerUnmanagedCallersOnly("GlitchyEngine.ScriptGlue, ScriptCore", "CreateScriptInstance", out _createScriptInstance); GetFunctionPointerUnmanagedCallersOnly("GlitchyEngine.ScriptGlue, ScriptCore", "RegisterComponentType", out _registerComponentType); InitEntityFunctions(); } static void InitEntityFunctions() { GetFunctionPointerUnmanagedCallersOnly("GlitchyEngine.ScriptGlue, ScriptCore", "InvokeEntityOnCreate", out _entityScriptFunctions.OnCreate); GetFunctionPointerUnmanagedCallersOnly("GlitchyEngine.ScriptGlue, ScriptCore", "InvokeEntityOnUpdate", out _entityScriptFunctions.OnUpdate); GetFunctionPointerUnmanagedCallersOnly("GlitchyEngine.ScriptGlue, ScriptCore", "InvokeEntityOnDestroy", out _entityScriptFunctions.OnDestroy); GetFunctionPointerUnmanagedCallersOnly("GlitchyEngine.ScriptGlue, ScriptCore", "InvokeEntityOnCollisionEnter2D", out _entityScriptFunctions.OnCollisionEnter2D); } public static void LoadScriptAssembly(Span appAssemblyData, Span pdbData) { _loadScriptAssembly(appAssemblyData.Ptr, appAssemblyData.Length, pdbData.Ptr, pdbData.Length); } public static void UnloadScriptAssembly() { _unloadScriptAssembly(); } public static void WaitUntilOldAssemblyDead() { _waitUntilOldAssemblyDead(); } /// Gets an array of script class infos. Use FreeScriptClassNames to release the buffer. public static void GetScriptClasses(out void* outBuffer, out int64 entryCount) { outBuffer = null; entryCount = 0; _getScriptClasses(&outBuffer, &entryCount); } /// Releases the buffer created by GetScriptClasses public static void FreeScriptClassNames() { _freeScriptClassNames(); } public static void CreateScriptInstance(UUID entityId, StringView scriptName) { _createScriptInstance(entityId, scriptName.Ptr); } public static int GetFunctionPointer(StringView typeName, StringView methodName, StringView delegateName, out T outDelegate) where T: operator explicit void* { void* funPtr = null; int rc = GetFunctionPointerFn(NetHostHelper.GetScopedRawPtr!(typeName), NetHostHelper.GetScopedRawPtr!(methodName), NetHostHelper.GetScopedRawPtr!(delegateName), null, null, out funPtr); outDelegate = (T)funPtr; return rc; } public static int GetFunctionPointerDefaultDelegate(StringView typeName, StringView methodName, out CoreClr.DefaultEntryPoint outDelegate) { void* funPtr = null; int rc = GetFunctionPointerFn(NetHostHelper.GetScopedRawPtr!(typeName), NetHostHelper.GetScopedRawPtr!(methodName), null, null, null, out funPtr); outDelegate = (CoreClr.DefaultEntryPoint)funPtr; return rc; } public static Result GetFunctionPointerUnmanagedCallersOnly(StringView typeName, StringView methodName, out T outDelegate) where T: operator explicit void* { void* funPtr = null; int rc = GetFunctionPointerFn(NetHostHelper.GetScopedRawPtr!(typeName), NetHostHelper.GetScopedRawPtr!(methodName), CoreClr.UNMANAGEDCALLERSONLY_METHOD, null, null, out funPtr); outDelegate = (T)funPtr; if (rc == 0) return .Ok; return .Err(rc); } public static void ThrowException(StringView message) { _throwException(message.Ptr); } public static void RegisterComponent(StringView fullComponentTypeName, function EngineResult(UUID entityId) addComponent, function EngineResult(UUID entityId) hasComponent, function EngineResult(UUID entityId) removeComponent) { _registerComponentType(fullComponentTypeName.Ptr, addComponent, hasComponent, removeComponent); } }