public override void Execute() { var displayPlays = new List <UserPlayInfo>(); var ruleset = LegacyHelper.GetRulesetFromLegacyID(Ruleset ?? 0); Console.WriteLine("Getting user data..."); dynamic userData = getJsonFromApi($"get_user?k={Key}&u={ProfileName}&m={Ruleset}")[0]; Console.WriteLine("Getting user top scores..."); foreach (var play in getJsonFromApi($"get_user_best?k={Key}&u={ProfileName}&m={Ruleset}&limit=100")) { string beatmapID = play.beatmap_id; string cachePath = Path.Combine("cache", $"{beatmapID}.osu"); if (!File.Exists(cachePath)) { Console.WriteLine($"Downloading {beatmapID}.osu..."); new FileWebRequest(cachePath, $"{base_url}/osu/{beatmapID}").Perform(); } Mod[] mods = ruleset.ConvertFromLegacyMods((LegacyMods)play.enabled_mods).ToArray(); var working = new ProcessorWorkingBeatmap(cachePath, (int)play.beatmap_id); var score = new ProcessorScoreDecoder(working).Parse(new ScoreInfo { Ruleset = ruleset.RulesetInfo, MaxCombo = play.maxcombo, Mods = mods, Statistics = new Dictionary <HitResult, int> { { HitResult.Perfect, (int)play.countgeki }, { HitResult.Great, (int)play.count300 }, { HitResult.Good, (int)play.count100 }, { HitResult.Ok, (int)play.countkatu }, { HitResult.Meh, (int)play.count50 }, { HitResult.Miss, (int)play.countmiss } } }); var thisPlay = new UserPlayInfo { Beatmap = working.BeatmapInfo, LocalPP = ruleset.CreatePerformanceCalculator(working, score.ScoreInfo).Calculate(), LivePP = play.pp, Mods = mods.Length > 0 ? mods.Select(m => m.Acronym).Aggregate((c, n) => $"{c}, {n}") : "None" }; displayPlays.Add(thisPlay); } var localOrdered = displayPlays.OrderByDescending(p => p.LocalPP).ToList(); var liveOrdered = displayPlays.OrderByDescending(p => p.LivePP).ToList(); int index = 0; double totalLocalPP = localOrdered.Sum(play => Math.Pow(0.95, index++) * play.LocalPP); double totalLivePP = userData.pp_raw; index = 0; double nonBonusLivePP = liveOrdered.Sum(play => Math.Pow(0.95, index++) * play.LivePP); //todo: implement properly. this is pretty damn wrong. var playcountBonusPP = (totalLivePP - nonBonusLivePP); totalLocalPP += playcountBonusPP; double totalDiffPP = totalLocalPP - totalLivePP; OutputDocument(new Document( new Span($"User: {userData.username}"), "\n", new Span($"Live PP: {totalLivePP:F1} (including {playcountBonusPP:F1}pp from playcount)"), "\n", new Span($"Local PP: {totalLocalPP:F1} ({totalDiffPP:+0.0;-0.0;-})"), "\n", new Grid { Columns = { GridLength.Auto, GridLength.Auto, GridLength.Auto, GridLength.Auto, GridLength.Auto }, Children = { new Cell("beatmap"), new Cell("live pp"), new Cell("local pp"), new Cell("pp change"), new Cell("position change"), localOrdered.Select(item => new[] { new Cell($"{item.Beatmap.OnlineBeatmapID} - {item.Beatmap}"), new Cell($"{item.LivePP:F1}") { Align = Align.Right }, new Cell($"{item.LocalPP:F1}") { Align = Align.Right }, new Cell($"{item.LocalPP - item.LivePP:F1}") { Align = Align.Right }, new Cell($"{liveOrdered.IndexOf(item) - localOrdered.IndexOf(item):+0;-0;-}") { Align = Align.Center }, }) } } )); }
public override void Execute() { var displayPlays = new List <UserPlayInfo>(); var ruleset = LegacyHelper.GetRulesetFromLegacyID(Ruleset ?? 0); var rulesetApiName = LegacyHelper.GetRulesetShortNameFromId(Ruleset ?? 0); Console.WriteLine("Getting user data..."); dynamic userData = GetJsonFromApi($"users/{ProfileName}/{rulesetApiName}"); Console.WriteLine("Getting user top scores..."); foreach (var play in GetJsonFromApi($"users/{userData.id}/scores/best?mode={rulesetApiName}&limit=100")) { var working = ProcessorWorkingBeatmap.FromFileOrId((string)play.beatmap.id); var modsAcronyms = ((JArray)play.mods).Select(x => x.ToString()).ToArray(); Mod[] mods = ruleset.CreateAllMods().Where(m => modsAcronyms.Contains(m.Acronym)).ToArray(); var scoreInfo = new ScoreInfo(working.BeatmapInfo, ruleset.RulesetInfo) { TotalScore = play.score, MaxCombo = play.max_combo, Mods = mods, Statistics = new Dictionary <HitResult, int>() }; scoreInfo.SetCount300((int)play.statistics.count_300); scoreInfo.SetCountGeki((int)play.statistics.count_geki); scoreInfo.SetCount100((int)play.statistics.count_100); scoreInfo.SetCountKatu((int)play.statistics.count_katu); scoreInfo.SetCount50((int)play.statistics.count_50); scoreInfo.SetCountMiss((int)play.statistics.count_miss); var score = new ProcessorScoreDecoder(working).Parse(scoreInfo); var difficultyCalculator = ruleset.CreateDifficultyCalculator(working); var difficultyAttributes = difficultyCalculator.Calculate(LegacyHelper.ConvertToLegacyDifficultyAdjustmentMods(ruleset, scoreInfo.Mods).ToArray()); var performanceCalculator = ruleset.CreatePerformanceCalculator(); var ppAttributes = performanceCalculator?.Calculate(score.ScoreInfo, difficultyAttributes); var thisPlay = new UserPlayInfo { Beatmap = working.BeatmapInfo, LocalPP = ppAttributes?.Total ?? 0, LivePP = play.pp, Mods = scoreInfo.Mods.Select(m => m.Acronym).ToArray(), MissCount = play.statistics.count_miss, Accuracy = scoreInfo.Accuracy * 100, Combo = play.max_combo, MaxCombo = difficultyAttributes.MaxCombo }; displayPlays.Add(thisPlay); } var localOrdered = displayPlays.OrderByDescending(p => p.LocalPP).ToList(); var liveOrdered = displayPlays.OrderByDescending(p => p.LivePP).ToList(); int index = 0; double totalLocalPP = localOrdered.Sum(play => Math.Pow(0.95, index++) * play.LocalPP); double totalLivePP = userData.statistics.pp; index = 0; double nonBonusLivePP = liveOrdered.Sum(play => Math.Pow(0.95, index++) * play.LivePP); //todo: implement properly. this is pretty damn wrong. var playcountBonusPP = (totalLivePP - nonBonusLivePP); totalLocalPP += playcountBonusPP; double totalDiffPP = totalLocalPP - totalLivePP; if (OutputJson) { var json = JsonConvert.SerializeObject(new { Username = userData.username, LivePp = totalLivePP, LocalPp = totalLocalPP, PlaycountPp = playcountBonusPP, Scores = localOrdered.Select(item => new { BeatmapId = item.Beatmap.OnlineID, BeatmapName = item.Beatmap.ToString(), item.Combo, item.Accuracy, item.MissCount, item.Mods, LivePp = item.LivePP, LocalPp = item.LocalPP, PositionChange = liveOrdered.IndexOf(item) - localOrdered.IndexOf(item) }) }); Console.Write(json); if (OutputFile != null) { File.WriteAllText(OutputFile, json); } } else { OutputDocument(new Document( new Span($"User: {userData.username}"), "\n", new Span($"Live PP: {totalLivePP:F1} (including {playcountBonusPP:F1}pp from playcount)"), "\n", new Span($"Local PP: {totalLocalPP:F1} ({totalDiffPP:+0.0;-0.0;-})"), "\n", new Grid { Columns = { GridLength.Auto, GridLength.Auto, GridLength.Auto, GridLength.Auto, GridLength.Auto, GridLength.Auto, GridLength.Auto, GridLength.Auto, GridLength.Auto, GridLength.Auto }, Children = { new Cell("#"), new Cell("beatmap"), new Cell("max combo"), new Cell("accuracy"), new Cell("misses"), new Cell("mods"), new Cell("live pp"), new Cell("local pp"), new Cell("pp change"), new Cell("position change"), localOrdered.Select(item => new[] { new Cell($"{localOrdered.IndexOf(item) + 1}"), new Cell($"{item.Beatmap.OnlineID} - {item.Beatmap}"), new Cell($"{item.Combo}/{item.MaxCombo}x") { Align = Align.Right }, new Cell($"{Math.Round(item.Accuracy, 2)}%") { Align = Align.Right }, new Cell($"{item.MissCount}") { Align = Align.Right }, new Cell($"{(item.Mods.Length > 0 ? string.Join(", ", item.Mods) : "None")}") { Align = Align.Right }, new Cell($"{item.LivePP:F1}") { Align = Align.Right }, new Cell($"{item.LocalPP:F1}") { Align = Align.Right }, new Cell($"{item.LocalPP - item.LivePP:F1}") { Align = Align.Right }, new Cell($"{liveOrdered.IndexOf(item) - localOrdered.IndexOf(item):+0;-0;-}") { Align = Align.Center }, }) } }) ); } }
public override void Execute() { var displayPlays = new List <UserPlayInfo>(); var ruleset = LegacyHelper.GetRulesetFromLegacyID(Ruleset ?? 0); Console.WriteLine("Getting user data..."); dynamic userData = getJsonFromApi($"get_user?k={Key}&u={ProfileName}&m={Ruleset}")[0]; Console.WriteLine("Getting user top scores..."); foreach (var play in getJsonFromApi($"get_user_best?k={Key}&u={ProfileName}&m={Ruleset}&limit=100")) { string beatmapID = play.beatmap_id; string cachePath = Path.Combine("cache", $"{beatmapID}.osu"); if (!File.Exists(cachePath)) { Console.WriteLine($"Downloading {beatmapID}.osu..."); new FileWebRequest(cachePath, $"{base_url}/osu/{beatmapID}").Perform(); } var working = new ProcessorWorkingBeatmap(cachePath, (int)play.beatmap_id); var scoreInfo = new ScoreInfo { Ruleset = ruleset.RulesetInfo, TotalScore = play.score, MaxCombo = play.maxcombo, Mods = ruleset.ConvertFromLegacyMods((LegacyMods)play.enabled_mods).ToArray(), Statistics = new Dictionary <HitResult, int>() }; scoreInfo.SetCount300((int)play.count300); scoreInfo.SetCountGeki((int)play.countgeki); scoreInfo.SetCount100((int)play.count100); scoreInfo.SetCountKatu((int)play.countkatu); scoreInfo.SetCount50((int)play.count50); scoreInfo.SetCountMiss((int)play.countmiss); var score = new ProcessorScoreDecoder(working).Parse(scoreInfo); var difficultyCalculator = ruleset.CreateDifficultyCalculator(working); var difficultyAttributes = difficultyCalculator.Calculate(LegacyHelper.TrimNonDifficultyAdjustmentMods(ruleset, scoreInfo.Mods).ToArray()); var performanceCalculator = ruleset.CreatePerformanceCalculator(difficultyAttributes, score.ScoreInfo); var categories = new Dictionary <string, double>(); var localPP = performanceCalculator.Calculate(categories); var thisPlay = new UserPlayInfo { Beatmap = working.BeatmapInfo, LocalPP = localPP, LivePP = play.pp, Mods = scoreInfo.Mods.Select(m => m.Acronym).ToArray(), MissCount = play.countmiss, Accuracy = scoreInfo.Accuracy * 100, Combo = play.maxcombo, MaxCombo = (int)categories.GetValueOrDefault("Max Combo") }; displayPlays.Add(thisPlay); } var localOrdered = displayPlays.OrderByDescending(p => p.LocalPP).ToList(); var liveOrdered = displayPlays.OrderByDescending(p => p.LivePP).ToList(); int index = 0; double totalLocalPP = localOrdered.Sum(play => Math.Pow(0.95, index++) * play.LocalPP); double totalLivePP = userData.pp_raw; index = 0; double nonBonusLivePP = liveOrdered.Sum(play => Math.Pow(0.95, index++) * play.LivePP); //todo: implement properly. this is pretty damn wrong. var playcountBonusPP = (totalLivePP - nonBonusLivePP); totalLocalPP += playcountBonusPP; double totalDiffPP = totalLocalPP - totalLivePP; if (OutputJson) { var json = JsonConvert.SerializeObject(new { Username = userData.username, LivePp = totalLivePP, LocalPp = totalLocalPP, PlaycountPp = playcountBonusPP, Scores = localOrdered.Select(item => new { BeatmapId = item.Beatmap.OnlineBeatmapID, BeatmapName = item.Beatmap.ToString(), item.Combo, item.Accuracy, item.MissCount, item.Mods, LivePp = item.LivePP, LocalPp = item.LocalPP, PositionChange = liveOrdered.IndexOf(item) - localOrdered.IndexOf(item) }) }); Console.Write(json); if (OutputFile != null) { File.WriteAllText(OutputFile, json); } } else { OutputDocument(new Document( new Span($"User: {userData.username}"), "\n", new Span($"Live PP: {totalLivePP:F1} (including {playcountBonusPP:F1}pp from playcount)"), "\n", new Span($"Local PP: {totalLocalPP:F1} ({totalDiffPP:+0.0;-0.0;-})"), "\n", new Grid { Columns = { GridLength.Auto, GridLength.Auto, GridLength.Auto, GridLength.Auto, GridLength.Auto, GridLength.Auto, GridLength.Auto, GridLength.Auto, GridLength.Auto, GridLength.Auto }, Children = { new Cell("#"), new Cell("beatmap"), new Cell("max combo"), new Cell("accuracy"), new Cell("misses"), new Cell("mods"), new Cell("live pp"), new Cell("local pp"), new Cell("pp change"), new Cell("position change"), localOrdered.Select(item => new[] { new Cell($"{localOrdered.IndexOf(item) + 1}"), new Cell($"{item.Beatmap.OnlineBeatmapID} - {item.Beatmap}"), new Cell($"{item.Combo}/{item.MaxCombo}x") { Align = Align.Right }, new Cell($"{Math.Round(item.Accuracy, 2)}%") { Align = Align.Right }, new Cell($"{item.MissCount}") { Align = Align.Right }, new Cell($"{(item.Mods.Length > 0 ? string.Join(", ", item.Mods) : "None")}") { Align = Align.Right }, new Cell($"{item.LivePP:F1}") { Align = Align.Right }, new Cell($"{item.LocalPP:F1}") { Align = Align.Right }, new Cell($"{item.LocalPP - item.LivePP:F1}") { Align = Align.Right }, new Cell($"{liveOrdered.IndexOf(item) - localOrdered.IndexOf(item):+0;-0;-}") { Align = Align.Center }, }) } }) ); } }
public override void Execute() { if (Database == "false") { var displayPlays = new List <UserPlayInfo>(); var ruleset = LegacyHelper.GetRulesetFromLegacyID(Ruleset ?? 0); Console.WriteLine("Getting user data..."); dynamic userData = getJsonFromApi($"get_user?k={Key}&u={ProfileName}&m={Ruleset}")[0]; Console.WriteLine("Getting user top scores..."); foreach (var play in getJsonFromApi($"get_user_best?k={Key}&u={ProfileName}&m={Ruleset}&limit=100")) { string beatmapID = play.beatmap_id; string cachePath = Path.Combine("cache", $"{beatmapID}.osu"); if (!File.Exists(cachePath)) { Console.WriteLine($"Downloading {beatmapID}.osu..."); new FileWebRequest(cachePath, $"{base_url}/osu/{beatmapID}").Perform(); } Mod[] mods = ruleset.ConvertLegacyMods((LegacyMods)play.enabled_mods).ToArray(); var working = new ProcessorWorkingBeatmap(cachePath, (int)play.beatmap_id); var score = new ProcessorScoreParser(working).Parse(new ScoreInfo { Ruleset = ruleset.RulesetInfo, MaxCombo = play.maxcombo, Mods = mods, Statistics = new Dictionary <HitResult, int> { { HitResult.Perfect, (int)play.countgeki }, { HitResult.Great, (int)play.count300 }, { HitResult.Good, (int)play.count100 }, { HitResult.Ok, (int)play.countkatu }, { HitResult.Meh, (int)play.count50 }, { HitResult.Miss, (int)play.countmiss } } }); var thisPlay = new UserPlayInfo { Beatmap = working.BeatmapInfo, LocalPP = ruleset.CreatePerformanceCalculator(working, score.ScoreInfo).Calculate(), LivePP = play.pp, Mods = mods.Length > 0 ? mods.Select(m => m.Acronym).Aggregate((c, n) => $"{c}, {n}") : "None" }; displayPlays.Add(thisPlay); } var localOrdered = displayPlays.OrderByDescending(p => p.LocalPP).ToList(); var liveOrdered = displayPlays.OrderByDescending(p => p.LivePP).ToList(); int index = 0; double totalLocalPP = localOrdered.Sum(play => Math.Pow(0.95, index++) * play.LocalPP); double totalLivePP = userData.pp_raw; index = 0; double nonBonusLivePP = liveOrdered.Sum(play => Math.Pow(0.95, index++) * play.LivePP); //todo: implement properly. this is pretty damn wrong. var playcountBonusPP = (totalLivePP - nonBonusLivePP); totalLocalPP += playcountBonusPP; double totalDiffPP = totalLocalPP - totalLivePP; OutputDocument(new Document( new Span($"User: {userData.username}"), "\n", new Span($"Live PP: {totalLivePP:F1} (including {playcountBonusPP:F1}pp from playcount)"), "\n", new Span($"Local PP: {totalLocalPP:F1} ({totalDiffPP:+0.0;-0.0;-})"), "\n", new Grid { Columns = { GridLength.Auto, GridLength.Auto, GridLength.Auto, GridLength.Auto, GridLength.Auto }, Children = { new Cell("beatmap"), new Cell("live pp"), new Cell("local pp"), new Cell("pp change"), new Cell("position change"), localOrdered.Select(item => new[] { new Cell($"{item.Beatmap.OnlineBeatmapID} - {item.Beatmap}"), new Cell($"{item.LivePP:F1}") { Align = Align.Right }, new Cell($"{item.LocalPP:F1}") { Align = Align.Right }, new Cell($"{item.LocalPP - item.LivePP:F1}") { Align = Align.Right }, new Cell($"{liveOrdered.IndexOf(item) - localOrdered.IndexOf(item):+0;-0;-}") { Align = Align.Center }, }) } } )); } else { var displayPlays = new List <UserPlayInfo>(); var ruleset = LegacyHelper.GetRulesetFromLegacyID(Ruleset ?? 0); Console.WriteLine("Getting user data..."); dynamic userData = getJsonFromApi($"get_user?k={Key}&u={ProfileName}&m={Ruleset}")[0]; string[] config = { }; if (File.Exists("db.cfg")) { config = File.ReadAllLines("db.cfg"); } else { File.AppendAllLines("db.cfg", new string[] { "server=", "database=", "mysqlUsername="******"mySqlPassword="******"config file generated, fill it in, and rerun this program"); Environment.Exit(-1); } MySqlConnection connection = new MySqlConnection($"server={config[0].Split("=")[1]};database={config[1].Split("=")[1]};uid={config[2].Split("=")[1]};password={config[3].Split("=")[1]}"); MySqlCommand cmd = new MySqlCommand($"SELECT * FROM `osu_scores_high` WHERE `user_id` = {userData.user_id}", connection); connection.Open(); MySqlDataReader rdr = cmd.ExecuteReader(); List <string> buffer = new List <string>(); while (rdr.Read()) { int pp = 0; try { pp = rdr.GetInt32("pp"); string beatmap_id = rdr.GetString("beatmap_id"); string[] blacklist = { "1257904" }; if (beatmap_id != blacklist[0]) { buffer.Add($"{beatmap_id}|{rdr.GetString("enabled_mods")}|{rdr.GetInt32("maxcombo")}|{rdr.GetInt32("countgeki")}|{rdr.GetInt32("count300")}|{rdr.GetInt32("count100")}|{rdr.GetInt32("countkatu")}|{rdr.GetInt32("count50")}|{rdr.GetInt32("countmiss")}|{pp}\n"); } } catch (Exception e) { if (e.Message.Contains("null")) { pp = 0; } continue; } } foreach (string s in buffer) { string[] split = s.Split('|'); string beatmapID = split[0]; string cachePath = Path.Combine("cache", $"{beatmapID}.osu"); if (!File.Exists(cachePath)) { Console.WriteLine($"Downloading {beatmapID}.osu..."); new FileWebRequest(cachePath, $"{base_url}/osu/{beatmapID}").Perform(); } Mod[] mods = ruleset.ConvertLegacyMods((LegacyMods)int.Parse(split[1])).ToArray(); var working = new ProcessorWorkingBeatmap(cachePath, int.Parse(split[0])); var score = new ProcessorScoreParser(working).Parse(new ScoreInfo { Ruleset = ruleset.RulesetInfo, MaxCombo = int.Parse(split[2]), Mods = mods, Statistics = new Dictionary <HitResult, int> { { HitResult.Perfect, int.Parse(split[3]) }, { HitResult.Great, int.Parse(split[4]) }, { HitResult.Good, int.Parse(split[5]) }, { HitResult.Ok, int.Parse(split[6]) }, { HitResult.Meh, int.Parse(split[7]) }, { HitResult.Miss, int.Parse(split[8]) } } }); var thisPlay = new UserPlayInfo { Beatmap = working.BeatmapInfo, LocalPP = ruleset.CreatePerformanceCalculator(working, score.ScoreInfo).Calculate(), LivePP = int.Parse(split[9]), Mods = mods.Length > 0 ? mods.Select(m => m.Acronym).Aggregate((c, n) => $"{c}, {n}") : "None" }; displayPlays.Add(thisPlay); } var localOrdered = displayPlays.OrderByDescending(p => p.LocalPP).ToList(); var liveOrdered = displayPlays.OrderByDescending(p => p.LivePP).ToList(); int index = 0; double totalLocalPP = localOrdered.Sum(play => Math.Pow(0.95, index++) * play.LocalPP); double totalLivePP = userData.pp_raw; index = 0; double nonBonusLivePP = liveOrdered.Sum(play => Math.Pow(0.95, index++) * play.LivePP); //todo: implement properly. this is pretty damn wrong. var playcountBonusPP = (totalLivePP - nonBonusLivePP); totalLocalPP += playcountBonusPP; double totalDiffPP = totalLocalPP - totalLivePP; OutputDocument(new Document( new Span($"User: {userData.username}"), "\n", new Span($"Live PP: {totalLivePP:F1} (including {playcountBonusPP:F1}pp from playcount)"), "\n", new Span($"Local PP: {totalLocalPP:F1} ({totalDiffPP:+0.0;-0.0;-})"), "\n", new Grid { Columns = { GridLength.Auto, GridLength.Auto, GridLength.Auto, GridLength.Auto, GridLength.Auto }, Children = { new Cell("beatmap"), new Cell("live pp"), new Cell("local pp"), new Cell("pp change"), new Cell("position change"), localOrdered.Select(item => new[] { new Cell($"{item.Beatmap.OnlineBeatmapID} - {item.Beatmap}"), new Cell($"{item.LivePP:F1}") { Align = Align.Right }, new Cell($"{item.LocalPP:F1}") { Align = Align.Right }, new Cell($"{item.LocalPP - item.LivePP:F1}") { Align = Align.Right }, new Cell($"{liveOrdered.IndexOf(item) - localOrdered.IndexOf(item):+0;-0;-}") { Align = Align.Center }, }) } } )); } }
public override void Execute() { var displayPlays = new List <UserPlayInfo>(); var ruleset = LegacyHelper.GetRulesetFromLegacyID(Ruleset ?? 0); Console.WriteLine("Getting user data..."); dynamic userData = getJsonFromApi($"get_user?k={Key}&u={ProfileName}&m={Ruleset}")[0]; Console.WriteLine("Getting user top scores..."); foreach (var play in getJsonFromApi($"get_user_best?k={Key}&u={ProfileName}&m={Ruleset}&limit=100")) { string beatmapID = play.beatmap_id; string cachePath = Path.Combine("cache", $"{beatmapID}.osu"); if (!File.Exists(cachePath)) { Console.WriteLine($"Downloading {beatmapID}.osu..."); new FileWebRequest(cachePath, $"{base_url}/osu/{beatmapID}").Perform(); } Mod[] mods = ruleset.ConvertFromLegacyMods((LegacyMods)play.enabled_mods).ToArray(); var working = new ProcessorWorkingBeatmap(cachePath, (int)play.beatmap_id); var scoreInfo = new ScoreInfo { Ruleset = ruleset.RulesetInfo, MaxCombo = play.maxcombo, Mods = mods, Statistics = new Dictionary <HitResult, int> { { HitResult.Perfect, (int)play.countgeki }, { HitResult.Great, (int)play.count300 }, { HitResult.Good, (int)play.countkatu }, { HitResult.Ok, (int)play.count100 }, { HitResult.Meh, (int)play.count50 }, { HitResult.Miss, (int)play.countmiss } } }; var score = new ProcessorScoreDecoder(working).Parse(scoreInfo); var categoryAttribs = new Dictionary <string, double>(); OsuPerformanceCalculator calculator = (OsuPerformanceCalculator)ruleset.CreatePerformanceCalculator(working, score.ScoreInfo); double localPP = calculator.Calculate(categoryAttribs); var thisPlay = new UserPlayInfo { Beatmap = working.BeatmapInfo, LocalPP = localPP, MapCategoryAttribs = categoryAttribs, LivePP = play.pp, Mods = mods.Length > 0 ? mods.Select(m => m.Acronym).Aggregate((c, n) => $"{c}|{n}") : "", PlayMaxCombo = scoreInfo.MaxCombo, BeatmapMaxCombo = calculator.Attributes.MaxCombo, PlayAccuracy = scoreInfo.Accuracy, MissCount = scoreInfo.Statistics[HitResult.Miss] }; displayPlays.Add(thisPlay); } var localOrdered = displayPlays.OrderByDescending(p => p.LocalPP).ToList(); var liveOrdered = displayPlays.OrderByDescending(p => p.LivePP).ToList(); int index = 0; double totalLocalPP = localOrdered.Sum(play => Math.Pow(0.95, index++) * play.LocalPP); double totalLivePP = userData.pp_raw; index = 0; double nonBonusLivePP = liveOrdered.Sum(play => Math.Pow(0.95, index++) * play.LivePP); //todo: implement properly. this is pretty damn wrong. var playcountBonusPP = (totalLivePP - nonBonusLivePP); totalLocalPP += playcountBonusPP; double totalDiffPP = totalLocalPP - totalLivePP; for (int i = 0; i < localOrdered.Count; i++) { localOrdered[i].Position = i + 1; } if (SortColumnName != null) { localOrdered.Sort((s1, s2) => s2.MapCategoryAttribs[SortColumnName].CompareTo(s1.MapCategoryAttribs[SortColumnName])); } foreach (var playInfo in localOrdered) { if (playInfo.Beatmap.ToString().Length > max_name_length) { playInfo.MapName = "..." + playInfo.Beatmap.ToString().Substring(playInfo.Beatmap.ToString().Length - max_name_length); } else { playInfo.MapName = playInfo.Beatmap.ToString(); } } Grid grid = new Grid(); ExtraColumns = new string[] { "Aim", "Speed", "Accuracy" }; grid.Columns.Add(createColumns(10 + (ExtraColumns?.Length ?? 0))); grid.Children.Add( new Cell("#") { Align = Align.Center }, new Cell("beatmap"), new Cell("mods") { Align = Align.Center }, new Cell("live pp"), new Cell("acc") { Align = Align.Center }, new Cell("miss"), new Cell("combo") { Align = Align.Center } ); if (ExtraColumns != null) { foreach (var extraColumn in ExtraColumns) { grid.Children.Add(new Cell(extraColumn) { Align = Align.Center }); } } grid.Children.Add( new Cell("local pp"), new Cell("pp change"), new Cell("position change") ); grid.Children.Add(localOrdered.Select((item) => { List <Cell> cells = new List <Cell> { new Cell(item.Position) { Align = Align.Left }, new Cell($" {item.Beatmap.OnlineBeatmapID} - {item.MapName}"), new Cell(item.Mods) { Align = Align.Right }, new Cell($"{item.LivePP:F1}") { Align = Align.Right }, new Cell($"{item.PlayAccuracy * 100f:F2}" + " %") { Align = Align.Right }, new Cell($"{item.MissCount}") { Align = Align.Right }, new Cell($"{item.PlayMaxCombo}/{item.BeatmapMaxCombo}") { Align = Align.Right }, }; if (ExtraColumns != null) { cells.AddRange(ExtraColumns.Select(extraColumn => new Cell($"{item.MapCategoryAttribs[extraColumn]:F1}") { Align = Align.Right })); } cells.AddRange(new List <Cell> { new Cell($"{item.LocalPP:F1}") { Align = Align.Right }, new Cell($"{item.LocalPP - item.LivePP:F1}") { Align = Align.Right }, new Cell($"{liveOrdered.IndexOf(item) - localOrdered.IndexOf(item):+0;-0;-}") { Align = Align.Center }, } ); return(cells); })); OutputDocument(new Document( new Span($"User: {userData.username}"), "\n", new Span($"Live PP: {totalLivePP:F1} (including {playcountBonusPP:F1}pp from playcount)"), "\n", new Span($"Local PP: {totalLocalPP:F1} ({totalDiffPP:+0.0;-0.0;-})"), "\n", grid )); }