diff --git a/Facepunch.Steamworks.Test/NetworkingSocketsTest.TestConnectionInterface.cs b/Facepunch.Steamworks.Test/NetworkingSocketsTest.TestConnectionInterface.cs index 9f6df14..efbf3cb 100644 --- a/Facepunch.Steamworks.Test/NetworkingSocketsTest.TestConnectionInterface.cs +++ b/Facepunch.Steamworks.Test/NetworkingSocketsTest.TestConnectionInterface.cs @@ -72,7 +72,7 @@ namespace Steamworks Receive(); await Task.Delay( 100 ); - if ( sw.Elapsed.TotalSeconds > 10 ) + if ( sw.Elapsed.TotalSeconds > 30 ) { Assert.Fail( "Client Took Too Long" ); break; @@ -95,9 +95,11 @@ namespace Steamworks Console.WriteLine( $"[Connection][{messageNum}][{recvTime}][{channel}] Sending: How do you like 20 messages in a row?" ); Connection.SendMessage( "How do you like 20 messages in a row?" ); + var connections = new[] { Connection }; for ( int i=0; i<20; i++ ) { - Connection.SendMessage( $"BLAMMO!" ); + Console.WriteLine( $"[Connection][{messageNum}][{recvTime}][{channel}] Sending: BLAMMO {i}!" ); + Broadcast( connections, connections.Length, $"BLAMMO {i}!" ); } Connection.Flush(); diff --git a/Facepunch.Steamworks.Test/NetworkingSocketsTest.TestSocketInterface.cs b/Facepunch.Steamworks.Test/NetworkingSocketsTest.TestSocketInterface.cs index aee056f..aff919e 100644 --- a/Facepunch.Steamworks.Test/NetworkingSocketsTest.TestSocketInterface.cs +++ b/Facepunch.Steamworks.Test/NetworkingSocketsTest.TestSocketInterface.cs @@ -97,7 +97,7 @@ namespace Steamworks Receive(); await Task.Delay( 100 ); - if ( sw.Elapsed.TotalSeconds > 5 ) + if ( sw.Elapsed.TotalSeconds > 100 ) { Console.WriteLine( "Socket: This all took too long - throwing an exception" ); Assert.Fail( "Socket Took Too Long" ); diff --git a/Facepunch.Steamworks/Generated/Interfaces/ISteamAppList.cs b/Facepunch.Steamworks/Generated/Interfaces/ISteamAppList.cs index 615b0f9..f483dbd 100644 --- a/Facepunch.Steamworks/Generated/Interfaces/ISteamAppList.cs +++ b/Facepunch.Steamworks/Generated/Interfaces/ISteamAppList.cs @@ -7,7 +7,7 @@ using Steamworks.Data; namespace Steamworks { - internal class ISteamAppList : SteamInterface + internal unsafe class ISteamAppList : SteamInterface { internal ISteamAppList( bool IsGameServer ) diff --git a/Facepunch.Steamworks/Generated/Interfaces/ISteamApps.cs b/Facepunch.Steamworks/Generated/Interfaces/ISteamApps.cs index cf43210..a9d9480 100644 --- a/Facepunch.Steamworks/Generated/Interfaces/ISteamApps.cs +++ b/Facepunch.Steamworks/Generated/Interfaces/ISteamApps.cs @@ -7,7 +7,7 @@ using Steamworks.Data; namespace Steamworks { - internal class ISteamApps : SteamInterface + internal unsafe class ISteamApps : SteamInterface { internal ISteamApps( bool IsGameServer ) diff --git a/Facepunch.Steamworks/Generated/Interfaces/ISteamClient.cs b/Facepunch.Steamworks/Generated/Interfaces/ISteamClient.cs index 7b8e945..418f5da 100644 --- a/Facepunch.Steamworks/Generated/Interfaces/ISteamClient.cs +++ b/Facepunch.Steamworks/Generated/Interfaces/ISteamClient.cs @@ -7,7 +7,7 @@ using Steamworks.Data; namespace Steamworks { - internal class ISteamClient : SteamInterface + internal unsafe class ISteamClient : SteamInterface { internal ISteamClient( bool IsGameServer ) diff --git a/Facepunch.Steamworks/Generated/Interfaces/ISteamController.cs b/Facepunch.Steamworks/Generated/Interfaces/ISteamController.cs index 4e26a15..1ff01b2 100644 --- a/Facepunch.Steamworks/Generated/Interfaces/ISteamController.cs +++ b/Facepunch.Steamworks/Generated/Interfaces/ISteamController.cs @@ -7,7 +7,7 @@ using Steamworks.Data; namespace Steamworks { - internal class ISteamController : SteamInterface + internal unsafe class ISteamController : SteamInterface { internal ISteamController( bool IsGameServer ) diff --git a/Facepunch.Steamworks/Generated/Interfaces/ISteamFriends.cs b/Facepunch.Steamworks/Generated/Interfaces/ISteamFriends.cs index 0d75d18..4cdd6d8 100644 --- a/Facepunch.Steamworks/Generated/Interfaces/ISteamFriends.cs +++ b/Facepunch.Steamworks/Generated/Interfaces/ISteamFriends.cs @@ -7,7 +7,7 @@ using Steamworks.Data; namespace Steamworks { - internal class ISteamFriends : SteamInterface + internal unsafe class ISteamFriends : SteamInterface { internal ISteamFriends( bool IsGameServer ) diff --git a/Facepunch.Steamworks/Generated/Interfaces/ISteamGameSearch.cs b/Facepunch.Steamworks/Generated/Interfaces/ISteamGameSearch.cs index 03d814f..ac4c2eb 100644 --- a/Facepunch.Steamworks/Generated/Interfaces/ISteamGameSearch.cs +++ b/Facepunch.Steamworks/Generated/Interfaces/ISteamGameSearch.cs @@ -7,7 +7,7 @@ using Steamworks.Data; namespace Steamworks { - internal class ISteamGameSearch : SteamInterface + internal unsafe class ISteamGameSearch : SteamInterface { internal ISteamGameSearch( bool IsGameServer ) diff --git a/Facepunch.Steamworks/Generated/Interfaces/ISteamGameServer.cs b/Facepunch.Steamworks/Generated/Interfaces/ISteamGameServer.cs index 719af09..7e749e5 100644 --- a/Facepunch.Steamworks/Generated/Interfaces/ISteamGameServer.cs +++ b/Facepunch.Steamworks/Generated/Interfaces/ISteamGameServer.cs @@ -7,7 +7,7 @@ using Steamworks.Data; namespace Steamworks { - internal class ISteamGameServer : SteamInterface + internal unsafe class ISteamGameServer : SteamInterface { internal ISteamGameServer( bool IsGameServer ) diff --git a/Facepunch.Steamworks/Generated/Interfaces/ISteamGameServerStats.cs b/Facepunch.Steamworks/Generated/Interfaces/ISteamGameServerStats.cs index fee8757..de293cb 100644 --- a/Facepunch.Steamworks/Generated/Interfaces/ISteamGameServerStats.cs +++ b/Facepunch.Steamworks/Generated/Interfaces/ISteamGameServerStats.cs @@ -7,7 +7,7 @@ using Steamworks.Data; namespace Steamworks { - internal class ISteamGameServerStats : SteamInterface + internal unsafe class ISteamGameServerStats : SteamInterface { internal ISteamGameServerStats( bool IsGameServer ) diff --git a/Facepunch.Steamworks/Generated/Interfaces/ISteamHTMLSurface.cs b/Facepunch.Steamworks/Generated/Interfaces/ISteamHTMLSurface.cs index e9bc08d..38c6b46 100644 --- a/Facepunch.Steamworks/Generated/Interfaces/ISteamHTMLSurface.cs +++ b/Facepunch.Steamworks/Generated/Interfaces/ISteamHTMLSurface.cs @@ -7,7 +7,7 @@ using Steamworks.Data; namespace Steamworks { - internal class ISteamHTMLSurface : SteamInterface + internal unsafe class ISteamHTMLSurface : SteamInterface { internal ISteamHTMLSurface( bool IsGameServer ) diff --git a/Facepunch.Steamworks/Generated/Interfaces/ISteamHTTP.cs b/Facepunch.Steamworks/Generated/Interfaces/ISteamHTTP.cs index 3d1d7b1..599054b 100644 --- a/Facepunch.Steamworks/Generated/Interfaces/ISteamHTTP.cs +++ b/Facepunch.Steamworks/Generated/Interfaces/ISteamHTTP.cs @@ -7,7 +7,7 @@ using Steamworks.Data; namespace Steamworks { - internal class ISteamHTTP : SteamInterface + internal unsafe class ISteamHTTP : SteamInterface { internal ISteamHTTP( bool IsGameServer ) diff --git a/Facepunch.Steamworks/Generated/Interfaces/ISteamInput.cs b/Facepunch.Steamworks/Generated/Interfaces/ISteamInput.cs index 5a1efd8..02c1980 100644 --- a/Facepunch.Steamworks/Generated/Interfaces/ISteamInput.cs +++ b/Facepunch.Steamworks/Generated/Interfaces/ISteamInput.cs @@ -7,7 +7,7 @@ using Steamworks.Data; namespace Steamworks { - internal class ISteamInput : SteamInterface + internal unsafe class ISteamInput : SteamInterface { internal ISteamInput( bool IsGameServer ) diff --git a/Facepunch.Steamworks/Generated/Interfaces/ISteamInventory.cs b/Facepunch.Steamworks/Generated/Interfaces/ISteamInventory.cs index 1122d97..10340de 100644 --- a/Facepunch.Steamworks/Generated/Interfaces/ISteamInventory.cs +++ b/Facepunch.Steamworks/Generated/Interfaces/ISteamInventory.cs @@ -7,7 +7,7 @@ using Steamworks.Data; namespace Steamworks { - internal class ISteamInventory : SteamInterface + internal unsafe class ISteamInventory : SteamInterface { internal ISteamInventory( bool IsGameServer ) diff --git a/Facepunch.Steamworks/Generated/Interfaces/ISteamMatchmaking.cs b/Facepunch.Steamworks/Generated/Interfaces/ISteamMatchmaking.cs index f5f61f3..4ac1647 100644 --- a/Facepunch.Steamworks/Generated/Interfaces/ISteamMatchmaking.cs +++ b/Facepunch.Steamworks/Generated/Interfaces/ISteamMatchmaking.cs @@ -7,7 +7,7 @@ using Steamworks.Data; namespace Steamworks { - internal class ISteamMatchmaking : SteamInterface + internal unsafe class ISteamMatchmaking : SteamInterface { internal ISteamMatchmaking( bool IsGameServer ) diff --git a/Facepunch.Steamworks/Generated/Interfaces/ISteamMatchmakingPingResponse.cs b/Facepunch.Steamworks/Generated/Interfaces/ISteamMatchmakingPingResponse.cs index 9f14a96..0306b76 100644 --- a/Facepunch.Steamworks/Generated/Interfaces/ISteamMatchmakingPingResponse.cs +++ b/Facepunch.Steamworks/Generated/Interfaces/ISteamMatchmakingPingResponse.cs @@ -7,7 +7,7 @@ using Steamworks.Data; namespace Steamworks { - internal class ISteamMatchmakingPingResponse : SteamInterface + internal unsafe class ISteamMatchmakingPingResponse : SteamInterface { internal ISteamMatchmakingPingResponse( bool IsGameServer ) diff --git a/Facepunch.Steamworks/Generated/Interfaces/ISteamMatchmakingPlayersResponse.cs b/Facepunch.Steamworks/Generated/Interfaces/ISteamMatchmakingPlayersResponse.cs index ce4f02a..a5ed926 100644 --- a/Facepunch.Steamworks/Generated/Interfaces/ISteamMatchmakingPlayersResponse.cs +++ b/Facepunch.Steamworks/Generated/Interfaces/ISteamMatchmakingPlayersResponse.cs @@ -7,7 +7,7 @@ using Steamworks.Data; namespace Steamworks { - internal class ISteamMatchmakingPlayersResponse : SteamInterface + internal unsafe class ISteamMatchmakingPlayersResponse : SteamInterface { internal ISteamMatchmakingPlayersResponse( bool IsGameServer ) diff --git a/Facepunch.Steamworks/Generated/Interfaces/ISteamMatchmakingRulesResponse.cs b/Facepunch.Steamworks/Generated/Interfaces/ISteamMatchmakingRulesResponse.cs index 7367634..78507b9 100644 --- a/Facepunch.Steamworks/Generated/Interfaces/ISteamMatchmakingRulesResponse.cs +++ b/Facepunch.Steamworks/Generated/Interfaces/ISteamMatchmakingRulesResponse.cs @@ -7,7 +7,7 @@ using Steamworks.Data; namespace Steamworks { - internal class ISteamMatchmakingRulesResponse : SteamInterface + internal unsafe class ISteamMatchmakingRulesResponse : SteamInterface { internal ISteamMatchmakingRulesResponse( bool IsGameServer ) diff --git a/Facepunch.Steamworks/Generated/Interfaces/ISteamMatchmakingServerListResponse.cs b/Facepunch.Steamworks/Generated/Interfaces/ISteamMatchmakingServerListResponse.cs index 9ab74dc..8fd0a9e 100644 --- a/Facepunch.Steamworks/Generated/Interfaces/ISteamMatchmakingServerListResponse.cs +++ b/Facepunch.Steamworks/Generated/Interfaces/ISteamMatchmakingServerListResponse.cs @@ -7,7 +7,7 @@ using Steamworks.Data; namespace Steamworks { - internal class ISteamMatchmakingServerListResponse : SteamInterface + internal unsafe class ISteamMatchmakingServerListResponse : SteamInterface { internal ISteamMatchmakingServerListResponse( bool IsGameServer ) diff --git a/Facepunch.Steamworks/Generated/Interfaces/ISteamMatchmakingServers.cs b/Facepunch.Steamworks/Generated/Interfaces/ISteamMatchmakingServers.cs index b1740fa..6f9ee8b 100644 --- a/Facepunch.Steamworks/Generated/Interfaces/ISteamMatchmakingServers.cs +++ b/Facepunch.Steamworks/Generated/Interfaces/ISteamMatchmakingServers.cs @@ -7,7 +7,7 @@ using Steamworks.Data; namespace Steamworks { - internal class ISteamMatchmakingServers : SteamInterface + internal unsafe class ISteamMatchmakingServers : SteamInterface { internal ISteamMatchmakingServers( bool IsGameServer ) diff --git a/Facepunch.Steamworks/Generated/Interfaces/ISteamMusic.cs b/Facepunch.Steamworks/Generated/Interfaces/ISteamMusic.cs index 1ed2f67..6534a83 100644 --- a/Facepunch.Steamworks/Generated/Interfaces/ISteamMusic.cs +++ b/Facepunch.Steamworks/Generated/Interfaces/ISteamMusic.cs @@ -7,7 +7,7 @@ using Steamworks.Data; namespace Steamworks { - internal class ISteamMusic : SteamInterface + internal unsafe class ISteamMusic : SteamInterface { internal ISteamMusic( bool IsGameServer ) diff --git a/Facepunch.Steamworks/Generated/Interfaces/ISteamMusicRemote.cs b/Facepunch.Steamworks/Generated/Interfaces/ISteamMusicRemote.cs index 620b7c6..cf73502 100644 --- a/Facepunch.Steamworks/Generated/Interfaces/ISteamMusicRemote.cs +++ b/Facepunch.Steamworks/Generated/Interfaces/ISteamMusicRemote.cs @@ -7,7 +7,7 @@ using Steamworks.Data; namespace Steamworks { - internal class ISteamMusicRemote : SteamInterface + internal unsafe class ISteamMusicRemote : SteamInterface { internal ISteamMusicRemote( bool IsGameServer ) diff --git a/Facepunch.Steamworks/Generated/Interfaces/ISteamNetworking.cs b/Facepunch.Steamworks/Generated/Interfaces/ISteamNetworking.cs index baef24d..f149557 100644 --- a/Facepunch.Steamworks/Generated/Interfaces/ISteamNetworking.cs +++ b/Facepunch.Steamworks/Generated/Interfaces/ISteamNetworking.cs @@ -7,7 +7,7 @@ using Steamworks.Data; namespace Steamworks { - internal class ISteamNetworking : SteamInterface + internal unsafe class ISteamNetworking : SteamInterface { internal ISteamNetworking( bool IsGameServer ) diff --git a/Facepunch.Steamworks/Generated/Interfaces/ISteamNetworkingMessages.cs b/Facepunch.Steamworks/Generated/Interfaces/ISteamNetworkingMessages.cs index 691ebd7..2033e5c 100644 --- a/Facepunch.Steamworks/Generated/Interfaces/ISteamNetworkingMessages.cs +++ b/Facepunch.Steamworks/Generated/Interfaces/ISteamNetworkingMessages.cs @@ -7,7 +7,7 @@ using Steamworks.Data; namespace Steamworks { - internal class ISteamNetworkingMessages : SteamInterface + internal unsafe class ISteamNetworkingMessages : SteamInterface { internal ISteamNetworkingMessages( bool IsGameServer ) diff --git a/Facepunch.Steamworks/Generated/Interfaces/ISteamNetworkingSockets.cs b/Facepunch.Steamworks/Generated/Interfaces/ISteamNetworkingSockets.cs index 2f2c7aa..46f0d23 100644 --- a/Facepunch.Steamworks/Generated/Interfaces/ISteamNetworkingSockets.cs +++ b/Facepunch.Steamworks/Generated/Interfaces/ISteamNetworkingSockets.cs @@ -7,7 +7,7 @@ using Steamworks.Data; namespace Steamworks { - internal class ISteamNetworkingSockets : SteamInterface + internal unsafe class ISteamNetworkingSockets : SteamInterface { internal ISteamNetworkingSockets( bool IsGameServer ) @@ -162,12 +162,12 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamNetworkingSockets_SendMessages", CallingConvention = Platform.CC)] - private static extern void _SendMessages( IntPtr self, int nMessages, ref NetMsg pMessages, [In,Out] long[] pOutMessageNumberOrResult ); + private static extern void _SendMessages( IntPtr self, int nMessages, NetMsg** pMessages, [In,Out] long[] pOutMessageNumberOrResult ); #endregion - internal void SendMessages( int nMessages, ref NetMsg pMessages, [In,Out] long[] pOutMessageNumberOrResult ) + internal void SendMessages( int nMessages, NetMsg** pMessages, [In,Out] long[] pOutMessageNumberOrResult ) { - _SendMessages( Self, nMessages, ref pMessages, pOutMessageNumberOrResult ); + _SendMessages( Self, nMessages, pMessages, pOutMessageNumberOrResult ); } #region FunctionMeta diff --git a/Facepunch.Steamworks/Generated/Interfaces/ISteamNetworkingUtils.cs b/Facepunch.Steamworks/Generated/Interfaces/ISteamNetworkingUtils.cs index d185602..7251479 100644 --- a/Facepunch.Steamworks/Generated/Interfaces/ISteamNetworkingUtils.cs +++ b/Facepunch.Steamworks/Generated/Interfaces/ISteamNetworkingUtils.cs @@ -7,7 +7,7 @@ using Steamworks.Data; namespace Steamworks { - internal class ISteamNetworkingUtils : SteamInterface + internal unsafe class ISteamNetworkingUtils : SteamInterface { internal ISteamNetworkingUtils( bool IsGameServer ) @@ -22,13 +22,13 @@ namespace Steamworks #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamNetworkingUtils_AllocateMessage", CallingConvention = Platform.CC)] - private static extern IntPtr _AllocateMessage( IntPtr self, int cbAllocateBuffer ); + private static extern NetMsg* _AllocateMessage( IntPtr self, int cbAllocateBuffer ); #endregion - internal NetMsg AllocateMessage( int cbAllocateBuffer ) + internal NetMsg* AllocateMessage( int cbAllocateBuffer ) { var returnValue = _AllocateMessage( Self, cbAllocateBuffer ); - return returnValue.ToType(); + return returnValue; } #region FunctionMeta diff --git a/Facepunch.Steamworks/Generated/Interfaces/ISteamParentalSettings.cs b/Facepunch.Steamworks/Generated/Interfaces/ISteamParentalSettings.cs index 3968079..56bfb1e 100644 --- a/Facepunch.Steamworks/Generated/Interfaces/ISteamParentalSettings.cs +++ b/Facepunch.Steamworks/Generated/Interfaces/ISteamParentalSettings.cs @@ -7,7 +7,7 @@ using Steamworks.Data; namespace Steamworks { - internal class ISteamParentalSettings : SteamInterface + internal unsafe class ISteamParentalSettings : SteamInterface { internal ISteamParentalSettings( bool IsGameServer ) diff --git a/Facepunch.Steamworks/Generated/Interfaces/ISteamParties.cs b/Facepunch.Steamworks/Generated/Interfaces/ISteamParties.cs index 3740561..86b6a1d 100644 --- a/Facepunch.Steamworks/Generated/Interfaces/ISteamParties.cs +++ b/Facepunch.Steamworks/Generated/Interfaces/ISteamParties.cs @@ -7,7 +7,7 @@ using Steamworks.Data; namespace Steamworks { - internal class ISteamParties : SteamInterface + internal unsafe class ISteamParties : SteamInterface { internal ISteamParties( bool IsGameServer ) diff --git a/Facepunch.Steamworks/Generated/Interfaces/ISteamRemotePlay.cs b/Facepunch.Steamworks/Generated/Interfaces/ISteamRemotePlay.cs index f2d98c2..6ad4204 100644 --- a/Facepunch.Steamworks/Generated/Interfaces/ISteamRemotePlay.cs +++ b/Facepunch.Steamworks/Generated/Interfaces/ISteamRemotePlay.cs @@ -7,7 +7,7 @@ using Steamworks.Data; namespace Steamworks { - internal class ISteamRemotePlay : SteamInterface + internal unsafe class ISteamRemotePlay : SteamInterface { internal ISteamRemotePlay( bool IsGameServer ) diff --git a/Facepunch.Steamworks/Generated/Interfaces/ISteamRemoteStorage.cs b/Facepunch.Steamworks/Generated/Interfaces/ISteamRemoteStorage.cs index cc2ee6b..7d3f5b9 100644 --- a/Facepunch.Steamworks/Generated/Interfaces/ISteamRemoteStorage.cs +++ b/Facepunch.Steamworks/Generated/Interfaces/ISteamRemoteStorage.cs @@ -7,7 +7,7 @@ using Steamworks.Data; namespace Steamworks { - internal class ISteamRemoteStorage : SteamInterface + internal unsafe class ISteamRemoteStorage : SteamInterface { internal ISteamRemoteStorage( bool IsGameServer ) diff --git a/Facepunch.Steamworks/Generated/Interfaces/ISteamScreenshots.cs b/Facepunch.Steamworks/Generated/Interfaces/ISteamScreenshots.cs index c076519..ff9380a 100644 --- a/Facepunch.Steamworks/Generated/Interfaces/ISteamScreenshots.cs +++ b/Facepunch.Steamworks/Generated/Interfaces/ISteamScreenshots.cs @@ -7,7 +7,7 @@ using Steamworks.Data; namespace Steamworks { - internal class ISteamScreenshots : SteamInterface + internal unsafe class ISteamScreenshots : SteamInterface { internal ISteamScreenshots( bool IsGameServer ) diff --git a/Facepunch.Steamworks/Generated/Interfaces/ISteamUGC.cs b/Facepunch.Steamworks/Generated/Interfaces/ISteamUGC.cs index cf15820..ab3ece4 100644 --- a/Facepunch.Steamworks/Generated/Interfaces/ISteamUGC.cs +++ b/Facepunch.Steamworks/Generated/Interfaces/ISteamUGC.cs @@ -7,7 +7,7 @@ using Steamworks.Data; namespace Steamworks { - internal class ISteamUGC : SteamInterface + internal unsafe class ISteamUGC : SteamInterface { internal ISteamUGC( bool IsGameServer ) diff --git a/Facepunch.Steamworks/Generated/Interfaces/ISteamUser.cs b/Facepunch.Steamworks/Generated/Interfaces/ISteamUser.cs index 205bda6..5e903b7 100644 --- a/Facepunch.Steamworks/Generated/Interfaces/ISteamUser.cs +++ b/Facepunch.Steamworks/Generated/Interfaces/ISteamUser.cs @@ -7,7 +7,7 @@ using Steamworks.Data; namespace Steamworks { - internal class ISteamUser : SteamInterface + internal unsafe class ISteamUser : SteamInterface { internal ISteamUser( bool IsGameServer ) diff --git a/Facepunch.Steamworks/Generated/Interfaces/ISteamUserStats.cs b/Facepunch.Steamworks/Generated/Interfaces/ISteamUserStats.cs index c6ede21..b738b30 100644 --- a/Facepunch.Steamworks/Generated/Interfaces/ISteamUserStats.cs +++ b/Facepunch.Steamworks/Generated/Interfaces/ISteamUserStats.cs @@ -7,7 +7,7 @@ using Steamworks.Data; namespace Steamworks { - internal class ISteamUserStats : SteamInterface + internal unsafe class ISteamUserStats : SteamInterface { internal ISteamUserStats( bool IsGameServer ) diff --git a/Facepunch.Steamworks/Generated/Interfaces/ISteamUtils.cs b/Facepunch.Steamworks/Generated/Interfaces/ISteamUtils.cs index 633766f..30b643e 100644 --- a/Facepunch.Steamworks/Generated/Interfaces/ISteamUtils.cs +++ b/Facepunch.Steamworks/Generated/Interfaces/ISteamUtils.cs @@ -7,7 +7,7 @@ using Steamworks.Data; namespace Steamworks { - internal class ISteamUtils : SteamInterface + internal unsafe class ISteamUtils : SteamInterface { internal ISteamUtils( bool IsGameServer ) diff --git a/Facepunch.Steamworks/Generated/Interfaces/ISteamVideo.cs b/Facepunch.Steamworks/Generated/Interfaces/ISteamVideo.cs index 28a9c13..0e462fd 100644 --- a/Facepunch.Steamworks/Generated/Interfaces/ISteamVideo.cs +++ b/Facepunch.Steamworks/Generated/Interfaces/ISteamVideo.cs @@ -7,7 +7,7 @@ using Steamworks.Data; namespace Steamworks { - internal class ISteamVideo : SteamInterface + internal unsafe class ISteamVideo : SteamInterface { internal ISteamVideo( bool IsGameServer ) diff --git a/Facepunch.Steamworks/Networking/BroadcastBufferManager.cs b/Facepunch.Steamworks/Networking/BroadcastBufferManager.cs new file mode 100644 index 0000000..cb2a0d2 --- /dev/null +++ b/Facepunch.Steamworks/Networking/BroadcastBufferManager.cs @@ -0,0 +1,277 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Threading; +using Steamworks.Data; + +namespace Steamworks +{ + internal static unsafe class BroadcastBufferManager + { + private sealed class ReferenceCounter + { + public IntPtr Pointer { get; private set; } + public int Size { get; private set; } + private int _count; + + public void Set( IntPtr ptr, int size, int referenceCount ) + { + if ( ptr == IntPtr.Zero ) + throw new ArgumentNullException( nameof( ptr ) ); + if ( size <= 0 ) + throw new ArgumentOutOfRangeException( nameof( size ) ); + if ( referenceCount <= 0 ) + throw new ArgumentOutOfRangeException( nameof( referenceCount ) ); + + Pointer = ptr; + Size = size; + + var prevCount = Interlocked.Exchange(ref _count, referenceCount); + if (prevCount != 0) + { +#if DEBUG + SteamNetworkingUtils.LogDebugMessage( NetDebugOutput.Warning, $"{nameof( BroadcastBufferManager )} set reference count when current count was not 0" ); +#endif + } + } + + public bool Decrement() + { + var newCount = Interlocked.Decrement( ref _count ); + if ( newCount < 0 ) + { + SteamNetworkingUtils.LogDebugMessage( NetDebugOutput.Bug, $"Prevented double free of {nameof(BroadcastBufferManager)} pointer" ); + return false; + } + + return newCount == 0; + } + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void FreeFn( NetMsg* msg ); + + private static readonly Stack ReferenceCounterPool = + new Stack( 1024 ); + + private static readonly Dictionary> BufferPools = + new Dictionary>(); + + private static readonly Dictionary ReferenceCounters = + new Dictionary( 1024 ); + + public static readonly IntPtr FreeFunctionPointer = Marshal.GetFunctionPointerForDelegate( Free ); + + public static IntPtr Get( int size, int referenceCount ) + { + const int maxSize = 16 * 1024 * 1024; + if ( size < 0 || size > maxSize ) + throw new ArgumentOutOfRangeException( nameof( size ) ); + if ( referenceCount <= 0 ) + throw new ArgumentOutOfRangeException( nameof( referenceCount ) ); + + AllocateBuffer( size, out var ptr, out var actualSize ); + var counter = AllocateReferenceCounter( ptr, actualSize, referenceCount ); + +#if DEBUG + SteamNetworkingUtils.LogDebugMessage( NetDebugOutput.Verbose, + $"{nameof( BroadcastBufferManager )} allocated {ptr.ToInt64():X8} (size={size}, actualSize={actualSize}) with {referenceCount} references" ); +#endif + + lock ( ReferenceCounters ) + { + ReferenceCounters.Add( ptr, counter ); + } + + return ptr; + } + + [MonoPInvokeCallback] + private static void Free( NetMsg* msg ) + { + var ptr = msg->DataPtr; + + lock ( ReferenceCounters ) + { + if ( !ReferenceCounters.TryGetValue( ptr, out var counter ) ) + { + SteamNetworkingUtils.LogDebugMessage( NetDebugOutput.Bug, $"Attempt to free pointer not tracked by {nameof(BroadcastBufferManager)}: {ptr.ToInt64():X8}" ); + return; + } + +#if DEBUG + SteamNetworkingUtils.LogDebugMessage( NetDebugOutput.Verbose, $"{nameof( BroadcastBufferManager )} decrementing reference count of {ptr.ToInt64():X8}" ); +#endif + + if ( counter.Decrement() ) + { +#if DEBUG + SteamNetworkingUtils.LogDebugMessage( NetDebugOutput.Verbose, $"{nameof( BroadcastBufferManager )} freeing {ptr.ToInt64():X8} as it is now unreferenced" ); + + if ( ptr != counter.Pointer ) + { + SteamNetworkingUtils.LogDebugMessage( NetDebugOutput.Bug, + $"{nameof( BroadcastBufferManager )} freed pointer ({ptr.ToInt64():X8}) does not match counter pointer ({counter.Pointer.ToInt64():X8})" ); + } + + var bucketSize = GetBucketSize( counter.Size ); + if ( counter.Size != bucketSize ) + { + SteamNetworkingUtils.LogDebugMessage( NetDebugOutput.Bug, + $"{nameof( BroadcastBufferManager )} freed pointer size ({counter.Size}) does not match bucket size ({bucketSize})" ); + } +#endif + + ReferenceCounters.Remove( ptr ); + FreeBuffer( ptr, counter.Size ); + FreeReferenceCounter( counter ); + } + } + } + + private static ReferenceCounter AllocateReferenceCounter( IntPtr ptr, int size, int referenceCount ) + { + lock ( ReferenceCounterPool ) + { + var counter = ReferenceCounterPool.Count > 0 + ? ReferenceCounterPool.Pop() + : new ReferenceCounter(); + + counter.Set( ptr, size, referenceCount ); + return counter; + } + } + + private static void FreeReferenceCounter( ReferenceCounter counter ) + { + if ( counter == null ) + throw new ArgumentNullException( nameof( counter ) ); + + lock ( ReferenceCounterPool ) + { + if ( ReferenceCounterPool.Count >= 1024 ) + { + // we don't want to keep a ton of these lying around - let it GC if we have too many + return; + } + + ReferenceCounterPool.Push( counter ); + } + } + + private static void AllocateBuffer( int minimumSize, out IntPtr ptr, out int size ) + { + var bucketSize = GetBucketSize( minimumSize ); + + if ( bucketSize <= 0 ) + { + // not bucketed, no pooling for this size + ptr = Marshal.AllocHGlobal( minimumSize ); + size = minimumSize; + +#if DEBUG + SteamNetworkingUtils.LogDebugMessage( NetDebugOutput.Verbose, + $"{nameof( BroadcastBufferManager )} allocated unpooled pointer {ptr.ToInt64():X8} (size={size})" ); +#endif + return; + } + + lock ( BufferPools ) + { + if ( !BufferPools.TryGetValue( bucketSize, out var bucketPool ) || bucketPool.Count == 0 ) + { + // nothing pooled yet, but we can pool this size + ptr = Marshal.AllocHGlobal( bucketSize ); + size = bucketSize; + +#if DEBUG + SteamNetworkingUtils.LogDebugMessage( NetDebugOutput.Verbose, + $"{nameof( BroadcastBufferManager )} allocated new poolable pointer {ptr.ToInt64():X8} (size={size})" ); +#endif + return; + } + + ptr = bucketPool.Pop(); + size = bucketSize; +#if DEBUG + SteamNetworkingUtils.LogDebugMessage( NetDebugOutput.Verbose, + $"{nameof( BroadcastBufferManager )} allocated pointer from pool {ptr.ToInt64():X8} (size={size})" ); +#endif + } + } + + private static void FreeBuffer( IntPtr ptr, int size ) + { + var bucketSize = GetBucketSize( size ); + var bucketLimit = GetBucketLimit( size ); + + if ( bucketSize <= 0 || bucketLimit <= 0 ) + { + // not bucketed, no pooling for this size + Marshal.FreeHGlobal( ptr ); + +#if DEBUG + SteamNetworkingUtils.LogDebugMessage( NetDebugOutput.Verbose, + $"{nameof( BroadcastBufferManager )} freed unpooled pointer {ptr.ToInt64():X8} (size={size})" ); +#endif + return; + } + + lock ( BufferPools ) + { + if ( !BufferPools.TryGetValue( bucketSize, out var bucketPool ) ) + { + bucketPool = new Stack( bucketLimit ); + BufferPools.Add( bucketSize, bucketPool ); + } + + if ( bucketPool.Count >= bucketLimit ) + { + // pool overflow, get rid + Marshal.FreeHGlobal( ptr ); + +#if DEBUG + SteamNetworkingUtils.LogDebugMessage( NetDebugOutput.Verbose, + $"{nameof( BroadcastBufferManager )} pool overflow, freed pooled pointer {ptr.ToInt64():X8} (size={size})" ); +#endif + return; + } + + bucketPool.Push( ptr ); + +#if DEBUG + SteamNetworkingUtils.LogDebugMessage( NetDebugOutput.Verbose, + $"{nameof( BroadcastBufferManager )} returned pointer to pool {ptr.ToInt64():X8} (size={size})" ); +#endif + } + } + + private const int Bucket1Kb = 1 * 1024; + private const int Bucket4Kb = 4 * 1024; + private const int Bucket16Kb = 16 * 1024; + private const int Bucket64Kb = 64 * 1024; + private const int Bucket256Kb = 256 * 1024; + + private static int GetBucketSize( int size ) + { + if ( size <= Bucket1Kb ) return Bucket1Kb; + if ( size <= Bucket4Kb ) return Bucket4Kb; + if ( size <= Bucket16Kb ) return Bucket16Kb; + if ( size <= Bucket64Kb ) return Bucket64Kb; + if ( size <= Bucket256Kb ) return Bucket256Kb; + + return -1; + } + + private static int GetBucketLimit( int size ) + { + if ( size <= Bucket1Kb ) return 256; + if ( size <= Bucket4Kb ) return 64; + if ( size <= Bucket16Kb ) return 32; + if ( size <= Bucket64Kb ) return 16; + if ( size <= Bucket256Kb ) return 8; + + return -1; + } + } +} diff --git a/Facepunch.Steamworks/Networking/ConnectionManager.cs b/Facepunch.Steamworks/Networking/ConnectionManager.cs index ce32b72..66f5a59 100644 --- a/Facepunch.Steamworks/Networking/ConnectionManager.cs +++ b/Facepunch.Steamworks/Networking/ConnectionManager.cs @@ -109,34 +109,119 @@ namespace Steamworks } public unsafe int Receive( int bufferSize = 32, bool receiveToEnd = true ) - { - if ( bufferSize < 1 || bufferSize > 256 ) throw new ArgumentOutOfRangeException( nameof( bufferSize ) ); + { + if ( bufferSize < 1 || bufferSize > 256 ) throw new ArgumentOutOfRangeException( nameof( bufferSize ) ); int totalProcessed = 0; - NetMsg** messageBuffer = stackalloc NetMsg*[bufferSize]; + NetMsg** messageBuffer = stackalloc NetMsg*[bufferSize]; while ( true ) - { + { int processed = SteamNetworkingSockets.Internal.ReceiveMessagesOnConnection( Connection, new IntPtr( &messageBuffer[0] ), bufferSize ); - totalProcessed += processed; + totalProcessed += processed; - for ( int i = 0; i < processed; i++ ) - { - // TODO: if this throws we will leak the remaining NetMsgs (probably not going to happen much though) - ReceiveMessage( messageBuffer[i] ); - } + try + { + for ( int i = 0; i < processed; i++ ) + { + ReceiveMessage( ref messageBuffer[i] ); + } + } + catch + { + for ( int i = 0; i < processed; i++ ) + { + if ( messageBuffer[i] != null ) + { + NetMsg.InternalRelease( messageBuffer[i] ); + } + } - // - // Keep going if receiveToEnd and we filled the buffer - // - if ( !receiveToEnd || processed < bufferSize ) - break; + throw; + } + + + // + // Keep going if receiveToEnd and we filled the buffer + // + if ( !receiveToEnd || processed < bufferSize ) + break; } return totalProcessed; } - internal unsafe void ReceiveMessage( NetMsg* msg ) + public unsafe void Broadcast( Connection[] connections, int connectionCount, IntPtr ptr, int size, SendType sendType = SendType.Reliable ) + { + if ( connections == null ) + throw new ArgumentNullException( nameof( connections ) ); + if ( connectionCount < 0 || connectionCount > connections.Length ) + throw new ArgumentException( nameof( connectionCount ) ); + if ( connectionCount > 1024 ) + throw new ArgumentOutOfRangeException( nameof( connectionCount ) ); + if ( ptr == IntPtr.Zero ) + throw new ArgumentNullException( nameof( ptr ) ); + if ( size == 0 ) + throw new ArgumentException( nameof( size ) ); + + if ( connectionCount == 0 ) + return; + + // SendMessages does not make a copy of the data. We will need to copy because we don't want to force the caller to keep the pointer valid. + // 1. We don't want a copy per message. They all refer to the same data. This is the benefit of using Broadcast vs. many sends. + // 2. We need to use unmanaged memory. Managed memory may move around and invalidate pointers so it's not an option. + // 3. We'll use a reference counter and custom free() function to release this unmanaged memory. + var copyPtr = BroadcastBufferManager.Get( size, connectionCount ); + Buffer.MemoryCopy( (void*)ptr, (void*)copyPtr, size, size ); + + var messages = stackalloc NetMsg*[connectionCount]; + for ( var i = 0; i < connectionCount; i++ ) + { + messages[i] = SteamNetworkingUtils.AllocateMessage(); + messages[i]->Connection = connections[i]; + messages[i]->Flags = sendType; + messages[i]->DataPtr = copyPtr; + messages[i]->DataSize = size; + messages[i]->FreeDataPtr = BroadcastBufferManager.FreeFunctionPointer; + } + + SteamNetworkingSockets.Internal.SendMessages( connectionCount, messages, null ); + } + + /// + /// Ideally should be using an IntPtr version unless you're being really careful with the byte[] array and + /// you're not creating a new one every frame (like using .ToArray()) + /// + public unsafe void Broadcast( Connection[] connections, int connectionCount, byte[] data, SendType sendType = SendType.Reliable ) + { + fixed ( byte* ptr = data ) + { + Broadcast( connections, connectionCount, (IntPtr)ptr, data.Length, sendType ); + } + } + + /// + /// Ideally should be using an IntPtr version unless you're being really careful with the byte[] array and + /// you're not creating a new one every frame (like using .ToArray()) + /// + public unsafe void Broadcast( Connection[] connections, int connectionCount, byte[] data, int offset, int length, SendType sendType = SendType.Reliable ) + { + fixed ( byte* ptr = data ) + { + Broadcast( connections, connectionCount, (IntPtr)ptr + offset, length, sendType ); + } + } + + /// + /// This creates a ton of garbage - so don't do anything with this beyond testing! + /// + public void Broadcast( Connection[] connections, int connectionCount, string str, SendType sendType = SendType.Reliable ) + { + var bytes = System.Text.Encoding.UTF8.GetBytes( str ); + Broadcast( connections, connectionCount, bytes, sendType ); + } + + internal unsafe void ReceiveMessage( ref NetMsg* msg ) { try { @@ -148,6 +233,7 @@ namespace Steamworks // Releases the message // NetMsg.InternalRelease( msg ); + msg = null; } } diff --git a/Facepunch.Steamworks/Networking/NetMsg.cs b/Facepunch.Steamworks/Networking/NetMsg.cs index 8bd0953..c84509f 100644 --- a/Facepunch.Steamworks/Networking/NetMsg.cs +++ b/Facepunch.Steamworks/Networking/NetMsg.cs @@ -1,5 +1,4 @@ -using Steamworks.Data; -using System; +using System; using System.Runtime.InteropServices; namespace Steamworks.Data @@ -17,5 +16,7 @@ namespace Steamworks.Data internal IntPtr FreeDataPtr; internal IntPtr ReleasePtr; internal int Channel; + internal SendType Flags; + internal long UserData; } -} \ No newline at end of file +} diff --git a/Facepunch.Steamworks/SteamNetworkingUtils.cs b/Facepunch.Steamworks/SteamNetworkingUtils.cs index a495bf7..ecd228d 100644 --- a/Facepunch.Steamworks/SteamNetworkingUtils.cs +++ b/Facepunch.Steamworks/SteamNetworkingUtils.cs @@ -271,6 +271,11 @@ namespace Steamworks debugMessages.Enqueue( new DebugMessage { Type = nType, Msg = Helpers.MemoryToString( str ) } ); } + internal static void LogDebugMessage( NetDebugOutput type, string message ) + { + debugMessages.Enqueue( new DebugMessage { Type = type, Msg = message } ); + } + /// /// Called regularly from the Dispatch loop so we can provide a timely /// stream of messages. @@ -286,6 +291,11 @@ namespace Steamworks } } + internal static unsafe NetMsg* AllocateMessage() + { + return Internal.AllocateMessage(0); + } + #region Config Internals internal unsafe static bool SetConfigInt( NetConfig type, int value ) diff --git a/Generator/CodeWriter/Interface.cs b/Generator/CodeWriter/Interface.cs index f85927a..1273cb9 100644 --- a/Generator/CodeWriter/Interface.cs +++ b/Generator/CodeWriter/Interface.cs @@ -23,7 +23,7 @@ namespace Generator StartBlock( $"namespace Steamworks" ); { - StartBlock( $"internal class {iface.Name} : SteamInterface" ); + StartBlock( $"internal unsafe class {iface.Name} : SteamInterface" ); { WriteLine(); StartBlock( $"internal {iface.Name}( bool IsGameServer )" ); diff --git a/Generator/Types/StructType.cs b/Generator/Types/StructType.cs index 6ef7166..9e5a616 100644 --- a/Generator/Types/StructType.cs +++ b/Generator/Types/StructType.cs @@ -15,17 +15,27 @@ internal class StructType : BaseType { public string StructName; - public override string TypeName => StructName; + public override string TypeName => IsPointer && TreatAsPointer ? StructName + PointerSuffix : StructName; - public override string TypeNameFrom => NativeType.EndsWith( "*" ) ? "IntPtr" : base.ReturnType; + public override string TypeNameFrom => IsPointer && !TreatAsPointer ? "IntPtr" : TypeName; - public override string Return( string varname ) + public override string AsArgument() => IsPointer && TreatAsPointer ? $"{TypeName} {VarName}" : base.AsArgument(); + + public override string AsCallArgument() => IsPointer && TreatAsPointer ? VarName : base.AsCallArgument(); + + public bool IsPointer => NativeType.EndsWith( "*" ); + + public bool TreatAsPointer => StructName == "NetMsg"; + + public override string Return( string varname ) { - if ( NativeType.EndsWith( "*" ) ) + if ( IsPointer && !TreatAsPointer ) { - return $"return {varname}.ToType<{TypeName}>();"; + return $"return {varname}.ToType<{TypeName}>();"; } return base.Return( varname ); } + + private string PointerSuffix => new string( '*', NativeType.Count( c => c == '*' ) ); } \ No newline at end of file