using System; using System.Runtime.InteropServices; namespace Steamworks; public class ISteamMatchmakingPingResponse { public delegate void ServerResponded(gameserveritem_t server); public delegate void ServerFailedToRespond(); [UnmanagedFunctionPointer(CallingConvention.StdCall)] private delegate void InternalServerResponded(gameserveritem_t server); [UnmanagedFunctionPointer(CallingConvention.StdCall)] private delegate void InternalServerFailedToRespond(); [StructLayout(LayoutKind.Sequential)] private class VTable { [NonSerialized] [MarshalAs(UnmanagedType.FunctionPtr)] public InternalServerResponded m_VTServerResponded; [NonSerialized] [MarshalAs(UnmanagedType.FunctionPtr)] public InternalServerFailedToRespond m_VTServerFailedToRespond; } private VTable m_VTable; private IntPtr m_pVTable; private GCHandle m_pGCHandle; private ServerResponded m_ServerResponded; private ServerFailedToRespond m_ServerFailedToRespond; public ISteamMatchmakingPingResponse(ServerResponded onServerResponded, ServerFailedToRespond onServerFailedToRespond) { if (onServerResponded == null || onServerFailedToRespond == null) { throw new ArgumentNullException(); } m_ServerResponded = onServerResponded; m_ServerFailedToRespond = onServerFailedToRespond; m_VTable = new VTable { m_VTServerResponded = InternalOnServerResponded, m_VTServerFailedToRespond = InternalOnServerFailedToRespond }; m_pVTable = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(VTable))); Marshal.StructureToPtr(m_VTable, m_pVTable, fDeleteOld: false); m_pGCHandle = GCHandle.Alloc(m_pVTable, GCHandleType.Pinned); } ~ISteamMatchmakingPingResponse() { if (m_pVTable != IntPtr.Zero) { Marshal.FreeHGlobal(m_pVTable); } if (m_pGCHandle.IsAllocated) { m_pGCHandle.Free(); } } private void InternalOnServerResponded(gameserveritem_t server) { m_ServerResponded(server); } private void InternalOnServerFailedToRespond() { m_ServerFailedToRespond(); } public static explicit operator IntPtr(ISteamMatchmakingPingResponse that) { return that.m_pGCHandle.AddrOfPinnedObject(); } }