-
-
Notifications
You must be signed in to change notification settings - Fork 1
Home
MedveMarci edited this page Apr 1, 2026
·
2 revisions
The main API is exposed through IStatsProvider. The singleton instance is accessible via:
StatsSystemPlugin.Instance.StatsProvidernamespace StatsSystem.APIbool TryGetStats(string userId, out PlayerStats stats);
bool TryGetStats(Player player, out PlayerStats stats);
bool TryGetOrCreateStats(string userId, out PlayerStats stats);
bool TryGetOrCreateStats(Player player, out PlayerStats stats);
IReadOnlyDictionary<string, PlayerStats> GetAllStatsSnapshot(string file = null);void IncrementCounter(string userId, string key, long amount = 1, string file = null);
void IncrementCounter(Player player, string key, long amount = 1, string file = null);
void SetCounter(string userId, string key, long value, string file = null);
void SetCounter(Player player, string key, long value, string file = null);
long GetCounter(string userId, string key, string file = null);
long GetCounter(Player player, string key, string file = null);void AddDuration(string userId, string key, TimeSpan delta, string file = null);
void AddDuration(Player player, string key, TimeSpan delta, string file = null);
void SetDuration(string userId, string key, TimeSpan value, string file = null);
void SetDuration(Player player, string key, TimeSpan value, string file = null);
TimeSpan GetDuration(string userId, string key, string file = null);
TimeSpan GetDuration(Player player, string key, string file = null);void SetTimestamp(string userId, string key, DateTime value, string file = null);
void SetTimestampOnce(string userId, string key, DateTime value, string file = null);
DateTime GetTimestamp(string userId, string key, string file = null);long GetLastDaysCounter(string userId, string key, int days, string file = null);
long GetLastDaysCounter(Player player, string key, int days, string file = null);
TimeSpan GetLastDaysDuration(string userId, string key, int days, string file = null);
TimeSpan GetLastDaysDuration(Player player, string key, int days, string file = null);bool DeletePlayerStats(string userId, string file = null);
bool DeleteStatKey(string userId, string key, string file = null);void Save(string file = null);
void Reload(string file = null);namespace StatsSystem.API
public class PlayerStats
{
public string UserId { get; set; }
public ConcurrentDictionary<string, long> Counters { get; }
public ConcurrentDictionary<string, TimeSpan> Durations { get; }
public ConcurrentDictionary<string, DateTime> Timestamps { get; }
public ConcurrentDictionary<string, ConcurrentDictionary<string, long>> DailyCounters { get; }
public ConcurrentDictionary<string, ConcurrentDictionary<string, TimeSpan>> DailyDurations { get; }
}Key methods on PlayerStats:
| Method | Description |
|---|---|
IncrementCounter(key, amount) |
Increases counter and updates daily tracking |
SetCounter(key, value) |
Sets counter to exact value and updates daily |
AddDuration(key, delta) |
Adds to duration and updates daily tracking |
SumLastDays(key, days) |
Sums counter values over the last N days |
SumLastDaysDuration(key, days) |
Sums duration values over the last N days |
PruneDaily() |
Removes daily entries older than max(LastDays) + 2 days |
Located in StatsSystem.Extensions.PlayerExtension. Provides a fluent API on both Player and string (userId).
using StatsSystem.Extensions;
// Get stats
player.TryGetPlayerStats(out var stats);
player.TryGetOrCreatePlayerStats(out var stats);
// Generic get/set (supports long, int, short, byte, TimeSpan)
player.GetStat<long>("Kills");
player.SetStat<long>("Kills", 42);
player.AddStat<long>("Kills", 5);
// Counters
player.IncrementStat("Kills");
player.IncrementStat("Kills", 3);
long kills = player.GetCounter("Kills");
// Durations
player.AddDuration("TotalPlayTime", TimeSpan.FromMinutes(30));
TimeSpan playtime = player.GetDuration("TotalPlayTime");
player.SetDuration("TotalPlayTime", TimeSpan.Zero);
// Timestamps
player.SetTimestamp("FirstJoin", DateTime.UtcNow);
player.SetTimestampOnce("FirstJoin", DateTime.UtcNow); // only sets if not already present
DateTime firstJoin = player.GetTimestamp("FirstJoin");
// Historical (by userId string too)
long weeklyKills = player.GetLastDaysCounter("Kills", 7);
TimeSpan weeklyPlay = player.GetLastDaysDuration("TotalPlayTime", 7);
// All configured periods at once → Dictionary<int, long>
var periods = player.GetConfiguredLastDaysCounters("Kills");
// e.g. { 7: 12, 30: 45, 90: 102 }All extension methods also work on
string userIdfor offline players.
The system supports three distinct stat types:
| Type | C# Type | Storage Key | Use Case |
|---|---|---|---|
| Counter | long |
Counters dict |
Kills, deaths, any numeric count |
| Duration | TimeSpan |
Durations dict |
Playtime, time-based metrics |
| Timestamp | DateTime |
Timestamps dict |
First join, last seen, event markers |
Counter and Duration are both tracked historically via DailyCounters and DailyDurations.
Every IncrementCounter, SetCounter, and AddDuration call automatically writes to the daily data structure alongside the aggregate total.
Storage structure (conceptual):
DailyCounters["Kills"] = {
"2026-03-25" → 3,
"2026-03-26" → 7,
"2026-03-27" → 1,
...
}
Pruning:
-
PruneDaily()runs automatically on every write - Removes entries older than
max(config.LastDays) + 2days - Keeps the data footprint bounded
Querying last N days:
// Sum of "Kills" for the last 7 days (today - 6 days through today)
long weeklyKills = provider.GetLastDaysCounter(userId, "Kills", 7);
// All configured periods
var allPeriods = player.GetConfiguredLastDaysCounters("Kills");namespace StatsSystem.Storage
public interface IStorageProvider
{
IReadOnlyDictionary<string, PlayerStats> Load(string identifier);
void Save(string identifier, IReadOnlyDictionary<string, PlayerStats> data);
Task SaveAsync(string identifier, IReadOnlyDictionary<string, PlayerStats> data);
}using StatsSystem.Extensions;
using LabApi.Features.Wrappers;
// Increment a custom stat when a player does something
public void OnMyCustomEvent(Player player)
{
player.IncrementStat("MyCustomStat");
}
// Read a custom stat for a player (online or offline)
public long GetMyCustomStat(string userId)
{
return userId.GetCounter("MyCustomStat");
}
// Read historical data
public long GetWeeklyCustomStat(Player player)
{
return player.GetLastDaysCounter("MyCustomStat", 7);
}