From 39d37c4779b82c1069a1cc89b4d0342ceb34b686 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 12 Nov 2020 15:24:45 +0900 Subject: [PATCH 01/19] Add support for nested hitobject pooling --- .../Objects/Drawables/DrawableHitObject.cs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 244cf831c3..2dba83f2be 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -19,6 +19,7 @@ using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Scoring; using osu.Game.Skinning; using osu.Game.Configuration; +using osu.Game.Rulesets.UI; using osuTK.Graphics; namespace osu.Game.Rulesets.Objects.Drawables @@ -126,6 +127,9 @@ namespace osu.Game.Rulesets.Objects.Drawables [CanBeNull] private HitObjectLifetimeEntry lifetimeEntry; + [Resolved(CanBeNull = true)] + private DrawableRuleset drawableRuleset { get; set; } + /// /// Creates a new . /// @@ -195,7 +199,9 @@ namespace osu.Game.Rulesets.Objects.Drawables foreach (var h in HitObject.NestedHitObjects) { - var drawableNested = CreateNestedHitObject(h) ?? throw new InvalidOperationException($"{nameof(CreateNestedHitObject)} returned null for {h.GetType().ReadableName()}."); + var drawableNested = drawableRuleset?.GetPooledDrawableRepresentation(h) + ?? CreateNestedHitObject(h) + ?? throw new InvalidOperationException($"{nameof(CreateNestedHitObject)} returned null for {h.GetType().ReadableName()}."); drawableNested.OnNewResult += onNewResult; drawableNested.OnRevertResult += onRevertResult; @@ -203,6 +209,8 @@ namespace osu.Game.Rulesets.Objects.Drawables nestedHitObjects.Value.Add(drawableNested); AddNestedHitObject(drawableNested); + + drawableNested.OnParentReceived(this); } StartTimeBindable.BindTo(HitObject.StartTimeBindable); @@ -291,6 +299,14 @@ namespace osu.Game.Rulesets.Objects.Drawables { } + /// + /// Invoked when this receives a new parenting . + /// + /// The parenting . + protected virtual void OnParentReceived(DrawableHitObject parent) + { + } + /// /// Invoked by the base to populate samples, once on initial load and potentially again on any change to the samples collection. /// From bf72961959c081ca9254b4183cf19e85a89c845c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 11 Nov 2020 00:22:06 +0900 Subject: [PATCH 02/19] Add top-level osu! hitobject pooling --- .../Objects/Drawables/DrawableHitCircle.cs | 5 ++ .../Objects/Drawables/DrawableSlider.cs | 5 ++ .../Objects/Drawables/DrawableSpinner.cs | 5 ++ .../UI/DrawableOsuRuleset.cs | 48 ++++++++++------- osu.Game.Rulesets.Osu/UI/OsuDrawablePool.cs | 33 ++++++++++++ osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs | 54 ++++++++++--------- 6 files changed, 108 insertions(+), 42 deletions(-) create mode 100644 osu.Game.Rulesets.Osu/UI/OsuDrawablePool.cs diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs index 77d24db084..2e63160d36 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs @@ -31,6 +31,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables private Container scaleContainer; private InputManager inputManager; + public DrawableHitCircle() + : this(null) + { + } + public DrawableHitCircle([CanBeNull] HitCircle h = null) : base(h) { diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index 3f91a31066..05e4587307 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -41,6 +41,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables private Container tickContainer; private Container repeatContainer; + public DrawableSlider() + : this(null) + { + } + public DrawableSlider([CanBeNull] Slider s = null) : base(s) { diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs index eb125969b0..f04a914fe3 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs @@ -33,6 +33,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables private Bindable isSpinning; private bool spinnerFrequencyModulate; + public DrawableSpinner() + : this(null) + { + } + public DrawableSpinner([CanBeNull] Spinner s = null) : base(s) { diff --git a/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs b/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs index b2299398e1..1c119e2e95 100644 --- a/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs @@ -4,12 +4,14 @@ using System; using System.Collections.Generic; using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Graphics.Pooling; using osu.Framework.Input; using osu.Game.Beatmaps; using osu.Game.Input.Handlers; using osu.Game.Replays; using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Configuration; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; @@ -24,11 +26,28 @@ namespace osu.Game.Rulesets.Osu.UI { protected new OsuRulesetConfigManager Config => (OsuRulesetConfigManager)base.Config; + public new OsuPlayfield Playfield => (OsuPlayfield)base.Playfield; + public DrawableOsuRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods = null) : base(ruleset, beatmap, mods) { } + protected override bool PoolHitObjects => true; + + [BackgroundDependencyLoader] + private void load() + { + RegisterPool(10, 100); + RegisterPool(10, 100); + RegisterPool(2, 20); + } + + protected override DrawablePool CreatePool(int initialSize, int? maximumSize = null) + => new OsuDrawablePool(Playfield.CheckHittable, Playfield.OnHitObjectLoaded, initialSize, maximumSize); + + protected override HitObjectLifetimeEntry CreateLifetimeEntry(OsuHitObject hitObject) => new OsuHitObjectLifetimeEntry(hitObject); + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; // always show the gameplay cursor protected override Playfield CreatePlayfield() => new OsuPlayfield(); @@ -39,23 +58,6 @@ namespace osu.Game.Rulesets.Osu.UI protected override ResumeOverlay CreateResumeOverlay() => new OsuResumeOverlay(); - public override DrawableHitObject CreateDrawableRepresentation(OsuHitObject h) - { - switch (h) - { - case HitCircle circle: - return new DrawableHitCircle(circle); - - case Slider slider: - return new DrawableSlider(slider); - - case Spinner spinner: - return new DrawableSpinner(spinner); - } - - return null; - } - protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new OsuFramedReplayInputHandler(replay); protected override ReplayRecorder CreateReplayRecorder(Replay replay) => new OsuReplayRecorder(replay); @@ -70,5 +72,15 @@ namespace osu.Game.Rulesets.Osu.UI return 0; } } + + private class OsuHitObjectLifetimeEntry : HitObjectLifetimeEntry + { + public OsuHitObjectLifetimeEntry(HitObject hitObject) + : base(hitObject) + { + } + + protected override double InitialLifetimeOffset => ((OsuHitObject)HitObject).TimePreempt; + } } } diff --git a/osu.Game.Rulesets.Osu/UI/OsuDrawablePool.cs b/osu.Game.Rulesets.Osu/UI/OsuDrawablePool.cs new file mode 100644 index 0000000000..148146f25a --- /dev/null +++ b/osu.Game.Rulesets.Osu/UI/OsuDrawablePool.cs @@ -0,0 +1,33 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Pooling; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Osu.Objects.Drawables; + +namespace osu.Game.Rulesets.Osu.UI +{ + public class OsuDrawablePool : DrawablePool + where T : DrawableHitObject, new() + { + private readonly Func checkHittable; + private readonly Action onLoaded; + + public OsuDrawablePool(Func checkHittable, Action onLoaded, int initialSize, int? maximumSize = null) + : base(initialSize, maximumSize) + { + this.checkHittable = checkHittable; + this.onLoaded = onLoaded; + } + + protected override T CreateNewDrawable() => base.CreateNewDrawable().With(o => + { + var osuObject = (DrawableOsuHitObject)(object)o; + + osuObject.CheckHittable = checkHittable; + osuObject.OnLoadComplete += onLoaded; + }); + } +} diff --git a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs index 321eeeab65..5d59a6ff38 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs @@ -13,6 +13,7 @@ using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Configuration; +using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects.Drawables.Connections; using osu.Game.Rulesets.Osu.Scoring; @@ -26,6 +27,8 @@ namespace osu.Game.Rulesets.Osu.UI { public class OsuPlayfield : Playfield { + public readonly Func CheckHittable; + private readonly PlayfieldBorder playfieldBorder; private readonly ProxyContainer approachCircles; private readonly ProxyContainer spinnerProxies; @@ -78,6 +81,7 @@ namespace osu.Game.Rulesets.Osu.UI }; hitPolicy = new OrderedHitPolicy(HitObjectContainer); + CheckHittable = hitPolicy.IsHittable; var hitWindows = new OsuHitWindows(); @@ -85,6 +89,8 @@ namespace osu.Game.Rulesets.Osu.UI poolDictionary.Add(result, new DrawableJudgementPool(result)); AddRangeInternal(poolDictionary.Values); + + NewResult += onNewResult; } [BackgroundDependencyLoader(true)] @@ -93,37 +99,37 @@ namespace osu.Game.Rulesets.Osu.UI config?.BindWith(OsuRulesetSetting.PlayfieldBorderStyle, playfieldBorder.PlayfieldBorderStyle); } - public override void Add(DrawableHitObject h) + protected override void OnHitObjectAdded(HitObject hitObject) { - DrawableOsuHitObject osuHitObject = (DrawableOsuHitObject)h; - - h.OnNewResult += onNewResult; - h.OnLoadComplete += d => - { - if (d is DrawableSpinner) - spinnerProxies.Add(d.CreateProxy()); - - if (d is IDrawableHitObjectWithProxiedApproach c) - approachCircles.Add(c.ProxiedLayer.CreateProxy()); - }; - - base.Add(h); - - osuHitObject.CheckHittable = hitPolicy.IsHittable; - - followPoints.AddFollowPoints(osuHitObject.HitObject); + base.OnHitObjectAdded(hitObject); + followPoints.AddFollowPoints((OsuHitObject)hitObject); } - public override bool Remove(DrawableHitObject h) + protected override void OnHitObjectRemoved(HitObject hitObject) { - DrawableOsuHitObject osuHitObject = (DrawableOsuHitObject)h; + base.OnHitObjectRemoved(hitObject); + followPoints.RemoveFollowPoints((OsuHitObject)hitObject); + } - bool result = base.Remove(h); + public void OnHitObjectLoaded(Drawable drawable) + { + switch (drawable) + { + case DrawableSliderHead _: + case DrawableSliderTail _: + case DrawableSliderTick _: + case DrawableSliderRepeat _: + case DrawableSpinnerTick _: + break; - if (result) - followPoints.RemoveFollowPoints(osuHitObject.HitObject); + case DrawableSpinner _: + spinnerProxies.Add(drawable.CreateProxy()); + break; - return result; + case IDrawableHitObjectWithProxiedApproach approach: + approachCircles.Add(approach.ProxiedLayer.CreateProxy()); + break; + } } private void onNewResult(DrawableHitObject judgedObject, JudgementResult result) From 1ea526b5ef72ac020ecc3fd3d589cbd23c2e9ff3 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 12 Nov 2020 14:19:26 +0900 Subject: [PATCH 03/19] Adjust pooling implementation with branch changes --- osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs b/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs index 1c119e2e95..86c7305439 100644 --- a/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs @@ -12,6 +12,7 @@ using osu.Game.Input.Handlers; using osu.Game.Replays; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Configuration; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; @@ -33,17 +34,21 @@ namespace osu.Game.Rulesets.Osu.UI { } - protected override bool PoolHitObjects => true; - [BackgroundDependencyLoader] private void load() { - RegisterPool(10, 100); - RegisterPool(10, 100); - RegisterPool(2, 20); + registerPool(10, 100); + registerPool(10, 100); + registerPool(2, 20); } - protected override DrawablePool CreatePool(int initialSize, int? maximumSize = null) + private void registerPool(int initialSize, int? maximumSize = null) + where TObject : HitObject + where TDrawable : DrawableHitObject, new() + => RegisterPool(CreatePool(initialSize, maximumSize)); + + protected virtual DrawablePool CreatePool(int initialSize, int? maximumSize = null) + where TDrawable : DrawableHitObject, new() => new OsuDrawablePool(Playfield.CheckHittable, Playfield.OnHitObjectLoaded, initialSize, maximumSize); protected override HitObjectLifetimeEntry CreateLifetimeEntry(OsuHitObject hitObject) => new OsuHitObjectLifetimeEntry(hitObject); From 3f78d81386db5deb57d42f55dcd841ead8d68a4c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 12 Nov 2020 15:59:48 +0900 Subject: [PATCH 04/19] Add nested osu! hitobject pooling --- .../Objects/Drawables/DrawableOsuHitObject.cs | 2 +- .../Objects/Drawables/DrawableSlider.cs | 18 +++----- .../Objects/Drawables/DrawableSliderHead.cs | 44 +++++++++++++++---- .../Objects/Drawables/DrawableSliderRepeat.cs | 31 +++++++++---- .../Objects/Drawables/DrawableSliderTail.cs | 12 +++-- .../Objects/Drawables/DrawableSliderTick.cs | 14 +++++- .../Objects/Drawables/DrawableSpinner.cs | 2 +- .../Drawables/DrawableSpinnerBonusTick.cs | 5 +++ .../Objects/Drawables/DrawableSpinnerTick.cs | 5 +++ .../UI/DrawableOsuRuleset.cs | 8 ++++ 10 files changed, 106 insertions(+), 35 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs index d17bf93fa0..bcaf73d34f 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs @@ -83,7 +83,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables private OsuInputManager osuActionInputManager; internal OsuInputManager OsuActionInputManager => osuActionInputManager ??= GetContainingInputManager() as OsuInputManager; - protected virtual void Shake(double maximumLength) => shakeContainer.Shake(maximumLength); + public virtual void Shake(double maximumLength) => shakeContainer.Shake(maximumLength); protected override void UpdateInitialTransforms() { diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index 05e4587307..5bbdc5ee7b 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -164,10 +164,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { base.ClearNestedHitObjects(); - headContainer.Clear(); - tailContainer.Clear(); - repeatContainer.Clear(); - tickContainer.Clear(); + headContainer.Clear(false); + tailContainer.Clear(false); + repeatContainer.Clear(false); + tickContainer.Clear(false); } protected override DrawableHitObject CreateNestedHitObject(HitObject hitObject) @@ -178,17 +178,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables return new DrawableSliderTail(tail); case SliderHeadCircle head: - return new DrawableSliderHead(HitObject, head) - { - OnShake = Shake, - CheckHittable = (d, t) => CheckHittable?.Invoke(d, t) ?? true - }; + return new DrawableSliderHead(head); case SliderTick tick: - return new DrawableSliderTick(tick) { Position = tick.Position - HitObject.Position }; + return new DrawableSliderTick(tick); case SliderRepeat repeat: - return new DrawableSliderRepeat(repeat, this) { Position = repeat.Position - HitObject.Position }; + return new DrawableSliderRepeat(repeat); } return base.CreateNestedHitObject(hitObject); diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs index 49ed9f12e3..fd0f35d20d 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs @@ -4,6 +4,8 @@ using System; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Types; namespace osu.Game.Rulesets.Osu.Objects.Drawables @@ -14,21 +16,43 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables protected override OsuSkinComponents CirclePieceComponent => OsuSkinComponents.SliderHeadHitCircle; - private readonly Slider slider; + private DrawableSlider drawableSlider; - public DrawableSliderHead(Slider slider, SliderHeadCircle h) + private Slider slider => drawableSlider?.HitObject; + + public DrawableSliderHead() + { + } + + public DrawableSliderHead(SliderHeadCircle h) : base(h) { - this.slider = slider; } [BackgroundDependencyLoader] private void load() { - pathVersion.BindTo(slider.Path.Version); - PositionBindable.BindValueChanged(_ => updatePosition()); - pathVersion.BindValueChanged(_ => updatePosition(), true); + pathVersion.BindValueChanged(_ => updatePosition()); + } + + protected override void OnFree(HitObject hitObject) + { + base.OnFree(hitObject); + + pathVersion.UnbindFrom(drawableSlider.PathVersion); + } + + protected override void OnParentReceived(DrawableHitObject parent) + { + base.OnParentReceived(parent); + + drawableSlider = (DrawableSlider)parent; + + pathVersion.BindTo(drawableSlider.PathVersion); + + OnShake = drawableSlider.Shake; + CheckHittable = (d, t) => drawableSlider.CheckHittable?.Invoke(d, t) ?? true; } protected override void Update() @@ -44,8 +68,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables public Action OnShake; - protected override void Shake(double maximumLength) => OnShake?.Invoke(maximumLength); + public override void Shake(double maximumLength) => OnShake?.Invoke(maximumLength); - private void updatePosition() => Position = HitObject.Position - slider.Position; + private void updatePosition() + { + if (slider != null) + Position = HitObject.Position - slider.Position; + } } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderRepeat.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderRepeat.cs index 9c382bd0a7..0735d48ae1 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderRepeat.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderRepeat.cs @@ -16,8 +16,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { public class DrawableSliderRepeat : DrawableOsuHitObject, ITrackSnaking { - private readonly SliderRepeat sliderRepeat; - private readonly DrawableSlider drawableSlider; + public new SliderRepeat HitObject => (SliderRepeat)base.HitObject; private double animDuration; @@ -27,11 +26,16 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables public override bool DisplayResult => false; - public DrawableSliderRepeat(SliderRepeat sliderRepeat, DrawableSlider drawableSlider) + private DrawableSlider drawableSlider; + + public DrawableSliderRepeat() + : base(null) + { + } + + public DrawableSliderRepeat(SliderRepeat sliderRepeat) : base(sliderRepeat) { - this.sliderRepeat = sliderRepeat; - this.drawableSlider = drawableSlider; } [BackgroundDependencyLoader] @@ -53,18 +57,27 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables } }; - ScaleBindable.BindValueChanged(scale => scaleContainer.Scale = new Vector2(scale.NewValue), true); + ScaleBindable.BindValueChanged(scale => scaleContainer.Scale = new Vector2(scale.NewValue)); + } + + protected override void OnParentReceived(DrawableHitObject parent) + { + base.OnParentReceived(parent); + + drawableSlider = (DrawableSlider)parent; + + Position = HitObject.Position - drawableSlider.Position; } protected override void CheckForResult(bool userTriggered, double timeOffset) { - if (sliderRepeat.StartTime <= Time.Current) + if (HitObject.StartTime <= Time.Current) ApplyResult(r => r.Type = drawableSlider.Tracking.Value ? r.Judgement.MaxResult : r.Judgement.MinResult); } protected override void UpdateInitialTransforms() { - animDuration = Math.Min(300, sliderRepeat.SpanDuration); + animDuration = Math.Min(300, HitObject.SpanDuration); this.Animate( d => d.FadeIn(animDuration), @@ -100,7 +113,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables // When the repeat is hit, the arrow should fade out on spot rather than following the slider if (IsHit) return; - bool isRepeatAtEnd = sliderRepeat.RepeatIndex % 2 == 0; + bool isRepeatAtEnd = HitObject.RepeatIndex % 2 == 0; List curve = ((PlaySliderBody)drawableSlider.Body.Drawable).CurrentCurve; Position = isRepeatAtEnd ? end : start; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs index 3be5983c57..eff72168ee 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs @@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { public class DrawableSliderTail : DrawableOsuHitObject, IRequireTracking, ITrackSnaking { - private readonly SliderTailCircle tailCircle; + public new SliderTailCircle HitObject => (SliderTailCircle)base.HitObject; /// /// The judgement text is provided by the . @@ -25,10 +25,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables private SkinnableDrawable circlePiece; private Container scaleContainer; + public DrawableSliderTail() + : base(null) + { + } + public DrawableSliderTail(SliderTailCircle tailCircle) : base(tailCircle) { - this.tailCircle = tailCircle; } [BackgroundDependencyLoader] @@ -52,7 +56,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables }, }; - ScaleBindable.BindValueChanged(scale => scaleContainer.Scale = new Vector2(scale.NewValue), true); + ScaleBindable.BindValueChanged(scale => scaleContainer.Scale = new Vector2(scale.NewValue)); } protected override void UpdateInitialTransforms() @@ -92,6 +96,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables } public void UpdateSnakingPosition(Vector2 start, Vector2 end) => - Position = tailCircle.RepeatIndex % 2 == 0 ? end : start; + Position = HitObject.RepeatIndex % 2 == 0 ? end : start; } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs index 2af51ea486..faccf5d4d1 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs @@ -24,6 +24,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables private SkinnableDrawable scaleContainer; + public DrawableSliderTick() + : base(null) + { + } + public DrawableSliderTick(SliderTick sliderTick) : base(sliderTick) { @@ -54,7 +59,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Origin = Anchor.Centre, }; - ScaleBindable.BindValueChanged(scale => scaleContainer.Scale = new Vector2(scale.NewValue), true); + ScaleBindable.BindValueChanged(scale => scaleContainer.Scale = new Vector2(scale.NewValue)); + } + + protected override void OnParentReceived(DrawableHitObject parent) + { + base.OnParentReceived(parent); + + Position = HitObject.Position - ((DrawableSlider)parent).HitObject.Position; } protected override void CheckForResult(bool userTriggered, double timeOffset) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs index f04a914fe3..6b33517c33 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs @@ -160,7 +160,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables protected override void ClearNestedHitObjects() { base.ClearNestedHitObjects(); - ticks.Clear(); + ticks.Clear(false); } protected override DrawableHitObject CreateNestedHitObject(HitObject hitObject) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinnerBonusTick.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinnerBonusTick.cs index 2e1c07c4c6..ffeb14b0a8 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinnerBonusTick.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinnerBonusTick.cs @@ -5,6 +5,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { public class DrawableSpinnerBonusTick : DrawableSpinnerTick { + public DrawableSpinnerBonusTick() + : base(null) + { + } + public DrawableSpinnerBonusTick(SpinnerBonusTick spinnerTick) : base(spinnerTick) { diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinnerTick.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinnerTick.cs index e9cede1398..fc9a7c00e6 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinnerTick.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinnerTick.cs @@ -7,6 +7,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { public override bool DisplayResult => false; + public DrawableSpinnerTick() + : base(null) + { + } + public DrawableSpinnerTick(SpinnerTick spinnerTick) : base(spinnerTick) { diff --git a/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs b/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs index 86c7305439..c89f138bcd 100644 --- a/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs @@ -38,8 +38,16 @@ namespace osu.Game.Rulesets.Osu.UI private void load() { registerPool(10, 100); + registerPool(10, 100); + registerPool(10, 100); + registerPool(10, 100); + registerPool(10, 100); + registerPool(5, 50); + registerPool(2, 20); + registerPool(10, 100); + registerPool(10, 100); } private void registerPool(int initialSize, int? maximumSize = null) From 3f0a1271966dd84047f1ddbbbae18b7098d637be Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 12 Nov 2020 18:51:58 +0900 Subject: [PATCH 05/19] Fix slider/spinner samples not being disposed --- osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs | 6 ++++-- .../Objects/Drawables/DrawableSpinner.cs | 8 +++++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index 5bbdc5ee7b..04fc755da5 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -40,6 +40,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables private Container tailContainer; private Container tickContainer; private Container repeatContainer; + private Container samplesContainer; public DrawableSlider() : this(null) @@ -68,6 +69,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Alpha = 0 }, headContainer = new Container { RelativeSizeAxes = Axes.Both }, + samplesContainer = new Container { RelativeSizeAxes = Axes.Both } }; PositionBindable.BindValueChanged(_ => Position = HitObject.StackedPosition); @@ -105,7 +107,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { base.LoadSamples(); - slidingSample?.Expire(); + samplesContainer.Clear(); slidingSample = null; var firstSample = HitObject.Samples.FirstOrDefault(); @@ -115,7 +117,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables var clone = HitObject.SampleControlPoint.ApplyTo(firstSample); clone.Name = "sliderslide"; - AddInternal(slidingSample = new PausableSkinnableSound(clone) + samplesContainer.Add(slidingSample = new PausableSkinnableSound(clone) { Looping = true }); diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs index 6b33517c33..824b8806e5 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs @@ -29,6 +29,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables private Container ticks; private SpinnerBonusDisplay bonusDisplay; + private Container samplesContainer; private Bindable isSpinning; private bool spinnerFrequencyModulate; @@ -75,7 +76,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Anchor = Anchor.Centre, Origin = Anchor.Centre, Y = -120, - } + }, + samplesContainer = new Container { RelativeSizeAxes = Axes.Both } }; PositionBindable.BindValueChanged(pos => Position = pos.NewValue); @@ -97,7 +99,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { base.LoadSamples(); - spinningSample?.Expire(); + samplesContainer.Clear(); spinningSample = null; var firstSample = HitObject.Samples.FirstOrDefault(); @@ -107,7 +109,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables var clone = HitObject.SampleControlPoint.ApplyTo(firstSample); clone.Name = "spinnerspin"; - AddInternal(spinningSample = new PausableSkinnableSound(clone) + samplesContainer.Add(spinningSample = new PausableSkinnableSound(clone) { Volume = { Value = 0 }, Looping = true, From beb6bbd2a14509f39bd5235bb8a7fb7583e51271 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 13 Nov 2020 14:58:32 +0900 Subject: [PATCH 06/19] Implement now abstract method --- osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs b/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs index c89f138bcd..1d16c47818 100644 --- a/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs @@ -59,6 +59,8 @@ namespace osu.Game.Rulesets.Osu.UI where TDrawable : DrawableHitObject, new() => new OsuDrawablePool(Playfield.CheckHittable, Playfield.OnHitObjectLoaded, initialSize, maximumSize); + public override DrawableHitObject CreateDrawableRepresentation(OsuHitObject h) => null; + protected override HitObjectLifetimeEntry CreateLifetimeEntry(OsuHitObject hitObject) => new OsuHitObjectLifetimeEntry(hitObject); public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; // always show the gameplay cursor From f7f70d41dfb73719b99aadcaa592684621031687 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 13 Nov 2020 17:28:14 +0900 Subject: [PATCH 07/19] Add osu! editor pooling support --- .../Edit/DrawableOsuEditRuleset.cs | 51 +-------------- .../UI/OsuEditDrawablePool.cs | 63 +++++++++++++++++++ 2 files changed, 66 insertions(+), 48 deletions(-) create mode 100644 osu.Game.Rulesets.Osu/UI/OsuEditDrawablePool.cs diff --git a/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditRuleset.cs b/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditRuleset.cs index 746ff4ac19..05396ebc8b 100644 --- a/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditRuleset.cs +++ b/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditRuleset.cs @@ -2,13 +2,9 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; -using System.Linq; -using osu.Framework.Graphics; +using osu.Framework.Graphics.Pooling; using osu.Game.Beatmaps; using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Osu.Objects; -using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.Osu.UI; using osu.Game.Rulesets.UI; using osuTK; @@ -17,54 +13,13 @@ namespace osu.Game.Rulesets.Osu.Edit { public class DrawableOsuEditRuleset : DrawableOsuRuleset { - /// - /// Hit objects are intentionally made to fade out at a constant slower rate than in gameplay. - /// This allows a mapper to gain better historical context and use recent hitobjects as reference / snap points. - /// - private const double editor_hit_object_fade_out_extension = 700; - public DrawableOsuEditRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods) : base(ruleset, beatmap, mods) { } - public override DrawableHitObject CreateDrawableRepresentation(OsuHitObject h) - => base.CreateDrawableRepresentation(h)?.With(d => d.ApplyCustomUpdateState += updateState); - - private void updateState(DrawableHitObject hitObject, ArmedState state) - { - if (state == ArmedState.Idle) - return; - - // adjust the visuals of certain object types to make them stay on screen for longer than usual. - switch (hitObject) - { - default: - // there are quite a few drawable hit types we don't want to extent (spinners, ticks etc.) - return; - - case DrawableSlider _: - // no specifics to sliders but let them fade slower below. - break; - - case DrawableHitCircle circle: // also handles slider heads - circle.ApproachCircle - .FadeOutFromOne(editor_hit_object_fade_out_extension) - .Expire(); - break; - } - - // Get the existing fade out transform - var existing = hitObject.Transforms.LastOrDefault(t => t.TargetMember == nameof(Alpha)); - - if (existing == null) - return; - - hitObject.RemoveTransform(existing); - - using (hitObject.BeginAbsoluteSequence(existing.StartTime)) - hitObject.FadeOut(editor_hit_object_fade_out_extension).Expire(); - } + protected override DrawablePool CreatePool(int initialSize, int? maximumSize = null) + => new OsuEditDrawablePool(Playfield.CheckHittable, Playfield.OnHitObjectLoaded, initialSize, maximumSize); protected override Playfield CreatePlayfield() => new OsuPlayfieldNoCursor(); diff --git a/osu.Game.Rulesets.Osu/UI/OsuEditDrawablePool.cs b/osu.Game.Rulesets.Osu/UI/OsuEditDrawablePool.cs new file mode 100644 index 0000000000..822ff076f5 --- /dev/null +++ b/osu.Game.Rulesets.Osu/UI/OsuEditDrawablePool.cs @@ -0,0 +1,63 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Linq; +using osu.Framework.Graphics; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Osu.Objects.Drawables; + +namespace osu.Game.Rulesets.Osu.UI +{ + public class OsuEditDrawablePool : OsuDrawablePool + where T : DrawableHitObject, new() + { + /// + /// Hit objects are intentionally made to fade out at a constant slower rate than in gameplay. + /// This allows a mapper to gain better historical context and use recent hitobjects as reference / snap points. + /// + private const double editor_hit_object_fade_out_extension = 700; + + public OsuEditDrawablePool(Func checkHittable, Action onLoaded, int initialSize, int? maximumSize = null) + : base(checkHittable, onLoaded, initialSize, maximumSize) + { + } + + protected override T CreateNewDrawable() => base.CreateNewDrawable().With(d => d.ApplyCustomUpdateState += updateState); + + private void updateState(DrawableHitObject hitObject, ArmedState state) + { + if (state == ArmedState.Idle) + return; + + // adjust the visuals of certain object types to make them stay on screen for longer than usual. + switch (hitObject) + { + default: + // there are quite a few drawable hit types we don't want to extent (spinners, ticks etc.) + return; + + case DrawableSlider _: + // no specifics to sliders but let them fade slower below. + break; + + case DrawableHitCircle circle: // also handles slider heads + circle.ApproachCircle + .FadeOutFromOne(editor_hit_object_fade_out_extension) + .Expire(); + break; + } + + // Get the existing fade out transform + var existing = hitObject.Transforms.LastOrDefault(t => t.TargetMember == nameof(Alpha)); + + if (existing == null) + return; + + hitObject.RemoveTransform(existing); + + using (hitObject.BeginAbsoluteSequence(existing.StartTime)) + hitObject.FadeOut(editor_hit_object_fade_out_extension).Expire(); + } + } +} From 832d52a05678b723aed22832bfc31cb5a77e6c8a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 13 Nov 2020 18:19:07 +0900 Subject: [PATCH 08/19] Fix hitobject sample tests --- osu.Game.Tests/Gameplay/TestSceneHitObjectSamples.cs | 2 -- osu.Game/Tests/Beatmaps/HitObjectSampleTest.cs | 11 +++++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Gameplay/TestSceneHitObjectSamples.cs b/osu.Game.Tests/Gameplay/TestSceneHitObjectSamples.cs index 6b95931b21..64eaafbe75 100644 --- a/osu.Game.Tests/Gameplay/TestSceneHitObjectSamples.cs +++ b/osu.Game.Tests/Gameplay/TestSceneHitObjectSamples.cs @@ -3,7 +3,6 @@ using NUnit.Framework; using osu.Framework.IO.Stores; -using osu.Framework.Testing; using osu.Game.Rulesets; using osu.Game.Rulesets.Osu; using osu.Game.Tests.Beatmaps; @@ -12,7 +11,6 @@ using static osu.Game.Skinning.LegacySkinConfiguration; namespace osu.Game.Tests.Gameplay { - [HeadlessTest] public class TestSceneHitObjectSamples : HitObjectSampleTest { protected override Ruleset CreatePlayerRuleset() => new OsuRuleset(); diff --git a/osu.Game/Tests/Beatmaps/HitObjectSampleTest.cs b/osu.Game/Tests/Beatmaps/HitObjectSampleTest.cs index 1e43e5d148..e3557222d5 100644 --- a/osu.Game/Tests/Beatmaps/HitObjectSampleTest.cs +++ b/osu.Game/Tests/Beatmaps/HitObjectSampleTest.cs @@ -9,11 +9,14 @@ using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.IO.Stores; +using osu.Framework.Testing; using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Formats; using osu.Game.IO; using osu.Game.Rulesets; +using osu.Game.Rulesets.Objects; +using osu.Game.Screens.Ranking; using osu.Game.Skinning; using osu.Game.Storyboards; using osu.Game.Tests.Visual; @@ -21,6 +24,7 @@ using osu.Game.Users; namespace osu.Game.Tests.Beatmaps { + [HeadlessTest] public abstract class HitObjectSampleTest : PlayerTestScene { protected abstract IResourceStore Resources { get; } @@ -44,7 +48,9 @@ namespace osu.Game.Tests.Beatmaps private readonly TestResourceStore beatmapSkinResourceStore = new TestResourceStore(); private SkinSourceDependencyContainer dependencies; private IBeatmap currentTestBeatmap; + protected sealed override bool HasCustomSteps => true; + protected override bool Autoplay => true; protected sealed override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) => new DependencyContainer(dependencies = new SkinSourceDependencyContainer(base.CreateChildDependencies(parent))); @@ -54,6 +60,8 @@ namespace osu.Game.Tests.Beatmaps protected sealed override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard = null) => new TestWorkingBeatmap(beatmapInfo, beatmapSkinResourceStore, beatmap, storyboard, Clock, Audio); + protected override TestPlayer CreatePlayer(Ruleset ruleset) => new TestPlayer(false); + protected void CreateTestWithBeatmap(string filename) { CreateTest(() => @@ -73,6 +81,9 @@ namespace osu.Game.Tests.Beatmaps currentTestBeatmap.BeatmapInfo.Ruleset = rulesetStore.GetRuleset(currentTestBeatmap.BeatmapInfo.RulesetID); }); }); + + AddStep("seek to completion", () => Player.GameplayClockContainer.Seek(Player.DrawableRuleset.Objects.Last().GetEndTime())); + AddUntilStep("results displayed", () => Stack.CurrentScreen is ResultsScreen); } protected void SetupSkins(string beatmapFile, string userFile) From feabca860bbbdc603fa391c7f9d19822789d26cc Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 13 Nov 2020 18:35:33 +0900 Subject: [PATCH 09/19] Fix sample playback test --- .../Visual/Gameplay/TestSceneGameplaySamplePlayback.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplaySamplePlayback.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplaySamplePlayback.cs index b86cb69eb4..7c6a213fe2 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplaySamplePlayback.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplaySamplePlayback.cs @@ -23,11 +23,13 @@ namespace osu.Game.Tests.Visual.Gameplay DrawableSample[] samples = null; ISamplePlaybackDisabler sampleDisabler = null; - AddStep("get variables", () => + AddUntilStep("get variables", () => { sampleDisabler = Player; - slider = Player.ChildrenOfType().OrderBy(s => s.HitObject.StartTime).First(); - samples = slider.ChildrenOfType().ToArray(); + slider = Player.ChildrenOfType().OrderBy(s => s.HitObject.StartTime).FirstOrDefault(); + samples = slider?.ChildrenOfType().ToArray(); + + return slider != null; }); AddUntilStep("wait for slider sliding then seek", () => From 1e05fd48e239e1f8622935122bb2bfb82e2af467 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 13 Nov 2020 21:43:53 +0900 Subject: [PATCH 10/19] Fix hidden mod crash --- osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs index 84a335750a..7c1dd46c02 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs @@ -28,10 +28,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override void ApplyToDrawableHitObjects(IEnumerable drawables) { foreach (var d in drawables) - { d.HitObjectApplied += applyFadeInAdjustment; - applyFadeInAdjustment(d); - } base.ApplyToDrawableHitObjects(drawables); } From 9792d1fc738ffeaad759d3a6826af94de612f763 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 13 Nov 2020 23:03:44 +0900 Subject: [PATCH 11/19] Fix slider tests --- .../Mods/TestSceneOsuModSpunOut.cs | 8 ++++++-- .../TestSceneSliderSnaking.cs | 17 ++++++++-------- osu.Game/Rulesets/UI/DrawableRuleset.cs | 20 ++++++++++++++++--- 3 files changed, 32 insertions(+), 13 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModSpunOut.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModSpunOut.cs index d8064d36ea..7b909d2907 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModSpunOut.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModSpunOut.cs @@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods Mod = new OsuModSpunOut(), Autoplay = false, Beatmap = singleSpinnerBeatmap, - PassCondition = () => Player.ChildrenOfType().Single().Progress >= 1 + PassCondition = () => Player.ChildrenOfType().SingleOrDefault()?.Progress >= 1 }); [TestCase(null)] @@ -45,7 +45,11 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods Mods = mods, Autoplay = false, Beatmap = singleSpinnerBeatmap, - PassCondition = () => Precision.AlmostEquals(Player.ChildrenOfType().Single().SpinsPerMinute, 286, 1) + PassCondition = () => + { + var counter = Player.ChildrenOfType().SingleOrDefault(); + return counter != null && Precision.AlmostEquals(counter.SpinsPerMinute, 286, 1); + } }); } diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderSnaking.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderSnaking.cs index 3d100e4b1c..b71400b71d 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderSnaking.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderSnaking.cs @@ -51,7 +51,7 @@ namespace osu.Game.Rulesets.Osu.Tests config.BindWith(OsuRulesetSetting.SnakingOutSliders, snakingOut); } - private DrawableSlider slider; + private DrawableSlider drawableSlider; [SetUpSteps] public override void SetUpSteps() @@ -68,7 +68,8 @@ namespace osu.Game.Rulesets.Osu.Tests AddUntilStep("wait for track to start running", () => Beatmap.Value.Track.IsRunning); double startTime = hitObjects[sliderIndex].StartTime; - retrieveDrawableSlider(sliderIndex); + addSeekStep(startTime); + retrieveDrawableSlider((Slider)hitObjects[sliderIndex]); setSnaking(true); ensureSnakingIn(startTime + fade_in_modifier); @@ -93,7 +94,8 @@ namespace osu.Game.Rulesets.Osu.Tests AddUntilStep("wait for track to start running", () => Beatmap.Value.Track.IsRunning); double startTime = hitObjects[sliderIndex].StartTime; - retrieveDrawableSlider(sliderIndex); + addSeekStep(startTime); + retrieveDrawableSlider((Slider)hitObjects[sliderIndex]); setSnaking(false); ensureNoSnakingIn(startTime + fade_in_modifier); @@ -127,9 +129,8 @@ namespace osu.Game.Rulesets.Osu.Tests checkPositionChange(16600, sliderRepeat, positionDecreased); } - private void retrieveDrawableSlider(int index) => - AddStep($"retrieve {(index + 1).ToOrdinalWords()} slider", () => - slider = (DrawableSlider)Player.DrawableRuleset.Playfield.AllHitObjects.ElementAt(index)); + private void retrieveDrawableSlider(Slider slider) => AddUntilStep($"retrieve slider @ {slider.StartTime}", () => + (drawableSlider = (DrawableSlider)Player.DrawableRuleset.Playfield.AllHitObjects.SingleOrDefault(d => d.HitObject == slider)) != null); private void ensureSnakingIn(double startTime) => checkPositionChange(startTime, sliderEnd, positionIncreased); private void ensureNoSnakingIn(double startTime) => checkPositionChange(startTime, sliderEnd, positionRemainsSame); @@ -150,13 +151,13 @@ namespace osu.Game.Rulesets.Osu.Tests private double timeAtRepeat(double startTime, int repeatIndex) => startTime + 100 + duration_of_span * repeatIndex; private Func positionAtRepeat(int repeatIndex) => repeatIndex % 2 == 0 ? (Func)sliderStart : sliderEnd; - private List sliderCurve => ((PlaySliderBody)slider.Body.Drawable).CurrentCurve; + private List sliderCurve => ((PlaySliderBody)drawableSlider.Body.Drawable).CurrentCurve; private Vector2 sliderStart() => sliderCurve.First(); private Vector2 sliderEnd() => sliderCurve.Last(); private Vector2 sliderRepeat() { - var drawable = Player.DrawableRuleset.Playfield.AllHitObjects.ElementAt(1); + var drawable = Player.DrawableRuleset.Playfield.AllHitObjects.SingleOrDefault(d => d.HitObject == hitObjects[1]); var repeat = drawable.ChildrenOfType>().First().Children.First(); return repeat.Position; } diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs index c912348604..e3c81d0f57 100644 --- a/osu.Game/Rulesets/UI/DrawableRuleset.cs +++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs @@ -593,10 +593,24 @@ namespace osu.Game.Rulesets.UI [CanBeNull] public DrawableHitObject GetPooledDrawableRepresentation([NotNull] HitObject hitObject) { - if (!pools.TryGetValue(hitObject.GetType(), out var pool)) - return null; + var lookupType = hitObject.GetType(); - return (DrawableHitObject)pool.Get(d => + IDrawablePool pool; + + // Tests may add derived hitobject instances for which pools don't exist. Try to find any applicable pool and dynamically assign the type if the pool exists. + if (!pools.TryGetValue(lookupType, out pool)) + { + foreach (var (t, p) in pools) + { + if (!t.IsInstanceOfType(hitObject)) + continue; + + pools[lookupType] = pool = p; + break; + } + } + + return (DrawableHitObject)pool?.Get(d => { var dho = (DrawableHitObject)d; From 36f1833f6eecbbacbb69d85c01a0d53d07f23241 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 14 Nov 2020 00:41:18 +0900 Subject: [PATCH 12/19] Move hitobject pooling to Playfield --- .../Gameplay/TestScenePoolingRuleset.cs | 19 +-- .../Objects/Drawables/DrawableHitObject.cs | 4 +- osu.Game/Rulesets/UI/DrawableRuleset.cs | 69 +---------- osu.Game/Rulesets/UI/HitObjectContainer.cs | 4 +- osu.Game/Rulesets/UI/HitObjectPoolProvider.cs | 111 ++++++++++++++++++ osu.Game/Rulesets/UI/Playfield.cs | 3 +- 6 files changed, 130 insertions(+), 80 deletions(-) create mode 100644 osu.Game/Rulesets/UI/HitObjectPoolProvider.cs diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePoolingRuleset.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePoolingRuleset.cs index 242eaf7b7d..2e1e667d0d 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePoolingRuleset.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePoolingRuleset.cs @@ -134,19 +134,13 @@ namespace osu.Game.Tests.Visual.Gameplay { } - [BackgroundDependencyLoader] - private void load() - { - RegisterPool(PoolSize); - } - protected override HitObjectLifetimeEntry CreateLifetimeEntry(TestHitObject hitObject) => new TestHitObjectLifetimeEntry(hitObject); public override DrawableHitObject CreateDrawableRepresentation(TestHitObject h) => null; protected override PassThroughInputManager CreateInputManager() => new PassThroughInputManager(); - protected override Playfield CreatePlayfield() => new TestPlayfield(); + protected override Playfield CreatePlayfield() => new TestPlayfield(PoolSize); private class TestHitObjectLifetimeEntry : HitObjectLifetimeEntry { @@ -161,11 +155,20 @@ namespace osu.Game.Tests.Visual.Gameplay private class TestPlayfield : Playfield { - public TestPlayfield() + private readonly int poolSize; + + public TestPlayfield(int poolSize) { + this.poolSize = poolSize; AddInternal(HitObjectContainer); } + [BackgroundDependencyLoader] + private void load() + { + RegisterPool(poolSize); + } + protected override GameplayCursorContainer CreateCursor() => null; } diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 3e3936b45a..c22257e544 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -137,7 +137,7 @@ namespace osu.Game.Rulesets.Objects.Drawables private HitObjectLifetimeEntry lifetimeEntry; [Resolved(CanBeNull = true)] - private DrawableRuleset drawableRuleset { get; set; } + private HitObjectPoolProvider poolProvider { get; set; } private Container samplesContainer; @@ -212,7 +212,7 @@ namespace osu.Game.Rulesets.Objects.Drawables foreach (var h in HitObject.NestedHitObjects) { - var drawableNested = drawableRuleset?.GetPooledDrawableRepresentation(h) + var drawableNested = poolProvider?.GetPooledDrawableRepresentation(h) ?? CreateNestedHitObject(h) ?? throw new InvalidOperationException($"{nameof(CreateNestedHitObject)} returned null for {h.GetType().ReadableName()}."); diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs index c912348604..5022b571fd 100644 --- a/osu.Game/Rulesets/UI/DrawableRuleset.cs +++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs @@ -15,10 +15,8 @@ using System.Linq; using System.Threading; using JetBrains.Annotations; using osu.Framework.Bindables; -using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Extensions.TypeExtensions; using osu.Framework.Graphics.Cursor; -using osu.Framework.Graphics.Pooling; using osu.Framework.Input; using osu.Framework.Input.Events; using osu.Game.Configuration; @@ -327,9 +325,8 @@ namespace osu.Game.Rulesets.UI /// Creates a to represent a . /// /// - /// If this method returns null, then this will assume the requested type is being pooled, - /// and will instead attempt to retrieve the s at the point they should become alive via pools registered through - /// or . + /// If this method returns null, then this will assume the requested type is being pooled inside the , + /// and will instead attempt to retrieve the s at the point they should become alive via pools registered in the . /// /// The to represent. /// The representing . @@ -550,68 +547,8 @@ namespace osu.Game.Rulesets.UI /// public abstract void CancelResume(); - private readonly Dictionary pools = new Dictionary(); private readonly Dictionary lifetimeEntries = new Dictionary(); - /// - /// Registers a default pool with this which is to be used whenever - /// representations are requested for the given type (via ). - /// - /// The number of s to be initially stored in the pool. - /// - /// The maximum number of s that can be stored in the pool. - /// If this limit is exceeded, every subsequent will be created anew instead of being retrieved from the pool, - /// until some of the existing s are returned to the pool. - /// - /// The type. - /// The receiver for s. - protected void RegisterPool(int initialSize, int? maximumSize = null) - where TObject : HitObject - where TDrawable : DrawableHitObject, new() - => RegisterPool(new DrawablePool(initialSize, maximumSize)); - - /// - /// Registers a custom pool with this which is to be used whenever - /// representations are requested for the given type (via ). - /// - /// The to register. - /// The type. - /// The receiver for s. - protected void RegisterPool([NotNull] DrawablePool pool) - where TObject : HitObject - where TDrawable : DrawableHitObject, new() - { - pools[typeof(TObject)] = pool; - AddInternal(pool); - } - - /// - /// Attempts to retrieve the poolable representation of a . - /// - /// The to retrieve the representation of. - /// The representing , or null if no poolable representation exists. - [CanBeNull] - public DrawableHitObject GetPooledDrawableRepresentation([NotNull] HitObject hitObject) - { - if (!pools.TryGetValue(hitObject.GetType(), out var pool)) - return null; - - return (DrawableHitObject)pool.Get(d => - { - var dho = (DrawableHitObject)d; - - // If this is the first time this DHO is being used (not loaded), then apply the DHO mods. - // This is done before Apply() so that the state is updated once when the hitobject is applied. - if (!dho.IsLoaded) - { - foreach (var m in Mods.OfType()) - m.ApplyToDrawableHitObjects(dho.Yield()); - } - - dho.Apply(hitObject, GetLifetimeEntry(hitObject)); - }); - } - /// /// Creates the for a given . /// @@ -629,7 +566,7 @@ namespace osu.Game.Rulesets.UI /// The to retrieve or create the for. /// The for . [NotNull] - protected HitObjectLifetimeEntry GetLifetimeEntry([NotNull] HitObject hitObject) + internal HitObjectLifetimeEntry GetLifetimeEntry([NotNull] HitObject hitObject) { if (lifetimeEntries.TryGetValue(hitObject, out var entry)) return entry; diff --git a/osu.Game/Rulesets/UI/HitObjectContainer.cs b/osu.Game/Rulesets/UI/HitObjectContainer.cs index 25fb7ab9f3..8de9f41482 100644 --- a/osu.Game/Rulesets/UI/HitObjectContainer.cs +++ b/osu.Game/Rulesets/UI/HitObjectContainer.cs @@ -73,7 +73,7 @@ namespace osu.Game.Rulesets.UI private readonly LifetimeEntryManager lifetimeManager = new LifetimeEntryManager(); [Resolved(CanBeNull = true)] - private DrawableRuleset drawableRuleset { get; set; } + private HitObjectPoolProvider poolProvider { get; set; } public HitObjectContainer() { @@ -105,7 +105,7 @@ namespace osu.Game.Rulesets.UI { Debug.Assert(!drawableMap.ContainsKey(entry)); - var drawable = drawableRuleset.GetPooledDrawableRepresentation(entry.HitObject); + var drawable = poolProvider.GetPooledDrawableRepresentation(entry.HitObject); if (drawable == null) throw new InvalidOperationException($"A drawable representation could not be retrieved for hitobject type: {entry.HitObject.GetType().ReadableName()}."); diff --git a/osu.Game/Rulesets/UI/HitObjectPoolProvider.cs b/osu.Game/Rulesets/UI/HitObjectPoolProvider.cs new file mode 100644 index 0000000000..ee3aee59b4 --- /dev/null +++ b/osu.Game/Rulesets/UI/HitObjectPoolProvider.cs @@ -0,0 +1,111 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using System.Linq; +using JetBrains.Annotations; +using osu.Framework.Allocation; +using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Pooling; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Drawables; + +namespace osu.Game.Rulesets.UI +{ + /// + /// A that pools s and allows children to retrieve them via . + /// + [Cached(typeof(HitObjectPoolProvider))] + public class HitObjectPoolProvider : CompositeDrawable + { + [Resolved] + private DrawableRuleset drawableRuleset { get; set; } + + [Resolved] + private IReadOnlyList mods { get; set; } + + [Resolved(CanBeNull = true)] + private HitObjectPoolProvider parentProvider { get; set; } + + private readonly Dictionary pools = new Dictionary(); + + /// + /// Registers a default pool with this which is to be used whenever + /// representations are requested for the given type (via ). + /// + /// The number of s to be initially stored in the pool. + /// + /// The maximum number of s that can be stored in the pool. + /// If this limit is exceeded, every subsequent will be created anew instead of being retrieved from the pool, + /// until some of the existing s are returned to the pool. + /// + /// The type. + /// The receiver for s. + protected void RegisterPool(int initialSize, int? maximumSize = null) + where TObject : HitObject + where TDrawable : DrawableHitObject, new() + => RegisterPool(new DrawablePool(initialSize, maximumSize)); + + /// + /// Registers a custom pool with this which is to be used whenever + /// representations are requested for the given type (via ). + /// + /// The to register. + /// The type. + /// The receiver for s. + protected void RegisterPool([NotNull] DrawablePool pool) + where TObject : HitObject + where TDrawable : DrawableHitObject, new() + { + pools[typeof(TObject)] = pool; + AddInternal(pool); + } + + /// + /// Attempts to retrieve the poolable representation of a . + /// + /// The to retrieve the representation of. + /// The representing , or null if no poolable representation exists. + [CanBeNull] + public DrawableHitObject GetPooledDrawableRepresentation([NotNull] HitObject hitObject) + { + var lookupType = hitObject.GetType(); + + IDrawablePool pool; + + // Tests may add derived hitobject instances for which pools don't exist. Try to find any applicable pool and dynamically assign the type if the pool exists. + if (!pools.TryGetValue(lookupType, out pool)) + { + foreach (var (t, p) in pools) + { + if (!t.IsInstanceOfType(hitObject)) + continue; + + pools[lookupType] = pool = p; + break; + } + } + + if (pool == null) + return parentProvider?.GetPooledDrawableRepresentation(hitObject); + + return (DrawableHitObject)pool.Get(d => + { + var dho = (DrawableHitObject)d; + + // If this is the first time this DHO is being used (not loaded), then apply the DHO mods. + // This is done before Apply() so that the state is updated once when the hitobject is applied. + if (!dho.IsLoaded) + { + foreach (var m in mods.OfType()) + m.ApplyToDrawableHitObjects(dho.Yield()); + } + + dho.Apply(hitObject, drawableRuleset.GetLifetimeEntry(hitObject)); + }); + } + } +} diff --git a/osu.Game/Rulesets/UI/Playfield.cs b/osu.Game/Rulesets/UI/Playfield.cs index 6747145d50..1d0196d173 100644 --- a/osu.Game/Rulesets/UI/Playfield.cs +++ b/osu.Game/Rulesets/UI/Playfield.cs @@ -9,7 +9,6 @@ using osu.Game.Rulesets.Objects.Drawables; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.IEnumerableExtensions; -using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; @@ -17,7 +16,7 @@ using osuTK; namespace osu.Game.Rulesets.UI { - public abstract class Playfield : CompositeDrawable + public abstract class Playfield : HitObjectPoolProvider { /// /// Invoked when a is judged. From c71b237c4f8d73ce957a23ae4a2c56478725307a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 14 Nov 2020 00:54:57 +0900 Subject: [PATCH 13/19] Merge all pooling support into Playfield --- .../Gameplay/TestScenePoolingRuleset.cs | 24 +-- .../Objects/Drawables/DrawableHitObject.cs | 4 +- osu.Game/Rulesets/UI/DrawableRuleset.cs | 55 +----- osu.Game/Rulesets/UI/HitObjectContainer.cs | 4 +- osu.Game/Rulesets/UI/HitObjectPoolProvider.cs | 111 ------------ .../Rulesets/UI/IPooledHitObjectProvider.cs | 20 +++ osu.Game/Rulesets/UI/Playfield.cs | 167 ++++++++++++++---- 7 files changed, 172 insertions(+), 213 deletions(-) delete mode 100644 osu.Game/Rulesets/UI/HitObjectPoolProvider.cs create mode 100644 osu.Game/Rulesets/UI/IPooledHitObjectProvider.cs diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePoolingRuleset.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePoolingRuleset.cs index 2e1e667d0d..d009d805f0 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePoolingRuleset.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePoolingRuleset.cs @@ -134,23 +134,11 @@ namespace osu.Game.Tests.Visual.Gameplay { } - protected override HitObjectLifetimeEntry CreateLifetimeEntry(TestHitObject hitObject) => new TestHitObjectLifetimeEntry(hitObject); - public override DrawableHitObject CreateDrawableRepresentation(TestHitObject h) => null; protected override PassThroughInputManager CreateInputManager() => new PassThroughInputManager(); protected override Playfield CreatePlayfield() => new TestPlayfield(PoolSize); - - private class TestHitObjectLifetimeEntry : HitObjectLifetimeEntry - { - public TestHitObjectLifetimeEntry(HitObject hitObject) - : base(hitObject) - { - } - - protected override double InitialLifetimeOffset => 0; - } } private class TestPlayfield : Playfield @@ -169,9 +157,21 @@ namespace osu.Game.Tests.Visual.Gameplay RegisterPool(poolSize); } + protected override HitObjectLifetimeEntry CreateLifetimeEntry(HitObject hitObject) => new TestHitObjectLifetimeEntry(hitObject); + protected override GameplayCursorContainer CreateCursor() => null; } + private class TestHitObjectLifetimeEntry : HitObjectLifetimeEntry + { + public TestHitObjectLifetimeEntry(HitObject hitObject) + : base(hitObject) + { + } + + protected override double InitialLifetimeOffset => 0; + } + private class TestBeatmapConverter : BeatmapConverter { public TestBeatmapConverter(IBeatmap beatmap, Ruleset ruleset) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index c22257e544..b400c532c5 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -137,7 +137,7 @@ namespace osu.Game.Rulesets.Objects.Drawables private HitObjectLifetimeEntry lifetimeEntry; [Resolved(CanBeNull = true)] - private HitObjectPoolProvider poolProvider { get; set; } + private IPooledHitObjectProvider pooledObjectProvider { get; set; } private Container samplesContainer; @@ -212,7 +212,7 @@ namespace osu.Game.Rulesets.Objects.Drawables foreach (var h in HitObject.NestedHitObjects) { - var drawableNested = poolProvider?.GetPooledDrawableRepresentation(h) + var drawableNested = pooledObjectProvider?.GetPooledDrawableRepresentation(h) ?? CreateNestedHitObject(h) ?? throw new InvalidOperationException($"{nameof(CreateNestedHitObject)} returned null for {h.GetType().ReadableName()}."); diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs index 5022b571fd..c1a601eaae 100644 --- a/osu.Game/Rulesets/UI/DrawableRuleset.cs +++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs @@ -15,7 +15,6 @@ using System.Linq; using System.Threading; using JetBrains.Annotations; using osu.Framework.Bindables; -using osu.Framework.Extensions.TypeExtensions; using osu.Framework.Graphics.Cursor; using osu.Framework.Input; using osu.Framework.Input.Events; @@ -246,7 +245,7 @@ namespace osu.Game.Rulesets.UI if (drawableRepresentation != null) Playfield.Add(drawableRepresentation); else - Playfield.Add(GetLifetimeEntry(hitObject)); + Playfield.Add(hitObject); } /// @@ -258,15 +257,10 @@ namespace osu.Game.Rulesets.UI /// The to remove. public bool RemoveHitObject(TObject hitObject) { - var entry = GetLifetimeEntry(hitObject); - - // May have been newly-created by the above call - remove it anyway. - RemoveLifetimeEntry(hitObject); - - if (Playfield.Remove(entry)) + if (Playfield.Remove(hitObject)) return true; - // If the entry was not removed from the playfield, assume the hitobject is not being pooled and attempt a direct removal. + // If the entry was not removed from the playfield, assume the hitobject is not being pooled and attempt a direct drawable removal. var drawableObject = Playfield.AllHitObjects.SingleOrDefault(d => d.HitObject == hitObject); if (drawableObject != null) return Playfield.Remove(drawableObject); @@ -274,16 +268,6 @@ namespace osu.Game.Rulesets.UI return false; } - protected sealed override HitObjectLifetimeEntry CreateLifetimeEntry(HitObject hitObject) - { - if (!(hitObject is TObject tHitObject)) - throw new InvalidOperationException($"Unexpected hitobject type: {hitObject.GetType().ReadableName()}"); - - return CreateLifetimeEntry(tHitObject); - } - - protected virtual HitObjectLifetimeEntry CreateLifetimeEntry(TObject hitObject) => new HitObjectLifetimeEntry(hitObject); - public override void SetRecordTarget(Replay recordingReplay) { if (!(KeyBindingInputManager is IHasRecordingHandler recordingInputManager)) @@ -546,39 +530,6 @@ namespace osu.Game.Rulesets.UI /// Invoked when the user requests to pause while the resume overlay is active. /// public abstract void CancelResume(); - - private readonly Dictionary lifetimeEntries = new Dictionary(); - - /// - /// Creates the for a given . - /// - /// - /// This may be overridden to provide custom lifetime control (e.g. via . - /// - /// The to create the entry for. - /// The . - [NotNull] - protected abstract HitObjectLifetimeEntry CreateLifetimeEntry([NotNull] HitObject hitObject); - - /// - /// Retrieves or creates the for a given . - /// - /// The to retrieve or create the for. - /// The for . - [NotNull] - internal HitObjectLifetimeEntry GetLifetimeEntry([NotNull] HitObject hitObject) - { - if (lifetimeEntries.TryGetValue(hitObject, out var entry)) - return entry; - - return lifetimeEntries[hitObject] = CreateLifetimeEntry(hitObject); - } - - /// - /// Removes the for a . - /// - /// The to remove the for. - internal void RemoveLifetimeEntry([NotNull] HitObject hitObject) => lifetimeEntries.Remove(hitObject); } public class BeatmapInvalidForRulesetException : ArgumentException diff --git a/osu.Game/Rulesets/UI/HitObjectContainer.cs b/osu.Game/Rulesets/UI/HitObjectContainer.cs index 8de9f41482..1dc029506f 100644 --- a/osu.Game/Rulesets/UI/HitObjectContainer.cs +++ b/osu.Game/Rulesets/UI/HitObjectContainer.cs @@ -73,7 +73,7 @@ namespace osu.Game.Rulesets.UI private readonly LifetimeEntryManager lifetimeManager = new LifetimeEntryManager(); [Resolved(CanBeNull = true)] - private HitObjectPoolProvider poolProvider { get; set; } + private IPooledHitObjectProvider pooledObjectProvider { get; set; } public HitObjectContainer() { @@ -105,7 +105,7 @@ namespace osu.Game.Rulesets.UI { Debug.Assert(!drawableMap.ContainsKey(entry)); - var drawable = poolProvider.GetPooledDrawableRepresentation(entry.HitObject); + var drawable = pooledObjectProvider.GetPooledDrawableRepresentation(entry.HitObject); if (drawable == null) throw new InvalidOperationException($"A drawable representation could not be retrieved for hitobject type: {entry.HitObject.GetType().ReadableName()}."); diff --git a/osu.Game/Rulesets/UI/HitObjectPoolProvider.cs b/osu.Game/Rulesets/UI/HitObjectPoolProvider.cs deleted file mode 100644 index ee3aee59b4..0000000000 --- a/osu.Game/Rulesets/UI/HitObjectPoolProvider.cs +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System; -using System.Collections.Generic; -using System.Linq; -using JetBrains.Annotations; -using osu.Framework.Allocation; -using osu.Framework.Extensions.IEnumerableExtensions; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Pooling; -using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Objects.Drawables; - -namespace osu.Game.Rulesets.UI -{ - /// - /// A that pools s and allows children to retrieve them via . - /// - [Cached(typeof(HitObjectPoolProvider))] - public class HitObjectPoolProvider : CompositeDrawable - { - [Resolved] - private DrawableRuleset drawableRuleset { get; set; } - - [Resolved] - private IReadOnlyList mods { get; set; } - - [Resolved(CanBeNull = true)] - private HitObjectPoolProvider parentProvider { get; set; } - - private readonly Dictionary pools = new Dictionary(); - - /// - /// Registers a default pool with this which is to be used whenever - /// representations are requested for the given type (via ). - /// - /// The number of s to be initially stored in the pool. - /// - /// The maximum number of s that can be stored in the pool. - /// If this limit is exceeded, every subsequent will be created anew instead of being retrieved from the pool, - /// until some of the existing s are returned to the pool. - /// - /// The type. - /// The receiver for s. - protected void RegisterPool(int initialSize, int? maximumSize = null) - where TObject : HitObject - where TDrawable : DrawableHitObject, new() - => RegisterPool(new DrawablePool(initialSize, maximumSize)); - - /// - /// Registers a custom pool with this which is to be used whenever - /// representations are requested for the given type (via ). - /// - /// The to register. - /// The type. - /// The receiver for s. - protected void RegisterPool([NotNull] DrawablePool pool) - where TObject : HitObject - where TDrawable : DrawableHitObject, new() - { - pools[typeof(TObject)] = pool; - AddInternal(pool); - } - - /// - /// Attempts to retrieve the poolable representation of a . - /// - /// The to retrieve the representation of. - /// The representing , or null if no poolable representation exists. - [CanBeNull] - public DrawableHitObject GetPooledDrawableRepresentation([NotNull] HitObject hitObject) - { - var lookupType = hitObject.GetType(); - - IDrawablePool pool; - - // Tests may add derived hitobject instances for which pools don't exist. Try to find any applicable pool and dynamically assign the type if the pool exists. - if (!pools.TryGetValue(lookupType, out pool)) - { - foreach (var (t, p) in pools) - { - if (!t.IsInstanceOfType(hitObject)) - continue; - - pools[lookupType] = pool = p; - break; - } - } - - if (pool == null) - return parentProvider?.GetPooledDrawableRepresentation(hitObject); - - return (DrawableHitObject)pool.Get(d => - { - var dho = (DrawableHitObject)d; - - // If this is the first time this DHO is being used (not loaded), then apply the DHO mods. - // This is done before Apply() so that the state is updated once when the hitobject is applied. - if (!dho.IsLoaded) - { - foreach (var m in mods.OfType()) - m.ApplyToDrawableHitObjects(dho.Yield()); - } - - dho.Apply(hitObject, drawableRuleset.GetLifetimeEntry(hitObject)); - }); - } - } -} diff --git a/osu.Game/Rulesets/UI/IPooledHitObjectProvider.cs b/osu.Game/Rulesets/UI/IPooledHitObjectProvider.cs new file mode 100644 index 0000000000..d8240d892f --- /dev/null +++ b/osu.Game/Rulesets/UI/IPooledHitObjectProvider.cs @@ -0,0 +1,20 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using JetBrains.Annotations; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Drawables; + +namespace osu.Game.Rulesets.UI +{ + internal interface IPooledHitObjectProvider + { + /// + /// Attempts to retrieve the poolable representation of a . + /// + /// The to retrieve the representation of. + /// The representing , or null if no poolable representation exists. + [CanBeNull] + public DrawableHitObject GetPooledDrawableRepresentation([NotNull] HitObject hitObject); + } +} diff --git a/osu.Game/Rulesets/UI/Playfield.cs b/osu.Game/Rulesets/UI/Playfield.cs index 1d0196d173..80e33e0ec5 100644 --- a/osu.Game/Rulesets/UI/Playfield.cs +++ b/osu.Game/Rulesets/UI/Playfield.cs @@ -4,11 +4,14 @@ using System; using System.Collections.Generic; using System.Linq; +using JetBrains.Annotations; using osu.Framework.Graphics; using osu.Game.Rulesets.Objects.Drawables; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Pooling; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; @@ -16,7 +19,8 @@ using osuTK; namespace osu.Game.Rulesets.UI { - public abstract class Playfield : HitObjectPoolProvider + [Cached(typeof(IPooledHitObjectProvider))] + public abstract class Playfield : CompositeDrawable, IPooledHitObjectProvider { /// /// Invoked when a is judged. @@ -137,39 +141,6 @@ namespace osu.Game.Rulesets.UI return false; } - /// - /// Adds a for a pooled to this . - /// - /// The controlling the lifetime of the . - public virtual void Add(HitObjectLifetimeEntry entry) - { - HitObjectContainer.Add(entry); - lifetimeEntryMap[entry.HitObject] = entry; - OnHitObjectAdded(entry.HitObject); - } - - /// - /// Removes a for a pooled from this . - /// - /// The controlling the lifetime of the . - /// Whether the was successfully removed. - public virtual bool Remove(HitObjectLifetimeEntry entry) - { - if (HitObjectContainer.Remove(entry)) - { - lifetimeEntryMap.Remove(entry.HitObject); - OnHitObjectRemoved(entry.HitObject); - return true; - } - - bool removedFromNested = false; - - if (nestedPlayfields.IsValueCreated) - removedFromNested = nestedPlayfields.Value.Any(p => p.Remove(entry)); - - return removedFromNested; - } - /// /// Invoked when a is added to this . /// @@ -245,6 +216,134 @@ namespace osu.Game.Rulesets.UI /// protected virtual HitObjectContainer CreateHitObjectContainer() => new HitObjectContainer(); + #region Pooling support + + [Resolved(CanBeNull = true)] + private IPooledHitObjectProvider parentPooledObjectProvider { get; set; } + + private readonly Dictionary pools = new Dictionary(); + + /// + /// Adds a for a pooled to this . + /// + /// + public virtual void Add(HitObject hitObject) + { + var entry = CreateLifetimeEntry(hitObject); + lifetimeEntryMap[entry.HitObject] = entry; + + HitObjectContainer.Add(entry); + OnHitObjectAdded(entry.HitObject); + } + + /// + /// Removes a for a pooled from this . + /// + /// + /// Whether the was successfully removed. + public virtual bool Remove(HitObject hitObject) + { + if (lifetimeEntryMap.Remove(hitObject, out var entry)) + { + HitObjectContainer.Remove(entry); + OnHitObjectRemoved(hitObject); + return true; + } + + bool removedFromNested = false; + + if (nestedPlayfields.IsValueCreated) + removedFromNested = nestedPlayfields.Value.Any(p => p.Remove(hitObject)); + + return removedFromNested; + } + + /// + /// Creates the for a given . + /// + /// + /// This may be overridden to provide custom lifetime control (e.g. via . + /// + /// The to create the entry for. + /// The . + [NotNull] + protected virtual HitObjectLifetimeEntry CreateLifetimeEntry([NotNull] HitObject hitObject) => new HitObjectLifetimeEntry(hitObject); + + /// + /// Registers a default pool with this which is to be used whenever + /// representations are requested for the given type. + /// + /// The number of s to be initially stored in the pool. + /// + /// The maximum number of s that can be stored in the pool. + /// If this limit is exceeded, every subsequent will be created anew instead of being retrieved from the pool, + /// until some of the existing s are returned to the pool. + /// + /// The type. + /// The receiver for s. + protected void RegisterPool(int initialSize, int? maximumSize = null) + where TObject : HitObject + where TDrawable : DrawableHitObject, new() + => RegisterPool(new DrawablePool(initialSize, maximumSize)); + + /// + /// Registers a custom pool with this which is to be used whenever + /// representations are requested for the given type. + /// + /// The to register. + /// The type. + /// The receiver for s. + protected void RegisterPool([NotNull] DrawablePool pool) + where TObject : HitObject + where TDrawable : DrawableHitObject, new() + { + pools[typeof(TObject)] = pool; + AddInternal(pool); + } + + DrawableHitObject IPooledHitObjectProvider.GetPooledDrawableRepresentation(HitObject hitObject) + { + var lookupType = hitObject.GetType(); + + IDrawablePool pool; + + // Tests may add derived hitobject instances for which pools don't exist. Try to find any applicable pool and dynamically assign the type if the pool exists. + if (!pools.TryGetValue(lookupType, out pool)) + { + foreach (var (t, p) in pools) + { + if (!t.IsInstanceOfType(hitObject)) + continue; + + pools[lookupType] = pool = p; + break; + } + } + + if (pool == null) + return parentPooledObjectProvider?.GetPooledDrawableRepresentation(hitObject); + + return (DrawableHitObject)pool.Get(d => + { + var dho = (DrawableHitObject)d; + + // If this is the first time this DHO is being used (not loaded), then apply the DHO mods. + // This is done before Apply() so that the state is updated once when the hitobject is applied. + if (!dho.IsLoaded) + { + foreach (var m in mods.OfType()) + m.ApplyToDrawableHitObjects(dho.Yield()); + } + + if (!lifetimeEntryMap.TryGetValue(hitObject, out var entry)) + lifetimeEntryMap[hitObject] = entry = CreateLifetimeEntry(hitObject); + + dho.Apply(hitObject, entry); + }); + } + + #endregion + #region Editor logic /// From 5b60f32c7f96bc1b17fff30bf875c7e8f6acaf6c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 14 Nov 2020 01:03:23 +0900 Subject: [PATCH 14/19] Move implementation into OsuPlayfield --- .../Edit/DrawableOsuEditRuleset.cs | 10 ++--- .../UI/DrawableOsuRuleset.cs | 41 ------------------- osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs | 33 +++++++++++++++ 3 files changed, 38 insertions(+), 46 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditRuleset.cs b/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditRuleset.cs index 05396ebc8b..8af6fd65ce 100644 --- a/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditRuleset.cs +++ b/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditRuleset.cs @@ -18,16 +18,16 @@ namespace osu.Game.Rulesets.Osu.Edit { } - protected override DrawablePool CreatePool(int initialSize, int? maximumSize = null) - => new OsuEditDrawablePool(Playfield.CheckHittable, Playfield.OnHitObjectLoaded, initialSize, maximumSize); - - protected override Playfield CreatePlayfield() => new OsuPlayfieldNoCursor(); + protected override Playfield CreatePlayfield() => new OsuEditPlayfield(); public override PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer() => new OsuPlayfieldAdjustmentContainer { Size = Vector2.One }; - private class OsuPlayfieldNoCursor : OsuPlayfield + private class OsuEditPlayfield : OsuPlayfield { protected override GameplayCursorContainer CreateCursor() => null; + + protected override DrawablePool CreatePool(int initialSize, int? maximumSize = null) + => new OsuEditDrawablePool(CheckHittable, OnHitObjectLoaded, initialSize, maximumSize); } } } diff --git a/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs b/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs index 1d16c47818..69179137a6 100644 --- a/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs @@ -4,18 +4,14 @@ using System; using System.Collections.Generic; using System.Linq; -using osu.Framework.Allocation; -using osu.Framework.Graphics.Pooling; using osu.Framework.Input; using osu.Game.Beatmaps; using osu.Game.Input.Handlers; using osu.Game.Replays; using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Configuration; using osu.Game.Rulesets.Osu.Objects; -using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.Osu.Replays; using osu.Game.Rulesets.UI; using osu.Game.Screens.Play; @@ -34,35 +30,8 @@ namespace osu.Game.Rulesets.Osu.UI { } - [BackgroundDependencyLoader] - private void load() - { - registerPool(10, 100); - - registerPool(10, 100); - registerPool(10, 100); - registerPool(10, 100); - registerPool(10, 100); - registerPool(5, 50); - - registerPool(2, 20); - registerPool(10, 100); - registerPool(10, 100); - } - - private void registerPool(int initialSize, int? maximumSize = null) - where TObject : HitObject - where TDrawable : DrawableHitObject, new() - => RegisterPool(CreatePool(initialSize, maximumSize)); - - protected virtual DrawablePool CreatePool(int initialSize, int? maximumSize = null) - where TDrawable : DrawableHitObject, new() - => new OsuDrawablePool(Playfield.CheckHittable, Playfield.OnHitObjectLoaded, initialSize, maximumSize); - public override DrawableHitObject CreateDrawableRepresentation(OsuHitObject h) => null; - protected override HitObjectLifetimeEntry CreateLifetimeEntry(OsuHitObject hitObject) => new OsuHitObjectLifetimeEntry(hitObject); - public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; // always show the gameplay cursor protected override Playfield CreatePlayfield() => new OsuPlayfield(); @@ -87,15 +56,5 @@ namespace osu.Game.Rulesets.Osu.UI return 0; } } - - private class OsuHitObjectLifetimeEntry : HitObjectLifetimeEntry - { - public OsuHitObjectLifetimeEntry(HitObject hitObject) - : base(hitObject) - { - } - - protected override double InitialLifetimeOffset => ((OsuHitObject)HitObject).TimePreempt; - } } } diff --git a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs index 5d59a6ff38..e8ff6c410f 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs @@ -97,8 +97,31 @@ namespace osu.Game.Rulesets.Osu.UI private void load(OsuRulesetConfigManager config) { config?.BindWith(OsuRulesetSetting.PlayfieldBorderStyle, playfieldBorder.PlayfieldBorderStyle); + + registerPool(10, 100); + + registerPool(10, 100); + registerPool(10, 100); + registerPool(10, 100); + registerPool(10, 100); + registerPool(5, 50); + + registerPool(2, 20); + registerPool(10, 100); + registerPool(10, 100); } + private void registerPool(int initialSize, int? maximumSize = null) + where TObject : HitObject + where TDrawable : DrawableHitObject, new() + => RegisterPool(CreatePool(initialSize, maximumSize)); + + protected virtual DrawablePool CreatePool(int initialSize, int? maximumSize = null) + where TDrawable : DrawableHitObject, new() + => new OsuDrawablePool(CheckHittable, OnHitObjectLoaded, initialSize, maximumSize); + + protected override HitObjectLifetimeEntry CreateLifetimeEntry(HitObject hitObject) => new OsuHitObjectLifetimeEntry(hitObject); + protected override void OnHitObjectAdded(HitObject hitObject) { base.OnHitObjectAdded(hitObject); @@ -172,5 +195,15 @@ namespace osu.Game.Rulesets.Osu.UI return judgement; } } + + private class OsuHitObjectLifetimeEntry : HitObjectLifetimeEntry + { + public OsuHitObjectLifetimeEntry(HitObject hitObject) + : base(hitObject) + { + } + + protected override double InitialLifetimeOffset => ((OsuHitObject)HitObject).TimePreempt; + } } } From 21b015d63afffa9b0acdb0027f37d0b228614698 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 14 Nov 2020 01:06:38 +0900 Subject: [PATCH 15/19] Remove explicit public --- osu.Game/Rulesets/UI/IPooledHitObjectProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/UI/IPooledHitObjectProvider.cs b/osu.Game/Rulesets/UI/IPooledHitObjectProvider.cs index d8240d892f..315926dfc6 100644 --- a/osu.Game/Rulesets/UI/IPooledHitObjectProvider.cs +++ b/osu.Game/Rulesets/UI/IPooledHitObjectProvider.cs @@ -15,6 +15,6 @@ namespace osu.Game.Rulesets.UI /// The to retrieve the representation of. /// The representing , or null if no poolable representation exists. [CanBeNull] - public DrawableHitObject GetPooledDrawableRepresentation([NotNull] HitObject hitObject); + DrawableHitObject GetPooledDrawableRepresentation([NotNull] HitObject hitObject); } } From 7a89e58483a998a484a944acd5edf4c61bdcbdcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 13 Nov 2020 20:49:06 +0100 Subject: [PATCH 16/19] Disable pressed/released action logic when rewinding --- .../Objects/Drawables/DrawableHoldNote.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs index d9d740c145..59899637f9 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs @@ -249,6 +249,10 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables if (action != Action.Value) return false; + // do not run any of this logic when rewinding, as it inverts order of presses/releases. + if (Time.Elapsed < 0) + return false; + if (CheckHittable?.Invoke(this, Time.Current) == false) return false; @@ -281,6 +285,10 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables if (action != Action.Value) return; + // do not run any of this logic when rewinding, as it inverts order of presses/releases. + if (Time.Elapsed < 0) + return; + // Make sure a hold was started if (HoldStartTime == null) return; From 90f37ff4ab651281059a5f8bdf2bc41cf6c69a1a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 14 Nov 2020 18:04:59 +0900 Subject: [PATCH 17/19] Rejig namespaces --- osu.Game.Rulesets.Osu/{UI => Edit}/OsuEditDrawablePool.cs | 2 +- .../{UI => Objects/Drawables}/OsuDrawablePool.cs | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) rename osu.Game.Rulesets.Osu/{UI => Edit}/OsuEditDrawablePool.cs (98%) rename osu.Game.Rulesets.Osu/{UI => Objects/Drawables}/OsuDrawablePool.cs (93%) diff --git a/osu.Game.Rulesets.Osu/UI/OsuEditDrawablePool.cs b/osu.Game.Rulesets.Osu/Edit/OsuEditDrawablePool.cs similarity index 98% rename from osu.Game.Rulesets.Osu/UI/OsuEditDrawablePool.cs rename to osu.Game.Rulesets.Osu/Edit/OsuEditDrawablePool.cs index 822ff076f5..946181d8a4 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuEditDrawablePool.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuEditDrawablePool.cs @@ -7,7 +7,7 @@ using osu.Framework.Graphics; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects.Drawables; -namespace osu.Game.Rulesets.Osu.UI +namespace osu.Game.Rulesets.Osu.Edit { public class OsuEditDrawablePool : OsuDrawablePool where T : DrawableHitObject, new() diff --git a/osu.Game.Rulesets.Osu/UI/OsuDrawablePool.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/OsuDrawablePool.cs similarity index 93% rename from osu.Game.Rulesets.Osu/UI/OsuDrawablePool.cs rename to osu.Game.Rulesets.Osu/Objects/Drawables/OsuDrawablePool.cs index 148146f25a..1d9330b962 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuDrawablePool.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/OsuDrawablePool.cs @@ -5,9 +5,8 @@ using System; using osu.Framework.Graphics; using osu.Framework.Graphics.Pooling; using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Osu.Objects.Drawables; -namespace osu.Game.Rulesets.Osu.UI +namespace osu.Game.Rulesets.Osu.Objects.Drawables { public class OsuDrawablePool : DrawablePool where T : DrawableHitObject, new() From 7ac4d2c4be80ffe933bd76ff6c1333e6b3d498fc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 14 Nov 2020 18:05:30 +0900 Subject: [PATCH 18/19] Move "drawable" to first prefix --- .../Edit/{OsuEditDrawablePool.cs => DrawableOsuEditPool.cs} | 4 ++-- osu.Game.Rulesets.Osu/Edit/DrawableOsuEditRuleset.cs | 2 +- .../Drawables/{OsuDrawablePool.cs => DrawableOsuPool.cs} | 4 ++-- osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) rename osu.Game.Rulesets.Osu/Edit/{OsuEditDrawablePool.cs => DrawableOsuEditPool.cs} (95%) rename osu.Game.Rulesets.Osu/Objects/Drawables/{OsuDrawablePool.cs => DrawableOsuPool.cs} (89%) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuEditDrawablePool.cs b/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditPool.cs similarity index 95% rename from osu.Game.Rulesets.Osu/Edit/OsuEditDrawablePool.cs rename to osu.Game.Rulesets.Osu/Edit/DrawableOsuEditPool.cs index 946181d8a4..569031752e 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuEditDrawablePool.cs +++ b/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditPool.cs @@ -9,7 +9,7 @@ using osu.Game.Rulesets.Osu.Objects.Drawables; namespace osu.Game.Rulesets.Osu.Edit { - public class OsuEditDrawablePool : OsuDrawablePool + public class DrawableOsuEditPool : DrawableOsuPool where T : DrawableHitObject, new() { /// @@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Osu.Edit /// private const double editor_hit_object_fade_out_extension = 700; - public OsuEditDrawablePool(Func checkHittable, Action onLoaded, int initialSize, int? maximumSize = null) + public DrawableOsuEditPool(Func checkHittable, Action onLoaded, int initialSize, int? maximumSize = null) : base(checkHittable, onLoaded, initialSize, maximumSize) { } diff --git a/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditRuleset.cs b/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditRuleset.cs index 8af6fd65ce..547dff88b5 100644 --- a/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditRuleset.cs +++ b/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditRuleset.cs @@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Osu.Edit protected override GameplayCursorContainer CreateCursor() => null; protected override DrawablePool CreatePool(int initialSize, int? maximumSize = null) - => new OsuEditDrawablePool(CheckHittable, OnHitObjectLoaded, initialSize, maximumSize); + => new DrawableOsuEditPool(CheckHittable, OnHitObjectLoaded, initialSize, maximumSize); } } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/OsuDrawablePool.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuPool.cs similarity index 89% rename from osu.Game.Rulesets.Osu/Objects/Drawables/OsuDrawablePool.cs rename to osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuPool.cs index 1d9330b962..1b5fd50022 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/OsuDrawablePool.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuPool.cs @@ -8,13 +8,13 @@ using osu.Game.Rulesets.Objects.Drawables; namespace osu.Game.Rulesets.Osu.Objects.Drawables { - public class OsuDrawablePool : DrawablePool + public class DrawableOsuPool : DrawablePool where T : DrawableHitObject, new() { private readonly Func checkHittable; private readonly Action onLoaded; - public OsuDrawablePool(Func checkHittable, Action onLoaded, int initialSize, int? maximumSize = null) + public DrawableOsuPool(Func checkHittable, Action onLoaded, int initialSize, int? maximumSize = null) : base(initialSize, maximumSize) { this.checkHittable = checkHittable; diff --git a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs index e8ff6c410f..c816502d61 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs @@ -118,7 +118,7 @@ namespace osu.Game.Rulesets.Osu.UI protected virtual DrawablePool CreatePool(int initialSize, int? maximumSize = null) where TDrawable : DrawableHitObject, new() - => new OsuDrawablePool(CheckHittable, OnHitObjectLoaded, initialSize, maximumSize); + => new DrawableOsuPool(CheckHittable, OnHitObjectLoaded, initialSize, maximumSize); protected override HitObjectLifetimeEntry CreateLifetimeEntry(HitObject hitObject) => new OsuHitObjectLifetimeEntry(hitObject); From deea75b2e9f53f92eff40109adef3e162d76b68c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 14 Nov 2020 18:05:51 +0900 Subject: [PATCH 19/19] Fix typo in comment --- osu.Game.Rulesets.Osu/Edit/DrawableOsuEditPool.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditPool.cs b/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditPool.cs index 569031752e..776aacd143 100644 --- a/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditPool.cs +++ b/osu.Game.Rulesets.Osu/Edit/DrawableOsuEditPool.cs @@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Osu.Edit switch (hitObject) { default: - // there are quite a few drawable hit types we don't want to extent (spinners, ticks etc.) + // there are quite a few drawable hit types we don't want to extend (spinners, ticks etc.) return; case DrawableSlider _: