Standardised score conversion would return a negative total score for
https://osu.ppy.sh/scores/taiko/182230346.
The underlying reason for this is that the estimation of the accuracy
portion for a score can be above the actual accuracy portion in the
taiko ruleset.
When calculating the maximum accuracy portion achievable,
`TaikoLegacyScoreSimulator` will include the extra 300 points from
a double hit on a strong hit, per strong hit. However, this double hit
is not factored into accuracy.
Both of the aforementioned facts mean that in taiko
maximumLegacyAccuracyScore * score.Accuracy
- which normally in other rulesets can be used pretty reliably as the
exact number of points gained from the accuracy portion - is an
estimate in the case of taiko, and an _upper_ estimate at that,
because it implicitly assumes that the user has also hit
`score.Accuracy` percent of all double hits possible in the beatmap. If
this assumption is not upheld, then the user will have earned _less_
points than that from the accuracy portion, which means that the combo
proportion estimate will go below zero.
It is possible that this has happened on other scores before, but did
not result in the total score going negative as the accuracy portion
gained would have counteracted the effect of that due to being larger in
magnitude than the score loss incurred from the negative combo
portion. In the case of the score in question this was not the case due
to very low accuracy _and_ very low max combo.
Co-authored-by: Zyf <zyfarok@gmail.com>
Closes https://github.com/ppy/osu/issues/25860
Users reported that some stable scores would convert to large negative
total scores in lazer after the introduction of combo exponent. Those
large negative total scores were actually mangled NaNs.
The root cause of this was the following calculation going below zero
unexpectedly:
8e8d9b2cd9/osu.Game/Database/StandardisedScoreMigrationTools.cs (L323)
which then propagates negative numbers onward until
8e8d9b2cd9/osu.Game/Database/StandardisedScoreMigrationTools.cs (L337)
which yields a NaN due to attempting to take the square root of a
negative number.
To fix, clamp `comboPortionInScoreV1` to sane limits: to
`comboPortionFromLongestComboInScoreV1` from below, and to
`maximumAchievableComboPortionInScoreV1` from above. This is a less
direct fix than perhaps imagined, but it seems like a better one as it
will also affect the calculation of both the lower and the upper
estimate of the score.
Progress didn't work for several reasons:
- It was spinning when nothing was actually happening yet
(especially egregious with autodownload off)
- It was blocking exits (as all progress notifications do)
- When actually going to download, two progress notifications would
pertain to one thing
- It wasn't helping much with the actual implementation of score
re-import, cancelling the progress notification would result in
similarly jank UX of beatmap importing but not the score.
Falls into the age-old trap of attempting to retrieve an item from realm
without first ensuring that realm is in an up-to-date state.
Consider this scenario:
- Editor is entered from main menu, causing it to create a new beatmap
from its async `load()` method.
- Editor opens correctly, then main thread performs a file operations on
the same beatmap.
- Main thread is potentially not refreshed yet, and will result in `null`
instance when performing the re-fetch in `performFileOperation`.
I've fixed this by using the safe implementation inside `RealmLive<T>`.
Feels like we want this is one place which is always used as the correct
method.
On a quick search, there are 10-20 other usages of `Realm.Find<T>` which
could also have similar issues, but it'll be a bit of a pain to go
through and fix each of these. In 99.9% of cases, the accesses are on
instances which couldn't have just been created (or the usage of
recently-imported/created is blocked by realm subscription flows, ie.
baetmap import) so I'm not touching them for now.
Something to keep in mind when working with realm going forward though.