diff --git a/Facepunch.Steamworks.Test/Client/LobbyTest.cs b/Facepunch.Steamworks.Test/Client/LobbyTest.cs
index b14398d..340cf4d 100644
--- a/Facepunch.Steamworks.Test/Client/LobbyTest.cs
+++ b/Facepunch.Steamworks.Test/Client/LobbyTest.cs
@@ -364,8 +364,8 @@ namespace Facepunch.Steamworks.Test
client.Lobby.OnLobbyMemberDataUpdated = (steamID) =>
{
- string name = client.Friends.GetName(steamID);
- Console.WriteLine(name + " updated data");
+ var friend = new global::Steamworks.Friend( steamID );
+ Console.WriteLine( friend.Name + " updated data");
Assert.IsTrue(client.Lobby.GetMemberData(steamID, "testkey") == "testvalue");
Console.WriteLine("testkey is now: " + client.Lobby.GetMemberData(steamID, "testkey"));
};
diff --git a/Facepunch.Steamworks/Client.cs b/Facepunch.Steamworks/Client.cs
index 9160b0a..2ca3c29 100644
--- a/Facepunch.Steamworks/Client.cs
+++ b/Facepunch.Steamworks/Client.cs
@@ -107,8 +107,6 @@ namespace Facepunch.Steamworks
MicroTransactions = new MicroTransactions( this );
RemoteStorage = new RemoteStorage( this );
- Workshop.friends = Friends;
-
Stats.UpdateStats();
//
@@ -149,8 +147,6 @@ namespace Facepunch.Steamworks
return;
RunCallbacks();
- Friends.Cycle();
-
base.Update();
}
diff --git a/Facepunch.Steamworks/Client/Friends.cs b/Facepunch.Steamworks/Client/Friends.cs
deleted file mode 100644
index f49a950..0000000
--- a/Facepunch.Steamworks/Client/Friends.cs
+++ /dev/null
@@ -1,396 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using SteamNative;
-
-namespace Facepunch.Steamworks
-{
- public partial class Client : IDisposable
- {
- Friends _friends;
-
- public Friends Friends
- {
- get
- {
- if ( _friends == null )
- _friends = new Friends( this );
-
- return _friends;
- }
- }
- }
-
- ///
- /// Handles most interactions with people in Steam, not just friends as the name would suggest.
- ///
- ///
- /// foreach ( var friend in client.Friends.AllFriends )
- /// {
- /// Console.WriteLine( $"{friend.Id}: {friend.Name}" );
- /// }
- ///
- public class Friends
- {
- internal Client client;
- private byte[] buffer = new byte[1024 * 128];
-
- internal Friends( Client c )
- {
- client = c;
-
- client.RegisterCallback( OnAvatarImageLoaded );
- client.RegisterCallback( OnPersonaStateChange );
- client.RegisterCallback( OnGameJoinRequested );
- client.RegisterCallback( OnFriendChatMessage );
- }
-
- public delegate void ChatMessageDelegate( SteamFriend friend, string type, string message );
-
- ///
- /// Called when chat message has been received from a friend. You'll need to turn on
- /// ListenForFriendsMessages to recieve this.
- ///
- public event ChatMessageDelegate OnChatMessage;
-
- private unsafe void OnFriendChatMessage( GameConnectedFriendChatMsg_t data )
- {
- if ( OnChatMessage == null ) return;
-
- var friend = Get( data.SteamIDUser );
- var type = ChatEntryType.ChatMsg;
- fixed ( byte* ptr = buffer )
- {
- var len = client.native.friends.GetFriendMessage( data.SteamIDUser, data.MessageID, (IntPtr)ptr, buffer.Length, out type );
-
- if ( len == 0 && type == ChatEntryType.Invalid )
- return;
-
- var typeName = type.ToString();
- var message = Encoding.UTF8.GetString( buffer, 0, len );
-
- OnChatMessage( friend, typeName, message );
- }
- }
-
- private bool _listenForFriendsMessages;
-
- ///
- /// Listens for Steam friends chat messages.
- /// You can then show these chats inline in the game. For example with a Blizzard style chat message system or the chat system in Dota 2.
- /// After enabling this you will receive callbacks when ever the user receives a chat message.
- ///
- public bool ListenForFriendsMessages
- {
- get
- {
- return _listenForFriendsMessages;
- }
-
- set
- {
- _listenForFriendsMessages = value;
- client.native.friends.SetListenForFriendsMessages( value );
- }
- }
-
-
- public delegate void JoinRequestedDelegate( SteamFriend friend, string connect );
-
- //
- // Called when a friend has invited you to their game (using InviteToGame)
- //
- public event JoinRequestedDelegate OnInvitedToGame;
-
-
- private void OnGameJoinRequested( GameRichPresenceJoinRequested_t data )
- {
- if ( OnInvitedToGame != null )
- {
- OnInvitedToGame( Get( data.SteamIDFriend ), data.Connect );
- }
- }
-
- ///
- /// Try to get information about this user - which as name and avatar.
- /// If returns true, we already have this user's information.
- ///
- public bool UpdateInformation( ulong steamid )
- {
- return !client.native.friends.RequestUserInformation( steamid, false );
- }
-
- ///
- /// Get this user's name
- ///
- public string GetName( ulong steamid )
- {
- client.native.friends.RequestUserInformation( steamid, true );
- return client.native.friends.GetFriendPersonaName( steamid );
- }
-
- private List _allFriends;
-
- ///
- /// Returns all friends, even blocked, ignored, friend requests etc
- ///
- public IEnumerable All
- {
- get
- {
- if ( _allFriends == null )
- {
- _allFriends = new List();
- Refresh();
- }
-
- return _allFriends;
- }
- }
-
- ///
- /// Returns only friends
- ///
- public IEnumerable AllFriends
- {
- get
- {
- foreach ( var friend in All )
- {
- if ( !friend.IsFriend ) continue;
-
- yield return friend;
- }
- }
- }
-
- ///
- /// Returns all blocked users
- ///
- public IEnumerable AllBlocked
- {
- get
- {
- foreach ( var friend in All )
- {
- if ( !friend.IsBlocked ) continue;
-
- yield return friend;
- }
- }
- }
-
- public void Refresh()
- {
- if ( _allFriends == null )
- {
- _allFriends = new List();
- }
-
- _allFriends.Clear();
-
- var flags = (int) SteamNative.FriendFlags.All;
- var count = client.native.friends.GetFriendCount( flags );
-
- for ( int i=0; i
- /// Should be 32x32 - but make sure to check!
- ///
- Small,
-
- ///
- /// Should be 64x64 - but make sure to check!
- ///
- Medium,
-
- ///
- /// Should be 184x184 - but make sure to check!
- ///
- Large
- }
-
- ///
- /// Try to get the avatar immediately. This should work for people on your friends list.
- ///
- public Image GetCachedAvatar( AvatarSize size, ulong steamid )
- {
- var imageid = 0;
-
- switch (size)
- {
- case AvatarSize.Small:
- imageid = client.native.friends.GetSmallFriendAvatar(steamid);
- break;
- case AvatarSize.Medium:
- imageid = client.native.friends.GetMediumFriendAvatar(steamid);
- break;
- case AvatarSize.Large:
- imageid = client.native.friends.GetLargeFriendAvatar(steamid);
- break;
- }
-
- if ( imageid == 1 ) return null; // Placeholder large
- if ( imageid == 2 ) return null; // Placeholder medium
- if ( imageid == 3 ) return null; // Placeholder small
-
- var img = new Image()
- {
- Id = imageid
- };
-
- if ( !img.TryLoad( client.native.utils ) )
- return null;
-
- return img;
- }
-
-
- ///
- /// Callback will be called when the avatar is ready. If we fail to get an
- /// avatar, might be called with a null Image.
- ///
- public void GetAvatar( AvatarSize size, ulong steamid, Action callback )
- {
- // Maybe we already have it downloaded?
- var image = GetCachedAvatar(size, steamid);
- if ( image != null )
- {
- callback(image);
- return;
- }
-
- // Lets request it from Steam
- if (!client.native.friends.RequestUserInformation(steamid, false))
- {
- // from docs: false means that we already have all the details about that user, and functions that require this information can be used immediately
- // but that's probably not true because we just checked the cache
-
- // check again in case it was just a race
- image = GetCachedAvatar(size, steamid);
- if ( image != null )
- {
- callback(image);
- return;
- }
-
- // maybe Steam returns false if it was already requested? just add another callback and hope it comes
- // if not it'll time out anyway
- }
-
- PersonaCallbacks.Add( new PersonaCallback
- {
- SteamId = steamid,
- Size = size,
- Callback = callback,
- Time = DateTime.Now
- });
- }
-
- private class PersonaCallback
- {
- public ulong SteamId;
- public AvatarSize Size;
- public Action Callback;
- public DateTime Time;
- }
-
- List PersonaCallbacks = new List();
-
- public SteamFriend Get( ulong steamid )
- {
- var friend = All.Where( x => x.Id == steamid ).FirstOrDefault();
- if ( friend != null ) return friend;
-
- var f = new SteamFriend()
- {
- Id = steamid,
- Client = client
- };
-
- f.Refresh();
-
- return f;
- }
-
- internal void Cycle()
- {
- if ( PersonaCallbacks.Count == 0 ) return;
-
- var timeOut = DateTime.Now.AddSeconds( -10 );
-
- for ( int i = PersonaCallbacks.Count-1; i >= 0; i-- )
- {
- var cb = PersonaCallbacks[i];
-
- // Timeout
- if ( cb.Time < timeOut )
- {
- if ( cb.Callback != null )
- {
- // final attempt, will be null unless the callback was missed somehow
- var image = GetCachedAvatar( cb.Size, cb.SteamId );
-
- cb.Callback( image );
- }
-
- PersonaCallbacks.Remove( cb );
- continue;
- }
- }
- }
-
-
- private void OnPersonaStateChange( PersonaStateChange_t data )
- {
- // k_EPersonaChangeAvatar
- if ( (data.ChangeFlags & 0x0040) == 0x0040 )
- {
- LoadAvatarForSteamId( data.SteamID );
- }
-
- //
- // Find and refresh this friend's status
- //
- foreach ( var friend in All )
- {
- if ( friend.Id != data.SteamID ) continue;
-
- friend.Refresh();
- }
- }
-
- void LoadAvatarForSteamId( ulong Steamid )
- {
- for ( int i = PersonaCallbacks.Count - 1; i >= 0; i-- )
- {
- var cb = PersonaCallbacks[i];
- if ( cb.SteamId != Steamid ) continue;
-
- var image = GetCachedAvatar( cb.Size, cb.SteamId );
- if ( image == null ) continue;
-
- PersonaCallbacks.Remove( cb );
-
- if ( cb.Callback != null )
- {
- cb.Callback( image );
- }
- }
- }
-
- private void OnAvatarImageLoaded( AvatarImageLoaded_t data )
- {
- LoadAvatarForSteamId( data.SteamID );
- }
-
- }
-}
diff --git a/Facepunch.Steamworks/Client/Leaderboard.cs b/Facepunch.Steamworks/Client/Leaderboard.cs
index c338906..ad5c6ae 100644
--- a/Facepunch.Steamworks/Client/Leaderboard.cs
+++ b/Facepunch.Steamworks/Client/Leaderboard.cs
@@ -283,7 +283,7 @@ namespace Facepunch.Steamworks
Score = entry.Score,
SteamId = entry.SteamIDUser,
SubScores = entry.CDetails == 0 ? null : subEntriesBuffer.Take( entry.CDetails ).ToArray(),
- Name = client.Friends.GetName( entry.SteamIDUser ),
+ // Name = client.Friends.GetName( entry.SteamIDUser ),
AttachedFile = (entry.UGC >> 32) == 0xffffffff ? null : new RemoteFile( client.RemoteStorage, entry.UGC )
} );
}
diff --git a/Facepunch.Steamworks/Client/SteamFriend.cs b/Facepunch.Steamworks/Client/SteamFriend.cs
deleted file mode 100644
index 91206dd..0000000
--- a/Facepunch.Steamworks/Client/SteamFriend.cs
+++ /dev/null
@@ -1,136 +0,0 @@
-using SteamNative;
-
-namespace Facepunch.Steamworks
-{
- public class SteamFriend
- {
- ///
- /// Steam Id
- ///
- public ulong Id { get; internal set; }
-
-
- ///
- /// Return true if blocked
- ///
- public bool IsBlocked => relationship == FriendRelationship.Blocked;
-
- ///
- /// Return true if is a friend. Returns false if blocked, request etc.
- ///
- public bool IsFriend => relationship == FriendRelationship.Friend;
-
- ///
- /// Their current display name
- ///
- public string Name;
-
- ///
- /// Returns true if this friend is online
- ///
- public bool IsOnline => personaState != PersonaState.Offline;
-
- ///
- /// Returns true if this friend is marked as away
- ///
- public bool IsAway => personaState == PersonaState.Away;
-
- ///
- /// Returns true if this friend is marked as busy
- ///
- public bool IsBusy => personaState == PersonaState.Busy;
-
- ///
- /// Returns true if this friend is marked as snoozing
- ///
- public bool IsSnoozing => personaState == PersonaState.Snooze;
-
- ///
- /// Returns true if this friend is online and playing this game
- ///
- public bool IsPlayingThisGame => CurrentAppId == Client.AppId;
-
- ///
- /// Returns true if this friend is online and playing this game
- ///
- public bool IsPlaying => CurrentAppId != 0;
-
- ///
- /// The AppId this guy is playing
- ///
- public ulong CurrentAppId { get; internal set; }
-
- public uint ServerIp { get; internal set; }
- public int ServerGamePort { get; internal set; }
- public int ServerQueryPort { get; internal set; }
- public ulong ServerLobbyId { get; internal set; }
-
- internal Client Client { get; set; }
- private PersonaState personaState;
- private FriendRelationship relationship;
-
- ///
- /// Returns null if the value doesn't exist
- ///
- public string GetRichPresence( string key )
- {
- var val = Client.native.friends.GetFriendRichPresence( Id, key );
- if ( string.IsNullOrEmpty( val ) ) return null;
- return val;
- }
-
- ///
- /// Update this friend, request the latest data from Steam's servers
- ///
- public void Refresh()
- {
- Name = Client.native.friends.GetFriendPersonaName( Id );
-
- relationship = Client.native.friends.GetFriendRelationship( Id );
- personaState = Client.native.friends.GetFriendPersonaState( Id );
-
- CurrentAppId = 0;
- ServerIp = 0;
- ServerGamePort = 0;
- ServerQueryPort = 0;
- ServerLobbyId = 0;
-
- var gameInfo = new SteamNative.FriendGameInfo_t();
- if ( Client.native.friends.GetFriendGamePlayed( Id, ref gameInfo ) && gameInfo.GameID > 0 )
- {
- CurrentAppId = gameInfo.GameID;
- ServerIp = gameInfo.GameIP;
- ServerGamePort = gameInfo.GamePort;
- ServerQueryPort = gameInfo.QueryPort;
- ServerLobbyId = gameInfo.SteamIDLobby;
- }
-
- Client.native.friends.RequestFriendRichPresence( Id );
- }
-
- ///
- /// This will return null if you don't have the target user's avatar in your cache.
- /// Which usually happens for people not on your friends list.
- ///
- public Image GetAvatar( Friends.AvatarSize size )
- {
- return Client.Friends.GetCachedAvatar( size, Id );
- }
-
- ///
- /// Invite this friend to the game that we are playing
- ///
- public bool InviteToGame(string Text)
- {
- return Client.native.friends.InviteUserToGame(Id, Text);
- }
-
- ///
- /// Sends a message to a Steam friend. Returns true if success
- ///
- public bool SendMessage( string message )
- {
- return Client.native.friends.ReplyToFriendMessage( Id, message );
- }
- }
-}
diff --git a/Facepunch.Steamworks/Interfaces/Workshop.Item.cs b/Facepunch.Steamworks/Interfaces/Workshop.Item.cs
index 24fda87..46c7032 100644
--- a/Facepunch.Steamworks/Interfaces/Workshop.Item.cs
+++ b/Facepunch.Steamworks/Interfaces/Workshop.Item.cs
@@ -203,11 +203,12 @@ namespace Facepunch.Steamworks
public int ReportScore { get; internal set; }
public string PreviewImageUrl { get; internal set; }
+ /*
string _ownerName = null;
- public string OwnerName
+ public Friend OwnerName
{
get
{
@@ -226,7 +227,7 @@ namespace Facepunch.Steamworks
return _ownerName;
}
- }
+ }*/
}
}
}
diff --git a/Facepunch.Steamworks/Interfaces/Workshop.Query.cs b/Facepunch.Steamworks/Interfaces/Workshop.Query.cs
index 4b26d47..4fd835d 100644
--- a/Facepunch.Steamworks/Interfaces/Workshop.Query.cs
+++ b/Facepunch.Steamworks/Interfaces/Workshop.Query.cs
@@ -56,7 +56,6 @@ namespace Facepunch.Steamworks
public int PerPage { get; set; } = SteamResponseSize;
internal Workshop workshop;
- internal Friends friends;
private int _resultPage = 0;
private int _resultsRemain = 0;
diff --git a/Facepunch.Steamworks/Interfaces/Workshop.cs b/Facepunch.Steamworks/Interfaces/Workshop.cs
index 7bf4e2a..e373b3a 100644
--- a/Facepunch.Steamworks/Interfaces/Workshop.cs
+++ b/Facepunch.Steamworks/Interfaces/Workshop.cs
@@ -31,7 +31,6 @@ namespace Facepunch.Steamworks
internal const ulong InvalidHandle = 0xffffffffffffffff;
internal SteamNative.SteamUGC ugc;
- internal Friends friends;
internal BaseSteamworks steamworks;
internal SteamNative.SteamRemoteStorage remoteStorage;
@@ -66,7 +65,6 @@ namespace Facepunch.Steamworks
ugc = null;
steamworks = null;
remoteStorage = null;
- friends = null;
OnFileDownloaded = null;
OnItemInstalled = null;
@@ -150,7 +148,6 @@ namespace Facepunch.Steamworks
{
AppId = steamworks.AppId,
workshop = this,
- friends = friends
};
}
diff --git a/Facepunch.Steamworks/Redux/Friends.cs b/Facepunch.Steamworks/Redux/Friends.cs
index 4cedc0f..f361f0f 100644
--- a/Facepunch.Steamworks/Redux/Friends.cs
+++ b/Facepunch.Steamworks/Redux/Friends.cs
@@ -33,11 +33,78 @@ namespace Steamworks
internal static void InstallEvents()
{
- //new Event( x => OnBroadcastStarted?.Invoke() );
- //new Event( x => OnBroadcastStopped?.Invoke( x.Result ) );
+ new Event( x => OnPersonaStateChange?.Invoke( new Friend( x.SteamID ) ) );
+ new Event( x => OnGameRichPresenceJoinRequested?.Invoke( new Friend( x.SteamIDFriend), x.Connect ) );
+ new Event( OnFriendChatMessage );
+ new Event( x => OnGameOverlayActivated?.Invoke() );
+ new Event( x => OnGameServerChangeRequested?.Invoke( x.Server, x.Password ) );
+ new Event( x => OnGameLobbyJoinRequested?.Invoke( x.SteamIDLobby, x.SteamIDFriend ) );
+ new Event( x => OnFriendRichPresenceUpdate?.Invoke( new Friend( x.SteamIDFriend ) ) );
}
- // public static event Action OnBroadcastStarted;
+ ///
+ /// Called when chat message has been received from a friend. You'll need to turn on
+ /// ListenForFriendsMessages to recieve this. (friend, msgtype, message)
+ ///
+ public static event Action OnChatMessage;
+
+ ///
+ /// called when a friends' status changes
+ ///
+ public static event Action OnPersonaStateChange;
+
+
+ ///
+ /// Called when the user tries to join a game from their friends list
+ /// rich presence will have been set with the "connect" key which is set here
+ ///
+ public static event Action OnGameRichPresenceJoinRequested;
+
+ ///
+ /// Posted when game overlay activates or deactivates
+ /// the game can use this to be pause or resume single player games
+ ///
+ public static event Action OnGameOverlayActivated;
+
+ ///
+ /// Called when the user tries to join a different game server from their friends list
+ /// game client should attempt to connect to specified server when this is received
+ ///
+ public static event Action OnGameServerChangeRequested;
+
+ ///
+ /// Called when the user tries to join a lobby from their friends list
+ /// game client should attempt to connect to specified lobby when this is received
+ ///
+ public static event Action OnGameLobbyJoinRequested;
+
+ ///
+ /// Callback indicating updated data about friends rich presence information
+ ///
+ public static event Action OnFriendRichPresenceUpdate;
+
+ static unsafe void OnFriendChatMessage( GameConnectedFriendChatMsg_t data )
+ {
+ if ( OnChatMessage == null ) return;
+
+ var friend = new Friend( data.SteamIDUser );
+
+ var buffer = Helpers.TakeBuffer( 1024 * 32 );
+ var type = ChatEntryType.ChatMsg;
+
+ fixed ( byte* ptr = buffer )
+ {
+ var len = Internal.GetFriendMessage( data.SteamIDUser, data.MessageID, (IntPtr)ptr, buffer.Length, ref type );
+
+ if ( len == 0 && type == ChatEntryType.Invalid )
+ return;
+
+ var typeName = type.ToString();
+ var message = Encoding.UTF8.GetString( buffer, 0, len );
+
+ OnChatMessage( friend, typeName, message );
+ }
+ }
///
/// returns the local players name - guaranteed to not be NULL.
@@ -196,5 +263,23 @@ namespace Steamworks
Internal.ClearRichPresence();
}
+ static bool _listenForFriendsMessages;
+
+ ///
+ /// Listens for Steam friends chat messages.
+ /// You can then show these chats inline in the game. For example with a Blizzard style chat message system or the chat system in Dota 2.
+ /// After enabling this you will receive callbacks when ever the user receives a chat message.
+ ///
+ public static bool ListenForFriendsMessages
+ {
+ get => _listenForFriendsMessages;
+
+ set
+ {
+ _listenForFriendsMessages = value;
+ Internal.SetListenForFriendsMessages( value );
+ }
+ }
+
}
}
\ No newline at end of file
diff --git a/Facepunch.Steamworks/Redux/Steam.cs b/Facepunch.Steamworks/Redux/Steam.cs
index accc51e..89fbec9 100644
--- a/Facepunch.Steamworks/Redux/Steam.cs
+++ b/Facepunch.Steamworks/Redux/Steam.cs
@@ -37,6 +37,7 @@ namespace Steamworks
Music.InstallEvents();
Video.InstallEvents();
User.InstallEvents();
+ Friends.InstallEvents();
RunCallbacks();
}
diff --git a/Facepunch.Steamworks/Redux/Structs/Friend.cs b/Facepunch.Steamworks/Redux/Structs/Friend.cs
index 9df1757..be32c87 100644
--- a/Facepunch.Steamworks/Redux/Structs/Friend.cs
+++ b/Facepunch.Steamworks/Redux/Structs/Friend.cs
@@ -18,6 +18,28 @@ namespace Steamworks
public bool IsFriend => Relationship == FriendRelationship.Friend;
public bool IsBlocked => Relationship == FriendRelationship.Blocked;
+ public bool IsPlayingThisGame => GameInfo?.GameID == Utils.AppId;
+
+ ///
+ /// Returns true if this friend is online
+ ///
+ public bool IsOnline => State != PersonaState.Offline;
+
+ ///
+ /// Returns true if this friend is marked as away
+ ///
+ public bool IsAway => State == PersonaState.Away;
+
+ ///
+ /// Returns true if this friend is marked as busy
+ ///
+ public bool IsBusy => State == PersonaState.Busy;
+
+ ///
+ /// Returns true if this friend is marked as snoozing
+ ///
+ public bool IsSnoozing => State == PersonaState.Snooze;
+
public FriendRelationship Relationship => Friends.Internal.GetFriendRelationship( Id );
@@ -95,5 +117,28 @@ namespace Steamworks
return await Friends.GetLargeAvatarAsync( Id );
}
+ public string GetRichPresence( string key )
+ {
+ var val = Friends.Internal.GetFriendRichPresence( Id, key );
+ if ( string.IsNullOrEmpty( val ) ) return null;
+ return val;
+ }
+
+ ///
+ /// Invite this friend to the game that we are playing
+ ///
+ public bool InviteToGame( string Text )
+ {
+ return Friends.Internal.InviteUserToGame( Id, Text );
+ }
+
+ ///
+ /// Sends a message to a Steam friend. Returns true if success
+ ///
+ public bool SendMessage( string message )
+ {
+ return Friends.Internal.ReplyToFriendMessage( Id, message );
+ }
+
}
}
\ No newline at end of file