From 3f88106c0bb2cf603fd3af155e50386deb79e6d2 Mon Sep 17 00:00:00 2001 From: Garry Newman Date: Wed, 19 Feb 2020 13:57:17 +0000 Subject: [PATCH] New dispatch system --- .../Callbacks/CallbackResult.cs | 11 +- Facepunch.Steamworks/Classes/Dispatch.cs | 146 ++++++++++++++++++ Facepunch.Steamworks/SteamClient.cs | 37 ++--- Facepunch.Steamworks/SteamServer.cs | 36 ++--- Generator/Generator.csproj | 1 + 5 files changed, 183 insertions(+), 48 deletions(-) create mode 100644 Facepunch.Steamworks/Classes/Dispatch.cs diff --git a/Facepunch.Steamworks/Callbacks/CallbackResult.cs b/Facepunch.Steamworks/Callbacks/CallbackResult.cs index 6a68adc..4fc3783 100644 --- a/Facepunch.Steamworks/Callbacks/CallbackResult.cs +++ b/Facepunch.Steamworks/Callbacks/CallbackResult.cs @@ -15,19 +15,12 @@ namespace Steamworks public CallbackResult( SteamAPICall_t call ) { this.call = call; + Console.WriteLine( $"{this.GetType().ToString()} == {call.Value}" ); } public void OnCompleted( Action continuation ) { - var ts = TaskScheduler.Current; - var sc = SynchronizationContext.Current; - - while ( !IsCompleted ) - { - // Nothing - } - - continuation(); + Dispatch.OnCallComplete( call, continuation ); } public T? GetResult() diff --git a/Facepunch.Steamworks/Classes/Dispatch.cs b/Facepunch.Steamworks/Classes/Dispatch.cs new file mode 100644 index 0000000..d083ea5 --- /dev/null +++ b/Facepunch.Steamworks/Classes/Dispatch.cs @@ -0,0 +1,146 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Threading.Tasks; +using Steamworks.Data; + +namespace Steamworks +{ + internal static class Dispatch + { + #region interop + [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ManualDispatch_Init", CallingConvention = CallingConvention.Cdecl )] + internal static extern void SteamAPI_ManualDispatch_Init(); + + [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ManualDispatch_RunFrame", CallingConvention = CallingConvention.Cdecl )] + internal static extern void SteamAPI_ManualDispatch_RunFrame( HSteamPipe pipe ); + + [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ManualDispatch_GetNextCallback", CallingConvention = CallingConvention.Cdecl )] + [return: MarshalAs( UnmanagedType.I1 )] + internal static extern bool SteamAPI_ManualDispatch_GetNextCallback( HSteamPipe pipe, [In, Out] ref CallbackMsg_t msg ); + + [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ManualDispatch_FreeLastCallback", CallingConvention = CallingConvention.Cdecl )] + [return: MarshalAs( UnmanagedType.I1 )] + internal static extern bool SteamAPI_ManualDispatch_FreeLastCallback( HSteamPipe pipe ); + + [StructLayout( LayoutKind.Sequential, Pack = Platform.StructPlatformPackSize )] + internal struct CallbackMsg_t + { + public HSteamUser m_hSteamUser; // Specific user to whom this callback applies. + public CallbackType Type; // Callback identifier. (Corresponds to the k_iCallback enum in the callback structure.) + public IntPtr m_pubParam; // Points to the callback structure + public int m_cubParam; // Size of the data pointed to by m_pubParam + }; + + #endregion + + internal static HSteamPipe ClientPipe { get; set; } + internal static HSteamPipe ServerPipe { get; set; } + + public static void Init() + { + SteamAPI_ManualDispatch_Init(); + } + + public static void Frame() + { + if ( ClientPipe != 0 ) + Frame( ClientPipe ); + + if ( ServerPipe != 0) + Frame( ServerPipe ); + } + + public static void Frame( HSteamPipe pipe ) + { + SteamAPI_ManualDispatch_RunFrame( pipe ); + + CallbackMsg_t msg = default; + + while ( SteamAPI_ManualDispatch_GetNextCallback( pipe, ref msg ) ) + { + try + { + ProcessCallback( msg ); + } + finally + { + SteamAPI_ManualDispatch_FreeLastCallback( pipe ); + } + } + } + + private static void ProcessCallback( CallbackMsg_t msg ) + { + if ( msg.Type == CallbackType.SteamAPICallCompleted ) + { + ProcessResult( msg ); + return; + } + + Console.WriteLine( $"Callback: {msg.Type}" ); + } + + private static void ProcessResult( CallbackMsg_t msg ) + { + var result = SteamAPICallCompleted_t.Fill( msg.m_pubParam ); + + Console.WriteLine( $"Result: {result.AsyncCall} / {result.Callback}" ); + + // + // Do we have an entry added via OnCallComplete + // + if ( !Callbacks.TryGetValue( result.AsyncCall, out var callbackInfo ) ) + { + // Do we care? Should we throw errors? + return; + } + + Callbacks.Remove( result.AsyncCall ); + + // At this point whatever async routine called this + // continues running. + callbackInfo.continuation(); + } + + public static async void LoopClientAsync() + { + while ( ClientPipe != 0 ) + { + Frame( ClientPipe ); + await Task.Delay( 16 ); + } + + Console.WriteLine( $"Exiting ClientPipe: {ClientPipe}" ); + } + + public static async void LoopServerAsync() + { + while ( ServerPipe != 0 ) + { + Frame( ServerPipe ); + await Task.Delay( 32 ); + } + + Console.WriteLine( $"Exiting ServerPipe: {ServerPipe}" ); + } + + struct CallbackInfo + { + public Action continuation; + } + + static Dictionary Callbacks = new Dictionary(); + + /// + /// Watch for a steam api call + /// + internal static void OnCallComplete( SteamAPICall_t call, Action continuation ) + { + Callbacks[call.Value] = new CallbackInfo + { + continuation = continuation + }; + } + } +} \ No newline at end of file diff --git a/Facepunch.Steamworks/SteamClient.cs b/Facepunch.Steamworks/SteamClient.cs index 194f39e..187c532 100644 --- a/Facepunch.Steamworks/SteamClient.cs +++ b/Facepunch.Steamworks/SteamClient.cs @@ -29,6 +29,14 @@ namespace Steamworks initialized = true; + // + // Dispatch is responsible for pumping the + // event loop. + // + Dispatch.Init(); + Dispatch.ClientPipe = SteamAPI.GetHSteamPipe(); + Console.WriteLine( $"Dispatch.ClientPipe = {Dispatch.ClientPipe.Value}" ); + AddInterface(); AddInterface(); AddInterface(); @@ -51,7 +59,11 @@ namespace Steamworks if ( asyncCallbacks ) { - RunCallbacksAsync(); + // + // This will keep looping in the background every 16 ms + // until we shut down. + // + Dispatch.LoopClientAsync(); } } @@ -78,22 +90,6 @@ namespace Steamworks public static bool IsValid => initialized; - internal static async void RunCallbacksAsync() - { - while ( IsValid ) - { - await Task.Delay( 16 ); - - try - { - RunCallbacks(); - } - catch ( System.Exception e ) - { - OnCallbackException?.Invoke( e ); - } - } - } public static void Shutdown() { @@ -106,6 +102,8 @@ namespace Steamworks internal static void Cleanup() { + Dispatch.ClientPipe = 0; + initialized = false; Event.DisposeAllClient(); @@ -119,9 +117,8 @@ namespace Steamworks public static void RunCallbacks() { - if ( !IsValid ) return; - - SteamAPI.RunCallbacks(); + if ( Dispatch.ClientPipe != 0 ) + Dispatch.Frame( Dispatch.ClientPipe ); } internal static void UnregisterCallback( IntPtr intPtr ) diff --git a/Facepunch.Steamworks/SteamServer.cs b/Facepunch.Steamworks/SteamServer.cs index a94c98e..a916b3b 100644 --- a/Facepunch.Steamworks/SteamServer.cs +++ b/Facepunch.Steamworks/SteamServer.cs @@ -85,6 +85,14 @@ namespace Steamworks throw new System.Exception( $"InitGameServer returned false ({ipaddress},{init.SteamPort},{init.GamePort},{init.QueryPort},{secure},\"{init.VersionString}\")" ); } + // + // Dispatch is responsible for pumping the + // event loop. + // + Dispatch.Init(); + Dispatch.ServerPipe = SteamGameServer.GetHSteamPipe(); + Console.WriteLine( $"Dispatch.ServerPipe = {Dispatch.ServerPipe.Value}" ); + AddInterface(); // @@ -103,7 +111,11 @@ namespace Steamworks if ( asyncCallbacks ) { - RunCallbacksAsync(); + // + // This will keep looping in the background every 16 ms + // until we shut down. + // + Dispatch.LoopServerAsync(); } } @@ -136,29 +148,15 @@ namespace Steamworks SteamGameServer.Shutdown(); } - internal static async void RunCallbacksAsync() - { - while ( IsValid ) - { - try - { - RunCallbacks(); - } - catch ( System.Exception e ) - { - OnCallbackException?.Invoke( e ); - } - - await Task.Delay( 16 ); - } - } - /// /// Run the callbacks. This is also called in Async callbacks. /// public static void RunCallbacks() { - SteamGameServer.RunCallbacks(); + if ( Dispatch.ServerPipe != 0 ) + { + Dispatch.Frame( Dispatch.ServerPipe ); + } } /// diff --git a/Generator/Generator.csproj b/Generator/Generator.csproj index 9f851cc..53a6a50 100644 --- a/Generator/Generator.csproj +++ b/Generator/Generator.csproj @@ -49,6 +49,7 @@ +