Intended as at least a partial solution to
https://github.com/ppy/osu/issues/26436. In testing, two of the three
replays from the issue are FCs with this change, and the third is
improved (and not fully fixed).
As it turns out, stable truncates the end time of sliders to integers:
79addff0f5/osu!/GameplayElements/HitObjects/Osu/SliderOsu.cs#L1037
and the start time of all juice stream parts is also truncated to
integers:
79addff0f5/osu!/GameplayElements/HitObjects/Fruits/SliderFruits.cs#L86-L166
This matters for replay playback when mappers push limits. For instance,
let's take one case from the issue. In one of the maps involved, there
was a juice-stream-ending fruit at time 7093.9655 according to lazer,
which was also a hyperfruit.
The broken replay on this map included one frame at time 7093, with the
catcher position before the hyperdash, and one frame at time 7097,
with the catcher position *after* the hyperdash. Which meant that
the replay handler moved *out of the way* of the hyperfruit at time
7093.9655, deciding that it is *after* the replay frame that was
supposed to be catching it.
(For reference, the relevant replay playback code is here:
3da5831075/osu.Game.Rulesets.Catch/Replays/CatchFramedReplayInputHandler.cs (L20-L31)
Note the handling of `position`. Any frame with an action is important,
which means that as long as any key is held, interpolation will not
take place.)
On stable this is not a thing, because the fruit's end time was being
truncated to `int`, therefore moving it back to time 7093 and restoring
temporal integrity.
This probably doesn't matter in other rulesets that much because
the input tolerances in something like osu! or taiko are much higher.
catch is rather knife-edge, what with mappers doing the "edge dash" /
"pixel jump" stuff (tl;dr: placing a circle just barely outside of
hyperdash range, so that a perfect normal dash is required to catch it).
Thus, this is applied locally to catch for now until proven necessary
to put it elsewhere too.
Closes https://github.com/ppy/osu/issues/21794.
I'm not actually super sure as to what the exact mode of failure is
here, but it's 99% to do with working beatmap cache invalidation. Likely
this can be even considered as another case of
https://github.com/ppy/osu/issues/21357, but because this is a one-liner
"fix," I'm PRing it anyways.
The issue is confusing to understand when working with the swap scenario
given in the issue, but it's a little easier to understand when
performing the following:
1. Have a beatmap set with 2 difficulties. Let's call them "A" and "B".
2. From song select, without ever exiting to main menu, edit "A". Change
the difficulty name to "AA". Save and exit back to song select; do
not exit out to main menu.
3. From song select, edit "B". Change the difficulty name to "BB". Save
and exit back to song select.
4. The difficulty names will be "A" and "BB".
Basically what I *think* is causing this, is the fact that even though
editor invalidates the working beatmap by refetching it afresh on exit,
song select is blissfully unaware of this, and continues working with
its own `BeatmapInfo` instances which have backlinks to
`BeatmapSetInfo`.
When editing the first of the two difficulties and then the second,
the editing of the first one only invalidates the first one rather than
the entire set, and the second difficulty continues to have a stale
reference to the first one via the beatmap set, and as such ends up
overwriting the changes from the first save when passed into the editor
and modified again.
Closes https://github.com/ppy/osu/issues/22783.
If the difficulty being edited has unsaved changes, the editor exit flow
would prompt for save *after* the deletion method has run. This is
undesirable from a UX standpoint, and also leaves the user in a broken
state.
Thus, just fake an update of the last saved hash of the beatmap to fool
the editor into thinking that it's not dirty, so that the exit flow will
not show a save dialog.
Closes https://github.com/ppy/osu/issues/24274.
Bit of an ad-hoc resolution but maybe fine? This basically proposes to
bypass the problem described in the issue by just not showing tick hits
at all on the distribution graph.