Skip to content

Commit 5225232

Browse files
committed
allow rating systems restart without restarting server
1 parent 6f75389 commit 5225232

File tree

4 files changed

+99
-43
lines changed

4 files changed

+99
-43
lines changed

Zero-K.info/Controllers/AdminController.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11

2+
using Ratings;
23
using System;
34
using System.Collections.Generic;
45
using System.Diagnostics;
@@ -32,6 +33,14 @@ public ActionResult ResetDb()
3233
else return Content("Not allowed!");
3334
}
3435

36+
[Auth(Role = AdminLevel.Moderator)]
37+
public ActionResult ResetRatings()
38+
{
39+
RatingSystems.ReinitializeRatingSystems();
40+
return Content("Recalculation in progress, check the trace log for details. This operation will take 5 to 10 minutes.");
41+
}
42+
43+
3544
[Auth(Role = AdminLevel.Moderator)]
3645
public ActionResult TraceLogs(TraceLogIndex model)
3746
{

Zero-K.info/Views/Shared/UserDetail.cshtml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,7 @@
398398
{
399399
<a href="@Url.Action("ResetDB", "Admin")" class="js_confirm">Reset database (copy from live)</a><br />
400400
}
401+
<a href="@Url.Action("ResetRatings", "Admin")" class="js_confirm">Recalculate all ratings</a><br />
401402
<a href="@Url.Action("Index", "Engines")">Engines</a><br />
402403
<a href="@Url.Action("ChatHistory", "Lobby")">Lobby chat history</a><br />
403404
<a href="@Url.Action("TraceLogs", "Admin")">Infrastructure trace logs</a><br />

ZkData/Ef/WHR/RatingSystems.cs

Lines changed: 81 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -5,27 +5,78 @@
55
using System.Threading.Tasks;
66
using ZkData;
77
using System.Data.Entity;
8+
using System.Threading;
89

910
namespace Ratings
1011
{
1112
public class RatingSystems
1213
{
13-
public static Dictionary<RatingCategory, WholeHistoryRating> whr = new Dictionary<RatingCategory, WholeHistoryRating>();
14-
1514
public static readonly IEnumerable<RatingCategory> ratingCategories = Enum.GetValues(typeof(RatingCategory)).Cast<RatingCategory>();
1615

17-
private static HashSet<int> processedBattles = new HashSet<int>();
18-
19-
public static bool Initialized { get; private set; }
16+
private static RatingSystems handler;
2017

21-
private static object processingLock = new object();
2218

2319
public static void Init()
2420
{
25-
Initialized = false;
26-
ratingCategories.ForEach(category => whr[category] = new WholeHistoryRating(category));
21+
handler = new RatingSystems(false);
22+
}
2723

24+
public static void ReinitializeRatingSystems()
25+
{
26+
Trace.TraceInformation("Reinitializing rating systems...");
2827
Task.Factory.StartNew(() => {
28+
handler = new RatingSystems(true);
29+
Trace.TraceInformation("Ratings have been recalculated!");
30+
});
31+
}
32+
33+
public static IEnumerable<IRatingSystem> GetRatingSystems()
34+
{
35+
return handler.whr.Values;
36+
}
37+
38+
public static IRatingSystem GetRatingSystem(RatingCategory category)
39+
{
40+
if (handler == null) return null;
41+
return handler.GetRatingSystemInternal(category);
42+
}
43+
44+
45+
public static void ProcessResult(SpringBattle battle)
46+
{
47+
if (!handler.Initialized) return;
48+
handler.ProcessBattle(battle);
49+
}
50+
51+
public static Tuple<int, int> GetPlanetwarsFactionStats(int factionID)
52+
{
53+
return handler.GetPlanetwarsFactionStatsInternal(factionID);
54+
}
55+
56+
public static int ConvertDateToDays(DateTime date)
57+
{
58+
return (int)(date.ToUniversalTime().Subtract(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalDays / 1);
59+
}
60+
public static DateTime ConvertDaysToDate(int days)
61+
{
62+
return new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddDays(days);
63+
}
64+
65+
66+
private Dictionary<RatingCategory, WholeHistoryRating> whr = new Dictionary<RatingCategory, WholeHistoryRating>();
67+
68+
private HashSet<int> processedBattles = new HashSet<int>();
69+
70+
private object processingLock = new object();
71+
72+
public bool Initialized { get; private set; } = false;
73+
74+
private RatingSystems(bool waitForCompleteInitialization)
75+
{
76+
ratingCategories.ForEach(category => whr[category] = new WholeHistoryRating(category, this));
77+
78+
Action initBattles = () =>
79+
{
2980
lock (processingLock)
3081
{
3182
try
@@ -48,24 +99,30 @@ public static void Init()
4899
}
49100
}
50101
Initialized = true;
51-
whr.Values.ForEach(w => w.UpdateRatings());
102+
if (waitForCompleteInitialization)
103+
{
104+
SemaphoreSlim completedUpdates = new SemaphoreSlim(0);
105+
whr.Values.ForEach(w => w.UpdateRatings(() => completedUpdates.Release()));
106+
whr.Values.ForEach(w => completedUpdates.Wait());
107+
}
108+
else
109+
{
110+
whr.Values.ForEach(w => w.UpdateRatings());
111+
}
52112
}
53113
}
54114
catch (Exception ex)
55115
{
56116
Trace.TraceError("WHR: Error reading battles from DB" + ex);
57117
}
58118
}
59-
});
60-
}
61-
62-
63-
public static IEnumerable<IRatingSystem> GetRatingSystems()
64-
{
65-
return whr.Values;
119+
};
120+
if (waitForCompleteInitialization) initBattles.Invoke();
121+
else Task.Factory.StartNew(initBattles);
66122
}
123+
67124

68-
public static IRatingSystem GetRatingSystem(RatingCategory category)
125+
private IRatingSystem GetRatingSystemInternal(RatingCategory category)
69126
{
70127
if (!whr.ContainsKey(category))
71128
{
@@ -75,15 +132,9 @@ public static IRatingSystem GetRatingSystem(RatingCategory category)
75132
return whr[category];
76133
}
77134

78-
private static int latestBattle;
135+
private int latestBattle;
79136

80-
public static void ProcessResult(SpringBattle battle)
81-
{
82-
if (!Initialized) return;
83-
ProcessBattle(battle);
84-
}
85-
86-
private static void ProcessBattle(SpringBattle battle)
137+
private void ProcessBattle(SpringBattle battle)
87138
{
88139
lock (processingLock)
89140
{
@@ -103,9 +154,9 @@ private static void ProcessBattle(SpringBattle battle)
103154
}
104155
}
105156

106-
private static Dictionary<int, Tuple<int, int, int>> factionCache = new Dictionary<int, Tuple<int, int, int>>();
157+
private Dictionary<int, Tuple<int, int, int>> factionCache = new Dictionary<int, Tuple<int, int, int>>();
107158

108-
public static Tuple<int, int> GetPlanetwarsFactionStats(int factionID)
159+
private Tuple<int, int> GetPlanetwarsFactionStatsInternal(int factionID)
109160
{
110161
try
111162
{
@@ -131,23 +182,15 @@ public static Tuple<int, int> GetPlanetwarsFactionStats(int factionID)
131182
count = factionCache[factionID].Item2;
132183
skill = factionCache[factionID].Item3;
133184
return new Tuple<int, int>(count, skill);
134-
}catch(Exception ex)
185+
}
186+
catch (Exception ex)
135187
{
136188
Trace.TraceError("WHR failed to calculate faction stats " + ex);
137189
return new Tuple<int, int>(-1, -1);
138190
}
139191
}
140192

141-
public static int ConvertDateToDays(DateTime date)
142-
{
143-
return (int)(date.ToUniversalTime().Subtract(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalDays / 1);
144-
}
145-
public static DateTime ConvertDaysToDate(int days)
146-
{
147-
return new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddDays(days);
148-
}
149-
150-
private static bool IsCategory(SpringBattle battle, RatingCategory category)
193+
private bool IsCategory(SpringBattle battle, RatingCategory category)
151194
{
152195
int battleID = -1;
153196
try

ZkData/Ef/WHR/WholeHistoryRating.cs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,11 @@ public class WholeHistoryRating : IRatingSystem
4242

4343
private bool runningInitialization = true;
4444
private readonly RatingCategory category;
45+
private readonly RatingSystems handler;
4546

46-
public WholeHistoryRating(RatingCategory category)
47+
public WholeHistoryRating(RatingCategory category, RatingSystems handler)
4748
{
49+
this.handler = handler;
4850
this.category = category;
4951
w2 = GlobalConst.EloDecayPerDaySquared;
5052
ladderRecalculationTimer = new Timer((t) => { UpdateRatings(); }, this, 15 * 60000, (int)(GlobalConst.LadderUpdatePeriod * 3600 * 1000 + 4242));
@@ -101,7 +103,7 @@ public void ProcessBattle(SpringBattle battle)
101103
else
102104
{
103105
createGame(losers, winners, false, date, battle.SpringBattleID);
104-
if (RatingSystems.Initialized)
106+
if (handler.Initialized)
105107
{
106108
Trace.TraceInformation(battlesRegistered + " battles registered for WHR " + category +", latest Battle: " + battle.SpringBattleID);
107109
UpdateRatings();
@@ -190,9 +192,9 @@ public void RemoveTopPlayerUpdateListener(ITopPlayersUpdateListener listener, in
190192
private readonly object updateLockInternal = new object();
191193
private readonly object dbLock = new object();
192194

193-
public void UpdateRatings()
195+
public void UpdateRatings(Action UpdateCompleted = null)
194196
{
195-
if (!RatingSystems.Initialized) return;
197+
if (!handler.Initialized) return;
196198
if (latestBattle == null)
197199
{
198200
//Trace.TraceInformation("WHR " + category +": No battles to evaluate");
@@ -262,6 +264,7 @@ public void UpdateRatings()
262264
db.SaveChanges();
263265
}
264266
RatingsUpdated(this, new RatingUpdate() { affectedPlayers = updatedRanks });
267+
if (UpdateCompleted != null) UpdateCompleted.Invoke();
265268
}
266269
}
267270
catch (Exception ex)
@@ -361,7 +364,7 @@ public void SaveToDB()
361364

362365
public string DebugPlayer(Account player)
363366
{
364-
if (!RatingSystems.Initialized) return "";
367+
if (!handler.Initialized) return "";
365368
if (!players.ContainsKey(player.AccountID)) return "Unknown player";
366369
string debugString = "";
367370
foreach (PlayerDay d in players[player.AccountID].days)

0 commit comments

Comments
 (0)