private double processBeatmap(WorkingBeatmap beatmap) { var ruleset = LegacyHelper.GetRulesetFromLegacyID(Ruleset ?? beatmap.BeatmapInfo.RulesetID); var attributes = ruleset.CreateDifficultyCalculator(beatmap).Calculate(getMods(ruleset).ToArray()); return(attributes.StarRating); }
private void processBeatmap(WorkingBeatmap beatmap, Document document) { // Get the ruleset var ruleset = LegacyHelper.GetRulesetFromLegacyID(Ruleset ?? beatmap.BeatmapInfo.RulesetID); var attributes = ruleset.CreateDifficultyCalculator(beatmap).Calculate(getMods(ruleset).ToArray()); document.Children.Add(new Span("Ruleset".PadRight(15) + $": {ruleset.ShortName}"), "\n"); document.Children.Add(new Span("Stars".PadRight(15) + $": {attributes.StarRating.ToString(CultureInfo.InvariantCulture)}"), "\n"); switch (attributes) { case OsuDifficultyAttributes osu: document.Children.Add(new Span("Aim".PadRight(15) + $": {osu.AimStrain.ToString(CultureInfo.InvariantCulture)}"), "\n"); document.Children.Add(new Span("Speed".PadRight(15) + $": {osu.SpeedStrain.ToString(CultureInfo.InvariantCulture)}"), "\n"); document.Children.Add(new Span("MaxCombo".PadRight(15) + $": {osu.MaxCombo.ToString(CultureInfo.InvariantCulture)}"), "\n"); document.Children.Add(new Span("AR".PadRight(15) + $": {osu.ApproachRate.ToString(CultureInfo.InvariantCulture)}"), "\n"); document.Children.Add(new Span("OD".PadRight(15) + $": {osu.OverallDifficulty.ToString(CultureInfo.InvariantCulture)}"), "\n", "\n"); break; case TaikoDifficultyAttributes taiko: document.Children.Add(new Span("HitWindow".PadRight(15) + $": {taiko.GreatHitWindow.ToString(CultureInfo.InvariantCulture)}"), "\n"); document.Children.Add(new Span("MaxCombo".PadRight(15) + $": {taiko.MaxCombo.ToString(CultureInfo.InvariantCulture)}"), "\n", "\n"); break; case CatchDifficultyAttributes c: document.Children.Add(new Span("MaxCombo".PadRight(15) + $": {c.MaxCombo.ToString(CultureInfo.InvariantCulture)}"), "\n"); document.Children.Add(new Span("AR".PadRight(15) + $": {c.ApproachRate.ToString(CultureInfo.InvariantCulture)}"), "\n", "\n"); break; case ManiaDifficultyAttributes mania: document.Children.Add(new Span("HitWindow".PadRight(15) + $": {mania.GreatHitWindow.ToString(CultureInfo.InvariantCulture)}"), "\n", "\n"); break; } }
public override void Execute() { var currentRuleset = LegacyHelper.GetRulesetFromLegacyID(0); var allowedUsers = new List <string>(); var songsFolderPath = OsuPathUtils.GetSongsFolderPath(); if (ExtraUsernames != null) { allowedUsers.AddRange(ExtraUsernames); } OsuDb osuDb = OsuDb.Read(OsuPathUtils.GetOsuDbPath()); ScoresDb scoresDb = ScoresDb.Read(OsuPathUtils.GetOsuScoresDbPath()); Dictionary <string, string> checkSumToOsuFile = new Dictionary <string, string>(); foreach (var beatmap in osuDb.Beatmaps.Where(beatmap => beatmap.BeatmapChecksum != null && beatmap.RankedStatus == SubmissionStatus.Ranked)) { if (!checkSumToOsuFile.TryAdd(beatmap.BeatmapChecksum, songsFolderPath + "/" + beatmap.FolderName + "/" + beatmap.BeatmapFileName)) { Console.WriteLine("WARNING: beatmap " + beatmap.BeatmapFileName + " found multiple times in osu db"); } } string[] keys = scoresDb.Beatmaps.Keys.ToArray(); var allScoresBag = new ConcurrentBag <LocalReplayInfo>(); string[] keysToProcess = TestRun ? keys[..20] : keys;
public override void Execute() { var results = new List <Result>(); if (Directory.Exists(Path)) { foreach (string file in Directory.GetFiles(Path, "*.osu", SearchOption.AllDirectories)) { var beatmap = new ProcessorWorkingBeatmap(file); results.Add(processBeatmap(beatmap)); } } else { results.Add(processBeatmap(new ProcessorWorkingBeatmap(Path))); } var document = new Document(); foreach (var group in results.GroupBy(r => r.RulesetId)) { var ruleset = LegacyHelper.GetRulesetFromLegacyID(group.First().RulesetId); document.Children.Add(new Span($"Ruleset: {ruleset.ShortName}"), "\n"); var grid = new Grid(); grid.Columns.Add(GridLength.Auto, GridLength.Auto); grid.Children.Add(new Cell("beatmap"), new Cell("star rating")); foreach (var attribute in group.First().AttributeData) { grid.Columns.Add(GridLength.Auto); grid.Children.Add(new Cell(attribute.name)); } foreach (var result in group) { grid.Children.Add(new Cell(result.Beatmap), new Cell(result.Stars) { Align = Align.Right }); foreach (var attribute in result.AttributeData) { grid.Children.Add(new Cell(attribute.value) { Align = Align.Right }); } } document.Children.Add(grid); document.Children.Add("\n"); } OutputDocument(document); }
private Result processBeatmap(WorkingBeatmap beatmap) { // Get the ruleset var ruleset = LegacyHelper.GetRulesetFromLegacyID(Ruleset ?? beatmap.BeatmapInfo.RulesetID); var attributes = ruleset.CreateDifficultyCalculator(beatmap).Calculate(getMods(ruleset).ToArray()); var beatmapName = $"{beatmap.BeatmapInfo.OnlineBeatmapID} - {beatmap.BeatmapInfo}"; if (beatmapName.Length > 100) { beatmapName = beatmapName.Substring(0, 100) + "..."; } var result = new Result { RulesetId = ruleset.RulesetInfo.ID ?? 0, Beatmap = beatmapName, Stars = attributes.StarRating.ToString("N3") }; switch (attributes) { case OsuDifficultyAttributes osu: result.AttributeData = new List <(string, object)> { }; break; case TaikoDifficultyAttributes taiko: result.AttributeData = new List <(string, object)> { ("hit window", taiko.GreatHitWindow.ToString("N2")), ("max combo", taiko.MaxCombo) }; break; case CatchDifficultyAttributes @catch: result.AttributeData = new List <(string, object)> { ("max combo", @catch.MaxCombo), ("approach rate", @catch.ApproachRate.ToString("N2")) }; break; case ManiaDifficultyAttributes mania: result.AttributeData = new List <(string, object)> { ("hit window", mania.GreatHitWindow.ToString("N2")) }; break; } return(result); }
private Result processBeatmap(WorkingBeatmap beatmap) { // Get the ruleset var ruleset = LegacyHelper.GetRulesetFromLegacyID(Ruleset ?? beatmap.BeatmapInfo.RulesetID); var attributes = ruleset.CreateDifficultyCalculator(beatmap).Calculate(getMods(ruleset).ToArray()); var result = new Result { RulesetId = ruleset.RulesetInfo.ID ?? 0, Beatmap = $"{beatmap.BeatmapInfo.OnlineBeatmapID} - {beatmap.BeatmapInfo}", Stars = attributes.StarRating.ToString("N2") }; switch (attributes) { case OsuDifficultyAttributes osu: result.AttributeData = new List <(string, object)> { ("aim rating", osu.AimStrain.ToString("N2")), ("speed rating", osu.SpeedStrain.ToString("N2")), ("max combo", osu.MaxCombo), ("approach rate", osu.ApproachRate.ToString("N2")), ("overall difficulty", osu.OverallDifficulty.ToString("N2")) }; break; case TaikoDifficultyAttributes taiko: result.AttributeData = new List <(string, object)> { ("hit window", taiko.GreatHitWindow.ToString("N2")), ("max combo", taiko.MaxCombo) }; break; case CatchDifficultyAttributes @catch: result.AttributeData = new List <(string, object)> { ("max combo", @catch.MaxCombo), ("approach rate", @catch.ApproachRate.ToString("N2")) }; break; case ManiaDifficultyAttributes mania: result.AttributeData = new List <(string, object)> { ("hit window", mania.GreatHitWindow.ToString("N2")) }; break; } return(result); }
public void ProcessBeatmap(string beatmap_path, string[] mods) { ProcessorWorkingBeatmap beatmap = new ProcessorWorkingBeatmap(beatmap_path); // Get the ruleset var ruleset = LegacyHelper.GetRulesetFromLegacyID(beatmap.BeatmapInfo.RulesetID); var attributes = ruleset.CreateDifficultyCalculator(beatmap).Calculate(getMods(ruleset, mods).ToArray()); var beatmapName = $"{beatmap.BeatmapInfo.OnlineBeatmapID} - {beatmap.BeatmapInfo}"; if (beatmapName.Length > 100) { beatmapName = beatmapName.Substring(0, 100) + "..."; } var result = new Result { RulesetId = ruleset.RulesetInfo.ID ?? 0, Beatmap = beatmapName, Stars = attributes.StarRating.ToString("N3") }; result.AttributeData = new List <(string, object)> { }; CalcResults = new Document(); CalcResults.Children.Add(new Span($"Ruleset: {ruleset.ShortName}"), "\n"); var grid = new Grid(); grid.Columns.Add(GridLength.Auto, GridLength.Auto); grid.Children.Add(new Cell("beatmap"), new Cell("star rating")); foreach (var attribute in result.AttributeData) { grid.Columns.Add(GridLength.Auto); grid.Children.Add(new Cell(attribute.name)); } grid.Children.Add(new Cell(result.Beatmap), new Cell(result.Stars) { Align = Align.Right }); foreach (var attribute in result.AttributeData) { grid.Children.Add(new Cell(attribute.value) { Align = Align.Right }); } CalcResults.Children.Add(grid); CalcResults.Children.Add("\n"); }
private Result processBeatmap(WorkingBeatmap beatmap) { // Get the ruleset var ruleset = LegacyHelper.GetRulesetFromLegacyID(Ruleset ?? beatmap.BeatmapInfo.Ruleset.OnlineID); var mods = NoClassicMod ? getMods(ruleset) : LegacyHelper.ConvertToLegacyDifficultyAdjustmentMods(ruleset, getMods(ruleset)); var attributes = ruleset.CreateDifficultyCalculator(beatmap).Calculate(mods); return(new Result { RulesetId = ruleset.RulesetInfo.OnlineID, BeatmapId = beatmap.BeatmapInfo.OnlineID, Beatmap = beatmap.BeatmapInfo.ToString(), Mods = mods.Select(m => new APIMod(m)).ToList(), Attributes = attributes }); }
/// <summary> /// Verifies all proposed mods are valid for the room's ruleset, returning instantiated <see cref="Mod"/>s for further processing. /// </summary> /// <param name="rulesetID">The legacy ruleset ID to check against.</param> /// <param name="proposedMods">The proposed mods.</param> /// <param name="valid">A list of valid deserialised mods.</param> /// <returns>Whether all <see cref="proposedMods"/> were valid.</returns> private static bool populateValidModsForRuleset(int rulesetID, IEnumerable <APIMod> proposedMods, out List <Mod> valid) { valid = new List <Mod>(); bool proposedWereValid = true; var ruleset = LegacyHelper.GetRulesetFromLegacyID(rulesetID); foreach (var apiMod in proposedMods) { try { // will throw if invalid valid.Add(apiMod.ToMod(ruleset)); } catch { proposedWereValid = false; } } return(proposedWereValid); }
private void processBeatmap(WorkingBeatmap beatmap) { // Get the ruleset var ruleset = LegacyHelper.GetRulesetFromLegacyID(Ruleset ?? beatmap.BeatmapInfo.RulesetID); var attributes = ruleset.CreateDifficultyCalculator(beatmap).Calculate(getMods(ruleset).ToArray()); writeAttribute("Ruleset", ruleset.ShortName); writeAttribute("Stars", attributes.StarRating.ToString(CultureInfo.InvariantCulture)); switch (attributes) { case OsuDifficultyAttributes osu: writeAttribute("Aim", osu.AimStrain.ToString(CultureInfo.InvariantCulture)); writeAttribute("Speed", osu.SpeedStrain.ToString(CultureInfo.InvariantCulture)); writeAttribute("MaxCombo", osu.MaxCombo.ToString(CultureInfo.InvariantCulture)); writeAttribute("AR", osu.ApproachRate.ToString(CultureInfo.InvariantCulture)); writeAttribute("OD", osu.OverallDifficulty.ToString(CultureInfo.InvariantCulture)); break; case TaikoDifficultyAttributes taiko: writeAttribute("HitWindow", taiko.GreatHitWindow.ToString(CultureInfo.InvariantCulture)); writeAttribute("MaxCombo", taiko.MaxCombo.ToString(CultureInfo.InvariantCulture)); break; case CatchDifficultyAttributes c: writeAttribute("MaxCombo", c.MaxCombo.ToString(CultureInfo.InvariantCulture)); writeAttribute("AR", c.ApproachRate.ToString(CultureInfo.InvariantCulture)); break; case ManiaDifficultyAttributes mania: writeAttribute("HitWindow", mania.GreatHitWindow.ToString(CultureInfo.InvariantCulture)); break; } Console.WriteLine(); }
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() { var rulesetApiName = LegacyHelper.GetRulesetShortNameFromId(Ruleset ?? 0); var leaderboard = GetJsonFromApi($"rankings/{rulesetApiName}/performance?cursor[page]={LeaderboardPage - 1}"); var calculatedPlayers = new List <LeaderboardPlayerInfo>(); foreach (var player in leaderboard.ranking) { if (calculatedPlayers.Count >= Limit) { break; } var plays = new List <(double, double)>(); // (local, live) var ruleset = LegacyHelper.GetRulesetFromLegacyID(Ruleset ?? 0); Console.WriteLine($"Calculating {player.user.username} top scores..."); foreach (var play in GetJsonFromApi($"users/{player.user.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(); plays.Add((performanceCalculator?.Calculate(score.ScoreInfo, difficultyAttributes).Total ?? 0, play.pp)); } var localOrdered = plays.Select(x => x.Item1).OrderByDescending(x => x).ToList(); var liveOrdered = plays.Select(x => x.Item2).OrderByDescending(x => x).ToList(); int index = 0; double totalLocalPP = localOrdered.Sum(play => Math.Pow(0.95, index++) * play); double totalLivePP = player.pp; index = 0; double nonBonusLivePP = liveOrdered.Sum(play => Math.Pow(0.95, index++) * play); //todo: implement properly. this is pretty damn wrong. var playcountBonusPP = (totalLivePP - nonBonusLivePP); totalLocalPP += playcountBonusPP; calculatedPlayers.Add(new LeaderboardPlayerInfo { LivePP = totalLivePP, LocalPP = totalLocalPP, Username = player.user.username }); } calculatedPlayers = calculatedPlayers.OrderByDescending(x => x.LocalPP).ToList(); var liveOrderedPlayers = calculatedPlayers.OrderByDescending(x => x.LivePP).ToList(); if (OutputJson) { var json = JsonConvert.SerializeObject(calculatedPlayers); Console.Write(json); if (OutputFile != null) { File.WriteAllText(OutputFile, json); } } else { OutputDocument(new Document( new Grid { Columns = { GridLength.Auto, GridLength.Auto, GridLength.Auto, GridLength.Auto, GridLength.Auto }, Children = { new Cell("#"), new Cell("username"), new Cell("live pp"), new Cell("local pp"), new Cell("pp change"), calculatedPlayers.Select(item => new[] { new Cell($"{liveOrderedPlayers.IndexOf(item) - calculatedPlayers.IndexOf(item):+0;-0;-}"), new Cell($"{item.Username}"), 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 } }) } }) ); } }
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 resultSet = new ResultSet(); if (Directory.Exists(Path)) { foreach (string file in Directory.GetFiles(Path, "*.osu", SearchOption.AllDirectories)) { try { var beatmap = new ProcessorWorkingBeatmap(file); resultSet.Results.Add(processBeatmap(beatmap)); } catch (Exception e) { resultSet.Errors.Add($"Processing beatmap \"{file}\" failed:\n{e.Message}"); } } } else { resultSet.Results.Add(processBeatmap(ProcessorWorkingBeatmap.FromFileOrId(Path))); } if (OutputJson) { string json = JsonConvert.SerializeObject(resultSet); Console.WriteLine(json); if (OutputFile != null) { File.WriteAllText(OutputFile, json); } } else { var document = new Document(); foreach (var error in resultSet.Errors) { document.Children.Add(new Span(error), "\n"); } if (resultSet.Errors.Count > 0) { document.Children.Add("\n"); } foreach (var group in resultSet.Results.GroupBy(r => r.RulesetId)) { var ruleset = LegacyHelper.GetRulesetFromLegacyID(group.First().RulesetId); document.Children.Add(new Span($"ruleset: {ruleset.ShortName}"), "\n"); Grid grid = new Grid(); bool firstResult = true; foreach (var result in group) { var attributeValues = JsonConvert.DeserializeObject <Dictionary <string, object> >(JsonConvert.SerializeObject(result.Attributes)) ?? new Dictionary <string, object>(); // Headers if (firstResult) { grid.Columns.Add(GridLength.Auto); grid.Children.Add(new Cell("beatmap")); foreach (var column in attributeValues) { grid.Columns.Add(GridLength.Auto); grid.Children.Add(new Cell(column.Key.Humanize())); } } // Values grid.Children.Add(new Cell($"{result.BeatmapId} - {result.Beatmap}")); foreach (var column in attributeValues) { grid.Children.Add(new Cell($"{column.Value:N2}") { Align = Align.Right }); } firstResult = false; } document.Children.Add(grid, "\n"); } OutputDocument(document); } }
public async Task ExecuteAsync(Label status_block) { if (Key == "" || ProfileName == "") { return; } var displayPlays = new List <UserPlayInfo>(); var ruleset = LegacyHelper.GetRulesetFromLegacyID(0); status_block.Dispatcher.Invoke(() => { status_block.Content = "Getting user data..."; }); dynamic userData = await getJsonFromApi($"get_user?k={Key}&u={ProfileName}&m={0}"); if (!((JArray)userData).Any()) { ResultsDoc = new Document(new Span("Could not find user " + ProfileName)); return; } userData = userData[0]; status_block.Dispatcher.Invoke(() => { status_block.Content = "Getting user top scores..."; }); foreach (var play in await getJsonFromApi($"get_user_best?k={Key}&u={ProfileName}&m={0}&limit=100")) { string beatmapID = play.beatmap_id; string cachePath = Path.Combine("cache", $"{beatmapID}.osu"); if (!File.Exists(cachePath)) { status_block.Dispatcher.Invoke(() => { status_block.Content = $"Downloading {beatmapID}.osu..."; }); await new FileWebRequest(cachePath, $"{base_url}/osu/{beatmapID}").PerformAsync(); } 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, AimPP = categoryAttribs["Aim"], TapPP = categoryAttribs["Speed"], AccPP = categoryAttribs["Accuracy"], 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; ResultsDoc = 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 Alba.CsConsoleFormat.Grid { Columns = { GridLength.Auto, GridLength.Auto, GridLength.Auto, GridLength.Auto, GridLength.Auto, GridLength.Auto, GridLength.Auto, GridLength.Auto, GridLength.Auto, GridLength.Auto, GridLength.Auto, GridLength.Auto }, Children = { 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 }, new Cell("aim pp"), new Cell("tap pp"), new Cell("acc pp"), new Cell("local pp"), new Cell("pp change"), new Cell("position change"), localOrdered.Select(item => new[] { new Cell($"{item.Beatmap.OnlineBeatmapID} - {item.Beatmap.ToString().Substring(0, Math.Min(80, item.Beatmap.ToString().Length))}"), new Cell(item.Mods) { Align = Align.Center }, new Cell($"{item.LivePP:F1}") { Align = Align.Right }, new Cell($"{item.PlayAccuracy * 100f:F2}" + " %") { Align = Align.Center }, new Cell($"{item.MissCount}") { Align = Align.Center }, new Cell($"{item.PlayMaxCombo}/{item.BeatmapMaxCombo}") { Align = Align.Center }, new Cell($"{item.AimPP:F1}") { Align = Align.Right }, new Cell($"{item.TapPP:F1}") { Align = Align.Right }, new Cell($"{item.AccPP: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 )); }