`HitResults` are not calculated properly at the hit-window edges compared to osu!stable.
See original GitHub issueDescribe the bug:
For the OsuRuleset
(did not research/test for other rulesets) I have found a bug as described in the title.
Example:
Given OD=10, which means the hit-window for Meh
(50) is supposed to be ±59.5ms:
In Stable: +59ms
=> Ok
, +60ms
=> Meh
In Lazer: +59ms
=> Ok
, +60ms
=> Ok
which is incorrect.
This is demonstrated in one score of mine:
Side by side comparison: https://www.youtube.com/watch?v=IQGsfzOEyJY
- If you skip to the end you can already see that there is a big accuracy difference.
- At timestamp 1:57 in the last stream: There is supposed to be three consecutive 50’s which is calculated correctly in Stable but not in Lazer.
I used gosumemory to calculate the hit offsets of this replay and at the suspicious note (=6th last note of the map) I hit the note with exactly +60ms offset. (https://gist.github.com/abstrakt-osu/46997fa9d203564ed1f1b1eee09a18ba#file-gosu-json-L894).
Screenshots or videos showing encountered issue:
Side by side comparison: https://www.youtube.com/watch?v=IQGsfzOEyJY
Beatmap: https://osu.ppy.sh/beatmapsets/1010865#osu/2117132 Score (+Replay): https://osu.ppy.sh/scores/osu/3321687146
Stable replay: https://youtu.be/FDhGuZh3P_U Lazer replay: https://youtu.be/Ositc3vu_bg
osu!lazer version: 2020.1225.0
Logs:
Issue Analytics
- State:
- Created 3 years ago
- Reactions:2
- Comments:11 (9 by maintainers)
Top GitHub Comments
In the above wiki pr I’ve done some investigation about how stable does hit window comparisons. In summary:
abs(round(hit error)) < floor(hit window)
abs(hit error) <= hit window
abs(round(hit error)) <= hit window
abs(round(hit error)) < floor(hit window)
, except for the miss window which uses<=
abs(hit error) <= hit window
abs(round(hit error)) <= hit window
abs(round(hit error)) <= floor(hit window)
abs(hit error) <= hit window
abs(round(hit error)) <= hit window
A few examples
Some theoreticals
floor(abs(hit error))
.ceil(abs(hit error))
.Neither of these two give correct effective hit windows (max error) for any case. The comparisons simply aren’t the same.
Of course, true hit timing aside, if we only compare replays between stable and lazer where we only see the ints, “ceiled&floored” would match stable in the case of osu!mania, but not for osu! (as in, you can be off by a whole millisecond in lazer but not in stable). Same for “floored&ceiled”.
In short:
<=
is used, but larger by 1 ms compared to stable wherever<
is used. (Assuming the hit windows have the same value in stable and lazer, of course.) The max hit error can be up to 0.5 ms smaller or largerI haven’t been able to verify this in-game though as I don’t have the tooling or time to do so. Maybe there’s a relevant test scene in lazer, or if not maybe one of those could be cooked up (comparing against stable rounding/truncation)?
Rounding the hit time for replays should be correct, but I’m not entirely sure what that implies. Are these rounded hit times used for replay playback of plays set on lazer/stable or only for .osr exports?
I haven’t been able to find anything of the sort in the stable reference code.
In stable, taiko misses are given here, the comparison for which (<=) can be seen here and here (variable name for the “miss window” is a bit misleading). Greats and Ok’s are compared with <. There’s no difference between regular and large taiko notes from what I can tell.
Both stable and lazer use absolute hit error, so it’s always comparing two positive values. There’s no special case to consider here, especially since hit times are rounded in stable anyway.
https://github.com/ppy/osu/blob/d38316bf4f552629cf86dc5ccfe78c3f2fd95f27/osu.Game/Rulesets/Scoring/HitWindows.cs#L127-L138
Where
timeOffset
is the hit error (positive or negative, but made positive above). This is the same in stable (example).Rounding the value emulates the rounded-integer temporal granularity of stable. With that, the only difference for replays in lazer (with integer time values) is that they’re still compared against doubles, non-floored hit windows (as well as the
<
vs<=
thing). Using ceil and floor (instead of round) would only cause yet another difference between stable and lazer.This affects the lazer part of the examples I presented I suppose. I’ve updated them with lazer replay timings and also made it all into a table.
Well I thought through the implications of this… (see updated examples section) but I can’t convince myself that this would solve anything.