using Facepunch.Steamworks.Data; using System; using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; namespace Facepunch.Steamworks { /// /// An awaitable version of a SteamAPICall_t /// internal struct CallResult : INotifyCompletion where T : struct, ICallbackData { SteamAPICall_t call; ISteamUtils utils; bool server; public CallResult( SteamAPICall_t call, bool server ) { this.call = call; this.server = server; utils = (server ? SteamUtils.InterfaceServer : SteamUtils.InterfaceClient) as ISteamUtils; if ( utils == null ) utils = SteamUtils.Interface as ISteamUtils; } /// /// This gets called if IsComplete returned false on the first call. /// The Action "continues" the async call. We pass it to the Dispatch /// to be called when the callback returns. /// public void OnCompleted( Action continuation ) { Dispatch.OnCallComplete( call, continuation, server ); } /// /// Gets the result. This is called internally by the async shit. /// public T? GetResult() { bool failed = false; if ( !utils.IsAPICallCompleted( call, ref failed ) || failed ) return null; var t = default( T ); var size = t.DataSize; var ptr = Marshal.AllocHGlobal( size ); try { if ( !utils.GetAPICallResult( call, ptr, size, (int)t.CallbackType, ref failed ) || failed ) { Dispatch.OnDebugCallback?.Invoke( t.CallbackType, "!GetAPICallResult or failed", server ); return null; } Dispatch.OnDebugCallback?.Invoke( t.CallbackType, Dispatch.CallbackToString( t.CallbackType, ptr, size ), server ); return ((T)Marshal.PtrToStructure( ptr, typeof( T ) )); } finally { Marshal.FreeHGlobal( ptr ); } } /// /// Return true if complete or failed /// public bool IsCompleted { get { bool failed = false; if ( utils.IsAPICallCompleted( call, ref failed ) || failed ) return true; return false; } } /// /// This is what makes this struct awaitable /// internal CallResult GetAwaiter() { return this; } } }