Browse Source

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.

console
Nico de Poel 5 years ago
parent
commit
c2f4d854e0
  1. 39
      Assets/Scripts/CallbackHandler.cs
  2. 3
      Assets/Scripts/CallbackHandler.cs.meta
  3. 102
      Assets/Scripts/SysCalls.cs
  4. 11
      Assets/Scripts/SysCalls.cs.meta
  5. 126
      Assets/Scripts/UniQuake.cs

39
Assets/Scripts/CallbackHandler.cs

@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
public abstract class CallbackHandler<THandler>
where THandler: CallbackHandler<THandler>
{
private GCHandle selfHandle;
private List<GCHandle> delegateHandles = new List<GCHandle>();
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>(TDelegate callback)
{
delegateHandles.Add(GCHandle.Alloc(callback));
return Marshal.GetFunctionPointerForDelegate(callback);
}
protected static THandler GetSelf(IntPtr target)
{
return (THandler)GCHandle.FromIntPtr(target).Target;
}
}

3
Assets/Scripts/CallbackHandler.cs.meta

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: c630080c312b4f238a73741919618661
timeCreated: 1617026999

102
Assets/Scripts/SysCalls.cs

@ -0,0 +1,102 @@
using System;
using System.Runtime.InteropServices;
using UnityEngine;
public class SysCalls: CallbackHandler<SysCalls>
{
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<SysPrintCallback>(Callback_SysPrint),
SysError = CreateCallback<SysErrorCallback>(Callback_SysError),
SysQuit = CreateCallback<SysQuitCallback>(Callback_SysQuit),
SysFloatTime = CreateCallback<SysFloatTimeCallback>(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;
}
}

11
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:

126
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<GCHandle> handles = new List<GCHandle>();
private UnityCallbacksContainer container;
private GCHandle containerHandle;
public UnityCallbacks()
{
container = new UnityCallbacksContainer
{
SysPrint = CreateCallback<SysPrintCallback>(Callback_SysPrint),
SysError = CreateCallback<SysErrorCallback>(Callback_SysError),
SysQuit = CreateCallback<SysQuitCallback>(Callback_SysQuit),
SysFloatTime = CreateCallback<SysFloatTimeCallback>(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>(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
{

Loading…
Cancel
Save