-
-
Notifications
You must be signed in to change notification settings - Fork 2.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Remove LegacyDifficultyControlPoint
/ LegacyBpmMultiplier
#24738
Conversation
Pushed a few menial fixups but looks good to me otherwise. That said, I haven't paid this much attention to the investigation, so definitely will want a @smoogipoo confirm on this one. |
Tried to do sheets for this one.
The taiko changes are slightly worrying as I wouldn't expect anything to change there, but I have done zero investigation what that is so far. Could even be that I didn't set the generation up correctly on my machine, so reader beware. |
Fwiw, taiko is where I'd expect most of the differences to surface, because the drumroll conversion code relies on super precise errors. |
FWIW I'd expect there to be changes as I fixed several actual bugs. We'll need to confirm they are correct to our expectations as a result. |
Well, there is at least one breakage on the mania side when compared with stable. /b/2364269 with DT, 10K: 2364269.mp4Appears to be caused by minuscule fluctuations of 10,16c10,16
< osu.Game.Rulesets.Objects.Legacy.Osu.ConvertSlider @ 26279: DistanceObjectPatternGenerator calculated beatLength = 499.9999809265137
< osu.Game.Rulesets.Objects.Legacy.Osu.ConvertSlider @ 27779: DistanceObjectPatternGenerator calculated beatLength = 499.9999809265137
< osu.Game.Rulesets.Objects.Legacy.Osu.ConvertSlider @ 29279: DistanceObjectPatternGenerator calculated beatLength = 499.9999809265137
< osu.Game.Rulesets.Objects.Legacy.Osu.ConvertSlider @ 30029: DistanceObjectPatternGenerator calculated beatLength = 499.9999809265137
< osu.Game.Rulesets.Objects.Legacy.Osu.ConvertSlider @ 31341: DistanceObjectPatternGenerator calculated beatLength = 499.9999809265137
< osu.Game.Rulesets.Objects.Legacy.Osu.ConvertSlider @ 37904: DistanceObjectPatternGenerator calculated beatLength = 249.99999046325684
< osu.Game.Rulesets.Objects.Legacy.Osu.ConvertSlider @ 38091: DistanceObjectPatternGenerator calculated beatLength = 249.99999046325684
---
> osu.Game.Rulesets.Objects.Legacy.Osu.ConvertSlider @ 26279: DistanceObjectPatternGenerator calculated beatLength = 499.99998092651373
> osu.Game.Rulesets.Objects.Legacy.Osu.ConvertSlider @ 27779: DistanceObjectPatternGenerator calculated beatLength = 499.99998092651373
> osu.Game.Rulesets.Objects.Legacy.Osu.ConvertSlider @ 29279: DistanceObjectPatternGenerator calculated beatLength = 499.99998092651373
> osu.Game.Rulesets.Objects.Legacy.Osu.ConvertSlider @ 30029: DistanceObjectPatternGenerator calculated beatLength = 499.99998092651373
> osu.Game.Rulesets.Objects.Legacy.Osu.ConvertSlider @ 31341: DistanceObjectPatternGenerator calculated beatLength = 499.99998092651373
> osu.Game.Rulesets.Objects.Legacy.Osu.ConvertSlider @ 37904: DistanceObjectPatternGenerator calculated beatLength = 249.99999046325686
> osu.Game.Rulesets.Objects.Legacy.Osu.ConvertSlider @ 38091: DistanceObjectPatternGenerator calculated beatLength = 249.99999046325686
74,79c74,79
< osu.Game.Rulesets.Objects.Legacy.Osu.ConvertSlider @ 71279: DistanceObjectPatternGenerator calculated beatLength = 249.99999046325684
< osu.Game.Rulesets.Objects.Legacy.Osu.ConvertSlider @ 71654: DistanceObjectPatternGenerator calculated beatLength = 249.99999046325684
< osu.Game.Rulesets.Objects.Legacy.Osu.ConvertSlider @ 72029: DistanceObjectPatternGenerator calculated beatLength = 249.99999046325684
< osu.Game.Rulesets.Objects.Legacy.Osu.ConvertSlider @ 72404: DistanceObjectPatternGenerator calculated beatLength = 249.99999046325684
< osu.Game.Rulesets.Objects.Legacy.Osu.ConvertSlider @ 72779: DistanceObjectPatternGenerator calculated beatLength = 249.99999046325684
< osu.Game.Rulesets.Objects.Legacy.Osu.ConvertSlider @ 73904: DistanceObjectPatternGenerator calculated beatLength = 249.99999046325684
---
> osu.Game.Rulesets.Objects.Legacy.Osu.ConvertSlider @ 71279: DistanceObjectPatternGenerator calculated beatLength = 249.99999046325686
> osu.Game.Rulesets.Objects.Legacy.Osu.ConvertSlider @ 71654: DistanceObjectPatternGenerator calculated beatLength = 249.99999046325686
> osu.Game.Rulesets.Objects.Legacy.Osu.ConvertSlider @ 72029: DistanceObjectPatternGenerator calculated beatLength = 249.99999046325686
> osu.Game.Rulesets.Objects.Legacy.Osu.ConvertSlider @ 72404: DistanceObjectPatternGenerator calculated beatLength = 249.99999046325686
> osu.Game.Rulesets.Objects.Legacy.Osu.ConvertSlider @ 72779: DistanceObjectPatternGenerator calculated beatLength = 249.99999046325686
> osu.Game.Rulesets.Objects.Legacy.Osu.ConvertSlider @ 73904: DistanceObjectPatternGenerator calculated beatLength = 249.99999046325686
99,104c99,104
< osu.Game.Rulesets.Objects.Legacy.Osu.ConvertSlider @ 86279: DistanceObjectPatternGenerator calculated beatLength = 499.9999809265137
< osu.Game.Rulesets.Objects.Legacy.Osu.ConvertSlider @ 86841: DistanceObjectPatternGenerator calculated beatLength = 499.9999809265137
< osu.Game.Rulesets.Objects.Legacy.Osu.ConvertSlider @ 87779: DistanceObjectPatternGenerator calculated beatLength = 499.9999809265137
< osu.Game.Rulesets.Objects.Legacy.Osu.ConvertSlider @ 89279: DistanceObjectPatternGenerator calculated beatLength = 499.9999809265137
< osu.Game.Rulesets.Objects.Legacy.Osu.ConvertSlider @ 89841: DistanceObjectPatternGenerator calculated beatLength = 499.9999809265137
< osu.Game.Rulesets.Objects.Legacy.Osu.ConvertSlider @ 90779: DistanceObjectPatternGenerator calculated beatLength = 499.9999809265137
---
> osu.Game.Rulesets.Objects.Legacy.Osu.ConvertSlider @ 86279: DistanceObjectPatternGenerator calculated beatLength = 499.99998092651373
> osu.Game.Rulesets.Objects.Legacy.Osu.ConvertSlider @ 86841: DistanceObjectPatternGenerator calculated beatLength = 499.99998092651373
> osu.Game.Rulesets.Objects.Legacy.Osu.ConvertSlider @ 87779: DistanceObjectPatternGenerator calculated beatLength = 499.99998092651373
> osu.Game.Rulesets.Objects.Legacy.Osu.ConvertSlider @ 89279: DistanceObjectPatternGenerator calculated beatLength = 499.99998092651373
> osu.Game.Rulesets.Objects.Legacy.Osu.ConvertSlider @ 89841: DistanceObjectPatternGenerator calculated beatLength = 499.99998092651373
> osu.Game.Rulesets.Objects.Legacy.Osu.ConvertSlider @ 90779: DistanceObjectPatternGenerator calculated beatLength = 499.99998092651373
114,120c114,120
< osu.Game.Rulesets.Objects.Legacy.Osu.ConvertSlider @ 26279: DistanceObjectPatternGenerator calculated beatLength = 499.9999809265137
< osu.Game.Rulesets.Objects.Legacy.Osu.ConvertSlider @ 27779: DistanceObjectPatternGenerator calculated beatLength = 499.9999809265137
< osu.Game.Rulesets.Objects.Legacy.Osu.ConvertSlider @ 29279: DistanceObjectPatternGenerator calculated beatLength = 499.9999809265137
< osu.Game.Rulesets.Objects.Legacy.Osu.ConvertSlider @ 30029: DistanceObjectPatternGenerator calculated beatLength = 499.9999809265137
< osu.Game.Rulesets.Objects.Legacy.Osu.ConvertSlider @ 31341: DistanceObjectPatternGenerator calculated beatLength = 499.9999809265137
< osu.Game.Rulesets.Objects.Legacy.Osu.ConvertSlider @ 37904: DistanceObjectPatternGenerator calculated beatLength = 249.99999046325684
< osu.Game.Rulesets.Objects.Legacy.Osu.ConvertSlider @ 38091: DistanceObjectPatternGenerator calculated beatLength = 249.99999046325684
---
> osu.Game.Rulesets.Objects.Legacy.Osu.ConvertSlider @ 26279: DistanceObjectPatternGenerator calculated beatLength = 499.99998092651373
> osu.Game.Rulesets.Objects.Legacy.Osu.ConvertSlider @ 27779: DistanceObjectPatternGenerator calculated beatLength = 499.99998092651373
> osu.Game.Rulesets.Objects.Legacy.Osu.ConvertSlider @ 29279: DistanceObjectPatternGenerator calculated beatLength = 499.99998092651373
> osu.Game.Rulesets.Objects.Legacy.Osu.ConvertSlider @ 30029: DistanceObjectPatternGenerator calculated beatLength = 499.99998092651373
> osu.Game.Rulesets.Objects.Legacy.Osu.ConvertSlider @ 31341: DistanceObjectPatternGenerator calculated beatLength = 499.99998092651373
> osu.Game.Rulesets.Objects.Legacy.Osu.ConvertSlider @ 37904: DistanceObjectPatternGenerator calculated beatLength = 249.99999046325686
> osu.Game.Rulesets.Objects.Legacy.Osu.ConvertSlider @ 38091: DistanceObjectPatternGenerator calculated beatLength = 249.99999046325686
178,183c178,183
< osu.Game.Rulesets.Objects.Legacy.Osu.ConvertSlider @ 71279: DistanceObjectPatternGenerator calculated beatLength = 249.99999046325684
< osu.Game.Rulesets.Objects.Legacy.Osu.ConvertSlider @ 71654: DistanceObjectPatternGenerator calculated beatLength = 249.99999046325684
< osu.Game.Rulesets.Objects.Legacy.Osu.ConvertSlider @ 72029: DistanceObjectPatternGenerator calculated beatLength = 249.99999046325684
< osu.Game.Rulesets.Objects.Legacy.Osu.ConvertSlider @ 72404: DistanceObjectPatternGenerator calculated beatLength = 249.99999046325684
< osu.Game.Rulesets.Objects.Legacy.Osu.ConvertSlider @ 72779: DistanceObjectPatternGenerator calculated beatLength = 249.99999046325684
< osu.Game.Rulesets.Objects.Legacy.Osu.ConvertSlider @ 73904: DistanceObjectPatternGenerator calculated beatLength = 249.99999046325684
---
> osu.Game.Rulesets.Objects.Legacy.Osu.ConvertSlider @ 71279: DistanceObjectPatternGenerator calculated beatLength = 249.99999046325686
> osu.Game.Rulesets.Objects.Legacy.Osu.ConvertSlider @ 71654: DistanceObjectPatternGenerator calculated beatLength = 249.99999046325686
> osu.Game.Rulesets.Objects.Legacy.Osu.ConvertSlider @ 72029: DistanceObjectPatternGenerator calculated beatLength = 249.99999046325686
> osu.Game.Rulesets.Objects.Legacy.Osu.ConvertSlider @ 72404: DistanceObjectPatternGenerator calculated beatLength = 249.99999046325686
> osu.Game.Rulesets.Objects.Legacy.Osu.ConvertSlider @ 72779: DistanceObjectPatternGenerator calculated beatLength = 249.99999046325686
> osu.Game.Rulesets.Objects.Legacy.Osu.ConvertSlider @ 73904: DistanceObjectPatternGenerator calculated beatLength = 249.99999046325686
203,208c203,208
< osu.Game.Rulesets.Objects.Legacy.Osu.ConvertSlider @ 86279: DistanceObjectPatternGenerator calculated beatLength = 499.9999809265137
< osu.Game.Rulesets.Objects.Legacy.Osu.ConvertSlider @ 86841: DistanceObjectPatternGenerator calculated beatLength = 499.9999809265137
< osu.Game.Rulesets.Objects.Legacy.Osu.ConvertSlider @ 87779: DistanceObjectPatternGenerator calculated beatLength = 499.9999809265137
< osu.Game.Rulesets.Objects.Legacy.Osu.ConvertSlider @ 89279: DistanceObjectPatternGenerator calculated beatLength = 499.9999809265137
< osu.Game.Rulesets.Objects.Legacy.Osu.ConvertSlider @ 89841: DistanceObjectPatternGenerator calculated beatLength = 499.9999809265137
< osu.Game.Rulesets.Objects.Legacy.Osu.ConvertSlider @ 90779: DistanceObjectPatternGenerator calculated beatLength = 499.9999809265137
\ No newline at end of file
---
> osu.Game.Rulesets.Objects.Legacy.Osu.ConvertSlider @ 86279: DistanceObjectPatternGenerator calculated beatLength = 499.99998092651373
> osu.Game.Rulesets.Objects.Legacy.Osu.ConvertSlider @ 86841: DistanceObjectPatternGenerator calculated beatLength = 499.99998092651373
> osu.Game.Rulesets.Objects.Legacy.Osu.ConvertSlider @ 87779: DistanceObjectPatternGenerator calculated beatLength = 499.99998092651373
> osu.Game.Rulesets.Objects.Legacy.Osu.ConvertSlider @ 89279: DistanceObjectPatternGenerator calculated beatLength = 499.99998092651373
> osu.Game.Rulesets.Objects.Legacy.Osu.ConvertSlider @ 89841: DistanceObjectPatternGenerator calculated beatLength = 499.99998092651373
> osu.Game.Rulesets.Objects.Legacy.Osu.ConvertSlider @ 90779: DistanceObjectPatternGenerator calculated beatLength = 499.99998092651373
\ No newline at end of file The underlying cause is probably that taking the reciprocal of Haven't looked at the taiko cases yet, but I'm not sure doing so is worth the non-trivial effort anymore given that we know that this change technically breaks stuff now... (getting to the bottom of the above took me ~2hrs already). |
Basically matching the old code more closely to avoid too much precision from doing math in a slightly different way.
I've adjusted things to the point it looks to match your output (to within I think we can probably use this as a way to test github actions diffcalc runs, so hold off on the manual run until I give that a try (@smoogipoo has been working on updating the action and I'll re-deploy my local runner). |
Map mentioned above looks to be back to the reference state now. As discussed elsewhere, going to re-run the sheets overnight to verify. |
switch (rulesetShortName) | ||
{ | ||
case "taiko": | ||
case "mania": | ||
bpmMultiplier = sliderVelocityAsBeatLength < 0 ? Math.Clamp((float)-sliderVelocityAsBeatLength, 10, 10000) / 100.0 : 1; | ||
break; | ||
|
||
default: | ||
bpmMultiplier = sliderVelocityAsBeatLength < 0 ? Math.Clamp((float)-sliderVelocityAsBeatLength, 10, 1000) / 100.0 : 1; | ||
break; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this is intended as a method for all rulesets to use, do you think we should instead specify explicit osu and catch branches and leave the default
branch as simple/lossless as possible?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Dunno. It may be sunk cost fallacy from all the overnight sheet running but I tend to think that this is okay. The fact that we're using legacy .osu
format and encoding slider velocity into files in one of the most opaque ways I can imagine also contributes to this feeling - like if we were redoing this to be simpler, then we'd redefine the actual way the thing is stored to not require the weird division by 100.
Can wait until further work in my opinion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I mean it from a different angle - this method is essentially a legacy method that pollutes an interface intended for any ruleset to use.
I can imagine any ruleset consumer would use this as a shortcut for timingPoint.BeatLength * SliderVelocityMultiplier
without understanding the consequences of this - the precision and the weird clamping behaviour.
How do you explain the existence and purpose of this method to a ruleset consumer? I believe it should be more properly indicated because the xmldoc doesn't do it justice.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see.
For one, it has to be public API, so that's that (unless we expose osu.Game
internals to rulesets, but I'd rather not), but what'd work for you? More xmldoc? Moving to extension method? Marking as deprecated with inline disables?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've moved it to an extension method. I've added more xmldoc, and I've added a throw
on non-legacy use.
/// Introduces floating-point errors to post-multiplied beat length for legacy rulesets that depend on it. | ||
/// You should definitely not use this unless you know exactly what you're doing. | ||
/// </summary> | ||
public static double GetPrecisionAdjustedBeatLength(this IHasSliderVelocity hasSliderVelocity, TimingControlPoint timingControlPoint, string rulesetShortName) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still don't like this being an extension method. We've gone from:
I don't see a reason why rulesets wouldn't use this method, and it'll cause precision loss
To:
I don't see a reason why rulesets wouldn't use this method, but it'll crash if they do
My suggestion was very explicit: have this return BeatLength * SliderVelocity
for non-legacy rulesets. A second option I would be okay with is this being an explicit static class call LegacyRulesetExtensions.Get...()
.
I don't know. I don't think I'm being as unreasonable here as I'm being brought to believe. I defer to a 3rd opinion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would be okay with is this being an explicit static class call
LegacyRulesetExtensions.Get...()
.
This sounds like a reasonable middle ground... @peppy?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Whatever works. I'm fine with any implementation as long as the method name remains intact, and this only works for legacy rulesets.
We discussed IRL and my rationale against making this work for every ruleset is that it would hide the floating point precision hacks in a seemingly benignly named method. Which is not great for readability and would need inline comments at each usage to make sense (which doesn't make much sense).
Here's the breaking change in the IScrollingInfo: ppy/osu#24550 And should remove the LegacyBpmMultiplier: ppy/osu#24738
As pointed out in discussion, the flow of data surrounding these legacy pieces is ugly. It only got worse after difficulty data has been moved to hit objects themselves (with the worst code smell being
LegacyBpmMultiplier
existing inHitObject
.This PR removes all of this, instead locally computing the required precision-adjusted values where necessary. It also moves the still-required
GenerateTicks
toDifficultyControlPoint
itself (we basically need to know if the BPM at point-in-time wasNaN
, but don't want to convey that inTimingControlPoint
, so this is required to exist somewhere).To make this work, I've also removed the precision limitations of
SliderVelocityMultiplier
. This doesn't seem to have an adverse effect on the editor luckily, so should play quite well.This PR fixes a few issues which seemingly went silently unnoticed:
SliderVelocityMultiplier
precision was not transferred toHitObject
when the value is transferredLegacyBpmMultiplier
was not being transferred at all inOsuBeatmapConverter
, so it was not being used anywhere (potentially affected difficulty calculation and gameplay)I haven't added test coverage for these two cases because I'm not sure where to start for that.