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.
Mostly places that can interact with imported replays.
There are other places that use the online ID as a sort tiebreaker, or
to check presence of a score on results screens, but they should
probably still continue to only use `OnlineID`, since all scores with a
legacy online ID should have an online ID, but the converse is not
generally true.
Introduces some error at all times, but if we're to store scores everywhere as
`long`, then the same precision should be applied to the "during
gameplay" path as well.