using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; namespace Steamworks.Data { public struct Leaderboard { internal SteamLeaderboard_t Id; /// /// the name of a leaderboard /// public string? Name => SteamUserStats.Internal?.GetLeaderboardName( Id ); public LeaderboardSort Sort => SteamUserStats.Internal?.GetLeaderboardSortMethod( Id ) ?? default; public LeaderboardDisplay Display => SteamUserStats.Internal?.GetLeaderboardDisplayType( Id ) ?? default; public int EntryCount => SteamUserStats.Internal?.GetLeaderboardEntryCount(Id) ?? 0; static int[] detailsBuffer = new int[64]; static int[] noDetails = Array.Empty(); /// /// Submit your score and replace your old score even if it was better /// public async Task ReplaceScore( int score, int[]? details = null ) { if (SteamUserStats.Internal is null) { return null; } if ( details == null ) details = noDetails; var r = await SteamUserStats.Internal.UploadLeaderboardScore( Id, LeaderboardUploadScoreMethod.ForceUpdate, score, details, details.Length ); if ( !r.HasValue ) return null; return LeaderboardUpdate.From( r.Value ); } /// /// Submit your new score, but won't replace your high score if it's lower /// public async Task SubmitScoreAsync( int score, int[]? details = null ) { if (SteamUserStats.Internal is null) { return null; } if ( details == null ) details = noDetails; var r = await SteamUserStats.Internal.UploadLeaderboardScore( Id, LeaderboardUploadScoreMethod.KeepBest, score, details, details.Length ); if ( !r.HasValue ) return null; return LeaderboardUpdate.From( r.Value ); } /// /// Attaches a piece of user generated content the user's entry on a leaderboard /// public async Task AttachUgc( Ugc file ) { if (SteamUserStats.Internal is null) { return Result.Fail; } var r = await SteamUserStats.Internal.AttachLeaderboardUGC( Id, file.Handle ); if ( !r.HasValue ) return Result.Fail; return r.Value.Result; } /// /// Fetches leaderboard entries for an arbitrary set of users on a specified leaderboard. /// public async Task GetScoresForUsersAsync( SteamId[]? users ) { if (SteamUserStats.Internal is null) { return null; } if ( users == null || users.Length == 0 ) return null; var r = await SteamUserStats.Internal.DownloadLeaderboardEntriesForUsers( Id, users, users.Length ); if ( !r.HasValue ) return null; return await LeaderboardResultToEntries( r.Value ); } /// /// Used to query for a sequential range of leaderboard entries by leaderboard Sort. /// public async Task GetScoresAsync( int count, int offset = 1 ) { if (SteamUserStats.Internal is null) { return null; } if ( offset <= 0 ) throw new System.ArgumentException( "Should be 1+", nameof( offset ) ); var r = await SteamUserStats.Internal.DownloadLeaderboardEntries( Id, LeaderboardDataRequest.Global, offset, offset + count - 1 ); if ( !r.HasValue ) return null; return await LeaderboardResultToEntries( r.Value ); } /// /// Used to retrieve leaderboard entries relative a user's entry. If there are not enough entries in the leaderboard /// before or after the user's entry, Steam will adjust the range to try to return the number of entries requested. /// For example, if the user is #1 on the leaderboard and start is set to -2, end is set to 2, Steam will return the first /// 5 entries in the leaderboard. If The current user has no entry, this will return null. /// public async Task GetScoresAroundUserAsync( int start = -10, int end = 10 ) { if (SteamUserStats.Internal is null) { return null; } var r = await SteamUserStats.Internal.DownloadLeaderboardEntries( Id, LeaderboardDataRequest.GlobalAroundUser, start, end ); if ( !r.HasValue ) return null; return await LeaderboardResultToEntries( r.Value ); } /// /// Used to retrieve all leaderboard entries for friends of the current user /// public async Task GetScoresFromFriendsAsync() { if (SteamUserStats.Internal is null) { return null; } var r = await SteamUserStats.Internal.DownloadLeaderboardEntries( Id, LeaderboardDataRequest.Friends, 0, 0 ); if ( !r.HasValue ) return null; return await LeaderboardResultToEntries( r.Value ); } #region util internal async Task LeaderboardResultToEntries( LeaderboardScoresDownloaded_t r ) { if (SteamUserStats.Internal is null) { return null; } if ( r.CEntryCount <= 0 ) return null; var output = new LeaderboardEntry[r.CEntryCount]; var e = default( LeaderboardEntry_t ); for ( int i = 0; i < output.Length; i++ ) { if ( SteamUserStats.Internal.GetDownloadedLeaderboardEntry( r.SteamLeaderboardEntries, i, ref e, detailsBuffer, detailsBuffer.Length ) ) { output[i] = LeaderboardEntry.From( e, detailsBuffer ); } } await WaitForUserNames( output ); return output; } internal static async Task WaitForUserNames( LeaderboardEntry[] entries) { bool gotAll = false; while ( !gotAll ) { if (SteamFriends.Internal is null) { return; } gotAll = true; foreach ( var entry in entries ) { if ( entry.User.Id == 0 ) continue; if ( !SteamFriends.Internal.RequestUserInformation( entry.User.Id, true ) ) continue; gotAll = false; } await Task.Delay( 1 ); } } #endregion } }