From d18086a1b87491bbb5f5cdbe57b42968bf881748 Mon Sep 17 00:00:00 2001 From: Jake Rich Date: Mon, 12 Aug 2024 20:46:16 -0400 Subject: [PATCH] Fix serverlist query allocating a large amount of memory when a large amount of servers are pending --- .../Interfaces/ISteamMatchmakingServers.cs | 19 ++++++++++++++++++- .../Generated/SteamStructs.cs | 5 ++++- Facepunch.Steamworks/ServerList/Base.cs | 16 +++++++++++----- 3 files changed, 33 insertions(+), 7 deletions(-) diff --git a/Facepunch.Steamworks/Generated/Interfaces/ISteamMatchmakingServers.cs b/Facepunch.Steamworks/Generated/Interfaces/ISteamMatchmakingServers.cs index 6f9ee8b3..c1ca82c0 100644 --- a/Facepunch.Steamworks/Generated/Interfaces/ISteamMatchmakingServers.cs +++ b/Facepunch.Steamworks/Generated/Interfaces/ISteamMatchmakingServers.cs @@ -106,7 +106,24 @@ internal gameserveritem_t GetServerDetails( HServerListRequest hRequest, int iSe var returnValue = _GetServerDetails( Self, hRequest, iServer ); return returnValue.ToType(); } - + + /// + /// Read gameserveritem_t.m_bHadSuccessfulResponse without allocating the struct on the heap + /// + /// + /// + /// + internal bool HasServerResponded( HServerListRequest hRequest, int iServer ) + { + IntPtr returnValue = _GetServerDetails( Self, hRequest, iServer ); + + // Return false if steam returned null + if ( returnValue == IntPtr.Zero ) return false; + + // first 8 bytes is IPAddress, next 4 bytes is ping, next 1 byte is m_bHadSuccessfulResponse + return Marshal.ReadByte( IntPtr.Add( returnValue, 12 ) ) == 1; + } + #region FunctionMeta [DllImport( Platform.LibraryName, EntryPoint = "SteamAPI_ISteamMatchmakingServers_CancelQuery", CallingConvention = Platform.CC)] private static extern void _CancelQuery( IntPtr self, HServerListRequest hRequest ); diff --git a/Facepunch.Steamworks/Generated/SteamStructs.cs b/Facepunch.Steamworks/Generated/SteamStructs.cs index 8724de68..13e0b4a6 100644 --- a/Facepunch.Steamworks/Generated/SteamStructs.cs +++ b/Facepunch.Steamworks/Generated/SteamStructs.cs @@ -25,14 +25,17 @@ internal partial struct servernetadr_t internal uint IP; // m_unIP uint32 } - + [StructLayout( LayoutKind.Sequential, Pack = Platform.StructPackSize )] internal partial struct gameserveritem_t { internal servernetadr_t NetAdr; // m_NetAdr servernetadr_t internal int Ping; // m_nPing int + + // NOTE: If you add fields above this you must change offset inISteamMatchmakingServers.HasServerResponded() [MarshalAs(UnmanagedType.I1)] internal bool HadSuccessfulResponse; // m_bHadSuccessfulResponse bool + [MarshalAs(UnmanagedType.I1)] internal bool DoNotRefresh; // m_bDoNotRefresh bool internal string GameDirUTF8() => System.Text.Encoding.UTF8.GetString( GameDir, 0, System.Array.IndexOf( GameDir, 0 ) ); diff --git a/Facepunch.Steamworks/ServerList/Base.cs b/Facepunch.Steamworks/ServerList/Base.cs index 41f8624b..2d11cc5c 100644 --- a/Facepunch.Steamworks/ServerList/Base.cs +++ b/Facepunch.Steamworks/ServerList/Base.cs @@ -161,11 +161,17 @@ public void UpdateResponsive() { watchList.RemoveAll( x => { - var info = Internal.GetServerDetails( request, x ); - if ( info.HadSuccessfulResponse ) + // First check if the server has responded without allocating server info + bool hasResponded = Internal.HasServerResponded( request, x ); + if ( hasResponded ) { - OnServer( ServerInfo.From( info ), info.HadSuccessfulResponse ); - return true; + // Now get all server info + var info = Internal.GetServerDetails( request, x ); + if ( info.HadSuccessfulResponse ) + { + OnServer( ServerInfo.From( info ), info.HadSuccessfulResponse ); + return true; + } } return false; @@ -194,4 +200,4 @@ private void OnServer( ServerInfo serverInfo, bool responded ) Unresponsive.Add( serverInfo ); } } -} \ No newline at end of file +}