context: // This file is provided under The MIT License as part of Steamworks.NET.
  // Copyright (c) 2013-2017 Riley Labrecque
  // Please see the included LICENSE.txt for additional information.
  
  // Changes to this file will be reverted when you update Steamworks.NET
  
  #if !DISABLESTEAMWORKS
  
  #if UNITY_3_5 || UNITY_4_0 || UNITY_4_1 || UNITY_4_2 || UNITY_4_3 || UNITY_4_5 || UNITY_4_6
  	#error Unsupported Unity platform. Steamworks.NET requires Unity 4.7 or higher.
  #elif UNITY_4_7 || UNITY_5 || UNITY_2017_1_OR_NEWER
  	#if UNITY_EDITOR_WIN || (UNITY_STANDALONE_WIN && !UNITY_EDITOR)
  		#define WINDOWS_BUILD
  	#endif
  #elif STEAMWORKS_WIN
  	#define WINDOWS_BUILD
  #elif STEAMWORKS_LIN_OSX
  	// So that we don't trigger the else.
  #else
  	#error You need to define STEAMWORKS_WIN, or STEAMWORKS_LIN_OSX. Refer to the readme for more details.
  #endif
  
  // Unity 32bit Mono on Windows crashes with ThisCall/Cdecl for some reason, StdCall without the 'this' ptr is the only thing that works..?
  #if (UNITY_EDITOR_WIN && !UNITY_EDITOR_64) || (!UNITY_EDITOR && UNITY_STANDALONE_WIN && !UNITY_64)
  	#define STDCALL
  #elif STEAMWORKS_WIN
  	#define THISCALL
  #endif
  
  // Calling Conventions:
  // Unity x86 Windows        - StdCall (No this pointer)
  // Unity x86 Linux          - Cdecl
  // Unity x86 OSX            - Cdecl
  // Unity x64 Windows        - Cdecl
  // Unity x64 Linux          - Cdecl
  // Unity x64 OSX            - Cdecl
  // Microsoft x86 Windows    - ThisCall
  // Microsoft x64 Windows    - ThisCall
  // Mono x86 Linux           - Cdecl
  // Mono x86 OSX             - Cdecl
  // Mono x64 Linux           - Cdecl
  // Mono x64 OSX             - Cdecl
  // Mono on Windows is probably not supported.
  
  using System;
  using System.Runtime.InteropServices;
  
  namespace Steamworks {
  	public static class CallbackDispatcher {
  		// We catch exceptions inside callbacks and reroute them here.
  		// For some reason throwing an exception causes RunCallbacks() to break otherwise.
  		// If you have a custom ExceptionHandler in your engine you can register it here manually until we get something more elegant hooked up.
  		public static void ExceptionHandler(Exception e) {
  #if UNITY_STANDALONE
  			UnityEngine.Debug.LogException(e);
  #elif STEAMWORKS_WIN || STEAMWORKS_LIN_OSX
  			Console.WriteLine(e.Message);
  #endif
          }
      }
  
  	public sealed class Callback<T> : IDisposable {
  		private CCallbackBaseVTable VTable;
  		private IntPtr m_pVTable = IntPtr.Zero;
  		private CCallbackBase m_CCallbackBase;
  		private GCHandle m_pCCallbackBase;
  
  		public delegate void DispatchDelegate(T param);
  		private event DispatchDelegate m_Func;
  
  		private bool m_bGameServer;
  		private readonly int m_size = Marshal.SizeOf(typeof(T));
  
  		private bool m_bDisposed = false;
  
  		/// <summary>
  		/// Creates a new Callback. You must be calling SteamAPI.RunCallbacks() to retrieve the callbacks.
  		/// <para>Returns a handle to the Callback. This must be assigned to a member variable to prevent the GC from cleaning it up.</para>
  		/// </summary>
  		public static Callback<T> Create(DispatchDelegate func) {
  			return new Callback<T>(func, bGameServer: false);
  		}
  
  		/// <summary>
  		/// Creates a new GameServer Callback. You must be calling GameServer.RunCallbacks() to retrieve the callbacks.
  		/// <para>Returns a handle to the Callback. This must be assigned to a member variable to prevent the GC from cleaning it up.</para>
  		/// </summary>
  		public static Callback<T> CreateGameServer(DispatchDelegate func) {
  			return new Callback<T>(func, bGameServer: true);
  		}
  
  		public Callback(DispatchDelegate func, bool bGameServer = false) {
  			m_bGameServer = bGameServer;
  			BuildCCallbackBase();
  			Register(func);
  		}
  
  		~Callback() {
  			Dispose();
  		}
  
  		public void Dispose() {
  			if (m_bDisposed) {
  				return;
  			}
  
  			GC.SuppressFinalize(this);
  
  			Unregister();
  
  			if (m_pVTable != IntPtr.Zero) {
  				Marshal.FreeHGlobal(m_pVTable);
  			}
  
  			if (m_pCCallbackBase.IsAllocated) {
  				m_pCCallbackBase.Free();
  			}
  
  			m_bDisposed = true;
  		}
  		
  		// Manual registration of the callback
  		public void Register(DispatchDelegate func) {
  			if (func == null) {
  				throw new Exception("Callback function must not be null.");
  			}
  
  			if ((m_CCallbackBase.m_nCallbackFlags & CCallbackBase.k_ECallbackFlagsRegistered) == CCallbackBase.k_ECallbackFlagsRegistered) {
  				Unregister();
  			}
  
  			if (m_bGameServer) {
  				SetGameserverFlag();
  			}
  
  			m_Func = func;
  
  			// k_ECallbackFlagsRegistered is set by SteamAPI_RegisterCallback.
  			NativeMethods.SteamAPI_RegisterCallback(m_pCCallbackBase.AddrOfPinnedObject(), CallbackIdentities.GetCallbackIdentity(typeof(T)));
  		}
  
  		public void Unregister() {
  			// k_ECallbackFlagsRegistered is removed by SteamAPI_UnregisterCallback.
  			NativeMethods.SteamAPI_UnregisterCallback(m_pCCallbackBase.AddrOfPinnedObject());
  		}
  
  		public void SetGameserverFlag() { m_CCallbackBase.m_nCallbackFlags |= CCallbackBase.k_ECallbackFlagsGameServer; }
  
  		private void OnRunCallback(
  #if !STDCALL
  			IntPtr thisptr,
  #endif
  			IntPtr pvParam) {
  			try {
  				m_Func((T)Marshal.PtrToStructure(pvParam, typeof(T)));
  			}
  			catch (Exception e) {
  				CallbackDispatcher.ExceptionHandler(e);
  			}
  		}
  
  		// Shouldn't ever get called here, but this is what C++ Steamworks does!
  		private void OnRunCallResult(
  #if !STDCALL
  			IntPtr thisptr,
  #endif
  			IntPtr pvParam, bool bFailed, ulong hSteamAPICall) {
  			try { 
  				m_Func((T)Marshal.PtrToStructure(pvParam, typeof(T)));
  			}
  			catch (Exception e) {
  				CallbackDispatcher.ExceptionHandler(e);
  			}
  		}
  
  		private int OnGetCallbackSizeBytes(
  #if !STDCALL
  			IntPtr thisptr
  #endif
  			) {
  			return m_size;
  		}
  
  		// Steamworks.NET Specific
  		private void BuildCCallbackBase() {
  			VTable = new CCallbackBaseVTable() {
  				m_RunCallResult = OnRunCallResult,
  				m_RunCallback = OnRunCallback,
  				m_GetCallbackSizeBytes = OnGetCallbackSizeBytes
  			};
  			m_pVTable = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(CCallbackBaseVTable)));
  			Marshal.StructureToPtr(VTable, m_pVTable, false);
  
  			m_CCallbackBase = new CCallbackBase() {
  				m_vfptr = m_pVTable,
  				m_nCallbackFlags = 0,
  				m_iCallback = CallbackIdentities.GetCallbackIdentity(typeof(T))
  			};
  			m_pCCallbackBase = GCHandle.Alloc(m_CCallbackBase, GCHandleType.Pinned);
  		}
  	}
  
  	public sealed class CallResult<T> : IDisposable {
  		private CCallbackBaseVTable VTable;
  		private IntPtr m_pVTable = IntPtr.Zero;
  		private CCallbackBase m_CCallbackBase;
  		private GCHandle m_pCCallbackBase;
  
  		public delegate void APIDispatchDelegate(T param, bool bIOFailure);
  		private event APIDispatchDelegate m_Func;
  
  		private SteamAPICall_t m_hAPICall = SteamAPICall_t.Invalid;
  		public SteamAPICall_t Handle { get { return m_hAPICall; } }
  
  		private readonly int m_size = Marshal.SizeOf(typeof(T));
  
  		private bool m_bDisposed = false;
  
  		/// <summary>
  		/// Creates a new async CallResult. You must be calling SteamAPI.RunCallbacks() to retrieve the callback.
  		/// <para>Returns a handle to the CallResult. This must be assigned to a member variable to prevent the GC from cleaning it up.</para>
  		/// </summary>
  		public static CallResult<T> Create(APIDispatchDelegate func = null) {
  			return new CallResult<T>(func);
  		}
  
  		public CallResult(APIDispatchDelegate func = null) {
  			m_Func = func;
  			BuildCCallbackBase();
  		}
  
  		~CallResult() {
  			Dispose();
  		}
  
  		public void Dispose() {
  			if (m_bDisposed) {
  				return;
  			}
  
  			GC.SuppressFinalize(this);
  
  			Cancel();
  
  
gt: 			if (m_pVTable != IntPtr.Zero) {