From c2f4d854e0a86850b776b42abd2ecb28d1ff5213 Mon Sep 17 00:00:00 2001 From: Nico de Poel Date: Mon, 29 Mar 2021 16:34:29 +0200 Subject: [PATCH] Moved all of the callbacks interop code into a separate class with a base class for some common abstractions. Allows for better organization of callbacks as the list grows. --- Assets/Scripts/CallbackHandler.cs | 39 ++++++++ Assets/Scripts/CallbackHandler.cs.meta | 3 + Assets/Scripts/SysCalls.cs | 102 ++++++++++++++++++++ Assets/Scripts/SysCalls.cs.meta | 11 +++ Assets/Scripts/UniQuake.cs | 126 ++----------------------- 5 files changed, 161 insertions(+), 120 deletions(-) create mode 100644 Assets/Scripts/CallbackHandler.cs create mode 100644 Assets/Scripts/CallbackHandler.cs.meta create mode 100644 Assets/Scripts/SysCalls.cs create mode 100644 Assets/Scripts/SysCalls.cs.meta diff --git a/Assets/Scripts/CallbackHandler.cs b/Assets/Scripts/CallbackHandler.cs new file mode 100644 index 0000000..0cec3c9 --- /dev/null +++ b/Assets/Scripts/CallbackHandler.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +public abstract class CallbackHandler + where THandler: CallbackHandler +{ + private GCHandle selfHandle; + private List delegateHandles = new List(); + + public IntPtr SelfPtr => GCHandle.ToIntPtr(selfHandle); + + protected CallbackHandler() + { + selfHandle = GCHandle.Alloc(this); + } + + public virtual void Destroy() + { + foreach (var handle in delegateHandles) + { + if (handle.IsAllocated) + handle.Free(); + } + + selfHandle.Free(); + } + + protected IntPtr CreateCallback(TDelegate callback) + { + delegateHandles.Add(GCHandle.Alloc(callback)); + return Marshal.GetFunctionPointerForDelegate(callback); + } + + protected static THandler GetSelf(IntPtr target) + { + return (THandler)GCHandle.FromIntPtr(target).Target; + } +} diff --git a/Assets/Scripts/CallbackHandler.cs.meta b/Assets/Scripts/CallbackHandler.cs.meta new file mode 100644 index 0000000..3150101 --- /dev/null +++ b/Assets/Scripts/CallbackHandler.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: c630080c312b4f238a73741919618661 +timeCreated: 1617026999 \ No newline at end of file diff --git a/Assets/Scripts/SysCalls.cs b/Assets/Scripts/SysCalls.cs new file mode 100644 index 0000000..74f79f3 --- /dev/null +++ b/Assets/Scripts/SysCalls.cs @@ -0,0 +1,102 @@ +using System; +using System.Runtime.InteropServices; +using UnityEngine; + +public class SysCalls: CallbackHandler +{ + private readonly UniQuake uq; + private readonly double startTime; + + private Callbacks callbacks; + private GCHandle callbacksHandle; + + public IntPtr CallbacksPtr => callbacksHandle.AddrOfPinnedObject(); + + public SysCalls(UniQuake uniQuake) + { + uq = uniQuake; + startTime = Time.timeAsDouble; + + callbacks = new Callbacks + { + SysPrint = CreateCallback(Callback_SysPrint), + SysError = CreateCallback(Callback_SysError), + SysQuit = CreateCallback(Callback_SysQuit), + SysFloatTime = CreateCallback(Callback_SysFloatTime), + }; + + callbacksHandle = GCHandle.Alloc(callbacks, GCHandleType.Pinned); + } + + public override void Destroy() + { + if (callbacksHandle.IsAllocated) + callbacksHandle.Free(); + + base.Destroy(); + } + + [StructLayout(LayoutKind.Sequential, Pack = 0)] + private class Callbacks + { + public IntPtr SysPrint; + public IntPtr SysError; + public IntPtr SysQuit; + public IntPtr SysFloatTime; + } + + private delegate void SysPrintCallback(IntPtr target, [MarshalAs(UnmanagedType.LPStr)] string message); + + [AOT.MonoPInvokeCallback(typeof(SysPrintCallback))] + private static void Callback_SysPrint(IntPtr target, string message) + { + GetSelf(target).Print(message); + } + + private void Print(string message) + { + Debug.Log(message); + } + + private delegate void SysErrorCallback(IntPtr target, [MarshalAs(UnmanagedType.LPStr)] string message); + + [AOT.MonoPInvokeCallback(typeof(SysErrorCallback))] + private static void Callback_SysError(IntPtr target, string message) + { + GetSelf(target).Error(message); + } + + private void Error(string message) + { + Debug.LogError(message); + // TODO: kill execution of the DLL entirely (Sys_Quit expects immediate exit, which this doesn't do) + Application.Quit(1); + } + + private delegate void SysQuitCallback(IntPtr target); + + [AOT.MonoPInvokeCallback(typeof(SysQuitCallback))] + private static void Callback_SysQuit(IntPtr target) + { + GetSelf(target).Quit(); + } + + private void Quit() + { + Debug.Log($"Quitting application normally"); + Application.Quit(0); + } + + private delegate double SysFloatTimeCallback(IntPtr target); + + [AOT.MonoPInvokeCallback(typeof(SysFloatTimeCallback))] + private static double Callback_SysFloatTime(IntPtr target) + { + return GetSelf(target).FloatTime(); + } + + private double FloatTime() + { + return Time.timeAsDouble - startTime; + } +} diff --git a/Assets/Scripts/SysCalls.cs.meta b/Assets/Scripts/SysCalls.cs.meta new file mode 100644 index 0000000..4546c81 --- /dev/null +++ b/Assets/Scripts/SysCalls.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3666423522d453147bd7d14ebc8b7ebe +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/UniQuake.cs b/Assets/Scripts/UniQuake.cs index 8d27f01..46b21cd 100644 --- a/Assets/Scripts/UniQuake.cs +++ b/Assets/Scripts/UniQuake.cs @@ -11,15 +11,10 @@ public class UniQuake: MonoBehaviour private const int MaxArgs = 50; // Corresponds with MAX_NUM_ARGVS in engine private QuakeParms quakeParms; - private UnityCallbacks callbacks = new UnityCallbacks(); - private GCHandle selfHandle; - - private double startTime; + private SysCalls sysCalls; void Start() { - startTime = Time.timeAsDouble; - string[] arguments = { "", @@ -43,10 +38,10 @@ public class UniQuake: MonoBehaviour memBase = Marshal.AllocHGlobal(MemSize), memSize = MemSize, }; - - selfHandle = GCHandle.Alloc(this); - - UniQuake_Init(callbacks.CallbacksPtr, GCHandle.ToIntPtr(selfHandle), quakeParms); + + sysCalls = new SysCalls(this); + + UniQuake_Init(sysCalls.CallbacksPtr, sysCalls.SelfPtr, quakeParms); } void Update() @@ -56,8 +51,7 @@ public class UniQuake: MonoBehaviour private void OnDestroy() { - callbacks.Destroy(); - selfHandle.Free(); + sysCalls.Destroy(); if (quakeParms != null) { @@ -81,120 +75,12 @@ public class UniQuake: MonoBehaviour } } - private void SysPrint(string message) - { - Debug.Log(message); - } - - private void SysError(string message) - { - Debug.LogError(message); - // TODO: kill execution of the DLL entirely (Sys_Quit expects immediate exit, which this doesn't do) - Application.Quit(1); - } - - private void SysQuit() - { - Debug.Log($"Quitting application normally"); - Application.Quit(0); - } - - private double SysFloatTime() - { - return Time.timeAsDouble - startTime; - } - [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] private static extern void UniQuake_Init(IntPtr callbacks, IntPtr callbackTarget, QuakeParms parms); [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] private static extern void UniQuake_Update(float deltaTime); - private delegate void SysPrintCallback(IntPtr target, [MarshalAs(UnmanagedType.LPStr)] string message); - - [AOT.MonoPInvokeCallback(typeof(SysPrintCallback))] - private static void Callback_SysPrint(IntPtr target, string message) - { - var self = (UniQuake)GCHandle.FromIntPtr(target).Target; - self.SysPrint(message); - } - - private delegate void SysErrorCallback(IntPtr target, [MarshalAs(UnmanagedType.LPStr)] string message); - - [AOT.MonoPInvokeCallback(typeof(SysErrorCallback))] - private static void Callback_SysError(IntPtr target, string message) - { - var self = (UniQuake)GCHandle.FromIntPtr(target).Target; - self.SysError(message); - } - - private delegate void SysQuitCallback(IntPtr target); - - [AOT.MonoPInvokeCallback(typeof(SysQuitCallback))] - private static void Callback_SysQuit(IntPtr target) - { - var self = (UniQuake)GCHandle.FromIntPtr(target).Target; - self.SysQuit(); - } - - private delegate double SysFloatTimeCallback(IntPtr target); - - [AOT.MonoPInvokeCallback(typeof(SysFloatTimeCallback))] - private static double Callback_SysFloatTime(IntPtr target) - { - var self = (UniQuake)GCHandle.FromIntPtr(target).Target; - return self.SysFloatTime(); - } - - private class UnityCallbacks - { - private List handles = new List(); - private UnityCallbacksContainer container; - private GCHandle containerHandle; - - public UnityCallbacks() - { - container = new UnityCallbacksContainer - { - SysPrint = CreateCallback(Callback_SysPrint), - SysError = CreateCallback(Callback_SysError), - SysQuit = CreateCallback(Callback_SysQuit), - SysFloatTime = CreateCallback(Callback_SysFloatTime), - }; - - containerHandle = GCHandle.Alloc(container, GCHandleType.Pinned); - } - - public IntPtr CallbacksPtr => containerHandle.AddrOfPinnedObject(); - - public void Destroy() - { - if (containerHandle.IsAllocated) - containerHandle.Free(); - - foreach (var handle in handles) - { - if (handle.IsAllocated) - handle.Free(); - } - } - - private IntPtr CreateCallback(TDelegate callback) - { - handles.Add(GCHandle.Alloc(callback)); - return Marshal.GetFunctionPointerForDelegate(callback); - } - } - - [StructLayout(LayoutKind.Sequential, Pack = 0)] - private class UnityCallbacksContainer - { - public IntPtr SysPrint; - public IntPtr SysError; - public IntPtr SysQuit; - public IntPtr SysFloatTime; - } - [StructLayout(LayoutKind.Sequential, Pack = 0)] private class QuakeParms {