Skip to content

Commit

Permalink
Fix online attribute flag in score performance command not retrieving…
Browse files Browse the repository at this point in the history
… full difficulty attributes

Found when investigating ppy/osu#28006.

If the `performance score` command is given the `-a` flag, it is
supposed to use the current online beatmap difficulty attributes. It
does so by querying API, which queries `osu-beatmap-difficulty-cache`.

Problem is, that some of the difficulty attributes, namely the ones that
are already present on `APIBeatmap`, are not serialised out by
`osu-beatmap-difficulty-cache`:

	https://github.com/ppy/osu-beatmap-difficulty-lookup-cache/blob/db2203368221109803f2031788da31deb94e0f11/BeatmapDifficultyLookupCache/DifficultyCache.cs#L125-L128

`osu-tools` is blissfully unaware of this, and as such attempts to
deserialise the difficulty attributes returned from cache-then-web raw,
which leads to it just using the default values (e.g. zeroes) for
attributes that the difficulty lookup cache does not serialise out. This
leads to obviously bogus results.

To fix, retrieve the `APIBeatmap` manually and do some (admittedly
gnarly) grafting in order to produce a fully populated object. It's a
bit ugly but it works locally without further changes so maybe fine?
  • Loading branch information
bdach committed Apr 29, 2024
1 parent c3cbe41 commit d6cf8dc
Showing 1 changed file with 22 additions and 4 deletions.
26 changes: 22 additions & 4 deletions PerformanceCalculator/Performance/ScorePerformanceCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,23 +84,41 @@ private DifficultyAttributes queryApiAttributes(int beatmapId, int rulesetId, Le
{ "mods", ((int)mods).ToString(CultureInfo.InvariantCulture) }
};

var beatmap = GetJsonFromApi<APIBeatmap>($"beatmaps/{beatmapId}");

switch (rulesetId)
{
case 0:
return GetJsonFromApi<AttributesResponse<OsuDifficultyAttributes>>($"beatmaps/{beatmapId}/attributes", HttpMethod.Post, parameters).Attributes;
return getMergedAttributes<OsuDifficultyAttributes>(beatmap);

case 1:
return GetJsonFromApi<AttributesResponse<TaikoDifficultyAttributes>>($"beatmaps/{beatmapId}/attributes", HttpMethod.Post, parameters).Attributes;
return getMergedAttributes<TaikoDifficultyAttributes>(beatmap);

case 2:
return GetJsonFromApi<AttributesResponse<CatchDifficultyAttributes>>($"beatmaps/{beatmapId}/attributes", HttpMethod.Post, parameters).Attributes;
return getMergedAttributes<CatchDifficultyAttributes>(beatmap);

case 3:
return GetJsonFromApi<AttributesResponse<ManiaDifficultyAttributes>>($"beatmaps/{beatmapId}/attributes", HttpMethod.Post, parameters).Attributes;
return getMergedAttributes<ManiaDifficultyAttributes>(beatmap);

default:
throw new ArgumentOutOfRangeException(nameof(rulesetId));
}

DifficultyAttributes getMergedAttributes<TAttributes>(APIBeatmap apiBeatmap)
where TAttributes : DifficultyAttributes, new()
{
// the osu-web endpoint queries osu-beatmap-difficulty-cache, which in turn does not return the full set of attributes -
// it skips ones that are already present on `APIBeatmap`
// (https://github.com/ppy/osu-beatmap-difficulty-lookup-cache/blob/db2203368221109803f2031788da31deb94e0f11/BeatmapDifficultyLookupCache/DifficultyCache.cs#L125-L128).
// to circumvent this, do some manual grafting on our side to produce a fully populated set of attributes.
var databasedAttributes = GetJsonFromApi<AttributesResponse<TAttributes>>($"beatmaps/{beatmapId}/attributes", HttpMethod.Post, parameters).Attributes;
var fullAttributes = new TAttributes();
fullAttributes.FromDatabaseAttributes(databasedAttributes.ToDatabaseAttributes().ToDictionary(
pair => pair.attributeId,
pair => Convert.ToDouble(pair.value, CultureInfo.InvariantCulture)
), apiBeatmap);
return fullAttributes;
}
}

[JsonObject(MemberSerialization.OptIn)]
Expand Down

0 comments on commit d6cf8dc

Please sign in to comment.