Closes https://github.com/ppy/osu/issues/22086
While this *is* a case of aspire-tier breakage, I hesitate to dismiss it
as wontfix, because the fix is somewhat easy, and probably results in
better accuracy across the board.
The issue in question here manifests when there is a significant amount
of lead-in time, like what the beatmap linked in the aforementioned
issue (https://osu.ppy.sh/beatmapsets/948643#osu/1981090) does.
Both stable and lazer record replay frames using absolute timestamps,
*but* the legacy replay format after the first frame uses time *deltas*,
i.e. amounts of time elapsed since the previous frame. This means that
the decoding process of the replay has to reconstruct absolute timestamps
by doing cumulative summation starting from the first frame.
When the very first frame has a timestamp that is very large in
magnitude (say, like the negative 2 billion that the beatmap in question
uses), this will lead to error if the cumulative summation is using
floating point numbers, because it will be adding a small magnitude
frame delta to a large magnitude cumulative absolute time. Which means
that sometimes adding the frame delta to the cumulative time *will not
change the cumulative time*, leading to the loss of time and thus replay
de-synchronisation.
Knowing that the legacy replay format only deals in integral time
values, however, this can be circumvented by just using a large enough
integral number type for the cumulative time tracking instead. I think
`long` in this case can be safely used "large enough" for our purposes:
> Console.WriteLine(long.MaxValue);
9223372036854775807
9 223 372 036 854 775 807 ms equals 292 277 024,6269277149 years
Supersedes https://github.com/ppy/osu/pull/32817.
The messaging of the failure to the user is maybe not the cleanest, but
I'm not sure it's worth putting time in to improve it?
Closes https://github.com/ppy/osu/issues/32420.
The failure cause here is that in editor the beatmap version for the
beatmap affected (or... any beatmap, really), is 0 (ZERO). That is
probably a regression from https://github.com/ppy/osu/pull/32315, but
like... can we universally agree that calling that change "a regression"
in any capacity is dumb? Like what was that code *doing* playing dumb
reference games and copying stuff into an arbitrary instance that could
get or not get used later on? And now you have a 50/50 chance of
accessing the *correct* model's field, depending on whether you go via
`BeatmapInfo` or `Beatmap.BeatmapInfo`?
Moving the field to `IBeatmap`, i.e. what is by now - by consensus,
since https://github.com/ppy/osu/pull/28473 - supposed to be the "decoded
and materialised" beatmap, fixes this issue.
I probably should have done this as part of
https://github.com/ppy/osu/pull/28473 but it slipped my mind. Probably
for the better too because this change has rather large chances of
breaking stuff so maybe better to examine it in isolation (via diffcalc
runs or whatever).
For added humour points, you'd say that the field on `BeatmapInfo` was
not `[Ignore]`d, so this is a realm schema change, right? No. As far as
I can tell, it's not. I opened realm studio and `BeatmapVersion` *is not
a listed column` on `Beatmap` models.
I'm also not gonna get into the fact that I think `EditorBeatmap` doing
dumb games with juggling two `BeatmapInfo` references since
https://github.com/ppy/osu/pull/15075 is bad, because I don't think I
have the mental capacity to hotfix this by going down that train of
thought.
This also optimises the manager classes to better support `Live` usage
where the managed object is already in a good state (ie. doesn't require
re-fetching).
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...