From bcd8495d6eda1e4d34fd83c5551842a61f7bc86c Mon Sep 17 00:00:00 2001 From: Kyle Kukshtel Date: Mon, 31 Jul 2017 22:44:46 -0700 Subject: [PATCH] Adds in ability to send Lobby messages Also adds in some callbacks around being invited to a lobby, as well callbacks for being requested to join a lobby. --- Facepunch.Steamworks.Test/Client/Lobby.cs | 43 +++++- Facepunch.Steamworks/Client/Lobby.cs | 179 +++++++++++++++++++--- 2 files changed, 203 insertions(+), 19 deletions(-) diff --git a/Facepunch.Steamworks.Test/Client/Lobby.cs b/Facepunch.Steamworks.Test/Client/Lobby.cs index 0a05800..b84d089 100644 --- a/Facepunch.Steamworks.Test/Client/Lobby.cs +++ b/Facepunch.Steamworks.Test/Client/Lobby.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Diagnostics; using Facepunch.Steamworks; +using System.Text; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace Facepunch.Steamworks.Test @@ -197,7 +198,7 @@ public void RefreshLobbyList() [TestMethod] public void RefreshLobbyListWithFilter() { - using (var client = new Facepunch.Steamworks.Client(480)) + using (var client = new Facepunch.Steamworks.Client(252490)) { Assert.IsTrue(client.IsValid); @@ -247,5 +248,45 @@ public void RefreshLobbyListWithFilter() } } + + [TestMethod] + public void SendChatMessage() + { + using (var client = new Facepunch.Steamworks.Client(252490)) + { + Assert.IsTrue(client.IsValid); + string testString = "Hello, World"; + + client.Lobby.OnLobbyCreated = (success) => + { + Assert.IsTrue(success); + Assert.IsTrue(client.Lobby.IsValid); + Console.WriteLine("lobby created: " + client.Lobby.CurrentLobby); + client.Lobby.CurrentLobbyData.SetData("testkey", "testvalue"); + client.Lobby.SendChatMessage(testString); + }; + + client.Lobby.OnChatMessageRecieved = (steamID, bytes, length) => + { + string message = Encoding.UTF8.GetString(bytes, 0, length); + Console.WriteLine("message recieved"); + Assert.IsTrue(message == testString); + }; + + client.Lobby.Create(Steamworks.Lobby.Type.Public, 10); + + var sw = Stopwatch.StartNew(); + + while (sw.Elapsed.TotalSeconds < 5) + { + client.Update(); + System.Threading.Thread.Sleep(10); + } + + client.Lobby.Leave(); + + } + } + } } diff --git a/Facepunch.Steamworks/Client/Lobby.cs b/Facepunch.Steamworks/Client/Lobby.cs index 4cc1645..a7a8c6a 100644 --- a/Facepunch.Steamworks/Client/Lobby.cs +++ b/Facepunch.Steamworks/Client/Lobby.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Text; using SteamNative; -using Result = SteamNative.Result; namespace Facepunch.Steamworks { @@ -38,6 +37,11 @@ public Lobby(Client c) { client = c; SteamNative.LobbyDataUpdate_t.RegisterCallback(client, OnLobbyDataUpdatedAPI); + SteamNative.LobbyChatMsg_t.RegisterCallback(client, OnLobbyChatMessageRecievedAPI); + SteamNative.LobbyChatUpdate_t.RegisterCallback(client, OnLobbyStateUpdatedAPI); + SteamNative.GameLobbyJoinRequested_t.RegisterCallback(client, OnLobbyJoinRequestedAPI); + SteamNative.LobbyInvite_t.RegisterCallback(client, OnUserInvitedToLobbyAPI); + SteamNative.PersonaStateChange_t.RegisterCallback(client, OnLobbyMemberPersonaChangeAPI); } /// @@ -53,11 +57,12 @@ public Lobby(Client c) public bool IsValid => CurrentLobby != 0; /// - /// Join a Lobby through its LobbyID. LobbyJoined is called when the lobby has successfully been joined. + /// Join a Lobby through its LobbyID. OnLobbyJoined is called with the result of the Join attempt. /// /// CSteamID of lobby to join public void Join(ulong lobbyID) { + Leave(); client.native.matchmaking.JoinLobby(lobbyID, OnLobbyJoinedAPI); } @@ -74,6 +79,9 @@ void OnLobbyJoinedAPI(LobbyEnter_t callback, bool error) if (OnLobbyJoined != null) { OnLobbyJoined(true); } } + /// + /// Called when a lobby has been attempted joined. Returns true if lobby was successfuly joined, false if not. + /// public Action OnLobbyJoined; /// @@ -216,9 +224,21 @@ internal void UpdateLobbyData() if(OnLobbyDataUpdated != null) { OnLobbyDataUpdated(); } } - //Called when the lobby data itself has been updated. This callback is slower than the actual setting/getting of LobbyData, but it ensures safety. + internal void UpdateLobbyMemberData() + { + if (OnLobbyMemberDataUpdated != null) { OnLobbyMemberDataUpdated(); } + } + + /// + /// Called when the lobby data itself has been updated. Called when someone has joined/Owner has updated data + /// public Action OnLobbyDataUpdated; + /// + /// Called when a member of the lobby has updated either their personal Lobby metadata or someone's global steam state has changed (like a display name) + /// + public Action OnLobbyMemberDataUpdated; + public Type LobbyType { @@ -253,6 +273,74 @@ public Type LobbyType } } + private unsafe void OnLobbyChatMessageRecievedAPI(LobbyChatMsg_t callback, bool error) + { + //from Client.Networking + if(error || callback.SteamIDLobby != CurrentLobby) { return; } + + byte[] ReceiveBuffer = new byte[1024]; + SteamNative.CSteamID steamid = 1; + ChatEntryType chatEntryType; //not used + int readData = 0; + fixed (byte* p = ReceiveBuffer) + { + readData = client.native.matchmaking.GetLobbyChatEntry(CurrentLobby, (int)callback.ChatID, out steamid, (IntPtr)p, ReceiveBuffer.Length, out chatEntryType); + while (ReceiveBuffer.Length < readData) + { + ReceiveBuffer = new byte[readData + 1024]; + readData = client.native.matchmaking.GetLobbyChatEntry(CurrentLobby, (int)callback.ChatID, out steamid, (IntPtr)p, ReceiveBuffer.Length, out chatEntryType); + } + + } + + if (OnChatMessageRecieved != null) { OnChatMessageRecieved(steamid, ReceiveBuffer, readData); } + + } + + /// + /// Callback to get chat messages. + /// + public Action OnChatMessageRecieved; + + /// + /// Broadcasts a chat message to the all the users in the lobby users in the lobby (including the local user) will receive a LobbyChatMsg_t callback. + /// + /// True if message successfully sent + public unsafe bool SendChatMessage(string message) + { + var data = Encoding.UTF8.GetBytes(message); + fixed (byte* p = data) + { + // pvMsgBody can be binary or text data, up to 4k + // if pvMsgBody is text, cubMsgBody should be strlen( text ) + 1, to include the null terminator + return client.native.matchmaking.SendLobbyChatMsg(CurrentLobby, (IntPtr)p, data.Length); + } + } + + public enum MemberStateChange + { + Entered = ChatMemberStateChange.Entered, + Left = ChatMemberStateChange.Left, + Disconnected = ChatMemberStateChange.Disconnected, + Kicked = ChatMemberStateChange.Kicked, + Banned = ChatMemberStateChange.Banned, + } + + internal void OnLobbyStateUpdatedAPI(LobbyChatUpdate_t callback, bool error) + { + if (error || callback.SteamIDLobby != CurrentLobby) { return; } + MemberStateChange change = (MemberStateChange)callback.GfChatMemberStateChange; + ulong initiator = callback.SteamIDMakingChange; + ulong affected = callback.SteamIDUserChanged; + + if (OnLobbyStateChanged != null) { OnLobbyStateChanged(change, initiator, affected); } + } + + /// + /// Called when the state of the Lobby is somehow shifted. Usually when someone joins or leaves the lobby. + /// + public Action OnLobbyStateChanged; + /// /// The name of the lobby as a property for easy getting/setting. Note that this is setting LobbyData, which you cannot do unless you are the Owner of the lobby /// @@ -291,7 +379,9 @@ private set } ulong _owner = 0; - // Can the lobby be joined by other people + /// + /// Is the Lobby joinable by other people? Defaults to true; + /// public bool Joinable { get @@ -318,7 +408,9 @@ public bool Joinable } } - // How many people can be in the Lobby + /// + /// How many people can be in the Lobby + /// public int MaxMembers { get @@ -333,16 +425,21 @@ public int MaxMembers } } - //How many people are currently in the lobby + /// + /// How many people are currently in the Lobby + /// public int NumMembers { get { return client.native.matchmaking.GetNumLobbyMembers(CurrentLobby);} } - //leave the current lobby + /// + /// Leave the CurrentLobby. + /// public void Leave() { - client.native.matchmaking.LeaveLobby(CurrentLobby); + if (CurrentLobby != 0) { client.native.matchmaking.LeaveLobby(CurrentLobby); } + CurrentLobby = 0; _owner = 0; CurrentLobbyData = null; } @@ -352,6 +449,60 @@ public void Dispose() client = null; } + /// + /// Get an array of all the CSteamIDs in the CurrentLobby. + /// Note that you must be in the Lobby you are trying to request the MemberIDs from + /// + /// + public ulong[] GetMemberIDs() + { + ulong[] memIDs = new ulong[NumMembers]; + for (int i = 0; i < NumMembers; i++) + { + memIDs[i] = client.native.matchmaking.GetLobbyMemberByIndex(CurrentLobby, i); + } + return memIDs; + } + + /// + /// Invites the specified user to the CurrentLobby the user is in. + /// + /// ulong ID of person to invite + public bool InviteUserToLobby(ulong friendID) + { + return client.native.matchmaking.InviteUserToLobby(CurrentLobby, friendID); + } + + internal void OnUserInvitedToLobbyAPI(LobbyInvite_t callback, bool error) + { + if (error || (callback.GameID != client.AppId)) { return; } + if (OnUserInvitedToLobby != null) { OnUserInvitedToLobby(callback.SteamIDLobby, callback.SteamIDUser); } + + } + + /// + /// Called when a user invites the current user to a lobby. The first parameter is the lobby the user was invited to, the second is the CSteamID of the person who invited this user + /// + public Action OnUserInvitedToLobby; + + /// + /// Joins a lobby is a request was made to join the lobby through the friends list or an invite + /// + internal void OnLobbyJoinRequestedAPI(GameLobbyJoinRequested_t callback, bool error) + { + if (error) { return; } + Join(callback.SteamIDLobby); + } + + /// + /// Makes sure we send an update callback if a Lobby user updates their information + /// + internal void OnLobbyMemberPersonaChangeAPI(PersonaStateChange_t callback, bool error) + { + if (error || !client.native.friends.IsUserInSource(callback.SteamID, CurrentLobby)) { return; } + UpdateLobbyMemberData(); + } + /*not implemented //set the game server of the lobby @@ -362,16 +513,8 @@ public void Dispose() client.native.matchmaking.SetLobbyMemberData; //local user client.native.matchmaking.GetLobbyMemberData; //any user in this lobby - // returns steamid of member - // note that the current user must be in a lobby to retrieve CSteamIDs of other users in that lobby - client.native.matchmaking.GetLobbyMemberByIndex; - - //chat functions - client.native.matchmaking.SendLobbyChatMsg; - client.native.matchmaking.GetLobbyChatEntry; - - //invite your frans - client.native.matchmaking.InviteUserToLobby //this invites the user the current lobby the invitee is in + //used with game server stuff + SteamNative.LobbyGameCreated_t */ } }