From 6b1daf81de921257ae57124262fdf01aba83eaf1 Mon Sep 17 00:00:00 2001 From: JoKeRZH429 <83122870+JoKeRZH429@users.noreply.github.com> Date: Wed, 12 Aug 2026 21:18:58 +0500 Subject: [PATCH] feat: fetch and store monthly Elo from External Leaderboard --- GenOnlineService/Constants.cs | 4 +++- .../PlayerStats/PlayerStatsController.cs | 2 +- .../Database/Database.PlayerStats.cs | 4 ++-- GenOnlineService/Database/Database.User.cs | 11 +++++++---- .../Database_Structure/structure.sql | 1 + GenOnlineService/ELO.cs | 8 ++++++++ GenOnlineService/ExternalLeaderboardsClient.cs | 17 ++++++++++++----- 7 files changed, 34 insertions(+), 13 deletions(-) diff --git a/GenOnlineService/Constants.cs b/GenOnlineService/Constants.cs index 31c3bcd..2c7b4e8 100644 --- a/GenOnlineService/Constants.cs +++ b/GenOnlineService/Constants.cs @@ -1385,11 +1385,12 @@ public class PlayerStats { const int numGeneralsEntries = 15; - public PlayerStats(Int64 inUserID, int inEloRating, int inEloMatches) + public PlayerStats(Int64 inUserID, int inEloRating, int inEloMatches, int inMonthlyEloRating) { userID = inUserID; EloRating = inEloRating; EloMatches = inEloMatches; + MonthlyEloRating = inMonthlyEloRating; // init arrays, rest are init'ed below for (int i = 0; i < numGeneralsEntries; ++i) @@ -1424,6 +1425,7 @@ public PlayerStats(Int64 inUserID, int inEloRating, int inEloMatches) public Int64 userID { get; set; } = -1; public int EloRating { get; set; } = EloConfig.BaseRating; public int EloMatches { get; set; } = 0; + public int MonthlyEloRating { get; set; } = EloConfig.BaseRating; public int[] wins { get; set; } = new int[numGeneralsEntries] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; public int[] losses { get; set; } = new int[numGeneralsEntries] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; diff --git a/GenOnlineService/Controllers/PlayerStats/PlayerStatsController.cs b/GenOnlineService/Controllers/PlayerStats/PlayerStatsController.cs index 9099a11..29213fb 100644 --- a/GenOnlineService/Controllers/PlayerStats/PlayerStatsController.cs +++ b/GenOnlineService/Controllers/PlayerStats/PlayerStatsController.cs @@ -85,7 +85,7 @@ public async Task Get(Int64 userID) { // TODO_ASP: Set error codes properly in all places (and use variable, not magic numbers) RouteHandler_GET_PlayerStats_Result result = new RouteHandler_GET_PlayerStats_Result(); - result.stats = new PlayerStats(userID, EloConfig.BaseRating, 0); // return 0s by default, incase client tries to use it + result.stats = new PlayerStats(userID, EloConfig.BaseRating, 0, EloConfig.BaseRating); // return 0s by default, incase client tries to use it var options = new JsonSerializerOptions { diff --git a/GenOnlineService/Database/Database.PlayerStats.cs b/GenOnlineService/Database/Database.PlayerStats.cs index 7b97634..597380c 100644 --- a/GenOnlineService/Database/Database.PlayerStats.cs +++ b/GenOnlineService/Database/Database.PlayerStats.cs @@ -92,10 +92,10 @@ public static async Task GetPlayerStats( { Console.WriteLine($"[ERROR] GetPlayerStats failed: {ex.Message}"); SentrySdk.CaptureException(ex); - return new PlayerStats(userId, EloConfig.BaseRating, 0); + return new PlayerStats(userId, EloConfig.BaseRating, 0, EloConfig.BaseRating); } - PlayerStats ps = new PlayerStats(userId, elo.Rating, elo.NumMatches); + PlayerStats ps = new PlayerStats(userId, elo.Rating, elo.NumMatches, elo.MonthlyRating); try { diff --git a/GenOnlineService/Database/Database.User.cs b/GenOnlineService/Database/Database.User.cs index 8d73f58..fd082b1 100644 --- a/GenOnlineService/Database/Database.User.cs +++ b/GenOnlineService/Database/Database.User.cs @@ -79,6 +79,7 @@ public class User // ELO public int EloRating { get; set; } = EloConfig.BaseRating; + public int MonthlyEloRating { get; set; } = EloConfig.BaseRating; public int EloNumberOfMatches { get; set; } = 0; // Bans @@ -162,6 +163,7 @@ public void Configure(EntityTypeBuilder builder) builder.Property(e => e.IsAdmin).HasColumnName("admin"); builder.Property(e => e.IsBanned).HasColumnName("banned"); builder.Property(e => e.EloRating).HasColumnName("elo_rating"); + builder.Property(e => e.MonthlyEloRating).HasColumnName("monthly_elo_rating"); builder.Property(e => e.EloNumberOfMatches).HasColumnName("elo_num_matches"); builder.Property(e => e.BanReason).HasColumnName("ban_reason").HasColumnType("varchar(128)"); ; builder.Property(e => e.BannedBy).HasColumnName("banned_by").HasColumnType("varchar(50)"); ; @@ -334,7 +336,7 @@ public static class Users (AppDbContext db, long userId) => db.Users .Where(u => u.ID == userId) - .Select(u => new EloData(u.EloRating, u.EloNumberOfMatches)) + .Select(u => new EloData(u.EloRating, u.MonthlyEloRating, u.EloNumberOfMatches)) .FirstOrDefault() ); @@ -411,14 +413,14 @@ public static async Task> GetBulkELOData( // Execute compiled query await foreach (var u in _compiledBulkQuery(db, userIds)) { - results[u.ID] = new EloData(u.EloRating, u.EloNumberOfMatches); + results[u.ID] = new EloData(u.EloRating, u.MonthlyEloRating, u.EloNumberOfMatches); } // Fill missing users with defaults foreach (var id in userIds) { if (!results.ContainsKey(id)) - results[id] = new EloData(EloConfig.BaseRating, 0); + results[id] = new EloData(EloConfig.BaseRating, EloConfig.BaseRating, 0); } } catch (Exception ex) @@ -600,7 +602,7 @@ public static async Task GetELOData(AppDbContext db, long userId) SentrySdk.CaptureException(ex); } - return new EloData(EloConfig.BaseRating, 0); + return new EloData(EloConfig.BaseRating, EloConfig.BaseRating, 0); } @@ -721,6 +723,7 @@ await db.Users .Where(u => u.ID == userId) .ExecuteUpdateAsync(setters => setters .SetProperty(u => u.EloRating, newEloData.Rating) + .SetProperty(u => u.MonthlyEloRating, newEloData.MonthlyRating) .SetProperty(u => u.EloNumberOfMatches, newEloData.NumMatches) ); } diff --git a/GenOnlineService/Database_Structure/structure.sql b/GenOnlineService/Database_Structure/structure.sql index 11149c9..b36a811 100644 --- a/GenOnlineService/Database_Structure/structure.sql +++ b/GenOnlineService/Database_Structure/structure.sql @@ -197,6 +197,7 @@ CREATE TABLE IF NOT EXISTS `users` ( `admin` tinyint(4) NOT NULL DEFAULT 0, `banned` tinyint(4) NOT NULL DEFAULT 0, `elo_rating` int(11) NOT NULL DEFAULT 1000, + `monthly_elo_rating` int(11) NOT NULL DEFAULT 1000, `elo_num_matches` int(11) NOT NULL DEFAULT 0, `ban_reason` varchar(128) DEFAULT NULL, `banned_by` varchar(50) DEFAULT NULL, diff --git a/GenOnlineService/ELO.cs b/GenOnlineService/ELO.cs index a60a4d1..73bba83 100644 --- a/GenOnlineService/ELO.cs +++ b/GenOnlineService/ELO.cs @@ -35,12 +35,20 @@ public class EloData { public int Rating { get; set; } = 1000; public int NumMatches { get; set; } = 0; + public int MonthlyRating { get; set; } = 1000; public EloData(int rating, int numMatches) { Rating = rating; NumMatches = numMatches; } + + public EloData(int rating, int monthlyRating, int numMatches) + { + Rating = rating; + MonthlyRating = monthlyRating; + NumMatches = numMatches; + } } public static class Elo diff --git a/GenOnlineService/ExternalLeaderboardsClient.cs b/GenOnlineService/ExternalLeaderboardsClient.cs index ab8355e..88bd04b 100644 --- a/GenOnlineService/ExternalLeaderboardsClient.cs +++ b/GenOnlineService/ExternalLeaderboardsClient.cs @@ -21,10 +21,15 @@ public class EloRefreshResponse } public class EloRefreshEntry + { + public EloRefreshRating overall { get; set; } + public EloRefreshRating season { get; set; } + } + + public class EloRefreshRating { public int rating { get; set; } public int matches { get; set; } - public int? rank { get; set; } } public static class ExternalLeaderboardsClient @@ -212,8 +217,9 @@ await retryPolicy.ExecuteAsync(async () => continue; } - int newRating = updatedPlayer.rating; - int newMatches = updatedPlayer.matches; + int newRating = updatedPlayer.overall.rating; + int newMatches = updatedPlayer.overall.matches; + int newMonthlyRating = updatedPlayer.season.rating; // Update in-memory session cache if the player is online var sharedData = WebSocketManager.GetSharedDataForUser(userId); @@ -221,10 +227,11 @@ await retryPolicy.ExecuteAsync(async () => { sharedData.GameStats.EloRating = newRating; sharedData.GameStats.EloMatches = newMatches; + sharedData.GameStats.MonthlyEloRating = newMonthlyRating; } // Call SaveELOData to persist as fallback - await Database.Users.SaveELOData(db, userId, new EloData(newRating, newMatches)); + await Database.Users.SaveELOData(db, userId, new EloData(newRating, newMonthlyRating, newMatches)); } } } @@ -270,7 +277,7 @@ await retryPolicy.ExecuteAsync(async () => return null; } - return new EloData(entry.rating, entry.matches); + return new EloData(entry.overall.rating, entry.season.rating, entry.overall.matches); } } }