Closes https://github.com/ppy/osu/issues/28216.
The affected user's database contained six sentakki scores with an empty
hash. When an online score is being imported, an online model (which
does not have a hash) will be transmogrified into a `ScoreInfo` with
an empty hash, which would end up accidentally matching those scores
and basically breaking everything at that point.
To fix, avoid attempting to match anything on empty hash. This does not
break online score matching because for those cases the actual online ID
of the score will be used.
Something I ran into when investigating
https://github.com/ppy/osu/issues/28169.
If there are two scores with the same online ID available in the
database - for instance, one being recorded locally, and one recorded by
spectator server, of one single play - the lookup code would use online
ID first to find the score and pick any first one that matched. This
could lead to the wrong replay being refetched and presented / exported.
(In the case of the aforementioned issue, I was confused as to whether
after restarting spectator server midway through a play and importing
the replay saved by spectator server after the restart, I was seeing a
complete replay with no dropped frames, even though there was nothing in
the code that prevented the frame drop. It turns out that I was getting
presented the locally recorded replay instead all along.)
Instead, jiggle the fallback preference to use hash first.
You'd hope that they'd be the same thing, but
post-https://github.com/ppy/osu-server-spectator/pull/230 it turns out
that cannot be guaranteed, so just attempt to use `User` in the encoder
consistently everywhere...
This class was only used in two places, both on the results screen, but
was holding references to `OsuPlayfield` game-wide (due to unrelated
issues, but still).
Because I can't really think of future use cases for this, and running
the calculation twice at results screen isn't a huge overhead, let's
just do that for now to keep things simple.
This subtle detail was messing with server-side score import flows.
Server-side, legacy total score will *already* be in `LegacyTotalScore`
from the start, and `TotalScore` will be zero until recomputed via
`StandardisedScoreMigrationTools.UpdateFromLegacy()` - so in that
context, attempting to move it across is incorrect.
Closes https://github.com/ppy/osu/issues/24061.
The gist of this change is that if the `LegacyReplaySoloScoreInfo`
bolt-on is present in the replay, then it can (and is) used to recompute
the accuracy, and rank is computed based on that.
This was the missing part of
https://github.com/ppy/osu/issues/24061#issuecomment-1888438151.
The accuracy would change on import before that because the encode
process is _lossy_ if the `LegacyReplaySoloScoreInfo` bolt-on is not
used, as the legacy format only has 6 fields for encoding judgement
counts, and some judgements that affect accuracy in lazer do not fit
into that.
Note that this _only_ fixes _relatively_ new lazer scores looking wrong
after reimport.
- Very old lazer scores, i.e. ones that don't have the
`LegacyReplaySoloScoreInfo` bolt-on, obviously can't use it
to repopulate. There's really not much good that can be done there,
so the stable pathways are used as a fallback that always works.
- For stable replays, `ScoreImporter` recalculates the accuracy of
the score _again_ in
15a5fd7e4c/osu.Game/Scoring/ScoreImporter.cs (L106-L110)
as `StandardisedScoreMigrationTools.UpdateFromLegacy()` recomputes
_both_ total score and accuracy.
This makes a _semblance_ of sense as it attempts to make the accuracy
of stable and lazer replays comparable. In most cases it also won't
matter, as the only ruleset where accuracy changed between the legacy
implementation and current lazer accuracy is mania.
But it is also an inaccurate process (as, again, some of the required
data is not in the replay, namely judgement counts of ticks
and so on).
For whatever's worth, a similar thing happens server-side in
106c2948db/osu.Server.Queues.ScoreStatisticsProcessor/Commands/Queue/BatchInserter.cs (L319)
- However, _ranks_ of stable scores will still use the local stable
reimplementation of ranks, i.e. a 1-miss stable score in osu! ruleset
will be an A rather than an S. See importer:
106c2948db/osu.Server.Queues.ScoreStatisticsProcessor/Commands/Queue/BatchInserter.cs (L237)
(it's the same method which is renamed
to `PopulateLegacyAccuracyAndRank()` in this commit).
That is all a bit of a mess honestly, but I'm not sure where to even
begin there...