LobbyQuery, Lobby Joining, Lobby Creating

This commit is contained in:
Garry Newman 2019-05-01 15:55:22 +01:00
parent 5a68e15676
commit e3840a9a3a
8 changed files with 416 additions and 4 deletions

View File

@ -38,6 +38,7 @@
<DefineConstants>TRACE</DefineConstants> <DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<PlatformTarget>x64</PlatformTarget>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<DebugSymbols>true</DebugSymbols> <DebugSymbols>true</DebugSymbols>
@ -62,7 +63,7 @@
<OutputPath>bin\x86\Debug\</OutputPath> <OutputPath>bin\x86\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants> <DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType> <DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup> </PropertyGroup>
@ -71,7 +72,7 @@
<DefineConstants>TRACE</DefineConstants> <DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize> <Optimize>true</Optimize>
<DebugType>pdbonly</DebugType> <DebugType>pdbonly</DebugType>
<PlatformTarget>x86</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup> </PropertyGroup>
@ -90,6 +91,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="FriendsTest.cs" /> <Compile Include="FriendsTest.cs" />
<Compile Include="SteamMatchmakingTest.cs" />
<Compile Include="RemoteStorageTest.cs" /> <Compile Include="RemoteStorageTest.cs" />
<Compile Include="InventoryTest.cs" /> <Compile Include="InventoryTest.cs" />
<Compile Include="SteamNetworkingTest.cs" /> <Compile Include="SteamNetworkingTest.cs" />

View File

@ -0,0 +1,73 @@
using System;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Steamworks.Data;
namespace Steamworks
{
[TestClass]
[DeploymentItem( "steam_api64.dll" )]
public class SteamMatchmakingTest
{
[TestMethod]
public async Task LobbyList()
{
await CreateLobby();
var list = await SteamMatchmaking.LobbyList
.RequestAsync();
if ( list == null )
{
Console.WriteLine( "No Lobbies Found!" );
return;
}
foreach ( var lobby in list )
{
Console.WriteLine( $"[{lobby.Id}] owned by {lobby.Owner} ({lobby.MemberCount}/{lobby.MaxMembers})" );
}
}
[TestMethod]
public async Task LobbyListWithAtLeastOne()
{
await CreateLobby();
await LobbyList();
}
[TestMethod]
public async Task CreateLobby()
{
var lobbyr = await SteamMatchmaking.CreateLobbyAsync( 32 );
if ( !lobbyr.HasValue )
{
Console.WriteLine( "No lobby created!" );
return;
}
var lobby = lobbyr.Value;
lobby.SetPublic();
lobby.SetData( "gametype", "sausage" );
lobby.SetData( "dicks", "unlicked" );
Console.WriteLine( $"lobby: {lobby.Id}" );
foreach ( var entry in lobby.Data )
{
Console.WriteLine( $" - {entry.Key} {entry.Value}" );
}
Console.WriteLine( $"members: {lobby.MemberCount}/{lobby.MaxMembers}" );
Console.WriteLine( $"Owner: {lobby.Owner}" );
Console.WriteLine( $"Owner Is Local Player: {lobby.Owner.IsMe}" );
lobby.SendChatString( "Hello I Love Lobbies" );
}
}
}

View File

@ -338,7 +338,7 @@ namespace Steamworks
// //
// EChatRoomEnterResponse // EChatRoomEnterResponse
// //
internal enum ChatRoomEnterResponse : int public enum RoomEnter : int
{ {
Success = 1, Success = 1,
DoesntExist = 2, DoesntExist = 2,

View File

@ -1429,7 +1429,7 @@ namespace Steamworks.Data
internal struct JoinClanChatRoomCompletionResult_t internal struct JoinClanChatRoomCompletionResult_t
{ {
internal ulong SteamIDClanChat; // m_steamIDClanChat class CSteamID internal ulong SteamIDClanChat; // m_steamIDClanChat class CSteamID
internal ChatRoomEnterResponse ChatRoomEnterResponse; // m_eChatRoomEnterResponse enum EChatRoomEnterResponse internal RoomEnter ChatRoomEnterResponse; // m_eChatRoomEnterResponse enum EChatRoomEnterResponse
#region SteamCallback #region SteamCallback
internal static readonly int StructSize = System.Runtime.InteropServices.Marshal.SizeOf( typeof(JoinClanChatRoomCompletionResult_t) ); internal static readonly int StructSize = System.Runtime.InteropServices.Marshal.SizeOf( typeof(JoinClanChatRoomCompletionResult_t) );

View File

@ -35,5 +35,18 @@ namespace Steamworks
//VolumeHasChanged_t.Install( x => OnVolumeChanged?.Invoke( x.NewVolume ) ); //VolumeHasChanged_t.Install( x => OnVolumeChanged?.Invoke( x.NewVolume ) );
} }
public static LobbyQuery LobbyList => new LobbyQuery();
/// <summary>
/// Creates a new invisible lobby. Call lobby.SetPublic to take it online.
/// </summary>
public static async Task<Lobby?> CreateLobbyAsync( int maxMembers = 100 )
{
var lobby = await Internal.CreateLobby( LobbyType.Invisible, maxMembers );
if ( !lobby.HasValue || lobby.Value.Result != Result.OK ) return null;
return new Lobby { Id = lobby.Value.SteamIDLobby };
}
} }
} }

View File

@ -0,0 +1,206 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Steamworks.Data
{
public struct Lobby
{
public SteamId Id { get; internal set; }
/// <summary>
/// Try to join this room. Will return RoomEnter.Success on success,
/// and anything else is a failure
/// </summary>
public async Task<RoomEnter> Join()
{
var result = await SteamMatchmaking.Internal.JoinLobby( Id );
if ( !result.HasValue ) return RoomEnter.Error;
return (RoomEnter) result.Value.EChatRoomEnterResponse;
}
/// <summary>
/// Leave a lobby; this will take effect immediately on the client side
/// other users in the lobby will be notified by a LobbyChatUpdate_t callback
/// </summary>
public void Leave()
{
SteamMatchmaking.Internal.LeaveLobby( Id );
}
/// <summary>
/// Invite another user to the lobby
/// will return true if the invite is successfully sent, whether or not the target responds
/// returns false if the local user is not connected to the Steam servers
/// </summary>
public bool InviteFriend( SteamId steamid )
{
return SteamMatchmaking.Internal.InviteUserToLobby( Id, steamid );
}
/// <summary>
/// returns the number of users in the specified lobby
/// </summary>
public int MemberCount => SteamMatchmaking.Internal.GetNumLobbyMembers( Id );
/// <summary>
/// Returns current members. Need to be in the lobby to see the users.
/// </summary>
public IEnumerable<Friend> Members
{
get
{
for( int i = 0; i < MemberCount; i++ )
{
yield return new Friend( SteamMatchmaking.Internal.GetLobbyMemberByIndex( Id, i ) );
}
}
}
/// <summary>
/// Get data associated with this lobby
/// </summary>
public string GetData( string key )
{
return SteamMatchmaking.Internal.GetLobbyData( Id, key );
}
/// <summary>
/// Get data associated with this lobby
/// </summary>
public bool SetData( string key, string value )
{
if ( key.Length > 255 ) throw new System.ArgumentException( "Key should be < 255 chars", nameof( key ) );
if ( value.Length > 8192 ) throw new System.ArgumentException( "Value should be < 8192 chars", nameof( key ) );
return SteamMatchmaking.Internal.SetLobbyData( Id, key, value );
}
/// <summary>
/// Removes a metadata key from the lobby
/// </summary>
public bool DeleteData( string key )
{
return SteamMatchmaking.Internal.DeleteLobbyData( Id, key );
}
/// <summary>
/// Get all data for this lobby
/// </summary>
public IEnumerable<KeyValuePair<string, string>> Data
{
get
{
var cnt = SteamMatchmaking.Internal.GetLobbyDataCount( Id );
var a = Helpers.TakeStringBuilder();
var b = Helpers.TakeStringBuilder();
for ( int i =0; i<cnt; i++)
{
if ( SteamMatchmaking.Internal.GetLobbyDataByIndex( Id, i, a, a.Capacity, b, b.Capacity ) )
{
yield return new KeyValuePair<string, string>( a.ToString(), b.ToString() );
}
}
}
}
/// <summary>
/// Gets per-user metadata for someone in this lobby
/// </summary>
public string GetMemberData( Friend member, string key )
{
return SteamMatchmaking.Internal.GetLobbyMemberData( Id, member.Id, key );
}
/// <summary>
/// Sets per-user metadata (for the local user implicitly)
/// </summary>
public void SetMemberData( Friend member, string key, string value )
{
SteamMatchmaking.Internal.SetLobbyMemberData( Id, key, value );
}
/// <summary>
/// Sends a string to the chat room
/// </summary>
public bool SendChatString( string message )
{
var data = System.Text.Encoding.UTF8.GetBytes( message );
return SendChatBytes( data );
}
/// <summary>
/// Sends bytes the the chat room
/// </summary>
public unsafe bool SendChatBytes( byte[] data )
{
fixed ( byte* ptr = data )
{
return SteamMatchmaking.Internal.SendLobbyChatMsg( Id, (IntPtr)ptr, data.Length );
}
}
/// <summary>
/// Refreshes metadata for a lobby you're not necessarily in right now
/// you never do this for lobbies you're a member of, only if your
/// this will send down all the metadata associated with a lobby
/// this is an asynchronous call
/// returns false if the local user is not connected to the Steam servers
/// results will be returned by a LobbyDataUpdate_t callback
/// if the specified lobby doesn't exist, LobbyDataUpdate_t::m_bSuccess will be set to false
/// </summary>
public bool Refresh()
{
return SteamMatchmaking.Internal.RequestLobbyData( Id );
}
/// <summary>
/// Max members able to join this lobby. Cannot be over 250.
/// Can only be set by the owner
/// </summary>
public int MaxMembers
{
get => SteamMatchmaking.Internal.GetLobbyMemberLimit( Id );
set => SteamMatchmaking.Internal.SetLobbyMemberLimit( Id, value );
}
public bool SetPublic()
{
return SteamMatchmaking.Internal.SetLobbyType( Id, LobbyType.Public );
}
public bool SetPrivate()
{
return SteamMatchmaking.Internal.SetLobbyType( Id, LobbyType.Private );
}
public bool SetInvisible()
{
return SteamMatchmaking.Internal.SetLobbyType( Id, LobbyType.Invisible );
}
public bool SetFriendsOnly()
{
return SteamMatchmaking.Internal.SetLobbyType( Id, LobbyType.FriendsOnly );
}
public bool SetJoinable( bool b )
{
return SteamMatchmaking.Internal.SetLobbyJoinable( Id, b );
}
/// <summary>
/// You must be the lobby owner to set the owner
/// </summary>
public Friend Owner
{
get => new Friend( SteamMatchmaking.Internal.GetLobbyOwner( Id ) );
set => SteamMatchmaking.Internal.SetLobbyOwner( Id, value.Id );
}
}
}

View File

@ -0,0 +1,116 @@
using System.Threading.Tasks;
namespace Steamworks.Data
{
public struct LobbyQuery
{
// TODO FILTERS
// AddRequestLobbyListStringFilter
// - WithKeyValue, WithoutKeyValue
// AddRequestLobbyListNumericalFilter
// - WithLower, WithHigher, WithEqual, WithNotEqual
// AddRequestLobbyListNearValueFilter
// - OrderByNear
#region Distance Filter
internal LobbyDistanceFilter? distance;
/// <summary>
/// only lobbies in the same immediate region will be returned
/// </summary>
public LobbyQuery FilterDistanceClose()
{
distance = LobbyDistanceFilter.Close;
return this;
}
/// <summary>
/// only lobbies in the same immediate region will be returned
/// </summary>
public LobbyQuery FilterDistanceFar()
{
distance = LobbyDistanceFilter.Far;
return this;
}
/// <summary>
/// only lobbies in the same immediate region will be returned
/// </summary>
public LobbyQuery FilterDistanceWorldwide()
{
distance = LobbyDistanceFilter.Worldwide;
return this;
}
#endregion
#region Slots Filter
internal int? slotsAvailable;
/// <summary>
/// returns only lobbies with the specified number of slots available
/// </summary>
public LobbyQuery WithSlotsAvailable( int minSlots )
{
slotsAvailable = minSlots;
return this;
}
#endregion
#region Max results filter
internal int? maxResults;
/// <summary>
/// sets how many results to return, the lower the count the faster it is to download the lobby results
/// </summary>
public LobbyQuery WithMaxResults( int max )
{
maxResults = max;
return this;
}
#endregion
void ApplyFilters()
{
if ( distance.HasValue )
{
SteamMatchmaking.Internal.AddRequestLobbyListDistanceFilter( distance.Value );
}
if ( slotsAvailable.HasValue )
{
SteamMatchmaking.Internal.AddRequestLobbyListFilterSlotsAvailable( slotsAvailable.Value );
}
if ( maxResults.HasValue )
{
SteamMatchmaking.Internal.AddRequestLobbyListResultCountFilter( maxResults.Value );
}
}
/// <summary>
/// Run the query, get the matching lobbies
/// </summary>
public async Task<Lobby[]> RequestAsync()
{
ApplyFilters();
LobbyMatchList_t? list = await SteamMatchmaking.Internal.RequestLobbyList();
if ( !list.HasValue || list.Value.LobbiesMatching == 0 )
{
return null;
}
Lobby[] lobbies = new Lobby[list.Value.LobbiesMatching];
for ( int i = 0; i < list.Value.LobbiesMatching; i++ )
{
lobbies[i] = new Lobby { Id = SteamMatchmaking.Internal.GetLobbyByIndex( i ) };
}
return lobbies;
}
}
}

View File

@ -25,6 +25,7 @@ public static class Cleanup
type = type.Replace( "UGCMatchingUGCType", "UgcType" ); type = type.Replace( "UGCMatchingUGCType", "UgcType" );
type = type.Replace( "SteamItemInstanceID_t", "InventoryItemId" ); type = type.Replace( "SteamItemInstanceID_t", "InventoryItemId" );
type = type.Replace( "SteamItemDef_t", "InventoryDefId" ); type = type.Replace( "SteamItemDef_t", "InventoryDefId" );
type = type.Replace( "ChatRoomEnterResponse", "RoomEnter" );
return type; return type;
} }
@ -59,6 +60,7 @@ public static class Cleanup
if ( name == "InventoryItemId" ) return "public"; if ( name == "InventoryItemId" ) return "public";
if ( name == "InventoryDefId" ) return "public"; if ( name == "InventoryDefId" ) return "public";
if ( name == "P2PSend" ) return "public"; if ( name == "P2PSend" ) return "public";
if ( name == "RoomEnter" ) return "public";
return "internal"; return "internal";
} }