Files
sbox-public/engine/Sandbox.Engine/Platform/Steam/SteamFriends.cs
s&box team 71f266059a Open source release
This commit imports the C# engine code and game files, excluding C++ source code.

[Source-Commit: ceb3d758046e50faa6258bc3b658a30c97743268]
2025-11-24 09:05:18 +00:00

162 lines
5.6 KiB
C#

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using Steamworks.Data;
namespace Steamworks
{
/// <summary>
/// Functions for clients to access data about Steam friends
/// </summary>
internal class SteamFriends : SteamClientClass<SteamFriends>
{
internal static ISteamFriends Internal => Interface as ISteamFriends;
private static bool IsInstalled => Internal?.IsValid ?? false;
internal override void InitializeInterface( bool server )
{
SetInterface( server, new ISteamFriends( server ) );
InstallEvents();
}
internal void InstallEvents()
{
Dispatch.Install<GameLobbyJoinRequested_t>( x => OnGameLobbyJoinRequested?.Invoke( x.SteamIDLobby ) );
Dispatch.Install<PersonaStateChange_t>( x => { OnPersonaStateChange?.Invoke( new Friend( x.SteamID ) ); Friend.SteamFriends_OnPersonaStateChange( x.SteamID, (PersonaChange)x.ChangeFlags ); } );
Dispatch.Install<GameRichPresenceJoinRequested_t>( x => OnGameRichPresenceJoinRequested?.Invoke( new Friend( x.SteamIDFriend ), x.ConnectUTF8() ) );
Dispatch.Install<FriendRichPresenceUpdate_t>( x => OnFriendRichPresenceUpdate?.Invoke( new Friend( x.SteamIDFriend ) ) );
}
/// <summary>
/// Called when a friends' status changes
/// </summary>
public static Action<Friend> OnPersonaStateChange { get; set; }
/// <summary>
/// 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
/// </summary>
internal static Action<Friend, string> OnGameRichPresenceJoinRequested { get; set; }
/// <summary>
/// Callback indicating updated data about friends rich presence information
/// </summary>
public static Action<Friend> OnFriendRichPresenceUpdate { get; set; }
/// <summary>
/// Called when the user tries to join a game from their friends list
/// in a lobby
/// </summary>
public static Action<Sandbox.SteamId> OnGameLobbyJoinRequested { get; set; }
private static IEnumerable<Friend> GetFriendsWithFlag( FriendFlags flag )
{
if ( !IsInstalled )
yield break;
for ( int i = 0; i < Internal.GetFriendCount( (int)flag ); i++ )
{
yield return new Friend( Internal.GetFriendByIndex( i, (int)flag ) );
}
}
public static IEnumerable<Friend> GetFriends()
{
return GetFriendsWithFlag( FriendFlags.Immediate );
}
/// <summary>
/// "steamid" - Opens the overlay web browser to the specified user or groups profile.
/// "chat" - Opens a chat window to the specified user, or joins the group chat.
/// "jointrade" - Opens a window to a Steam Trading session that was started with the ISteamEconomy/StartTrade Web API.
/// "stats" - Opens the overlay web browser to the specified user's stats.
/// "achievements" - Opens the overlay web browser to the specified user's achievements.
/// "friendadd" - Opens the overlay in minimal mode prompting the user to add the target user as a friend.
/// "friendremove" - Opens the overlay in minimal mode prompting the user to remove the target friend.
/// "friendrequestaccept" - Opens the overlay in minimal mode prompting the user to accept an incoming friend invite.
/// "friendrequestignore" - Opens the overlay in minimal mode prompting the user to ignore an incoming friend invite.
/// </summary>
internal static void OpenUserOverlay( SteamId id, string type )
{
if ( !IsInstalled ) return;
Internal.ActivateGameOverlayToUser( type, id );
}
/// <summary>
/// Activates the Steam Overlay to open the invite dialog. Invitations sent from this dialog will be for the provided lobby.
/// </summary>
internal static void OpenGameInviteOverlay( SteamId lobby )
{
if ( !IsInstalled ) return;
Internal.ActivateGameOverlayInviteDialog( lobby );
}
/// <summary>
/// Requests the persona name and optionally the avatar of a specified user.
/// NOTE: It's a lot slower to download avatars and churns the local cache, so if you don't need avatars, don't request them.
/// returns true if we're fetching the data, false if we already have it
/// </summary>
internal static bool RequestUserInformation( SteamId steamid, bool nameonly = true )
{
return IsInstalled && Internal.RequestUserInformation( steamid, nameonly );
}
internal static async Task CacheUserInformationAsync( SteamId steamid, bool nameonly )
{
// Got it straight away, skip any waiting.
if ( !RequestUserInformation( steamid, nameonly ) )
return;
await Task.Delay( 100 );
while ( RequestUserInformation( steamid, nameonly ) )
{
await Task.Delay( 50 );
}
//
// And extra wait here seems to solve avatars loading as [?]
//
await Task.Delay( 500 );
}
internal static async Task<Data.Image?> GetSmallAvatarAsync( SteamId steamid )
{
if ( !IsInstalled ) return null;
await CacheUserInformationAsync( steamid, false );
return SteamUtils.GetImage( Internal.GetSmallFriendAvatar( steamid ) );
}
internal static async Task<Data.Image?> GetMediumAvatarAsync( SteamId steamid )
{
if ( !IsInstalled ) return null;
await CacheUserInformationAsync( steamid, false );
return SteamUtils.GetImage( Internal.GetMediumFriendAvatar( steamid ) );
}
internal static async Task<Data.Image?> GetLargeAvatarAsync( SteamId steamid )
{
if ( !IsInstalled ) return null;
await CacheUserInformationAsync( steamid, false );
var imageid = Internal.GetLargeFriendAvatar( steamid );
// Wait for the image to download
while ( imageid == -1 )
{
await Task.Delay( 50 );
imageid = Internal.GetLargeFriendAvatar( steamid );
}
return SteamUtils.GetImage( imageid );
}
}
}