Server requests

This commit is contained in:
Garry Newman 2016-07-07 16:55:08 +01:00
parent 99a804c14b
commit 15d1d8a0df
8 changed files with 391 additions and 15 deletions

7
.gitignore vendored
View File

@ -61,4 +61,9 @@ Facepunch.Steamworks/bin/Debug/Facepunch.Steamworks.Api.dll
Facepunch.Steamworks/bin/Debug/Facepunch.Steamworks.dll
Facepunch.Steamworks/bin/Release/Facepunch.Steamworks.dll
*.opendb
*.db
*.db
Facepunch.Steamworks.dll
Facepunch.Steamworks.Test.dll
*UnitTestFramework.dll
mscorlib.dll
*.nlp

View File

@ -9,7 +9,7 @@ namespace Facepunch.Steamworks.Test
public class Client
{
[TestMethod]
public void ClientInit()
public void Init()
{
using ( var client = new Facepunch.Steamworks.Client( 252490 ) )
{
@ -18,7 +18,7 @@ public void ClientInit()
}
[TestMethod]
public void ClientName()
public void Name()
{
using ( var client = new Facepunch.Steamworks.Client( 252490 ) )
{
@ -30,7 +30,7 @@ public void ClientName()
}
[TestMethod]
public void ClientSteamId()
public void SteamId()
{
using ( var client = new Facepunch.Steamworks.Client( 252490 ) )
{
@ -42,7 +42,7 @@ public void ClientSteamId()
}
[TestMethod]
public void ClientAuthSessionTicket()
public void AuthSessionTicket()
{
using ( var client = new Facepunch.Steamworks.Client( 252490 ) )
{
@ -59,7 +59,7 @@ public void ClientAuthSessionTicket()
}
[TestMethod]
public void ClientVoiceOptimalSampleRate()
public void VoiceOptimalSampleRate()
{
using ( var client = new Facepunch.Steamworks.Client( 252490 ) )
{
@ -69,7 +69,7 @@ public void ClientVoiceOptimalSampleRate()
}
[TestMethod]
public void ClientUpdate()
public void Update()
{
using ( var client = new Facepunch.Steamworks.Client( 252490 ) )
{
@ -82,7 +82,7 @@ public void ClientUpdate()
}
[TestMethod]
public void ClientGetVoice()
public void GetVoice()
{
using ( var client = new Facepunch.Steamworks.Client( 252490 ) )
{
@ -110,5 +110,26 @@ public void ClientGetVoice()
Console.Write( dataRead );
}
}
[TestMethod]
public void GetServers()
{
using ( var client = new Facepunch.Steamworks.Client( 252490 ) )
{
var query = client.ServerList.Test();
for ( int i = 0; i < 100 ; i++ )
{
client.Update();
System.Threading.Thread.Sleep( 5 );
if ( query.Finished )
break;
}
Console.WriteLine( "Responded: " + query.Responded.Count.ToString() );
Console.WriteLine( "Unresponsive: " + query.Unresponsive.Count.ToString() );
}
}
}
}

View File

@ -34,6 +34,42 @@
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<OutputPath>bin\x64\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x86\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<OutputPath>bin\x86\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
</ItemGroup>

View File

@ -0,0 +1,210 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using Valve.Steamworks;
namespace Facepunch.Steamworks
{
public partial class ServerList
{
public class Request : IDisposable
{
internal Client client;
internal uint Id;
private IntPtr m_pVTable;
private GCHandle m_pGCHandle;
public struct Server
{
public string Name { get; set; }
public int Ping { get; set; }
public string GameDir { get; set; }
public string Map { get; set; }
public string Description { get; set; }
public uint AppId { get; set; }
public int Players { get; set; }
public int MaxPlayers { get; set; }
public int BotPlayers { get; set; }
public bool Passworded { get; set; }
public bool Secure { get; set; }
public uint LastTimePlayed { get; set; }
public int Version { get; set; }
public string[] Tags { get; set; }
public ulong SteamId { get; set; }
internal static Server FromSteam( gameserveritem_t item )
{
return new Server()
{
Name = item.m_szServerName,
Ping = item.m_nPing,
GameDir = item.m_szGameDir,
Map = item.m_szMap,
Description = item.m_szGameDescription,
AppId = item.m_nAppID,
Players = item.m_nPlayers,
MaxPlayers = item.m_nMaxPlayers,
BotPlayers = item.m_nBotPlayers,
Passworded = item.m_bPassword,
Secure = item.m_bSecure,
LastTimePlayed = item.m_ulTimeLastPlayed,
Version = item.m_nServerVersion,
Tags = item.m_szGameTags.Split( ',' ),
SteamId = item.m_steamID
};
}
}
public servernetadr_t m_NetAdr;
public int m_nPing;
[MarshalAs(UnmanagedType.I1)]
public bool m_bHadSuccessfulResponse;
[MarshalAs(UnmanagedType.I1)]
public bool m_bDoNotRefresh;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string m_szGameDir; //char[32]
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string m_szMap; //char[32]
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
public string m_szGameDescription; //char[64]
public uint m_nAppID;
public int m_nPlayers;
public int m_nMaxPlayers;
public int m_nBotPlayers;
[MarshalAs(UnmanagedType.I1)]
public bool m_bPassword;
[MarshalAs(UnmanagedType.I1)]
public bool m_bSecure;
public uint m_ulTimeLastPlayed;
public int m_nServerVersion;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
public string m_szServerName; //char[64]
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
public string m_szGameTags; //char[128]
public ulong m_steamID;
/// <summary>
/// A list of servers that responded. If you're only interested in servers that responded since you
/// last updated, then simply clear this list.
/// </summary>
public List<Server> Responded = new List<Server>();
/// <summary>
/// A list of servers that were in the master list but didn't respond.
/// </summary>
public List<Server> Unresponsive = new List<Server>();
/// <summary>
/// True when we have finished
/// </summary>
public bool Finished = false;
internal Request()
{
//
// Create a fake vtable for Steam to respond to
//
var vt = new VTable()
{
responded = OnServerResponded,
nonresponsive = NonResponsive,
complete = Complete
};
m_pVTable = Marshal.AllocHGlobal( Marshal.SizeOf( typeof( VTable ) ) );
Marshal.StructureToPtr( vt, m_pVTable, false );
m_pGCHandle = GCHandle.Alloc( m_pVTable, GCHandleType.Pinned );
}
~Request()
{
Dispose();
}
/// <summary>
/// Disposing will end the query
/// </summary>
public void Dispose()
{
//
// Cancel the query if it's still running
//
if ( !Finished && Id > 0 )
{
if ( client.Valid )
client._servers.CancelQuery( Id );
Id = 0;
}
//
// Release the pinned GC resources
//
if ( m_pVTable != IntPtr.Zero )
{
Marshal.FreeHGlobal( m_pVTable );
m_pVTable = IntPtr.Zero;
}
if ( m_pGCHandle.IsAllocated )
{
m_pGCHandle.Free();
}
}
private void Complete( IntPtr thisptr, uint RequestId, int response )
{
if ( RequestId != Id )
throw new Exception( "Request ID is invalid!" );
Finished = true;
Id = 0;
}
private void NonResponsive( IntPtr thisptr, uint RequestId, int iServer )
{
if ( RequestId != Id )
throw new Exception( "Request ID is invalid!" );
var info = client._servers.GetServerDetails( Id, iServer );
Unresponsive.Add( Server.FromSteam( info ) );
}
private void OnServerResponded( IntPtr thisptr, uint RequestId, int iServer )
{
if ( RequestId != Id )
throw new Exception( "Request ID is invalid!" );
var info = client._servers.GetServerDetails( Id, iServer );
Responded.Add( Server.FromSteam( info ) );
}
internal IntPtr GetVTablePointer()
{
return m_pGCHandle.AddrOfPinnedObject();
}
[StructLayout( LayoutKind.Sequential )]
internal class VTable
{
[UnmanagedFunctionPointer( CallingConvention.ThisCall )]
internal delegate void InternalServerResponded( IntPtr thisptr, uint hRequest, int iServer );
[UnmanagedFunctionPointer( CallingConvention.ThisCall )]
internal delegate void InternalServerFailedToRespond( IntPtr thisptr, uint hRequest, int iServer );
[UnmanagedFunctionPointer( CallingConvention.ThisCall )]
internal delegate void InternalRefreshComplete( IntPtr thisptr, uint hRequest, int response );
[NonSerialized, MarshalAs(UnmanagedType.FunctionPtr)]
internal InternalServerResponded responded;
[NonSerialized, MarshalAs(UnmanagedType.FunctionPtr)]
internal InternalServerFailedToRespond nonresponsive;
[NonSerialized, MarshalAs(UnmanagedType.FunctionPtr)]
internal InternalRefreshComplete complete;
}
}
}
}

View File

@ -1,7 +1,9 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using Valve.Steamworks;
namespace Facepunch.Steamworks
{
@ -21,9 +23,40 @@ public ServerList ServerList
}
}
public class ServerList
public partial class ServerList
{
internal Client client;
[StructLayout( LayoutKind.Sequential )]
private struct MatchPair
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public string key;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public string value;
}
public unsafe Request Test()
{
var filters = new List<MatchPair>();
filters.Add( new MatchPair() { key = "gamedir", value = "rust" } );
var array = filters.ToArray();
//fixed ( void* a = array )
{
var request = new Request()
{
client = client
};
request.Id = client._servers.RequestInternetServerList( client.AppId, new IntPtr[] { }, request.GetVTablePointer() );
return request;
}
}
}
}

View File

@ -18,7 +18,7 @@ public partial class Client : IDisposable
/// <summary>
/// Current running program's AppId
/// </summary>
public int AppId;
public uint AppId;
/// <summary>
/// Current user's Username
@ -30,9 +30,9 @@ public partial class Client : IDisposable
/// </summary>
public ulong SteamId;
public Client( int appId )
public Client( uint appId )
{
Valve.Steamworks.SteamAPI.Init( (uint) appId );
Valve.Steamworks.SteamAPI.Init( appId );
_client = Valve.Steamworks.SteamAPI.SteamClient();
if ( _client.GetIntPtr() == IntPtr.Zero )

View File

@ -34,6 +34,76 @@
</DocumentationFile>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug64|AnyCPU'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\Debug64\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DebugType>full</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<OutputPath>bin\x64\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug64|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\Debug64\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x86\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<OutputPath>bin\x86\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug64|x86'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x86\Debug64\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
@ -45,6 +115,7 @@
<ItemGroup>
<Compile Include="Client.Overlay.cs" />
<Compile Include="Client.ServerList.cs" />
<Compile Include="Client.ServerList.Request.cs" />
<Compile Include="Client.Voice.cs" />
<Compile Include="Client.Auth.cs" />
<Compile Include="Client.cs" />

View File

@ -1816,7 +1816,7 @@ internal abstract class ISteamMatchmakingRulesResponse
internal abstract class ISteamMatchmakingServers
{
internal abstract IntPtr GetIntPtr();
internal abstract uint RequestInternetServerList( uint iApp, IntPtr[] ppchFilters, ISteamMatchmakingServerListResponse pRequestServersResponse );
internal abstract uint RequestInternetServerList( uint iApp, IntPtr[] ppchFilters, IntPtr pRequestServersResponse );
internal abstract uint RequestLANServerList( uint iApp, ISteamMatchmakingServerListResponse pRequestServersResponse );
internal abstract uint RequestFriendsServerList( uint iApp, IntPtr[] ppchFilters, ISteamMatchmakingServerListResponse pRequestServersResponse );
internal abstract uint RequestFavoritesServerList( uint iApp, IntPtr[] ppchFilters, ISteamMatchmakingServerListResponse pRequestServersResponse );
@ -3775,10 +3775,10 @@ private void CheckIfUsable()
throw new Exception( "Steam Pointer not configured" );
}
}
internal override uint RequestInternetServerList( uint iApp, IntPtr[] ppchFilters, ISteamMatchmakingServerListResponse pRequestServersResponse )
internal override uint RequestInternetServerList( uint iApp, IntPtr[] ppchFilters, IntPtr pRequestServersResponse )
{
CheckIfUsable();
uint result = NativeEntrypoints.SteamAPI_ISteamMatchmakingServers_RequestInternetServerList(m_pSteamMatchmakingServers,iApp,ppchFilters,(uint) ppchFilters.Length,pRequestServersResponse.GetIntPtr());
uint result = NativeEntrypoints.SteamAPI_ISteamMatchmakingServers_RequestInternetServerList(m_pSteamMatchmakingServers,iApp,ppchFilters,(uint) ppchFilters.Length,pRequestServersResponse);
return result;
}
internal override uint RequestLANServerList( uint iApp, ISteamMatchmakingServerListResponse pRequestServersResponse )