From afce976f086bc8aabe3b4d5f4ee6bcf63c91c5f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 12 Jan 2022 19:24:59 +0100 Subject: [PATCH] Fix oversubscription to `StartTimeBindable.ValueChanged` --- osu.Game/Rulesets/Objects/HitObject.cs | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/osu.Game/Rulesets/Objects/HitObject.cs b/osu.Game/Rulesets/Objects/HitObject.cs index 7d08261035..c590cc302f 100644 --- a/osu.Game/Rulesets/Objects/HitObject.cs +++ b/osu.Game/Rulesets/Objects/HitObject.cs @@ -136,10 +136,19 @@ namespace osu.Game.Rulesets.Objects foreach (var h in nestedHitObjects) h.ApplyDefaults(controlPointInfo, difficulty, cancellationToken); - // importantly, this callback is only registered after default application - // to ensure that the read of `this.GetEndTime()` within doesn't return an invalid value + // `ApplyDefaults()` may be called multiple times on a single hitobject. + // to prevent subscribing to `StartTimeBindable.ValueChanged` multiple times with the same callback, + // remove the previous subscription (if present) before (re-)registering. + StartTimeBindable.ValueChanged -= onStartTimeChanged; + + // this callback must be (re-)registered after default application + // to ensure that the read of `this.GetEndTime()` within `onStartTimeChanged` doesn't return an invalid value // if `StartTimeBindable` is changed prior to default application. - StartTimeBindable.ValueChanged += time => + StartTimeBindable.ValueChanged += onStartTimeChanged; + + DefaultsApplied?.Invoke(this); + + void onStartTimeChanged(ValueChangedEvent time) { double offset = time.NewValue - time.OldValue; @@ -148,9 +157,7 @@ namespace osu.Game.Rulesets.Objects DifficultyControlPoint.Time = time.NewValue; SampleControlPoint.Time = this.GetEndTime() + control_point_leniency; - }; - - DefaultsApplied?.Invoke(this); + } } protected virtual void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, IBeatmapDifficultyInfo difficulty)