You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
216 lines
6.2 KiB
216 lines
6.2 KiB
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Runtime.InteropServices;
|
|
using UnityEngine;
|
|
|
|
public class UniQuake: MonoBehaviour
|
|
{
|
|
private const string DllName = "uniquake.dll";
|
|
private const int MemSize = 0x4000000; // 64 MB of heap space
|
|
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;
|
|
|
|
void Start()
|
|
{
|
|
startTime = Time.timeAsDouble;
|
|
|
|
string[] arguments =
|
|
{
|
|
"",
|
|
"-window",
|
|
"-width", "1440",
|
|
"-height", "1080",
|
|
};
|
|
|
|
IntPtr[] argv = new IntPtr[MaxArgs];
|
|
for (int i = 0; i < arguments.Length && i < MaxArgs; ++i)
|
|
{
|
|
argv[i] = Marshal.StringToHGlobalAnsi(arguments[i]);
|
|
}
|
|
|
|
quakeParms = new QuakeParms
|
|
{
|
|
baseDir = Application.persistentDataPath,
|
|
cacheDir = null,
|
|
argc = arguments.Length,
|
|
argv = argv,
|
|
memBase = Marshal.AllocHGlobal(MemSize),
|
|
memSize = MemSize,
|
|
};
|
|
|
|
selfHandle = GCHandle.Alloc(this);
|
|
|
|
UniQuake_Init(callbacks.CallbacksPtr, GCHandle.ToIntPtr(selfHandle), quakeParms);
|
|
}
|
|
|
|
void Update()
|
|
{
|
|
UniQuake_Update(Time.deltaTime);
|
|
}
|
|
|
|
private void OnDestroy()
|
|
{
|
|
callbacks.Destroy();
|
|
selfHandle.Free();
|
|
|
|
if (quakeParms != null)
|
|
{
|
|
if (quakeParms.memBase != IntPtr.Zero)
|
|
{
|
|
Marshal.FreeHGlobal(quakeParms.memBase);
|
|
quakeParms.memBase = IntPtr.Zero;
|
|
}
|
|
|
|
if (quakeParms.argv != null)
|
|
{
|
|
for (int i = 0; i < quakeParms.argv.Length; ++i)
|
|
{
|
|
if (quakeParms.argv[i] != IntPtr.Zero)
|
|
{
|
|
Marshal.FreeHGlobal(quakeParms.argv[i]);
|
|
quakeParms.argv[i] = IntPtr.Zero;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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
|
|
{
|
|
[MarshalAs(UnmanagedType.LPStr)]
|
|
public string baseDir;
|
|
|
|
[MarshalAs(UnmanagedType.LPStr)]
|
|
public string cacheDir;
|
|
|
|
public int argc;
|
|
|
|
[MarshalAs(UnmanagedType.LPArray, SizeConst = MaxArgs)]
|
|
public IntPtr[] argv;
|
|
|
|
public IntPtr memBase;
|
|
|
|
public int memSize;
|
|
}
|
|
}
|