namespace Aeshnidae.Trials; /// /// trials.json beside the dll, for the website: written (atomically) after every /// recorded clear and every award, and on enable. Aeshnidae.Leaderboard writes its /// leaderboard.json the same way and the VPS timer publish-leaderboard.sh copies /// both into the web root - this file sits beside Leaderboard's rather than inside /// it so neither mod has to know the other's shape. /// /// generated, cooldownMinutes, nextAward (server-local), week (the current key) /// trials[]: landblock, name, records[] (per tier: tier, name, seconds, time, kills, when), /// top[] (best per character, any tier, fastest first), weekLeader (this week's) /// awards[]: the most recent trophies (week, landblock, name, tier, seconds, time, claimed) /// public static class Feed { public static string JsonPath => Path.Combine(Mod.ModPath, "trials.json"); private static readonly JsonSerializerOptions JsonOptions = new() { WriteIndented = false }; public static DateTime LastWriteUtc { get; private set; } public static void Write() { if (!Mod.Settings.WriteJson || !TrialDb.Ready) return; try { var s = Mod.Settings; var week = Weeks.KeyOf(DateTime.Now, s); var bests = TrialDb.ServerBests(); var payload = new { generated = DateTime.UtcNow.ToString("O"), cooldownMinutes = s.CooldownMinutes, week, weekOf = Weeks.Describe(week, s), nextAward = Weeks.NextAward(DateTime.Now, s).ToString("yyyy-MM-dd HH:mm"), trials = Mod.Trials.OrderBy(kv => kv.Key).Select(kv => { var leader = TrialDb.WeekBest(week, kv.Key); return new { landblock = $"{kv.Key:X4}", name = Mod.TrialName(kv.Key), records = bests.Where(r => r.Landblock == kv.Key).OrderBy(r => r.Tier) .Select(r => new { tier = r.Tier, name = r.CharacterName, seconds = r.Seconds, time = r.Time, kills = r.Kills, when = r.FinishedAt.ToString("O") }), top = TrialDb.Top(kv.Key, 10) .Select((r, i) => new { rank = i + 1, name = r.CharacterName, tier = r.Tier, seconds = r.Seconds, time = r.Time, kills = r.Kills, when = r.FinishedAt.ToString("O") }), weekLeader = leader is null ? null : new { name = leader.CharacterName, tier = leader.Tier, seconds = leader.Seconds, time = leader.Time }, }; }), awards = TrialDb.RecentAwards(20).Select(a => new { week = a.Week, landblock = $"{a.Landblock:X4}", trial = Mod.TrialName(a.Landblock), name = a.CharacterName, tier = a.Tier, seconds = a.Seconds, time = a.Time, awarded = a.AwardedAt.ToString("O"), claimed = a.ClaimedAt is not null, }), }; var tmp = JsonPath + ".tmp"; File.WriteAllText(tmp, JsonSerializer.Serialize(payload, JsonOptions)); File.Move(tmp, JsonPath, overwrite: true); LastWriteUtc = DateTime.UtcNow; } catch (Exception ex) { ModManager.Log($"[{Mod.Name}] could not write {JsonPath}: {ex.Message}", ModManager.LogLevel.Warn); } } }