using System; using System.IO; using System.Runtime.InteropServices; using UnityEngine; public class UniQuake: MonoBehaviour { #if UNITY_EDITOR private const string DllPath = "Plugins/windows/x86_64/uniquake.dll"; #elif UNITY_STANDALONE_WIN #if UNITY_64 private const string DllPath = "Plugins/x86_64/uniquake.dll"; #else private const string DllPath = "Plugins/x86/uniquake.dll"; #endif #endif private const int MemSize = 0x4000000; // 64 MB of heap space private IntPtr libraryHandle; private QuakeParms quakeParms; private SystemModule systemModule; private RenderModule renderModule; private bool initialized = false; private double startTime; /// /// Time since startup for this particular instance of Quake /// public double CurrentTime => Time.timeAsDouble - startTime; void Start() { systemModule = new SystemModule(this); renderModule = new RenderModule(this); LoadLibrary(); string[] arguments = { "", "-window", "-width", "1440", "-height", "1080", "+developer", "1", }; quakeParms = new QuakeParms { baseDir = Application.persistentDataPath, cacheDir = null, }; quakeParms.SetArguments(arguments); quakeParms.AllocateMemory(MemSize); startTime = Time.timeAsDouble; try { UniQuake_SetFmodSystem(AudioManager.Instance.FmodSystem.handle); UniQuake_Init(quakeParms, systemModule.ToIntPtr, renderModule.ToIntPtr); initialized = true; } catch (QuakeException ex) { if (ex.ExitCode == 0) Debug.Log(ex.Message); else Debug.LogException(ex); Shutdown(); } catch (Exception ex) { Debug.LogException(ex); Shutdown(); } } void Update() { if (!initialized) return; try { UniQuake_Update(Time.deltaTime); } catch (QuakeException ex) { if (ex.ExitCode == 0) Debug.Log(ex.Message); else Debug.LogException(ex); Shutdown(); } catch (Exception ex) { Debug.LogException(ex); Shutdown(); } } private void Shutdown() { initialized = false; UniQuake_Shutdown(); Destroy(this); } private void OnDestroy() { renderModule.Destroy(); systemModule.Destroy(); if (quakeParms != null) { quakeParms.Destroy(); quakeParms = null; } FreeLibrary(); } [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate void UniQuake_InitFunc(QuakeParms parms, IntPtr sysCalls, IntPtr modCalls); private UniQuake_InitFunc UniQuake_Init; [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate void UniQuake_UpdateFunc(float deltaTime); private UniQuake_UpdateFunc UniQuake_Update; [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate void UniQuake_ShutdownFunc(); private UniQuake_ShutdownFunc UniQuake_Shutdown; [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate void UniQuake_SetFmodSystemFunc(IntPtr fmodSystem); private UniQuake_SetFmodSystemFunc UniQuake_SetFmodSystem; private void LoadLibrary() { string dllFile = Path.Combine(Application.dataPath, DllPath); libraryHandle = SystemLibrary.LoadLibrary(dllFile); if (libraryHandle == IntPtr.Zero) { throw new DllNotFoundException($"Failed to load UniQuake library from path: {dllFile}, last error = {Marshal.GetLastWin32Error()}"); } UniQuake_Init = LoadLibraryFunction("UniQuake_Init"); UniQuake_Update = LoadLibraryFunction("UniQuake_Update"); UniQuake_Shutdown = LoadLibraryFunction("UniQuake_Shutdown"); UniQuake_SetFmodSystem = LoadLibraryFunction("UniQuake_SetFmodSystem"); } private TDelegate LoadLibraryFunction(string name) { IntPtr procAddress = SystemLibrary.GetProcAddress(libraryHandle, name); if (procAddress == IntPtr.Zero) { throw new DllNotFoundException($"Could not find library function: {name}"); } return Marshal.GetDelegateForFunctionPointer(procAddress); } private void FreeLibrary() { if (libraryHandle != IntPtr.Zero) { SystemLibrary.FreeLibrary(libraryHandle); libraryHandle = IntPtr.Zero; } } } public class QuakeException: Exception { public int ExitCode { get; } public QuakeException(string message, int exitCode = 0) : base(message) { ExitCode = exitCode; } }