Static leaderboard helper for the DesertPump game. Submits a "tallest_tree" stat to the Sandbox stat service, fetches the global leaderboard via Sandbox Leaderboards, and exposes rows, status text, and a fetching flag. It always ensures at least the local player appears on the board.
using System.Threading.Tasks;
using Sandbox.Services;
namespace DesertPump;
/// <summary>
/// The tallest-tree board.
/// </summary>
/// <remarks>
/// The global board is real - it submits to s&box's stat service under
/// <see cref="StatName"/> and reads the board back. It only has anything in it once the
/// game is published to sbox.game with a leaderboard configured; running locally the
/// backend has no rows to give, so <see cref="Entries"/> comes back empty and the panel
/// says so rather than pretending.
///
/// Your own height is tracked locally either way, so the board always shows you.
/// </remarks>
public static class Leaderboard
{
public const string StatName = "tallest_tree";
public sealed class Row
{
public int Rank { get; init; }
public string Name { get; init; }
public int Height { get; init; }
public bool IsMe { get; init; }
}
/// <summary>Rows from the last fetch, best first. Empty until one succeeds.</summary>
public static List<Row> Entries { get; private set; } = new();
/// <summary>Null until we've tried, then a short human-readable status.</summary>
public static string Status { get; private set; }
public static bool Fetching { get; private set; }
/// <summary>Push the height up as a stat. Safe to call often - the service batches.</summary>
public static void Submit( int height )
{
try
{
Stats.SetValue( StatName, height );
}
catch ( Exception e )
{
// No backend when running locally - not worth spamming the console over.
Status = $"Couldn't submit: {e.Message}";
}
}
/// <summary>Pull the global board. Falls back to a board of one - you.</summary>
public static async Task Refresh( int myHeight, string myName )
{
if ( Fetching ) return;
Fetching = true;
try
{
var board = Leaderboards.Get( StatName );
board.MaxEntries = 20;
await board.Refresh();
var rows = new List<Row>();
foreach ( var entry in board.Entries )
{
rows.Add( new Row
{
Rank = (int)entry.Rank,
Name = entry.DisplayName,
Height = (int)entry.Value,
IsMe = entry.Me
} );
}
Entries = rows;
Status = rows.Count > 0
? null
: "No global scores yet - publish the game to sbox.game to open the board up.";
}
catch ( Exception e )
{
Entries = new List<Row>();
Status = $"Global board unavailable ({e.Message})";
}
finally
{
// However it went, you should always see your own tree on the board.
if ( Entries.Count == 0 )
{
Entries = new List<Row>
{
new() { Rank = 1, Name = myName, Height = myHeight, IsMe = true }
};
}
Fetching = false;
}
}
}