-
Notifications
You must be signed in to change notification settings - Fork 78
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #195 from smoogipoo/new-scores-command
Add support for computing performance of non-legacy scores
- Loading branch information
Showing
10 changed files
with
190 additions
and
68 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
// Copyright (c) ppy Pty Ltd <[email protected]>. Licensed under the MIT Licence. | ||
// See the LICENCE file in the repository root for full licence text. | ||
|
||
using System.Collections.Generic; | ||
using System.ComponentModel.DataAnnotations; | ||
using System.Globalization; | ||
using System.Net.Http; | ||
|
@@ -33,11 +34,19 @@ public override void OnExecute(CommandLineApplication app, IConsole console) | |
base.OnExecute(app, console); | ||
} | ||
|
||
protected T GetJsonFromApi<T>(string request) | ||
protected T GetJsonFromApi<T>(string request, HttpMethod method = null, Dictionary<string, string> parameters = null) | ||
{ | ||
using var req = new JsonWebRequest<T>($"{Program.ENDPOINT_CONFIGURATION.APIEndpointUrl}/api/v2/{request}"); | ||
req.Method = method ?? HttpMethod.Get; | ||
req.AddHeader("x-api-version", api_version.ToString(CultureInfo.InvariantCulture)); | ||
req.AddHeader(System.Net.HttpRequestHeader.Authorization.ToString(), $"Bearer {apiAccessToken}"); | ||
|
||
if (parameters != null) | ||
{ | ||
foreach ((string key, string value) in parameters) | ||
req.AddParameter(key, value); | ||
} | ||
|
||
req.Perform(); | ||
|
||
return req.ResponseObject; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
42 changes: 42 additions & 0 deletions
42
PerformanceCalculator/Performance/LegacyScorePerformanceCommand.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
// Copyright (c) ppy Pty Ltd <[email protected]>. Licensed under the MIT Licence. | ||
// See the LICENCE file in the repository root for full licence text. | ||
|
||
using System.Linq; | ||
using McMaster.Extensions.CommandLineUtils; | ||
using osu.Game.Beatmaps; | ||
using osu.Game.Database; | ||
using osu.Game.Online.API.Requests.Responses; | ||
using osu.Game.Rulesets; | ||
using osu.Game.Rulesets.Mods; | ||
using osu.Game.Rulesets.Scoring.Legacy; | ||
using osu.Game.Scoring; | ||
using osu.Game.Scoring.Legacy; | ||
|
||
namespace PerformanceCalculator.Performance | ||
{ | ||
[Command(Name = "legacy-score", Description = "Computes the performance (pp) of an online score.")] | ||
public class LegacyScorePerformanceCommand : ScorePerformanceCommand | ||
{ | ||
[Argument(1, "ruleset-id", "The ID of the ruleset that the score was set on.")] | ||
public int RulesetId { get; set; } | ||
|
||
protected override SoloScoreInfo QueryScore() => GetJsonFromApi<SoloScoreInfo>($"scores/{LegacyHelper.GetRulesetShortNameFromId(RulesetId)}/{ScoreId}"); | ||
|
||
protected override ScoreInfo CreateScore(SoloScoreInfo apiScore, Ruleset ruleset, APIBeatmap apiBeatmap, WorkingBeatmap workingBeatmap) | ||
{ | ||
var score = base.CreateScore(apiScore, ruleset, apiBeatmap, workingBeatmap); | ||
|
||
score.Mods = score.Mods.Append(ruleset.CreateMod<ModClassic>()).ToArray(); | ||
score.IsLegacyScore = true; | ||
score.LegacyTotalScore = (int)score.TotalScore; | ||
LegacyScoreDecoder.PopulateMaximumStatistics(score, workingBeatmap); | ||
StandardisedScoreMigrationTools.UpdateFromLegacy( | ||
score, | ||
ruleset, | ||
LegacyBeatmapConversionDifficultyInfo.FromAPIBeatmap(apiBeatmap), | ||
((ILegacyRuleset)ruleset).CreateLegacyScoreSimulator().Simulate(workingBeatmap, workingBeatmap.GetPlayableBeatmap(ruleset.RulesetInfo, score.Mods))); | ||
|
||
return score; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,36 +1,70 @@ | ||
// Copyright (c) ppy Pty Ltd <[email protected]>. Licensed under the MIT Licence. | ||
// See the LICENCE file in the repository root for full licence text. | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.Globalization; | ||
using System.Linq; | ||
using System.Net.Http; | ||
using McMaster.Extensions.CommandLineUtils; | ||
using Newtonsoft.Json; | ||
using osu.Game.Beatmaps; | ||
using osu.Game.Database; | ||
using osu.Game.Beatmaps.Legacy; | ||
using osu.Game.Models; | ||
using osu.Game.Online.API.Requests.Responses; | ||
using osu.Game.Rulesets; | ||
using osu.Game.Rulesets.Mods; | ||
using osu.Game.Rulesets.Scoring.Legacy; | ||
using osu.Game.Scoring.Legacy; | ||
using osu.Game.Rulesets.Catch.Difficulty; | ||
using osu.Game.Rulesets.Difficulty; | ||
using osu.Game.Rulesets.Mania.Difficulty; | ||
using osu.Game.Rulesets.Osu.Difficulty; | ||
using osu.Game.Rulesets.Taiko.Difficulty; | ||
using osu.Game.Scoring; | ||
|
||
namespace PerformanceCalculator.Performance | ||
{ | ||
[Command(Name = "score", Description = "Computes the performance (pp) of an online score.")] | ||
public class ScorePerformanceCommand : ApiCommand | ||
{ | ||
[Argument(0, "ruleset-id", "The ID of the ruleset that the score was set on.")] | ||
public int RulesetId { get; set; } | ||
|
||
[Argument(1, "score-id", "The score's online ID.")] | ||
[Argument(0, "score-id", "The score's online ID.")] | ||
public ulong ScoreId { get; set; } | ||
|
||
[Option(CommandOptionType.NoValue, Template = "-a|--online-attributes", Description = "Whether to use the currently-live difficulty attributes for the beatmap.")] | ||
public bool OnlineAttributes { get; set; } | ||
|
||
public override void Execute() | ||
{ | ||
base.Execute(); | ||
|
||
SoloScoreInfo apiScore = GetJsonFromApi<SoloScoreInfo>($"scores/{LegacyHelper.GetRulesetShortNameFromId(RulesetId)}/{ScoreId}"); | ||
SoloScoreInfo apiScore = QueryScore(); | ||
APIBeatmap apiBeatmap = GetJsonFromApi<APIBeatmap>($"beatmaps/lookup?id={apiScore.BeatmapID}"); | ||
|
||
var ruleset = LegacyHelper.GetRulesetFromLegacyID(apiScore.RulesetID); | ||
var workingBeatmap = ProcessorWorkingBeatmap.FromFileOrId(apiScore.BeatmapID.ToString()); | ||
var score = CreateScore(apiScore, ruleset, apiBeatmap, workingBeatmap); | ||
|
||
DifficultyAttributes attributes; | ||
|
||
if (OnlineAttributes) | ||
{ | ||
LegacyMods legacyMods = LegacyHelper.ConvertToLegacyDifficultyAdjustmentMods(workingBeatmap.BeatmapInfo, ruleset, score.Mods); | ||
attributes = queryApiAttributes(apiScore.BeatmapID, apiScore.RulesetID, legacyMods); | ||
} | ||
else | ||
{ | ||
var difficultyCalculator = ruleset.CreateDifficultyCalculator(workingBeatmap); | ||
attributes = difficultyCalculator.Calculate(LegacyHelper.FilterDifficultyAdjustmentMods(workingBeatmap.BeatmapInfo, ruleset, score.Mods)); | ||
} | ||
|
||
var performanceCalculator = ruleset.CreatePerformanceCalculator(); | ||
var performanceAttributes = performanceCalculator?.Calculate(score, attributes); | ||
|
||
OutputPerformance(score, performanceAttributes, attributes); | ||
} | ||
|
||
protected virtual SoloScoreInfo QueryScore() => GetJsonFromApi<SoloScoreInfo>($"scores/{ScoreId}"); | ||
|
||
protected virtual ScoreInfo CreateScore(SoloScoreInfo apiScore, Ruleset ruleset, APIBeatmap apiBeatmap, WorkingBeatmap workingBeatmap) | ||
{ | ||
var score = apiScore.ToScoreInfo(apiScore.Mods.Select(m => m.ToMod(ruleset)).ToArray(), apiBeatmap); | ||
score.Ruleset = ruleset.RulesetInfo; | ||
score.BeatmapInfo!.Metadata = new BeatmapMetadata | ||
|
@@ -40,27 +74,41 @@ public override void Execute() | |
Author = new RealmUser { Username = apiBeatmap.Metadata.Author.Username }, | ||
}; | ||
|
||
var workingBeatmap = ProcessorWorkingBeatmap.FromFileOrId(score.BeatmapInfo!.OnlineID.ToString()); | ||
return score; | ||
} | ||
|
||
if (apiScore.BuildID == null) | ||
private DifficultyAttributes queryApiAttributes(int beatmapId, int rulesetId, LegacyMods mods) | ||
{ | ||
Dictionary<string, string> parameters = new Dictionary<string, string> | ||
{ | ||
score.Mods = score.Mods.Append(ruleset.CreateMod<ModClassic>()).ToArray(); | ||
score.IsLegacyScore = true; | ||
score.LegacyTotalScore = (int)score.TotalScore; | ||
LegacyScoreDecoder.PopulateMaximumStatistics(score, workingBeatmap); | ||
StandardisedScoreMigrationTools.UpdateFromLegacy( | ||
score, | ||
ruleset, | ||
LegacyBeatmapConversionDifficultyInfo.FromAPIBeatmap(apiBeatmap), | ||
((ILegacyRuleset)ruleset).CreateLegacyScoreSimulator().Simulate(workingBeatmap, workingBeatmap.GetPlayableBeatmap(ruleset.RulesetInfo, score.Mods))); | ||
} | ||
{ "mods", ((int)mods).ToString(CultureInfo.InvariantCulture) } | ||
}; | ||
|
||
var difficultyCalculator = ruleset.CreateDifficultyCalculator(workingBeatmap); | ||
var difficultyAttributes = difficultyCalculator.Calculate(LegacyHelper.ConvertToLegacyDifficultyAdjustmentMods(workingBeatmap.BeatmapInfo, ruleset, score.Mods)); | ||
var performanceCalculator = ruleset.CreatePerformanceCalculator(); | ||
var performanceAttributes = performanceCalculator?.Calculate(score, difficultyAttributes); | ||
switch (rulesetId) | ||
{ | ||
case 0: | ||
return GetJsonFromApi<AttributesResponse<OsuDifficultyAttributes>>($"beatmaps/{beatmapId}/attributes", HttpMethod.Post, parameters).Attributes; | ||
|
||
OutputPerformance(score, performanceAttributes, difficultyAttributes); | ||
case 1: | ||
return GetJsonFromApi<AttributesResponse<TaikoDifficultyAttributes>>($"beatmaps/{beatmapId}/attributes", HttpMethod.Post, parameters).Attributes; | ||
|
||
case 2: | ||
return GetJsonFromApi<AttributesResponse<CatchDifficultyAttributes>>($"beatmaps/{beatmapId}/attributes", HttpMethod.Post, parameters).Attributes; | ||
|
||
case 3: | ||
return GetJsonFromApi<AttributesResponse<ManiaDifficultyAttributes>>($"beatmaps/{beatmapId}/attributes", HttpMethod.Post, parameters).Attributes; | ||
|
||
default: | ||
throw new ArgumentOutOfRangeException(nameof(rulesetId)); | ||
} | ||
} | ||
|
||
[JsonObject(MemberSerialization.OptIn)] | ||
private class AttributesResponse<T> | ||
where T : DifficultyAttributes | ||
{ | ||
[JsonProperty("attributes")] | ||
public T Attributes { get; set; } | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters