From 43f8f3638a9550a0d2d14d827ef3ee53f552f4e3 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 27 Dec 2020 02:42:13 +0300 Subject: [PATCH 001/203] Fix mod using reference equality unless casted to `IMod` --- osu.Game/Online/API/APIMod.cs | 4 ++-- osu.Game/Rulesets/Mods/IMod.cs | 3 +-- osu.Game/Rulesets/Mods/Mod.cs | 4 ++-- osu.Game/Scoring/ScoreInfo.cs | 4 ++-- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/osu.Game/Online/API/APIMod.cs b/osu.Game/Online/API/APIMod.cs index 780e5daa16..f4e0e1b11f 100644 --- a/osu.Game/Online/API/APIMod.cs +++ b/osu.Game/Online/API/APIMod.cs @@ -13,7 +13,7 @@ using osu.Game.Rulesets.Mods; namespace osu.Game.Online.API { - public class APIMod : IMod + public class APIMod : IMod, IEquatable { [JsonProperty("acronym")] public string Acronym { get; set; } @@ -52,7 +52,7 @@ namespace osu.Game.Online.API return resultMod; } - public bool Equals(IMod other) => Acronym == other?.Acronym; + public bool Equals(APIMod other) => Acronym == other?.Acronym; public override string ToString() { diff --git a/osu.Game/Rulesets/Mods/IMod.cs b/osu.Game/Rulesets/Mods/IMod.cs index a5e19f293c..448ad0eb30 100644 --- a/osu.Game/Rulesets/Mods/IMod.cs +++ b/osu.Game/Rulesets/Mods/IMod.cs @@ -1,12 +1,11 @@ // 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 Newtonsoft.Json; namespace osu.Game.Rulesets.Mods { - public interface IMod : IEquatable + public interface IMod { /// /// The shortened name of this mod. diff --git a/osu.Game/Rulesets/Mods/Mod.cs b/osu.Game/Rulesets/Mods/Mod.cs index b8dc7a2661..33550e070b 100644 --- a/osu.Game/Rulesets/Mods/Mod.cs +++ b/osu.Game/Rulesets/Mods/Mod.cs @@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Mods /// The base class for gameplay modifiers. /// [ExcludeFromDynamicCompile] - public abstract class Mod : IMod, IJsonSerializable + public abstract class Mod : IMod, IEquatable, IJsonSerializable { /// /// The name of this mod. @@ -149,6 +149,6 @@ namespace osu.Game.Rulesets.Mods return copy; } - public bool Equals(IMod other) => GetType() == other?.GetType(); + public bool Equals(Mod other) => GetType() == other?.GetType(); } } diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index f5192f3a40..59eaa994c2 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -252,11 +252,11 @@ namespace osu.Game.Scoring } [Serializable] - protected class DeserializedMod : IMod + protected class DeserializedMod : IMod, IEquatable { public string Acronym { get; set; } - public bool Equals(IMod other) => Acronym == other?.Acronym; + public bool Equals(DeserializedMod other) => Acronym == other?.Acronym; } public override string ToString() => $"{User} playing {Beatmap}"; From 5efcdbd431153a902fd510d727f9b02400afcb66 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 28 Dec 2020 15:19:28 +0300 Subject: [PATCH 002/203] Fix IMod now using reference equality as well --- osu.Game/Online/API/APIMod.cs | 3 ++- osu.Game/Rulesets/Mods/IMod.cs | 3 ++- osu.Game/Rulesets/Mods/Mod.cs | 3 ++- osu.Game/Scoring/ScoreInfo.cs | 3 ++- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/osu.Game/Online/API/APIMod.cs b/osu.Game/Online/API/APIMod.cs index f4e0e1b11f..c1b243c743 100644 --- a/osu.Game/Online/API/APIMod.cs +++ b/osu.Game/Online/API/APIMod.cs @@ -52,7 +52,8 @@ namespace osu.Game.Online.API return resultMod; } - public bool Equals(APIMod other) => Acronym == other?.Acronym; + public bool Equals(IMod other) => other is APIMod them && Equals(them); + public bool Equals(APIMod other) => ((IMod)this).Equals(other); public override string ToString() { diff --git a/osu.Game/Rulesets/Mods/IMod.cs b/osu.Game/Rulesets/Mods/IMod.cs index 448ad0eb30..a5e19f293c 100644 --- a/osu.Game/Rulesets/Mods/IMod.cs +++ b/osu.Game/Rulesets/Mods/IMod.cs @@ -1,11 +1,12 @@ // 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 Newtonsoft.Json; namespace osu.Game.Rulesets.Mods { - public interface IMod + public interface IMod : IEquatable { /// /// The shortened name of this mod. diff --git a/osu.Game/Rulesets/Mods/Mod.cs b/osu.Game/Rulesets/Mods/Mod.cs index 33550e070b..3ccebe4174 100644 --- a/osu.Game/Rulesets/Mods/Mod.cs +++ b/osu.Game/Rulesets/Mods/Mod.cs @@ -149,6 +149,7 @@ namespace osu.Game.Rulesets.Mods return copy; } - public bool Equals(Mod other) => GetType() == other?.GetType(); + public bool Equals(IMod other) => other is Mod them && Equals(them); + public bool Equals(Mod other) => Acronym == other?.Acronym; } } diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index 59eaa994c2..335671ea4e 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -256,7 +256,8 @@ namespace osu.Game.Scoring { public string Acronym { get; set; } - public bool Equals(DeserializedMod other) => Acronym == other?.Acronym; + bool IEquatable.Equals(IMod other) => other is DeserializedMod them && Equals(them); + public bool Equals(DeserializedMod other) => ((IMod)this).Equals(other); } public override string ToString() => $"{User} playing {Beatmap}"; From 41b79d938b401d7101440e4ee21f6f28a7590b4a Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 28 Dec 2020 15:30:52 +0300 Subject: [PATCH 003/203] Fix wrong checks.. --- osu.Game/Online/API/APIMod.cs | 2 +- osu.Game/Rulesets/Mods/Mod.cs | 2 +- osu.Game/Scoring/ScoreInfo.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Online/API/APIMod.cs b/osu.Game/Online/API/APIMod.cs index c1b243c743..f4fed4e5c5 100644 --- a/osu.Game/Online/API/APIMod.cs +++ b/osu.Game/Online/API/APIMod.cs @@ -53,7 +53,7 @@ namespace osu.Game.Online.API } public bool Equals(IMod other) => other is APIMod them && Equals(them); - public bool Equals(APIMod other) => ((IMod)this).Equals(other); + public bool Equals(APIMod other) => Acronym == other?.Acronym; public override string ToString() { diff --git a/osu.Game/Rulesets/Mods/Mod.cs b/osu.Game/Rulesets/Mods/Mod.cs index 3ccebe4174..dbb2a0fdc1 100644 --- a/osu.Game/Rulesets/Mods/Mod.cs +++ b/osu.Game/Rulesets/Mods/Mod.cs @@ -150,6 +150,6 @@ namespace osu.Game.Rulesets.Mods } public bool Equals(IMod other) => other is Mod them && Equals(them); - public bool Equals(Mod other) => Acronym == other?.Acronym; + public bool Equals(Mod other) => GetType() == other?.GetType(); } } diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index 335671ea4e..1e5742c358 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -257,7 +257,7 @@ namespace osu.Game.Scoring public string Acronym { get; set; } bool IEquatable.Equals(IMod other) => other is DeserializedMod them && Equals(them); - public bool Equals(DeserializedMod other) => ((IMod)this).Equals(other); + public bool Equals(DeserializedMod other) => Acronym == other?.Acronym; } public override string ToString() => $"{User} playing {Beatmap}"; From 9d9c0df64c9213757c51f379ee6be56cce68b87b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 29 Dec 2020 17:17:44 +0100 Subject: [PATCH 004/203] Make DeserializedMod equality members match other IMods --- osu.Game/Scoring/ScoreInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index 1e5742c358..3084afb833 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -256,7 +256,7 @@ namespace osu.Game.Scoring { public string Acronym { get; set; } - bool IEquatable.Equals(IMod other) => other is DeserializedMod them && Equals(them); + public bool Equals(IMod other) => other is DeserializedMod them && Equals(them); public bool Equals(DeserializedMod other) => Acronym == other?.Acronym; } From d58ef5310bf534f76a8d10170cdd71cd098bace0 Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Sun, 28 Mar 2021 17:36:22 +0200 Subject: [PATCH 005/203] Add verify tab Currently empty, but works. --- .../Input/Bindings/GlobalActionContainer.cs | 3 + osu.Game/Screens/Edit/Editor.cs | 9 + osu.Game/Screens/Edit/EditorScreenMode.cs | 3 + osu.Game/Screens/Edit/Verify/Issue.cs | 10 + osu.Game/Screens/Edit/Verify/IssueTable.cs | 191 ++++++++++++++++++ osu.Game/Screens/Edit/Verify/VerifyScreen.cs | 68 +++++++ 6 files changed, 284 insertions(+) create mode 100644 osu.Game/Screens/Edit/Verify/Issue.cs create mode 100644 osu.Game/Screens/Edit/Verify/IssueTable.cs create mode 100644 osu.Game/Screens/Edit/Verify/VerifyScreen.cs diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index 8ccdb9249e..6705d3ee61 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -193,6 +193,9 @@ namespace osu.Game.Input.Bindings [Description("Timing mode")] EditorTimingMode, + [Description("Verify mode")] + EditorVerifyMode, + [Description("Hold for HUD")] HoldForHUD, diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 0c24eb6a4d..57499bf219 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -36,6 +36,7 @@ using osu.Game.Screens.Edit.Compose; using osu.Game.Screens.Edit.Design; using osu.Game.Screens.Edit.Setup; using osu.Game.Screens.Edit.Timing; +using osu.Game.Screens.Edit.Verify; using osu.Game.Screens.Play; using osu.Game.Users; using osuTK.Graphics; @@ -445,6 +446,10 @@ namespace osu.Game.Screens.Edit menuBar.Mode.Value = EditorScreenMode.SongSetup; return true; + case GlobalAction.EditorVerifyMode: + menuBar.Mode.Value = EditorScreenMode.Verify; + return true; + default: return false; } @@ -632,6 +637,10 @@ namespace osu.Game.Screens.Edit case EditorScreenMode.Timing: currentScreen = new TimingScreen(); break; + + case EditorScreenMode.Verify: + currentScreen = new VerifyScreen(); + break; } LoadComponentAsync(currentScreen, newScreen => diff --git a/osu.Game/Screens/Edit/EditorScreenMode.cs b/osu.Game/Screens/Edit/EditorScreenMode.cs index 12cfcc605b..ecd39f9b57 100644 --- a/osu.Game/Screens/Edit/EditorScreenMode.cs +++ b/osu.Game/Screens/Edit/EditorScreenMode.cs @@ -18,5 +18,8 @@ namespace osu.Game.Screens.Edit [Description("timing")] Timing, + + [Description("verify")] + Verify, } } diff --git a/osu.Game/Screens/Edit/Verify/Issue.cs b/osu.Game/Screens/Edit/Verify/Issue.cs new file mode 100644 index 0000000000..25e913d819 --- /dev/null +++ b/osu.Game/Screens/Edit/Verify/Issue.cs @@ -0,0 +1,10 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Screens.Edit.Verify +{ + public class Issue + { + public readonly double Time; + } +} diff --git a/osu.Game/Screens/Edit/Verify/IssueTable.cs b/osu.Game/Screens/Edit/Verify/IssueTable.cs new file mode 100644 index 0000000000..6476cebe48 --- /dev/null +++ b/osu.Game/Screens/Edit/Verify/IssueTable.cs @@ -0,0 +1,191 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Input.Events; +using osu.Game.Extensions; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using osuTK.Graphics; + +namespace osu.Game.Screens.Edit.Verify +{ + public class IssueTable : TableContainer + { + private const float horizontal_inset = 20; + private const float row_height = 25; + private const int text_size = 14; + + private readonly FillFlowContainer backgroundFlow; + + public IssueTable() + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + + Padding = new MarginPadding { Horizontal = horizontal_inset }; + RowSize = new Dimension(GridSizeMode.Absolute, row_height); + + AddInternal(backgroundFlow = new FillFlowContainer + { + RelativeSizeAxes = Axes.Both, + Depth = 1f, + Padding = new MarginPadding { Horizontal = -horizontal_inset }, + Margin = new MarginPadding { Top = row_height } + }); + } + + public IEnumerable Issues + { + set + { + Content = null; + backgroundFlow.Clear(); + + if (value?.Any() != true) + return; + + foreach (var issue in value) + { + backgroundFlow.Add(new IssueTable.RowBackground(issue)); + } + + Columns = createHeaders(); + Content = value.Select((g, i) => createContent(i, g)).ToArray().ToRectangular(); + } + } + + private TableColumn[] createHeaders() + { + var columns = new List + { + new TableColumn(string.Empty, Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), + new TableColumn("Time", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), + new TableColumn(), + new TableColumn("Attributes", Anchor.CentreLeft), + }; + + return columns.ToArray(); + } + + private Drawable[] createContent(int index, Issue issue) => new Drawable[] + { + new OsuSpriteText + { + Text = $"#{index + 1}", + Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Bold), + Margin = new MarginPadding(10) + }, + new OsuSpriteText + { + Text = issue.Time.ToEditorFormattedString(), + Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Bold) + }, + null, + null //new ControlGroupAttributes(issue), + }; + + public class RowBackground : OsuClickableContainer + { + private readonly Issue issue; + private const int fade_duration = 100; + + private readonly Box hoveredBackground; + + [Resolved] + private EditorClock clock { get; set; } + + [Resolved] + private Bindable selectedIssue { get; set; } + + public RowBackground(Issue issue) + { + this.issue = issue; + RelativeSizeAxes = Axes.X; + Height = 25; + + AlwaysPresent = true; + + CornerRadius = 3; + Masking = true; + + Children = new Drawable[] + { + hoveredBackground = new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0, + }, + }; + + Action = () => + { + selectedIssue.Value = issue; + clock.SeekSmoothlyTo(issue.Time); + }; + } + + private Color4 colourHover; + private Color4 colourSelected; + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + hoveredBackground.Colour = colourHover = colours.BlueDarker; + colourSelected = colours.YellowDarker; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + selectedIssue.BindValueChanged(group => { Selected = issue == group.NewValue; }, true); + } + + private bool selected; + + protected bool Selected + { + get => selected; + set + { + if (value == selected) + return; + + selected = value; + updateState(); + } + } + + protected override bool OnHover(HoverEvent e) + { + updateState(); + return base.OnHover(e); + } + + protected override void OnHoverLost(HoverLostEvent e) + { + updateState(); + base.OnHoverLost(e); + } + + private void updateState() + { + hoveredBackground.FadeColour(selected ? colourSelected : colourHover, 450, Easing.OutQuint); + + if (selected || IsHovered) + hoveredBackground.FadeIn(fade_duration, Easing.OutQuint); + else + hoveredBackground.FadeOut(fade_duration, Easing.OutQuint); + } + } + } +} diff --git a/osu.Game/Screens/Edit/Verify/VerifyScreen.cs b/osu.Game/Screens/Edit/Verify/VerifyScreen.cs new file mode 100644 index 0000000000..c15cefae83 --- /dev/null +++ b/osu.Game/Screens/Edit/Verify/VerifyScreen.cs @@ -0,0 +1,68 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; + +namespace osu.Game.Screens.Edit.Verify +{ + public class VerifyScreen : EditorScreenWithTimeline + { + public VerifyScreen() + : base(EditorScreenMode.Verify) + { + } + + protected override Drawable CreateMainContent() => new GridContainer + { + RelativeSizeAxes = Axes.Both, + ColumnDimensions = new[] + { + new Dimension(), + new Dimension(GridSizeMode.Absolute, 200), + }, + Content = new[] + { + new Drawable[] + { + new ControlPointList() + }, + } + }; + + public class ControlPointList : CompositeDrawable + { + private IssueTable table; + + [Resolved] + private EditorClock clock { get; set; } + + [Resolved] + protected EditorBeatmap Beatmap { get; private set; } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + RelativeSizeAxes = Axes.Both; + + InternalChildren = new Drawable[] + { + new Box + { + Colour = colours.Gray0, + RelativeSizeAxes = Axes.Both, + }, + new OsuScrollContainer + { + RelativeSizeAxes = Axes.Both, + Child = table = new IssueTable(), + } + }; + } + } + } +} From f49481e308b68176e560a283870d6dbcbd43c581 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 26 Mar 2021 12:48:21 +0300 Subject: [PATCH 006/203] Add old skin spinner SPM background for testing --- .../Resources/old-skin/spinner-rpm.png | Bin 0 -> 10583 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/old-skin/spinner-rpm.png diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/old-skin/spinner-rpm.png b/osu.Game.Rulesets.Osu.Tests/Resources/old-skin/spinner-rpm.png new file mode 100644 index 0000000000000000000000000000000000000000..73753554f74fab988c7e5d8c7722d22928120682 GIT binary patch literal 10583 zcmW++1yob-8@_`9BNT?TfG|-yq+4PmrF-N+Kp4{9AmB(TL6i;!VT3S3Qba;Z5Qa!8 z5-Kq|9P#J(zvtZh-a6;I=RNm(-sgRuFVRq6^BNT!6#xL&w6);I003gXI+ulzUma)c zI-Xnwke{)p8c;jN{^#m~+)-B(eig5_Q}y$7{E4snt^tTxuxj+T znAzO+qdn2}l@j6@jS3Ag`ylD|Qr%mhb&&|dQtCyw^18zCGjG2i0XNO&zRx7EIAZK= z2VbUE>($QT?>sxSyGfV$wiyDD0AHs4Y}6&AvSMiO_0iq(azp_;zz?8@B)aP22n}(y z9bO5gf!qN{vjFr!rA3tH^oINB`k&j1FSrv}mVc=V)hAdA0SrJoNCm01#yD{z#WYHF zaIMSKVg(9PAq6x*#gxFSx9hmS=R5L(>t#I8xQA%&^$k3=A@9p1QK1Epz!&o9>%f6v zW&`8UVCo>*OzVAIlBqOl4;b(ujphY@pnz}2ozhd*SL1onPK{234}n43ltD01HL*7Epf^xPSo4w17M& z)0}07+23^IE!Rk5HUdV@15#z(p`t3~8cywhzb1ORGkj95t^Dr#>~gy6({I2^?}HKL zoo3AA3~4?5t9nE;rH<;c9B@Jayc=No6;ezzXvGwx z;c_(ZVs#)^twrn?Jb_1|Z{VoQ5-Bpc$WM6iXurw4ykNGby-9dOX!}NnsA|=~1u@Bx z+=zz)D6QtlmZU&ZfgT1K_@v!r%g}A8QN{g29q1mzf!uVpIf8;gz^rI}HXX3gs}~3;U7p z$^_yE#l^*0UL@48A;!YE1(uwc9ISA)5DRSw=Ow9~LNdyGG?H%E^mL#>(PiB3Tcaf* z9{3dpE!o^i_xQz9E4f$9;F<~@$n~~vsb@n6d%nE1)fGG0n zyFP z8ix0LNu)HT8v5njUy|sc*~UFIBb%S@H6Ghh{hGBLhH$~bC^`qaM^t6e3{U}>DUv&k zX(K;)d^z70T@Z|d=1h94L}z-i#8LcGdr(?mexHfXL9D+aAPq{VMJj-(WZ;Iyc)K}- zjH_YN;oSW`l_XegC^x@aJh+e*3|DMqacbie5>y+a`juDbca6+zS?=19Gv--?)3X)r zcy{>btjV10W-hKx+w|sjTC@H^U7(%@SokdUj?|a3c*Kb{UR*$u2|lD|d@C;*ooXK8 zv7va~hUvpJQ4I8>$6Yo0L)@fJNg9kfUUi~!rU|A7>s3Kh#!gZ}p^h#?dunk~?prFW zFd#Yx&q5vpL$GLq+$ug(>cuTK#(}g^LKIZ|iT-YRvZC<~%HyfDO)#t)d#gYk6`X~E z|3Rh}h~(hwytCUj6dF*RPB#%UzS!93Qw(9OKQ1OsMOo74n^S!hyf{@@@Pb|N1*&!v|5}tq`rn?oEsaj{sg)-G`yZ3%2smXKy!o|A$2C%~b z*wp&ZT?bas!nw%R`wJ96aoNAjG_);aUz?bP%%YTB(36hN4>eYP#}!;$2uuvV>G4Le z?@G)^>lP2TYBHoCqJp{?);qG?u`X!RxaH*4-BRB=CDLSWs!Jc>O{mQvZY#*L{!u9b zitn06_ZTB@7h1_L*zG0<8H@|#C3*@4z$`bj~NRAdIfS$CW#FbrxC`Vq%V4}$_A)8k5h5au7kh{G7 zzYLUu$5+-yZ%gqY-Yb&t8o#$#&U>@Q6sEZp?iv`{@4_UUPIA!9!z?N!(`b`GLZG{; zwdOqa{cmLsnRX{f-{Geu8_vp;+qEf-Y~_2jnQQKVHw8?d1FU4MnubZ6y-lL!R!#$F z?Aw^qR&h{;)9un}mbg#-`tjfAtXp*NooZ^Z9ll4pc3|~etgG+TT^y{=)QL5Euswzw zCz z_ke-sHJPq|2e;>%J!khWPIYr5&(5j|91$CxZN13x!cVe-LASokTnsFoV8=;;vmb4_ z_LujUE{BM1_a0z&20tD@zQhwEPj`6|OAq_MZXXg4Wy4q7$2)=-Ed^~#*)~Rnb2F`j z2SwC97{vG=XQzpCz-{6FFkj6(SU7qp-a$%GhCEuHzX0m?1m2Zx5!{31Q!%DY_bsEO z#S~LQ5#BYkkZpygD57PVoh#w|%(s11hz7z3d!QoO6|jHusBuEEDeAayw`H+l<4s%j zod!G-`|w;u1czK~)C50-8Wlg?zUnSZa*b|V|?leB^e|c#9=^j1k zRgr^|1F}s)$J-4Wor{&cUfq#r1*}X^QJUL_NLg7fEW`9Np9&x&ji({VwYpko!ZR>~ zBg=dAi4Yh}g4zw+?8!Md^=N8?c)ZCz!zL;~hJgXShHS`-oW6SxbGW5*$eujVu`+(o z#rSYBuOI0M0@%}sxe`SHAYSV_z=E)Rf%x_W;DTwfsvtU)mx?Stj;oMBdQ>2$b)TRl zJr#ly5~7`!Yw?OvaTp+hwE%k5HinDy?!_bH|+G{$)c z1{6Qp>!<1Ev^wkY0F{fYecG50V}w#s^1-Oe47FD^Q`4l`j@jqiM-yGi;3>`zNTK=4S(Z(JwIo`E)jk zyrDpnIs#M70Oii4h^Ef1)&HYXt)$TWnD{rhFm&^3qMHmD!&RdO6`>#gmdX@xD6tP7EfS z_1|xD-wie==TE*yXwG2EFbi{FD2C;{%EP7UFKP8 zny0e3d}x_1GqONG$JwjH=d4c%CaE|NJb`A5~+*5EBwvOz&yD?*=*r0I-0r&}clmo-hQ!d?JC810zcpMbFK)L&lq*gHP05x$OD#&8{e zHIb~J7)&$a#W!)~@9eYPVe2PzgnXnHG0(4`l4vIL^Y3PFEDn$QQ-SShXqx)ldGNhq ze_vNOL^u(LO9KHcbe)ZkSZ4spTD5y?HO40Vd3|WqBetd7?8m9j!Tr_*V64ry&upLhX)$m{sFY1Rq2O zPY1SQ0e@2%+|O4En|&8zlIfDyCaKg|4!t*7VvOV;A3^ftLWX~9v4BDD2q79gi~iRs zRx~@HlCyex9ChgO1U%XV;*9~i@nwEIy4GyOYaB8Zw`g%SEUzrvH)ghH&W%*7q z=*ff34nB|SX%YE_E?XPVf+8!+&8VNd2DcWKBg`zR$!w!r{NP z_zJOvGBFxKY7*wP+@#1txoK|VvhRj3y5xD}?w+n$z&KCNa(E?UZ|R#9g!b6mptsZZ0>(|g*A)_BY1Q)p-@q& zJ%YG$GBtIuJhEwn-}V=xUmiKI2|Qya-6ml z^8Q?25W?lEKludzJ!m^U{Zs)Pr(Sv-6c%+lZ(yK_<9jhLu8efI#F%XvTX{zV7H?c~ z?Ly8^ruADk_7q+1oZ5n;Ze~(lzao~`G(<>;ooy|;J?iSO0Y@Y~oxo0=DV=a%>V$-m zxOjD}SgQNzC)Jkmi`>FD~QGd+L@IZF19mhDcfg` z92y{gY&xjNiWgzWsowszVIlIbd_k>lRewg=xi^c1MuCy#p1$5yg{opI0) z4nUR`@SABzRGTM5Fu`=YmO9dAeUBWPYJRcNa3*&+!20yxtfw?g5{_+I5^7TjFq(%} z>-i56KleL!3YEC%!Kx~oN@9Wl6N*4K+#zL}lrB_k{FcUy;w4E1PbWIug(Md|MiknRiGJGf(lv&Q4Hi>spZb?q;E)5Ak5 zVN`?J<5qEZXh`19&~pqx5k3!IPU$#T?CU4>)(6<-`)wB7P>WM>ql%4=RY4V#QuRic z^6}_VVFTDTGpL$izn>U9UhBk7293G8t$~18u3}@}UB`kOD1Jd)UPWb1scJUFtMTUV zdT$?0>N?Yq-U6(1(YC3{rhG)4%+8H9S|+IR^Lh1Mnvm@_z9hzbs{HXWc(@LVbqH!y zE=EOatb@XG3i80|$SV@TteDRa{?U;NDfEcNA)l;kWg}COC2BNfD*P%Bk5=glf(bgt z^eD=v*LxHYZEn8OcbdAz5P0-#3^RoexBRdg_(fldPU8r3$j?;VW+!t61360N3Lqg* zj)jsSZGZXw7KU0oFDp^>3v@S)zpMs$CI3q>r#4ODS#<}^thLSe+nRfNY&RY|rtYHe ze}<>WapT<^zT^BfxBR+~z8z&`xWr*9pTisgF3YNfv3Dbd%6_#07n7@=-9_)WvaGWV zI77VAjkB0+V~iOyFAo?kq(Er|M*1bM@tttExMXWi$T24u*MS6lo4CS;Z_?V~PodFyNn zwr_>}zQ8VB;AILSA61q-mX-H64W8L|`5uBXC+*wcWsb03ky{hOzg}s2NzWJ*Dn_7C zs9i?1(7ILs6J*INzdm~}qqve{g}wyReZ6bfBGkWE4PsOQK~xZKn&gsRz8AZcm<30a zyyl@aD>J92=$kb7syLop`VXZF_mKXJhV^|Z4m-l&rN{qWP2&h8RQ@hO{xZ{G9bTE$t`?T8ti^FzKc&AqqQruzHO{Mmvx z;^^+B-Ddn$B0!n3x_;HsMI3FmAymMw1v5UC&lH0%Ii9hH>~KLduS+3Nl?>1jf)zhP zqR~%xOP?3FsM|6k{QD!ZM^!cZd74|T_(2bxExjVS3+EIA}1&o#B466EW{wg@R4 zVf*LD21QnBrYbcBd@vp;q{+@GHpruBzFa%&I4y>*px&eC1xr=9^g{yKiw`Wf?~YsH zA5MPOY!#XRBFyIaF@t&u>1Xxy`R9VT)sOsLQ6EFRQ>mk4;TV#R$%R{VyFV@>f@)eW zeXAF?4nxN*KN?DVUWo>QZ@vmrIRQYUkm%ZLR|Rl1Qm~gsQGwi$1dn8GYUywqI7(Cq z^eAebAo$A{Tr4jed)7REcggs~ChPU(M8@!cLzBK6)|1kT_&RJ0uTNq!<1`BC?(d#0 zM=Efks@~c7Nt;<~1JUNy3mdLhA|t&vp+QNQf9FD%r{nhRVgUhfi(K1u8>gVqdlHwM z&u0vZ&VTe8+pR?Ugvb@WvL9S$X4?Do$(fWy?7f?$STo(akUB!E)9(JpG11DVZlEkR z6dNjBP*CJ`zKjj3eK0=a@>*=OBP)i#RZaK=nwd; zT~$u_)|(FPw03|?rwz`M2g?kk|F*W zC{27cKDJ=3adWhT>=@kzDJ38i#f6+}7gFI*#t0&l{R=a;=54j);pZa)R)tneu}x8F$IEMa$FV z%X$482@MHPwUWOVJ#XgR0mI7oxO*sa6NY@3Dw$@F+-HEp-h4}r^<7b_rL9Ao?;8rg z6q!T1j4jGZQxWFDwiUEXegxb0nVa8f3r{C@FVE~R=Gr?w*J_UGHO!tjVVaPSB@`51 ziHHc5cVD#e(#{7hErdl82p2~u+nw!W;g@;03apUG{Cz=3kxLwwhQa3ejAo&fTbjKM zs9Wj9YKr)hCh>^|GVxhE?o;{IbI~pIW#`VZO-qrEB-^Bav2XQm2-^Ji>J}!tV}EQY zKABnca9?rV^Lvoh{r$x-l*<<#6kR;aBRyBqe6Q}ngWrF4M_+B&NEIU`L{veW2k-w> znY>|^-%1BH^{(e0Z?^fV z30Ay3)c9RwmFv?wWl$KUl!_9-;0bI}aNtE2+UOUUxP-P`V;aiBqxH%OnIa zE{UdZ{aYc`)ro_ef1q)yS||qNvI#>Y7GoB09NeJ>Md*C!)^C*)awQeo2ToWZQJFkb zk0cMY8cSY{Ce+Ss$iKZ)u2s@vo`Um8Sydbjck#7fA*21w#sK3on%!iAmLT(t$X-^` zMC2+FXLXGj=C}L{0`#ezzOj8oOXbepiML;CRHtcx{=l;05buYXX3sYl-kVL_ddCaL zdv?^0mu0JQJn9oD>LmL~U^NTV(;55CYYwKtOlJtobM29~ z@Gy7$924R?K@B4WQ;~u?rCQwd+2B%s8a08MpLTWY?Oc6T@Q4xq{{!(8}q*^(ReIG0?vY9!mYmqitF?@x_!$A?XG{vK-7~EKf81_A&Mz z!8Lm=kC}<GDk(|4 z3PV*6P%7=7xbn()EwUHM?W=C|wG*1NLQ9h+kNepI$A)Tc6|%;a4p*9GlMWOJg*Ngb zf6vp7g?#A$hHbY89mEg`H;amj#tS2k2EJ17Q)`gL^^)L_HOjkRo;W>IKKlKzeOe^) zqS3;oz|Z$&r)1*eQ`*!|sLEcxNgYA9gZ+T9FcY(YaS@@1UR|fjWZ-CuTcHl&11k@0 z1@+1qtK1`WEm#P7vvRzy$|nw|6!*_2{Eb#>a4_^Rfz|gj-A&7hc-3FTIiKX6&;_z| z-E)w|ISd3~ht2VG&3bpEb`F=?k4v6%&aVRwz8m*hYSjfwvULO14oaC zS}re^!h**a6?n%B74Dy}N9`^I*#+-b%y!t-3kr$ia<)2;7Zi@(MD2}7D$$UfkJu{_ z@lhwwF8{zq^=unwX}XT4m*h{Liz=W0*qlGx8_FV_*Uxz>aDX3SpCdb8N1js`EgcRl z{rfoHq)>2iFg0b5`~H}C=ykTJR1|TL(e4?5q;{I`K5e<&wnB46ouj*nJzbq)%?q3R z>tpsWBG)v>BfW5(>Uc{*+5ZSxtLKF9;HtXb`{6S=a)7M#L|yM$FISH&6@Qv~uRG54 z8eo4h=`}y|=P-%cF4#vPE%$M;q~~0h@CCMe|2gUZ(<4@?fqkv{YE(sqCnc6K19siX zwr*My>R4t+d&L<7F1tGCE+<+pRm<$^I4(~G>&6&D#tt(Q7Mt9J*yb;A=8}_-$T7en zQqN?X>V_p39u#kM2T$e4t|3sl$PBHTcK1WcD9{8?<1Lo_@ zuYcIHhxsW}!49X^i{?-B29zTl0-}0&wEeM0GccGByI^gO!jw zXYh5y+O6>YjiUMY0m~~JUhD<7HZ~(NsVVR$7hduZK5fUf)CNDgZ9D6n*%T z#Dy#VMPCNh6XOuZE5_KXXmF~qbiVkB5P2NN{HpAB==u={PV`D>IX6(eKnJya-?}I| zAHO_bYolfkEASjBKkrh9pY>(j#(k*T@|Qoasy5N>58GYbUro-tk_f>uJ0TW{lIZ0G9$Pw^ish zsjxq_1m9*ZubiDyOSDN{6skWBjNA(9`d3w;K#<-4TBIDMtnH{b9ZK?2i%eC;huw%J z&Yh0_F5E+s?ZfMd32$!!4GoQ0z9a?!pf5>t8YM}0&2xg%!GAwH>hxO`NQ;KJ6%y@Q z(CyeOaxwBryR@Q79{?OS@Yq+)o(u2YybE6+6YN8GKNVf+5OUxX0Duhtw-$i#>18Ye zeAeS=rG8hI!T;$u%!;w_@6g6a(B5k^BJ_N@A#XCR@!_QOvF2>?Wa-0d8ejmO8b<7E zx8OXV;{6yAcIL6x>8b>>?6N## zQ<1{oI^2ZV=@6afUy&B~oS0DPS-If?^EnW}hj{U(?WU;#DfJ7*<8!;2RjajqA!n7H zMI>kbt5QCX_JwUTJV)fthgmCt9IVQthE0r~&84uWr>SlARBw5Nxwg){FzNf@uqIcW zMi`jE*8iT331aH-Gk($QCk3)n8H@4`wCYeGu9z5bMy_NSaDai6Pu5-qQN+H)JcIlH zeit86q)ItrQ5ql<>@JPr(QLqCQiH|=l%;@hY@V}EB=_Y9`^d}9`4JC7)tkcBjqQ(5 z10OF*x5+_BQh{hYa^2_Nbl?sbMs**2LyMh1JN->wX&Db{YlMeolH_s0YR+REa?EPL z>g6d9#sLR3ZxMJY%&(8&^$CZe`8n5P7*<@U%w8uKQCD73K}oCa#K=< zgMWRETv|P!64ZO)oJR&!PHzmE7$`SXebTWi&Pev|1baPMc zJuxA4cb_pYoo+M0qG50fv^iBes_79XwW@7iKS8ASmywyw6k3>BEq;t?o)8d`a}<%^ z2Le5ZGbdyJ1lN0XCh6S<$#dg&Q`?EbS#nwe__m;UoS8Zv=5m9;EuNhCp9~)V>PVxQ$ z*8JI|-QRs7*Y$i1ZIkc4e>!@3qPki` z4|R_AbVa*!coL~%)88-D{`b`k1I)r@!1*Kv-c@Y@@64Z_bEczbSB`O7ebJ z7NlFqtjqb7sIYL}j9u9IS)l^KU9SLn5r`zYe9wP1G&0b0``fL^{a5FS ziNdKZOUn0JIyxSP71TP>1)o;rx)PNAfA@8r6Px*9Nb7^!&+leAyY2oeIy;!}JhS+YD`~?&3fPDP@ zyONXRCUqu{xe-eSe%1Y1F{Hz(wvyKK>KpE7G$~QddbrJ(EXLT)gzEn?brc zxbW@ehq<{eKpNwW;J<$T`fzd*Z0RNK&wwbWQx(+1TZ(vmK-hT~u67kwbS=2g1-{w( zDtTBPUx5DOiQ$&cww4s=ZHJ#k(zUsn9pD^2+g@v#iJkqVzOPHS7_kZVAXZMN;C4&oa;(YpG;EPU)F&h?V^Fr+f zqDHC|*Dth1)UU?&^1*>+E8NiuG%Cngluf+(oe@n2PMW ze_|OPv(j!HIsT_asL4__ldNU({VXq&-G7V-s=0Ey8?g~znIdm_njhi(CeA8GZ$$=`sB|i(Zu=e zh_}JPsn;7y{;ayhZl_jhlFPm4YWd7*`#93y`h$kR6FMf2~&{N{1UXai9oj}RE;^y0ZfUtJs;+!C1V>AySNFF*G4A%`R88TG+q zSpyp22B3m&zL`xOK8RymA@{?bZd}+A3u{6;B86_bjW7PU Date: Fri, 26 Mar 2021 13:09:44 +0300 Subject: [PATCH 007/203] Refactor spinner SPM counter for skinning purposes --- .../Mods/TestSceneOsuModAutoplay.cs | 10 +-- .../Mods/TestSceneOsuModSpunOut.cs | 4 +- .../TestSceneSpinnerRotation.cs | 10 +-- .../Objects/Drawables/DrawableSpinner.cs | 48 +++++------- .../Skinning/Default/DefaultSpinner.cs | 64 ++++++++++++++++ ...rSpmCounter.cs => SpinnerSpmCalculator.cs} | 74 +++++-------------- 6 files changed, 114 insertions(+), 96 deletions(-) rename osu.Game.Rulesets.Osu/Skinning/Default/{SpinnerSpmCounter.cs => SpinnerSpmCalculator.cs} (61%) diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModAutoplay.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModAutoplay.cs index 856b6554b9..0ba775e5c7 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModAutoplay.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModAutoplay.cs @@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods private void runSpmTest(Mod mod) { - SpinnerSpmCounter spmCounter = null; + SpinnerSpmCalculator spmCalculator = null; CreateModTest(new ModTestData { @@ -53,13 +53,13 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods PassCondition = () => Player.ScoreProcessor.JudgedHits >= 1 }); - AddUntilStep("fetch SPM counter", () => + AddUntilStep("fetch SPM calculator", () => { - spmCounter = this.ChildrenOfType().SingleOrDefault(); - return spmCounter != null; + spmCalculator = this.ChildrenOfType().SingleOrDefault(); + return spmCalculator != null; }); - AddUntilStep("SPM is correct", () => Precision.AlmostEquals(spmCounter.SpinsPerMinute, 477, 5)); + AddUntilStep("SPM is correct", () => Precision.AlmostEquals(spmCalculator.Result.Value, 477, 5)); } } } diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModSpunOut.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModSpunOut.cs index 7df5ca0f7c..24e69703a6 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModSpunOut.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModSpunOut.cs @@ -47,8 +47,8 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods Beatmap = singleSpinnerBeatmap, PassCondition = () => { - var counter = Player.ChildrenOfType().SingleOrDefault(); - return counter != null && Precision.AlmostEquals(counter.SpinsPerMinute, 286, 1); + var counter = Player.ChildrenOfType().SingleOrDefault(); + return counter != null && Precision.AlmostEquals(counter.Result.Value, 286, 1); } }); } diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs index ac8d5c81bc..14c709cae1 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs @@ -168,13 +168,13 @@ namespace osu.Game.Rulesets.Osu.Tests double estimatedSpm = 0; addSeekStep(1000); - AddStep("retrieve spm", () => estimatedSpm = drawableSpinner.SpmCounter.SpinsPerMinute); + AddStep("retrieve spm", () => estimatedSpm = drawableSpinner.SpinsPerMinute.Value); addSeekStep(2000); - AddAssert("spm still valid", () => Precision.AlmostEquals(drawableSpinner.SpmCounter.SpinsPerMinute, estimatedSpm, 1.0)); + AddAssert("spm still valid", () => Precision.AlmostEquals(drawableSpinner.SpinsPerMinute.Value, estimatedSpm, 1.0)); addSeekStep(1000); - AddAssert("spm still valid", () => Precision.AlmostEquals(drawableSpinner.SpmCounter.SpinsPerMinute, estimatedSpm, 1.0)); + AddAssert("spm still valid", () => Precision.AlmostEquals(drawableSpinner.SpinsPerMinute.Value, estimatedSpm, 1.0)); } [TestCase(0.5)] @@ -188,7 +188,7 @@ namespace osu.Game.Rulesets.Osu.Tests AddStep("retrieve spinner state", () => { expectedProgress = drawableSpinner.Progress; - expectedSpm = drawableSpinner.SpmCounter.SpinsPerMinute; + expectedSpm = drawableSpinner.SpinsPerMinute.Value; }); addSeekStep(0); @@ -197,7 +197,7 @@ namespace osu.Game.Rulesets.Osu.Tests addSeekStep(1000); AddAssert("progress almost same", () => Precision.AlmostEquals(expectedProgress, drawableSpinner.Progress, 0.05)); - AddAssert("spm almost same", () => Precision.AlmostEquals(expectedSpm, drawableSpinner.SpmCounter.SpinsPerMinute, 2.0)); + AddAssert("spm almost same", () => Precision.AlmostEquals(expectedSpm, drawableSpinner.SpinsPerMinute.Value, 2.0)); } private Replay applyRateAdjustment(Replay scoreReplay, double rate) => new Replay diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs index 39e78a14aa..1a89fc308e 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs @@ -30,7 +30,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables public new OsuSpinnerJudgementResult Result => (OsuSpinnerJudgementResult)base.Result; public SpinnerRotationTracker RotationTracker { get; private set; } - public SpinnerSpmCounter SpmCounter { get; private set; } + + private SpinnerSpmCalculator spmCalculator; private Container ticks; private PausableSkinnableSound spinningSample; @@ -43,7 +44,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables /// public IBindable GainedBonus => gainedBonus; - private readonly Bindable gainedBonus = new Bindable(); + private readonly Bindable gainedBonus = new BindableDouble(); + + /// + /// The number of spins per minute this spinner is spinning at, for display purposes. + /// + public readonly IBindable SpinsPerMinute = new BindableDouble(); private const double fade_out_duration = 160; @@ -63,7 +69,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Origin = Anchor.Centre; RelativeSizeAxes = Axes.Both; - InternalChildren = new Drawable[] + AddInternal(spmCalculator = new SpinnerSpmCalculator + { + Result = { BindTarget = SpinsPerMinute }, + }); + + AddRangeInternal(new Drawable[] { ticks = new Container(), new AspectContainer @@ -77,20 +88,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables RotationTracker = new SpinnerRotationTracker(this) } }, - SpmCounter = new SpinnerSpmCounter - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Y = 120, - Alpha = 0 - }, spinningSample = new PausableSkinnableSound { Volume = { Value = 0 }, Looping = true, Frequency = { Value = spinning_sample_initial_frequency } } - }; + }); PositionBindable.BindValueChanged(pos => Position = pos.NewValue); } @@ -161,17 +165,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables } } - protected override void UpdateStartTimeStateTransforms() - { - base.UpdateStartTimeStateTransforms(); - - if (Result?.TimeStarted is double startTime) - { - using (BeginAbsoluteSequence(startTime)) - fadeInCounter(); - } - } - protected override void UpdateHitStateTransforms(ArmedState state) { base.UpdateHitStateTransforms(state); @@ -282,22 +275,17 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { base.UpdateAfterChildren(); - if (!SpmCounter.IsPresent && RotationTracker.Tracking) - { - Result.TimeStarted ??= Time.Current; - fadeInCounter(); - } + if (Result.TimeStarted == null && RotationTracker.Tracking) + Result.TimeStarted = Time.Current; // don't update after end time to avoid the rate display dropping during fade out. // this shouldn't be limited to StartTime as it causes weirdness with the underlying calculation, which is expecting updates during that period. if (Time.Current <= HitObject.EndTime) - SpmCounter.SetRotation(Result.RateAdjustedRotation); + spmCalculator.SetRotation(Result.RateAdjustedRotation); updateBonusScore(); } - private void fadeInCounter() => SpmCounter.FadeIn(HitObject.TimeFadeIn); - private static readonly int score_per_tick = new SpinnerBonusTick.OsuSpinnerBonusTickJudgement().MaxNumericResult; private int wholeSpins; diff --git a/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSpinner.cs b/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSpinner.cs index 891821fe2f..ae8c03dad1 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSpinner.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Default/DefaultSpinner.cs @@ -1,6 +1,7 @@ // 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.Globalization; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -19,6 +20,9 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default private OsuSpriteText bonusCounter; + private Container spmContainer; + private OsuSpriteText spmCounter; + public DefaultSpinner() { RelativeSizeAxes = Axes.Both; @@ -46,11 +50,37 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default Origin = Anchor.Centre, Font = OsuFont.Numeric.With(size: 24), Y = -120, + }, + spmContainer = new Container + { + Alpha = 0f, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Y = 120, + Children = new[] + { + spmCounter = new OsuSpriteText + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Text = @"0", + Font = OsuFont.Numeric.With(size: 24) + }, + new OsuSpriteText + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Text = @"SPINS PER MINUTE", + Font = OsuFont.Numeric.With(size: 12), + Y = 30 + } + } } }); } private IBindable gainedBonus; + private IBindable spinsPerMinute; protected override void LoadComplete() { @@ -63,6 +93,40 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default bonusCounter.FadeOutFromOne(1500); bonusCounter.ScaleTo(1.5f).Then().ScaleTo(1f, 1000, Easing.OutQuint); }); + + spinsPerMinute = drawableSpinner.SpinsPerMinute.GetBoundCopy(); + spinsPerMinute.BindValueChanged(spm => + { + spmCounter.Text = Math.Truncate(spm.NewValue).ToString(@"#0"); + }, true); + + drawableSpinner.ApplyCustomUpdateState += updateStateTransforms; + updateStateTransforms(drawableSpinner, drawableSpinner.State.Value); + } + + protected override void Update() + { + base.Update(); + + if (!spmContainer.IsPresent && drawableSpinner.Result?.TimeStarted != null) + fadeCounterOnTimeStart(); + } + + private void updateStateTransforms(DrawableHitObject drawableHitObject, ArmedState state) + { + if (!(drawableHitObject is DrawableSpinner)) + return; + + fadeCounterOnTimeStart(); + } + + private void fadeCounterOnTimeStart() + { + if (drawableSpinner.Result?.TimeStarted is double startTime) + { + using (BeginAbsoluteSequence(startTime)) + spmContainer.FadeIn(drawableSpinner.HitObject.TimeFadeIn); + } } } } diff --git a/osu.Game.Rulesets.Osu/Skinning/Default/SpinnerSpmCounter.cs b/osu.Game.Rulesets.Osu/Skinning/Default/SpinnerSpmCalculator.cs similarity index 61% rename from osu.Game.Rulesets.Osu/Skinning/Default/SpinnerSpmCounter.cs rename to osu.Game.Rulesets.Osu/Skinning/Default/SpinnerSpmCalculator.cs index 69355f624b..a5205bbb8c 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Default/SpinnerSpmCounter.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Default/SpinnerSpmCalculator.cs @@ -1,77 +1,37 @@ // 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 osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Framework.Utils; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; using osu.Game.Rulesets.Objects.Drawables; namespace osu.Game.Rulesets.Osu.Skinning.Default { - public class SpinnerSpmCounter : Container + public class SpinnerSpmCalculator : Component { + private readonly Queue records = new Queue(); + private const double spm_count_duration = 595; // not using hundreds to avoid frame rounding issues + + /// + /// The resultant spins per minute value, which is updated via . + /// + public IBindable Result => result; + + private readonly Bindable result = new BindableDouble(); + [Resolved] private DrawableHitObject drawableSpinner { get; set; } - private readonly OsuSpriteText spmText; - - public SpinnerSpmCounter() - { - Children = new Drawable[] - { - spmText = new OsuSpriteText - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Text = @"0", - Font = OsuFont.Numeric.With(size: 24) - }, - new OsuSpriteText - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Text = @"SPINS PER MINUTE", - Font = OsuFont.Numeric.With(size: 12), - Y = 30 - } - }; - } - protected override void LoadComplete() { base.LoadComplete(); drawableSpinner.HitObjectApplied += resetState; } - private double spm; - - public double SpinsPerMinute - { - get => spm; - private set - { - if (value == spm) return; - - spm = value; - spmText.Text = Math.Truncate(value).ToString(@"#0"); - } - } - - private struct RotationRecord - { - public float Rotation; - public double Time; - } - - private readonly Queue records = new Queue(); - private const double spm_count_duration = 595; // not using hundreds to avoid frame rounding issues - public void SetRotation(float currentRotation) { // Never calculate SPM by same time of record to avoid 0 / 0 = NaN or X / 0 = Infinity result. @@ -88,7 +48,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default while (records.Count > 0 && Time.Current - records.Peek().Time > spm_count_duration) record = records.Dequeue(); - SpinsPerMinute = (currentRotation - record.Rotation) / (Time.Current - record.Time) * 1000 * 60 / 360; + result.Value = (currentRotation - record.Rotation) / (Time.Current - record.Time) * 1000 * 60 / 360; } records.Enqueue(new RotationRecord { Rotation = currentRotation, Time = Time.Current }); @@ -96,7 +56,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default private void resetState(DrawableHitObject hitObject) { - SpinsPerMinute = 0; + result.Value = 0; records.Clear(); } @@ -107,5 +67,11 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default if (drawableSpinner != null) drawableSpinner.HitObjectApplied -= resetState; } + + private struct RotationRecord + { + public float Rotation; + public double Time; + } } } From f848ef534747babe4c020e3b1a759b2fc42d259d Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 26 Mar 2021 13:10:04 +0300 Subject: [PATCH 008/203] Add legacy spinner SPM counter support --- .../Skinning/Legacy/LegacySpinner.cs | 39 +++++++++++++++++-- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySpinner.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySpinner.cs index 064b7a4680..7eb6898abc 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySpinner.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacySpinner.cs @@ -28,6 +28,8 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy protected const float SPRITE_SCALE = 0.625f; + private const float spm_hide_offset = 50f; + protected DrawableSpinner DrawableSpinner { get; private set; } private Sprite spin; @@ -35,6 +37,9 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy private LegacySpriteText bonusCounter; + private Sprite spmBackground; + private LegacySpriteText spmCounter; + [BackgroundDependencyLoader] private void load(DrawableHitObject drawableHitObject, ISkinSource source) { @@ -79,11 +84,27 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy Scale = new Vector2(SPRITE_SCALE), Y = SPINNER_TOP_OFFSET + 299, }.With(s => s.Font = s.Font.With(fixedWidth: false)), + spmBackground = new Sprite + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopLeft, + Texture = source.GetTexture("spinner-rpm"), + Scale = new Vector2(SPRITE_SCALE), + Position = new Vector2(-87, 445 + spm_hide_offset), + }, + spmCounter = new LegacySpriteText(source, LegacyFont.Score) + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopRight, + Scale = new Vector2(SPRITE_SCALE * 0.9f), + Position = new Vector2(80, 448 + spm_hide_offset), + }.With(s => s.Font = s.Font.With(fixedWidth: false)), } }); } private IBindable gainedBonus; + private IBindable spinsPerMinute; private readonly Bindable completed = new Bindable(); @@ -99,6 +120,12 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy bonusCounter.ScaleTo(SPRITE_SCALE * 2f).Then().ScaleTo(SPRITE_SCALE * 1.28f, 800, Easing.Out); }); + spinsPerMinute = DrawableSpinner.SpinsPerMinute.GetBoundCopy(); + spinsPerMinute.BindValueChanged(spm => + { + spmCounter.Text = Math.Truncate(spm.NewValue).ToString(@"#0"); + }, true); + completed.BindValueChanged(onCompletedChanged, true); DrawableSpinner.ApplyCustomUpdateState += UpdateStateTransforms; @@ -142,10 +169,16 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy switch (drawableHitObject) { case DrawableSpinner d: - double fadeOutLength = Math.Min(400, d.HitObject.Duration); + using (BeginAbsoluteSequence(d.HitObject.StartTime - d.HitObject.TimeFadeIn)) + { + spmBackground.MoveToOffset(new Vector2(0, -spm_hide_offset), d.HitObject.TimeFadeIn, Easing.Out); + spmCounter.MoveToOffset(new Vector2(0, -spm_hide_offset), d.HitObject.TimeFadeIn, Easing.Out); + } - using (BeginAbsoluteSequence(drawableHitObject.HitStateUpdateTime - fadeOutLength, true)) - spin.FadeOutFromOne(fadeOutLength); + double spinFadeOutLength = Math.Min(400, d.HitObject.Duration); + + using (BeginAbsoluteSequence(drawableHitObject.HitStateUpdateTime - spinFadeOutLength, true)) + spin.FadeOutFromOne(spinFadeOutLength); break; case DrawableSpinnerTick d: From 9504fe3f3c1fb78b8b4f6510ec988933f26bd71d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 30 Mar 2021 13:43:05 +0900 Subject: [PATCH 009/203] Inline add of spm calculation (no need for it to be a separate call) --- .../Objects/Drawables/DrawableSpinner.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs index 1a89fc308e..3a4753761a 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs @@ -69,13 +69,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Origin = Anchor.Centre; RelativeSizeAxes = Axes.Both; - AddInternal(spmCalculator = new SpinnerSpmCalculator - { - Result = { BindTarget = SpinsPerMinute }, - }); - AddRangeInternal(new Drawable[] { + spmCalculator = new SpinnerSpmCalculator + { + Result = { BindTarget = SpinsPerMinute }, + }, ticks = new Container(), new AspectContainer { From b24ce66a0d4bfcc9f53d84fe998b6d4027ef8a5a Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Wed, 7 Apr 2021 14:35:33 +0200 Subject: [PATCH 010/203] Add check/issue classes --- osu.Game/Rulesets/Edit/Checker.cs | 25 ++++++ .../Edit/Verify/Components/BeatmapCheck.cs | 19 +++++ .../Screens/Edit/Verify/Components/Check.cs | 41 +++++++++ .../Edit/Verify/Components/CheckMetadata.cs | 62 ++++++++++++++ .../Screens/Edit/Verify/Components/Issue.cs | 83 ++++++++++++++++++ .../Edit/Verify/Components/IssueTemplate.cs | 84 +++++++++++++++++++ osu.Game/Screens/Edit/Verify/Issue.cs | 10 --- 7 files changed, 314 insertions(+), 10 deletions(-) create mode 100644 osu.Game/Rulesets/Edit/Checker.cs create mode 100644 osu.Game/Screens/Edit/Verify/Components/BeatmapCheck.cs create mode 100644 osu.Game/Screens/Edit/Verify/Components/Check.cs create mode 100644 osu.Game/Screens/Edit/Verify/Components/CheckMetadata.cs create mode 100644 osu.Game/Screens/Edit/Verify/Components/Issue.cs create mode 100644 osu.Game/Screens/Edit/Verify/Components/IssueTemplate.cs delete mode 100644 osu.Game/Screens/Edit/Verify/Issue.cs diff --git a/osu.Game/Rulesets/Edit/Checker.cs b/osu.Game/Rulesets/Edit/Checker.cs new file mode 100644 index 0000000000..1c267c3435 --- /dev/null +++ b/osu.Game/Rulesets/Edit/Checker.cs @@ -0,0 +1,25 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using System.Linq; +using osu.Game.Beatmaps; +using osu.Game.Checks; +using osu.Game.Screens.Edit.Verify.Components; + +namespace osu.Game.Rulesets.Edit +{ + public abstract class Checker + { + // These are all mode-invariant, hence here instead of in e.g. `OsuChecker`. + private readonly List beatmapChecks = new List + { + new CheckMetadataVowels() + }; + + public virtual IEnumerable Run(IBeatmap beatmap) + { + return beatmapChecks.SelectMany(check => check.Run(beatmap)); + } + } +} diff --git a/osu.Game/Screens/Edit/Verify/Components/BeatmapCheck.cs b/osu.Game/Screens/Edit/Verify/Components/BeatmapCheck.cs new file mode 100644 index 0000000000..7297dab60d --- /dev/null +++ b/osu.Game/Screens/Edit/Verify/Components/BeatmapCheck.cs @@ -0,0 +1,19 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using osu.Game.Beatmaps; + +namespace osu.Game.Screens.Edit.Verify.Components +{ + public abstract class BeatmapCheck : Check + { + /// + /// Returns zero, one, or several issues detected by this + /// check on the given beatmap. + /// + /// The beatmap to run the check on. + /// + public abstract override IEnumerable Run(IBeatmap beatmap); + } +} diff --git a/osu.Game/Screens/Edit/Verify/Components/Check.cs b/osu.Game/Screens/Edit/Verify/Components/Check.cs new file mode 100644 index 0000000000..2ae21fd350 --- /dev/null +++ b/osu.Game/Screens/Edit/Verify/Components/Check.cs @@ -0,0 +1,41 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; + +namespace osu.Game.Screens.Edit.Verify.Components +{ + public abstract class Check + { + /// + /// Returns the for this check. + /// Basically, its information. + /// + /// + public abstract CheckMetadata Metadata(); + + /// + /// The templates for issues that this check may use. + /// Basically, what issues this check can detect. + /// + /// + public abstract IEnumerable Templates(); + + protected Check() + { + foreach (var template in Templates()) + template.Origin = this; + } + } + + public abstract class Check : Check + { + /// + /// Returns zero, one, or several issues detected by + /// this check on the given object. + /// + /// The object to run the check on. + /// + public abstract IEnumerable Run(T obj); + } +} diff --git a/osu.Game/Screens/Edit/Verify/Components/CheckMetadata.cs b/osu.Game/Screens/Edit/Verify/Components/CheckMetadata.cs new file mode 100644 index 0000000000..1cac99d47d --- /dev/null +++ b/osu.Game/Screens/Edit/Verify/Components/CheckMetadata.cs @@ -0,0 +1,62 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Screens.Edit.Verify +{ + public class CheckMetadata + { + /// + /// The category of an issue. + /// + public enum CheckCategory + { + /// Anything to do with control points. + Timing, + + /// Anything to do with artist, title, creator, etc. + Metadata, + + /// Anything to do with non-audio files, e.g. background, skin, sprites, and video. + Resources, + + /// Anything to do with audio files, e.g. song and hitsounds. + Audio, + + /// Anything to do with files that don't fit into the above, e.g. unused, osu, or osb. + Files, + + /// Anything to do with hitobjects unrelated to spread. + Compose, + + /// Anything to do with difficulty levels or their progression. + Spread, + + /// Anything to do with variables like CS, OD, AR, HP, and global SV. + Settings, + + /// Anything to do with hitobject feedback. + Hitsounds, + + /// Anything to do with storyboarding, breaks, video offset, etc. + Events + } + + /// + /// The category this check belongs to. E.g. , + /// , or . + /// + public readonly CheckCategory Category; + + /// + /// Describes the issue(s) that this check looks for. Keep this brief, such that + /// it fits into "No {description}". E.g. "Offscreen objects" / "Too short sliders". + /// + public readonly string Description; + + public CheckMetadata(CheckCategory category, string description) + { + Category = category; + Description = description; + } + } +} diff --git a/osu.Game/Screens/Edit/Verify/Components/Issue.cs b/osu.Game/Screens/Edit/Verify/Components/Issue.cs new file mode 100644 index 0000000000..fe81cb9335 --- /dev/null +++ b/osu.Game/Screens/Edit/Verify/Components/Issue.cs @@ -0,0 +1,83 @@ +// 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 osu.Game.Extensions; +using osu.Game.Rulesets.Objects; + +namespace osu.Game.Screens.Edit.Verify.Components +{ + public class Issue + { + /// + /// The time which this issue is associated with, if any, otherwise null. + /// + public double? Time; + + /// + /// The hitobjects which this issue is associated with. Empty by default. + /// + public IReadOnlyList HitObjects; + + /// + /// The template which this issue is using. This provides properties + /// such as the , and the + /// . + /// + public IssueTemplate Template; + + /// + /// The arguments that give this issue its context, based on the + /// . These are then substituted into the + /// . + /// E.g. timestamps, which diff is being compared to, what some volume is, etc. + /// + public object[] Arguments; + + public Issue(IssueTemplate template, params object[] args) + { + Time = null; + HitObjects = System.Array.Empty(); + Template = template; + Arguments = args; + + if (template.Origin == null) + { + throw new ArgumentException( + "A template had no origin. Make sure the `Templates()` method contains all templates used." + ); + } + } + + public Issue(double? time, IssueTemplate template, params object[] args) + : this(template, args) + { + Time = time; + } + + public Issue(IEnumerable hitObjects, IssueTemplate template, params object[] args) + : this(template, args) + { + Time = hitObjects.FirstOrDefault()?.StartTime; + HitObjects = hitObjects.ToArray(); + } + + public override string ToString() + { + return Template.Message(Arguments); + } + + public string GetEditorTimestamp() + { + // TODO: Editor timestamp formatting is handled in https://github.com/ppy/osu/pull/12030 + // We may be able to use that here too (if we decouple it from the HitObjectComposer class). + + if (Time == null) + return string.Empty; + + return Time.Value.ToEditorFormattedString(); + } + } +} diff --git a/osu.Game/Screens/Edit/Verify/Components/IssueTemplate.cs b/osu.Game/Screens/Edit/Verify/Components/IssueTemplate.cs new file mode 100644 index 0000000000..b178fa7122 --- /dev/null +++ b/osu.Game/Screens/Edit/Verify/Components/IssueTemplate.cs @@ -0,0 +1,84 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using Humanizer; +using osu.Framework.Graphics; +using osuTK.Graphics; + +namespace osu.Game.Screens.Edit.Verify.Components +{ + public class IssueTemplate + { + /// + /// The type, or severity, of an issue. This decides its priority. + /// + public enum IssueType + { + /// A must-fix in the vast majority of cases. + Problem = 3, + + /// A possible mistake. Often requires critical thinking. + Warning = 2, + + // TODO: Try/catch all checks run and return error templates if exceptions occur. + /// An error occurred and a complete check could not be made. + Error = 1, + + // TODO: Negligible issues should be hidden by default. + /// A possible mistake so minor/unlikely that it can often be safely ignored. + Negligible = 0, + } + + /// + /// The check that this template originates from. + /// + public Check Origin; + + /// + /// The type of the issue. E.g. , + /// , or . + /// + public readonly IssueType Type; + + /// + /// The unformatted message given when this issue is detected. + /// This gets populated later when an issue is constructed with this template. + /// E.g. "Inconsistent snapping (1/{0}) with [{1}] (1/{2})." + /// + public readonly string UnformattedMessage; + + public IssueTemplate(IssueType type, string unformattedMessage) + { + Type = type; + UnformattedMessage = unformattedMessage; + } + + /// + /// Returns the formatted message given the arguments used to format it. + /// + /// The arguments used to format the message. + /// + public string Message(params object[] args) => UnformattedMessage.FormatWith(args); + + public static readonly Color4 PROBLEM_RED = new Colour4(1.0f, 0.4f, 0.4f, 1.0f); + public static readonly Color4 WARNING_YELLOW = new Colour4(1.0f, 0.8f, 0.2f, 1.0f); + public static readonly Color4 NEGLIGIBLE_GREEN = new Colour4(0.33f, 0.8f, 0.5f, 1.0f); + public static readonly Color4 ERROR_GRAY = new Colour4(0.5f, 0.5f, 0.5f, 1.0f); + + /// + /// Returns the colour corresponding to the type of this issue. + /// + /// + public Colour4 TypeColour() + { + return Type switch + { + IssueType.Problem => PROBLEM_RED, + IssueType.Warning => WARNING_YELLOW, + IssueType.Negligible => NEGLIGIBLE_GREEN, + IssueType.Error => ERROR_GRAY, + _ => Color4.White + }; + } + } +} diff --git a/osu.Game/Screens/Edit/Verify/Issue.cs b/osu.Game/Screens/Edit/Verify/Issue.cs deleted file mode 100644 index 25e913d819..0000000000 --- a/osu.Game/Screens/Edit/Verify/Issue.cs +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -namespace osu.Game.Screens.Edit.Verify -{ - public class Issue - { - public readonly double Time; - } -} From 0343ef7f147b12a8bd71dfced08c6b503b6b8b79 Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Wed, 7 Apr 2021 14:36:43 +0200 Subject: [PATCH 011/203] Add ruleset-specific checker --- osu.Game.Rulesets.Osu/Edit/OsuChecker.cs | 30 ++++++++++++++++++++++++ osu.Game.Rulesets.Osu/OsuRuleset.cs | 2 ++ osu.Game/Rulesets/Ruleset.cs | 2 ++ 3 files changed, 34 insertions(+) create mode 100644 osu.Game.Rulesets.Osu/Edit/OsuChecker.cs diff --git a/osu.Game.Rulesets.Osu/Edit/OsuChecker.cs b/osu.Game.Rulesets.Osu/Edit/OsuChecker.cs new file mode 100644 index 0000000000..9918b53c85 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Edit/OsuChecker.cs @@ -0,0 +1,30 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using System.Linq; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Osu.Edit.Checks; +using osu.Game.Screens.Edit.Verify.Components; + +namespace osu.Game.Rulesets.Osu.Edit +{ + public class OsuChecker : Checker + { + public readonly List beatmapChecks = new List + { + new CheckConsecutiveCircles() + }; + + public override IEnumerable Run(IBeatmap beatmap) + { + // Also run mode-invariant checks. + foreach (var issue in base.Run(beatmap)) + yield return issue; + + foreach (var issue in beatmapChecks.SelectMany(check => check.Run(beatmap))) + yield return issue; + } + } +} diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 838d707d64..74679bd578 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -206,6 +206,8 @@ namespace osu.Game.Rulesets.Osu public override HitObjectComposer CreateHitObjectComposer() => new OsuHitObjectComposer(this); + public override Checker CreateChecker() => new OsuChecker(); + public override string Description => "osu!"; public override string ShortName => SHORT_NAME; diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index 38d30a2e31..71f80c9982 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -202,6 +202,8 @@ namespace osu.Game.Rulesets public virtual HitObjectComposer CreateHitObjectComposer() => null; + public virtual Checker CreateChecker() => null; + public virtual Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.Solid.QuestionCircle }; public virtual IResourceStore CreateResourceStore() => new NamespacedResourceStore(new DllResourceStore(GetType().Assembly), @"Resources"); From 9c4604e3c5fdbfa9f5201f39ee41584fa5b47d18 Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Wed, 7 Apr 2021 14:36:53 +0200 Subject: [PATCH 012/203] Add example checks --- .../Edit/Checks/CheckConsecutiveCircles.cs | 86 +++++++++++++++++++ osu.Game/Checks/CheckMetadataVowels.cs | 65 ++++++++++++++ 2 files changed, 151 insertions(+) create mode 100644 osu.Game.Rulesets.Osu/Edit/Checks/CheckConsecutiveCircles.cs create mode 100644 osu.Game/Checks/CheckMetadataVowels.cs diff --git a/osu.Game.Rulesets.Osu/Edit/Checks/CheckConsecutiveCircles.cs b/osu.Game.Rulesets.Osu/Edit/Checks/CheckConsecutiveCircles.cs new file mode 100644 index 0000000000..c41c0dac2b --- /dev/null +++ b/osu.Game.Rulesets.Osu/Edit/Checks/CheckConsecutiveCircles.cs @@ -0,0 +1,86 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using System.Linq; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Screens.Edit.Verify; +using osu.Game.Screens.Edit.Verify.Components; + +namespace osu.Game.Rulesets.Osu.Edit.Checks +{ + public class CheckConsecutiveCircles : BeatmapCheck + { + private const double consecutive_threshold = 3; + private const double delta_time_min_expected = 300; + private const double delta_time_min_threshold = 100; + + public override CheckMetadata Metadata() => new CheckMetadata + ( + category: CheckMetadata.CheckCategory.Spread, + description: "Too many or fast consecutive circles." + ); + + private IssueTemplate templateManyInARow = new IssueTemplate + ( + type: IssueTemplate.IssueType.Problem, + unformattedMessage: "There are {0} circles in a row here, expected at most {1}." + ); + + private IssueTemplate templateTooFast = new IssueTemplate + ( + type: IssueTemplate.IssueType.Warning, + unformattedMessage: "These circles are too fast ({0:0} ms), expected at least {1:0} ms." + ); + + private IssueTemplate templateAlmostTooFast = new IssueTemplate + ( + type: IssueTemplate.IssueType.Negligible, + unformattedMessage: "These circles are almost too fast ({0:0} ms), expected at least {1:0} ms." + ); + + public override IEnumerable Templates() => new[] + { + templateManyInARow, + templateTooFast, + templateAlmostTooFast + }; + + public override IEnumerable Run(IBeatmap beatmap) + { + List prevCircles = new List(); + + foreach (HitObject hitobject in beatmap.HitObjects) + { + if (!(hitobject is HitCircle circle) || hitobject == beatmap.HitObjects.Last()) + { + if (prevCircles.Count > consecutive_threshold) + { + yield return new Issue( + prevCircles, + templateManyInARow, + prevCircles.Count, consecutive_threshold + ); + } + + prevCircles.Clear(); + continue; + } + + double? prevDeltaTime = circle.StartTime - prevCircles.LastOrDefault()?.StartTime; + prevCircles.Add(circle); + + if (prevDeltaTime == null || prevDeltaTime >= delta_time_min_expected) + continue; + + yield return new Issue( + prevCircles.TakeLast(2), + prevDeltaTime < delta_time_min_threshold ? templateTooFast : templateAlmostTooFast, + prevDeltaTime, delta_time_min_expected + ); + } + } + } +} diff --git a/osu.Game/Checks/CheckMetadataVowels.cs b/osu.Game/Checks/CheckMetadataVowels.cs new file mode 100644 index 0000000000..8bcfe89c3a --- /dev/null +++ b/osu.Game/Checks/CheckMetadataVowels.cs @@ -0,0 +1,65 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using System.Linq; +using osu.Game.Beatmaps; +using osu.Game.Screens.Edit.Verify; +using osu.Game.Screens.Edit.Verify.Components; + +namespace osu.Game.Checks +{ + public class CheckMetadataVowels : BeatmapCheck + { + private static readonly char[] vowels = { 'a', 'e', 'i', 'o', 'u' }; + + public override CheckMetadata Metadata() => new CheckMetadata + ( + category: CheckMetadata.CheckCategory.Metadata, + description: "Metadata fields contain vowels" + ); + + public override IEnumerable Templates() => new[] + { + templateArtistHasVowels + }; + + private IssueTemplate templateArtistHasVowels = new IssueTemplate + ( + type: IssueTemplate.IssueType.Warning, + unformattedMessage: "The {0} field \"{1}\" contains the vowel(s) {2}." + ); + + public override IEnumerable Run(IBeatmap beatmap) + { + foreach (var issue in GetVowelIssues("artist", beatmap.Metadata.Artist)) + yield return issue; + + foreach (var issue in GetVowelIssues("unicode artist", beatmap.Metadata.ArtistUnicode)) + yield return issue; + + foreach (var issue in GetVowelIssues("title", beatmap.Metadata.Title)) + yield return issue; + + foreach (var issue in GetVowelIssues("unicode title", beatmap.Metadata.TitleUnicode)) + yield return issue; + } + + private IEnumerable GetVowelIssues(string fieldName, string fieldValue) + { + if (fieldValue == null) + // Unicode fields can be null if same as respective romanized fields. + yield break; + + List matches = vowels.Where(c => fieldValue.ToLower().Contains(c)).ToList(); + + if (!matches.Any()) + yield break; + + yield return new Issue( + templateArtistHasVowels, + fieldName, fieldValue, string.Join(", ", matches) + ); + } + } +} From bab36e529a5b9e1ff5b2300e9def2b5bc11687a7 Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Wed, 7 Apr 2021 14:38:43 +0200 Subject: [PATCH 013/203] Update UI with new components --- osu.Game/Screens/Edit/Verify/IssueSettings.cs | 47 +++++++++ osu.Game/Screens/Edit/Verify/IssueTable.cs | 91 +++++++++-------- osu.Game/Screens/Edit/Verify/VerifyScreen.cs | 98 +++++++++++++++---- .../Screens/Edit/Verify/VisibilitySettings.cs | 52 ++++++++++ 4 files changed, 229 insertions(+), 59 deletions(-) create mode 100644 osu.Game/Screens/Edit/Verify/IssueSettings.cs create mode 100644 osu.Game/Screens/Edit/Verify/VisibilitySettings.cs diff --git a/osu.Game/Screens/Edit/Verify/IssueSettings.cs b/osu.Game/Screens/Edit/Verify/IssueSettings.cs new file mode 100644 index 0000000000..608be877de --- /dev/null +++ b/osu.Game/Screens/Edit/Verify/IssueSettings.cs @@ -0,0 +1,47 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; + +namespace osu.Game.Screens.Edit.Verify +{ + public class IssueSettings : CompositeDrawable + { + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + RelativeSizeAxes = Axes.Both; + + InternalChildren = new Drawable[] + { + new Box + { + Colour = colours.Gray3, + RelativeSizeAxes = Axes.Both, + }, + new OsuScrollContainer + { + RelativeSizeAxes = Axes.Both, + Child = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Children = createSections() + }, + } + }; + } + + private IReadOnlyList createSections() => new Drawable[] + { + new VisibilitySettings() + }; + } +} diff --git a/osu.Game/Screens/Edit/Verify/IssueTable.cs b/osu.Game/Screens/Edit/Verify/IssueTable.cs index 6476cebe48..c70695a849 100644 --- a/osu.Game/Screens/Edit/Verify/IssueTable.cs +++ b/osu.Game/Screens/Edit/Verify/IssueTable.cs @@ -4,16 +4,16 @@ using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; -using osu.Framework.Bindables; using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; -using osu.Game.Extensions; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; +using osu.Game.Input.Bindings; +using osu.Game.Screens.Edit.Verify.Components; using osuTK.Graphics; namespace osu.Game.Screens.Edit.Verify @@ -34,6 +34,9 @@ namespace osu.Game.Screens.Edit.Verify Padding = new MarginPadding { Horizontal = horizontal_inset }; RowSize = new Dimension(GridSizeMode.Absolute, row_height); + Masking = true; + CornerRadius = 6; + AddInternal(backgroundFlow = new FillFlowContainer { RelativeSizeAxes = Axes.Both, @@ -55,7 +58,7 @@ namespace osu.Game.Screens.Edit.Verify foreach (var issue in value) { - backgroundFlow.Add(new IssueTable.RowBackground(issue)); + backgroundFlow.Add(new RowBackground(issue)); } Columns = createHeaders(); @@ -68,9 +71,10 @@ namespace osu.Game.Screens.Edit.Verify var columns = new List { new TableColumn(string.Empty, Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), + new TableColumn("Type", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), new TableColumn("Time", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), - new TableColumn(), - new TableColumn("Attributes", Anchor.CentreLeft), + new TableColumn("Message", Anchor.CentreLeft), + new TableColumn("Category", Anchor.CentreRight, new Dimension(GridSizeMode.AutoSize)), }; return columns.ToArray(); @@ -81,21 +85,36 @@ namespace osu.Game.Screens.Edit.Verify new OsuSpriteText { Text = $"#{index + 1}", + Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Medium) + }, + new OsuSpriteText + { + Text = issue.Template.Type.ToString(), + Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Bold), + Margin = new MarginPadding { Left = 10 }, + Colour = issue.Template.TypeColour() + }, + new OsuSpriteText + { + Text = issue.GetEditorTimestamp(), Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Bold), Margin = new MarginPadding(10) }, new OsuSpriteText { - Text = issue.Time.ToEditorFormattedString(), - Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Bold) + Text = issue.ToString(), + Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Medium) }, - null, - null //new ControlGroupAttributes(issue), + new OsuSpriteText + { + Text = issue.Template.Origin.Metadata().Category.ToString(), + Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Bold), + Margin = new MarginPadding(10) + } }; public class RowBackground : OsuClickableContainer { - private readonly Issue issue; private const int fade_duration = 100; private readonly Box hoveredBackground; @@ -104,13 +123,15 @@ namespace osu.Game.Screens.Edit.Verify private EditorClock clock { get; set; } [Resolved] - private Bindable selectedIssue { get; set; } + private Editor editor { get; set; } + + [Resolved] + private EditorBeatmap editorBeatmap { get; set; } public RowBackground(Issue issue) { - this.issue = issue; RelativeSizeAxes = Axes.X; - Height = 25; + Height = row_height; AlwaysPresent = true; @@ -128,41 +149,29 @@ namespace osu.Game.Screens.Edit.Verify Action = () => { - selectedIssue.Value = issue; - clock.SeekSmoothlyTo(issue.Time); + // Supposed to work like clicking timestamps outside of the game. + // TODO: Is there already defined behaviour for this I may be able to call? + + if (issue.Time != null) + { + clock.Seek(issue.Time.Value); + editor.OnPressed(GlobalAction.EditorComposeMode); + } + + if (!issue.HitObjects.Any()) + return; + + editorBeatmap.SelectedHitObjects.Clear(); + editorBeatmap.SelectedHitObjects.AddRange(issue.HitObjects); }; } private Color4 colourHover; - private Color4 colourSelected; [BackgroundDependencyLoader] private void load(OsuColour colours) { hoveredBackground.Colour = colourHover = colours.BlueDarker; - colourSelected = colours.YellowDarker; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - selectedIssue.BindValueChanged(group => { Selected = issue == group.NewValue; }, true); - } - - private bool selected; - - protected bool Selected - { - get => selected; - set - { - if (value == selected) - return; - - selected = value; - updateState(); - } } protected override bool OnHover(HoverEvent e) @@ -179,9 +188,9 @@ namespace osu.Game.Screens.Edit.Verify private void updateState() { - hoveredBackground.FadeColour(selected ? colourSelected : colourHover, 450, Easing.OutQuint); + hoveredBackground.FadeColour(colourHover, 450, Easing.OutQuint); - if (selected || IsHovered) + if (IsHovered) hoveredBackground.FadeIn(fade_duration, Easing.OutQuint); else hoveredBackground.FadeOut(fade_duration, Easing.OutQuint); diff --git a/osu.Game/Screens/Edit/Verify/VerifyScreen.cs b/osu.Game/Screens/Edit/Verify/VerifyScreen.cs index c15cefae83..88397fdbff 100644 --- a/osu.Game/Screens/Edit/Verify/VerifyScreen.cs +++ b/osu.Game/Screens/Edit/Verify/VerifyScreen.cs @@ -1,40 +1,70 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Linq; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Containers; +using osu.Game.Graphics.UserInterface; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Edit; +using osuTK; namespace osu.Game.Screens.Edit.Verify { - public class VerifyScreen : EditorScreenWithTimeline + public class VerifyScreen : EditorScreen { + private Ruleset ruleset; + private static Checker checker; // TODO: Should not be static, but apparently needs to be? + public VerifyScreen() : base(EditorScreenMode.Verify) { } - protected override Drawable CreateMainContent() => new GridContainer + protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) { - RelativeSizeAxes = Axes.Both, - ColumnDimensions = new[] - { - new Dimension(), - new Dimension(GridSizeMode.Absolute, 200), - }, - Content = new[] - { - new Drawable[] - { - new ControlPointList() - }, - } - }; + var dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); - public class ControlPointList : CompositeDrawable + ruleset = parent.Get>().Value.BeatmapInfo.Ruleset?.CreateInstance(); + checker = ruleset?.CreateChecker(); + + return dependencies; + } + + [BackgroundDependencyLoader] + private void load() + { + Child = new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding(20), + Child = new GridContainer + { + RelativeSizeAxes = Axes.Both, + ColumnDimensions = new[] + { + new Dimension(), + new Dimension(GridSizeMode.Absolute, 200), + }, + Content = new[] + { + new Drawable[] + { + new IssueList(), + new IssueSettings(), + }, + } + } + }; + } + + public class IssueList : CompositeDrawable { private IssueTable table; @@ -60,9 +90,41 @@ namespace osu.Game.Screens.Edit.Verify { RelativeSizeAxes = Axes.Both, Child = table = new IssueTable(), - } + }, + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, + Margin = new MarginPadding(20), + Children = new Drawable[] + { + new TriangleButton + { + Text = "Refresh", + Action = refresh, + Size = new Vector2(120, 40), + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, + }, + } + }, }; } + + protected override void LoadComplete() + { + base.LoadComplete(); + + refresh(); + } + + private void refresh() + { + table.Issues = checker.Run(Beatmap) + .OrderByDescending(issue => issue.Template.Type) + .ThenByDescending(issue => issue.Template.Origin.Metadata().Category); + } } } } diff --git a/osu.Game/Screens/Edit/Verify/VisibilitySettings.cs b/osu.Game/Screens/Edit/Verify/VisibilitySettings.cs new file mode 100644 index 0000000000..6488c616e4 --- /dev/null +++ b/osu.Game/Screens/Edit/Verify/VisibilitySettings.cs @@ -0,0 +1,52 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics.UserInterfaceV2; +using osuTK; + +namespace osu.Game.Screens.Edit.Verify +{ + internal class VisibilitySettings : CompositeDrawable + { + [BackgroundDependencyLoader] + private void load() + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + + Padding = new MarginPadding(10); + + InternalChildren = new Drawable[] + { + new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Spacing = new Vector2(10), + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + new LabelledSwitchButton + { + Label = "Show problems", + Current = new Bindable(true) + }, + new LabelledSwitchButton + { + Label = "Show warnings", + Current = new Bindable(true) + }, + new LabelledSwitchButton + { + Label = "Show negligibles" + } + } + }, + }; + } + } +} From 648a9d52584896db0cd3ce3cc7b5bb350bb46d32 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 7 Apr 2021 23:15:09 +0900 Subject: [PATCH 014/203] Add multiplayer spectator player grid --- .../Multiplayer/Spectate/PlayerGrid.cs | 142 ++++++++++++++++++ .../Multiplayer/Spectate/PlayerGrid_Cell.cs | 78 ++++++++++ .../Multiplayer/Spectate/PlayerGrid_Facade.cs | 19 +++ 3 files changed, 239 insertions(+) create mode 100644 osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/PlayerGrid.cs create mode 100644 osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/PlayerGrid_Cell.cs create mode 100644 osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/PlayerGrid_Facade.cs diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/PlayerGrid.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/PlayerGrid.cs new file mode 100644 index 0000000000..a8bd1db9dc --- /dev/null +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/PlayerGrid.cs @@ -0,0 +1,142 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osuTK; + +namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate +{ + public partial class PlayerGrid : CompositeDrawable + { + private const float player_spacing = 5; + + public Drawable MaximisedFacade => maximisedFacade; + + private readonly PlayerGridFacade maximisedFacade; + private readonly Container paddingContainer; + private readonly FillFlowContainer facadeContainer; + private readonly Container cellContainer; + + public PlayerGrid() + { + InternalChildren = new Drawable[] + { + paddingContainer = new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding(player_spacing), + Children = new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.Both, + Child = facadeContainer = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Spacing = new Vector2(player_spacing), + } + }, + maximisedFacade = new PlayerGridFacade { RelativeSizeAxes = Axes.Both } + } + }, + cellContainer = new Container { RelativeSizeAxes = Axes.Both } + }; + } + + public void AddContent(Drawable content) + { + var facade = new PlayerGridFacade(); + facadeContainer.Add(facade); + + var cell = new Cell(content) { ToggleMaximisationState = toggleMaximisationState }; + cell.SetFacade(facade); + + cellContainer.Add(cell); + } + + // A depth value that gets decremented every time a new instance is maximised in order to reduce underlaps. + private float maximisedInstanceDepth; + + private void toggleMaximisationState(Cell target) + { + // Iterate through all cells to ensure only one is maximised at any time. + foreach (var i in cellContainer) + { + if (i == target) + i.IsMaximised = !i.IsMaximised; + else + i.IsMaximised = false; + + if (i.IsMaximised) + { + // Transfer cell to the maximised facade. + i.SetFacade(maximisedFacade); + cellContainer.ChangeChildDepth(i, maximisedInstanceDepth -= 0.001f); + } + else + { + // Transfer cell back to its original facade. + i.SetFacade(facadeContainer[cellContainer.IndexOf(target)]); + } + } + } + + protected override void Update() + { + base.Update(); + + Vector2 cellsPerDimension; + + switch (facadeContainer.Count) + { + case 1: + cellsPerDimension = Vector2.One; + break; + + case 2: + cellsPerDimension = new Vector2(2, 1); + break; + + case 3: + case 4: + cellsPerDimension = new Vector2(2); + break; + + case 5: + case 6: + cellsPerDimension = new Vector2(3, 2); + break; + + case 7: + case 8: + case 9: + // 3 rows / 3 cols. + cellsPerDimension = new Vector2(3); + break; + + case 10: + case 11: + case 12: + // 3 rows / 4 cols. + cellsPerDimension = new Vector2(4, 3); + break; + + default: + // 4 rows / 4 cols. + cellsPerDimension = new Vector2(4); + break; + } + + // Total spacing between cells + Vector2 totalCellSpacing = player_spacing * (cellsPerDimension - Vector2.One); + + Vector2 fullSize = paddingContainer.ChildSize - totalCellSpacing; + Vector2 cellSize = Vector2.Divide(fullSize, new Vector2(cellsPerDimension.X, cellsPerDimension.Y)); + + foreach (var cell in facadeContainer) + cell.Size = cellSize; + } + } +} diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/PlayerGrid_Cell.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/PlayerGrid_Cell.cs new file mode 100644 index 0000000000..1f6e718aa7 --- /dev/null +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/PlayerGrid_Cell.cs @@ -0,0 +1,78 @@ +// 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 JetBrains.Annotations; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Input.Events; +using osuTK; + +namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate +{ + public partial class PlayerGrid + { + private class Cell : CompositeDrawable + { + public Action ToggleMaximisationState; + public bool IsMaximised; + + private PlayerGridFacade facade; + private bool isTracking = true; + + public Cell(Drawable content) + { + Origin = Anchor.Centre; + + InternalChild = content; + } + + protected override void Update() + { + base.Update(); + + if (isTracking) + { + Position = getFinalPosition(); + Size = getFinalSize(); + } + } + + public void SetFacade([NotNull] PlayerGridFacade newFacade) + { + PlayerGridFacade lastFacade = facade; + facade = newFacade; + + if (lastFacade == null || lastFacade == newFacade) + return; + + isTracking = false; + + this.MoveTo(getFinalPosition(), 400, Easing.OutQuint).ResizeTo(getFinalSize(), 400, Easing.OutQuint) + .Then() + .OnComplete(_ => + { + if (facade == newFacade) + isTracking = true; + }); + } + + private Vector2 getFinalPosition() + { + var topLeft = Parent.ToLocalSpace(facade.ToScreenSpace(Vector2.Zero)); + return topLeft + facade.DrawSize / 2; + } + + private Vector2 getFinalSize() => facade.DrawSize; + + // Todo: Temporary? + protected override bool ShouldBeConsideredForInput(Drawable child) => false; + + protected override bool OnClick(ClickEvent e) + { + ToggleMaximisationState(this); + return true; + } + } + } +} diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/PlayerGrid_Facade.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/PlayerGrid_Facade.cs new file mode 100644 index 0000000000..c565e2fec6 --- /dev/null +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/PlayerGrid_Facade.cs @@ -0,0 +1,19 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics; + +namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate +{ + public partial class PlayerGrid + { + private class PlayerGridFacade : Drawable + { + public PlayerGridFacade() + { + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + } + } + } +} From 024adb699c44b5748eebc77a33c2010eb841f562 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 8 Apr 2021 00:06:14 +0900 Subject: [PATCH 015/203] Add test and fix several issues --- ...TestSceneMultiplayerSpectatorPlayerGrid.cs | 115 ++++++++++++++++++ .../Multiplayer/Spectate/PlayerGrid.cs | 30 +++-- .../Multiplayer/Spectate/PlayerGrid_Cell.cs | 26 +++- .../Multiplayer/Spectate/PlayerGrid_Facade.cs | 7 +- 4 files changed, 161 insertions(+), 17 deletions(-) create mode 100644 osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectatorPlayerGrid.cs diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectatorPlayerGrid.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectatorPlayerGrid.cs new file mode 100644 index 0000000000..c0958c7fe8 --- /dev/null +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectatorPlayerGrid.cs @@ -0,0 +1,115 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Utils; +using osu.Game.Screens.OnlinePlay.Multiplayer.Spectate; +using osuTK.Graphics; +using osuTK.Input; + +namespace osu.Game.Tests.Visual.Multiplayer +{ + public class TestSceneMultiplayerSpectatorPlayerGrid : OsuManualInputManagerTestScene + { + private PlayerGrid grid; + + [SetUp] + public void Setup() => Schedule(() => + { + Child = grid = new PlayerGrid { RelativeSizeAxes = Axes.Both }; + }); + + [Test] + public void TestMaximiseAndMinimise() + { + addCells(2); + + assertMaximisation(0, false, true); + assertMaximisation(1, false, true); + + clickCell(0); + assertMaximisation(0, true); + assertMaximisation(1, false, true); + clickCell(0); + assertMaximisation(0, false); + assertMaximisation(1, false, true); + + clickCell(1); + assertMaximisation(1, true); + assertMaximisation(0, false, true); + clickCell(1); + assertMaximisation(1, false); + assertMaximisation(0, false, true); + } + + [Test] + public void TestClickBothCellsSimultaneously() + { + addCells(2); + + AddStep("click cell 0 then 1", () => + { + InputManager.MoveMouseTo(grid.Content.ElementAt(0)); + InputManager.Click(MouseButton.Left); + + InputManager.MoveMouseTo(grid.Content.ElementAt(1)); + InputManager.Click(MouseButton.Left); + }); + + assertMaximisation(1, true); + assertMaximisation(0, false); + } + + [TestCase(1)] + [TestCase(2)] + [TestCase(3)] + [TestCase(4)] + [TestCase(5)] + [TestCase(9)] + [TestCase(11)] + [TestCase(12)] + [TestCase(15)] + [TestCase(16)] + public void TestCellCount(int count) + { + addCells(count); + AddWaitStep("wait for display", 2); + } + + private void addCells(int count) => AddStep($"add {count} grid cells", () => + { + for (int i = 0; i < count; i++) + grid.Add(new GridContent()); + }); + + private void clickCell(int index) => AddStep($"click cell index {index}", () => + { + InputManager.MoveMouseTo(grid.Content.ElementAt(index)); + InputManager.Click(MouseButton.Left); + }); + + private void assertMaximisation(int index, bool shouldBeMaximised, bool instant = false) + { + string assertionText = $"cell index {index} {(shouldBeMaximised ? "is" : "is not")} maximised"; + + if (instant) + AddAssert(assertionText, checkAction); + else + AddUntilStep(assertionText, checkAction); + + bool checkAction() => Precision.AlmostEquals(grid.MaximisedFacade.DrawSize, grid.Content.ElementAt(index).DrawSize, 10) == shouldBeMaximised; + } + + private class GridContent : Box + { + public GridContent() + { + RelativeSizeAxes = Axes.Both; + Colour = new Color4(RNG.NextSingle(), RNG.NextSingle(), RNG.NextSingle(), 1f); + } + } + } +} diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/PlayerGrid.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/PlayerGrid.cs index a8bd1db9dc..f41948217c 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/PlayerGrid.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/PlayerGrid.cs @@ -13,9 +13,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate public Drawable MaximisedFacade => maximisedFacade; - private readonly PlayerGridFacade maximisedFacade; + private readonly Facade maximisedFacade; private readonly Container paddingContainer; - private readonly FillFlowContainer facadeContainer; + private readonly FillFlowContainer facadeContainer; private readonly Container cellContainer; public PlayerGrid() @@ -31,38 +31,50 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate new Container { RelativeSizeAxes = Axes.Both, - Child = facadeContainer = new FillFlowContainer + Child = facadeContainer = new FillFlowContainer { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, Spacing = new Vector2(player_spacing), } }, - maximisedFacade = new PlayerGridFacade { RelativeSizeAxes = Axes.Both } + maximisedFacade = new Facade { RelativeSizeAxes = Axes.Both } } }, cellContainer = new Container { RelativeSizeAxes = Axes.Both } }; } - public void AddContent(Drawable content) + /// + /// Adds a new cell with content to this grid. + /// + /// The content the cell should contain. + /// If more than 16 cells are added. + public void Add(Drawable content) { - var facade = new PlayerGridFacade(); + int index = cellContainer.Count; + + var facade = new Facade(); facadeContainer.Add(facade); - var cell = new Cell(content) { ToggleMaximisationState = toggleMaximisationState }; + var cell = new Cell(index, content) { ToggleMaximisationState = toggleMaximisationState }; cell.SetFacade(facade); cellContainer.Add(cell); } + /// + /// The content added to this grid. + /// + public IEnumerable Content => cellContainer.OrderBy(c => c.FacadeIndex).Select(c => c.Content); + // A depth value that gets decremented every time a new instance is maximised in order to reduce underlaps. private float maximisedInstanceDepth; private void toggleMaximisationState(Cell target) { // Iterate through all cells to ensure only one is maximised at any time. - foreach (var i in cellContainer) + foreach (var i in cellContainer.ToList()) { if (i == target) i.IsMaximised = !i.IsMaximised; @@ -78,7 +90,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate else { // Transfer cell back to its original facade. - i.SetFacade(facadeContainer[cellContainer.IndexOf(target)]); + i.SetFacade(facadeContainer[i.FacadeIndex]); } } } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/PlayerGrid_Cell.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/PlayerGrid_Cell.cs index 1f6e718aa7..c2ac190d40 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/PlayerGrid_Cell.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/PlayerGrid_Cell.cs @@ -14,17 +14,28 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate { private class Cell : CompositeDrawable { + /// + /// The index of the original facade of this cell. + /// + public readonly int FacadeIndex; + + /// + /// The contained content. + /// + public readonly Drawable Content; + public Action ToggleMaximisationState; public bool IsMaximised; - private PlayerGridFacade facade; + private Facade facade; private bool isTracking = true; - public Cell(Drawable content) + public Cell(int facadeIndex, Drawable content) { - Origin = Anchor.Centre; + FacadeIndex = facadeIndex; - InternalChild = content; + Origin = Anchor.Centre; + InternalChild = Content = content; } protected override void Update() @@ -38,9 +49,12 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate } } - public void SetFacade([NotNull] PlayerGridFacade newFacade) + /// + /// Makes this cell track a new facade. + /// + public void SetFacade([NotNull] Facade newFacade) { - PlayerGridFacade lastFacade = facade; + Facade lastFacade = facade; facade = newFacade; if (lastFacade == null || lastFacade == newFacade) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/PlayerGrid_Facade.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/PlayerGrid_Facade.cs index c565e2fec6..6b363c6040 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/PlayerGrid_Facade.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/PlayerGrid_Facade.cs @@ -7,9 +7,12 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate { public partial class PlayerGrid { - private class PlayerGridFacade : Drawable + /// + /// A facade of the grid which is used as a dummy object to store the required position/size of cells. + /// + private class Facade : Drawable { - public PlayerGridFacade() + public Facade() { Anchor = Anchor.Centre; Origin = Anchor.Centre; From 5dc939c2f365d09395f0fe1880bb88a3d8dbda21 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 8 Apr 2021 00:06:32 +0900 Subject: [PATCH 016/203] More documentation --- .../OnlinePlay/Multiplayer/Spectate/PlayerGrid.cs | 15 ++++++++++++++- .../Multiplayer/Spectate/PlayerGrid_Cell.cs | 10 ++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/PlayerGrid.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/PlayerGrid.cs index f41948217c..830378f129 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/PlayerGrid.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/PlayerGrid.cs @@ -1,16 +1,25 @@ // 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 osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osuTK; namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate { + /// + /// A grid of players playing the multiplayer match. + /// public partial class PlayerGrid : CompositeDrawable { private const float player_spacing = 5; + /// + /// The currently-maximised facade. + /// public Drawable MaximisedFacade => maximisedFacade; private readonly Facade maximisedFacade; @@ -52,6 +61,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate /// If more than 16 cells are added. public void Add(Drawable content) { + if (cellContainer.Count == 16) + throw new InvalidOperationException("Only 16 cells are supported."); + int index = cellContainer.Count; var facade = new Facade(); @@ -99,6 +111,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate { base.Update(); + // Different layouts are used for varying cell counts in order to maximise dimensions. Vector2 cellsPerDimension; switch (facadeContainer.Count) @@ -141,7 +154,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate break; } - // Total spacing between cells + // Total inter-cell spacing. Vector2 totalCellSpacing = player_spacing * (cellsPerDimension - Vector2.One); Vector2 fullSize = paddingContainer.ChildSize - totalCellSpacing; diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/PlayerGrid_Cell.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/PlayerGrid_Cell.cs index c2ac190d40..37d88693ee 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/PlayerGrid_Cell.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/PlayerGrid_Cell.cs @@ -12,6 +12,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate { public partial class PlayerGrid { + /// + /// A cell of the grid. Contains the content and tracks to the linked facade. + /// private class Cell : CompositeDrawable { /// @@ -24,7 +27,14 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate /// public readonly Drawable Content; + /// + /// An action that toggles the maximisation state of this cell. + /// public Action ToggleMaximisationState; + + /// + /// Whether this cell is currently maximised. + /// public bool IsMaximised; private Facade facade; From 0a6baf670eff5a90704b95277cf8a8b89dd44375 Mon Sep 17 00:00:00 2001 From: Christine Chen Date: Wed, 7 Apr 2021 14:41:21 -0400 Subject: [PATCH 017/203] Send a warning notification if device is unplugged and low battery - Uses Xamarin.Essentials in osu.Game.PlayerLoader to check battery level - Encapsulated battery checking in the public BatteryManager class so battery level and plugged in status can be accessed and edited in TestPlayerLoader - When checking battery level, catch NotImplementedException thrown by Xamarin.Essentials.Battery on non-mobile platforms - Added visual unit tests for battery notification To mock battery status and level, we had to define a batteryManager object in TestPlayerLoader and add a new function ResetPlayerWithBattery() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Marlina José --- osu.Android/Properties/AndroidManifest.xml | 1 + .../Visual/Gameplay/TestScenePlayerLoader.cs | 48 +++++++++++++ osu.Game.Tests/osu.Game.Tests.csproj | 2 +- osu.Game/Configuration/SessionStatics.cs | 2 + osu.Game/Screens/Play/PlayerLoader.cs | 71 +++++++++++++++++++ osu.Game/osu.Game.csproj | 3 +- 6 files changed, 125 insertions(+), 2 deletions(-) diff --git a/osu.Android/Properties/AndroidManifest.xml b/osu.Android/Properties/AndroidManifest.xml index 770eaf2222..e717bab310 100644 --- a/osu.Android/Properties/AndroidManifest.xml +++ b/osu.Android/Properties/AndroidManifest.xml @@ -6,5 +6,6 @@ + \ No newline at end of file diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs index 88fbf09ef4..a31d53e3b6 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs @@ -93,6 +93,26 @@ namespace osu.Game.Tests.Visual.Gameplay LoadScreen(loader = new TestPlayerLoader(() => player = new TestPlayer(interactive, interactive))); } + /// + /// Sets the input manager child to a new test player loader container instance with a custom battery level + /// + /// If the test player should behave like the production one. + /// If the player's device is plugged in. + /// A custom battery level for the test player. + private void resetPlayerWithBattery(bool interactive, bool pluggedIn, double batteryLevel) + { + Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo); + Beatmap.Value.BeatmapInfo.EpilepsyWarning = epilepsyWarning; + + foreach (var mod in SelectedMods.Value.OfType()) + mod.ApplyToTrack(Beatmap.Value.Track); + + loader = new TestPlayerLoader(() => player = new TestPlayer(interactive, interactive)); + loader.batteryManager.ChargeLevel = batteryLevel; + loader.batteryManager.PluggedIn = pluggedIn; + LoadScreen(loader); + } + [Test] public void TestEarlyExitBeforePlayerConstruction() { @@ -270,6 +290,32 @@ namespace osu.Game.Tests.Visual.Gameplay AddUntilStep("wait for player load", () => player.IsLoaded); } + [TestCase(false, 1.0)] // not plugged in, full battery, no notification + [TestCase(false, 0.2)] // not plugged in, at warning level, no notification + [TestCase(true, 0.1)] // plugged in, charging, below warning level, no notification + [TestCase(false, 0.1)] // not plugged in, below warning level, notification + public void TestLowBatteryNotification(bool pluggedIn, double batteryLevel) + { + AddStep("reset notification lock", () => sessionStatics.GetBindable(Static.BatteryLowNotificationShownOnce).Value = false); + + // mock phone on battery + AddStep("load player", () => resetPlayerWithBattery(false, pluggedIn, batteryLevel)); + AddUntilStep("wait for player", () => player?.LoadState == LoadState.Ready); + int notificationCount = !pluggedIn && batteryLevel < PlayerLoader.battery_tolerance ? 1 : 0; + AddAssert("check for notification", () => notificationOverlay.UnreadCount.Value == notificationCount); + AddStep("click notification", () => + { + var scrollContainer = (OsuScrollContainer)notificationOverlay.Children.Last(); + var flowContainer = scrollContainer.Children.OfType>().First(); + var notification = flowContainer.First(); + + InputManager.MoveMouseTo(notification); + InputManager.Click(MouseButton.Left); + }); + + AddUntilStep("wait for player load", () => player.IsLoaded); + } + [TestCase(true)] [TestCase(false)] public void TestEpilepsyWarning(bool warning) @@ -310,6 +356,8 @@ namespace osu.Game.Tests.Visual.Gameplay public IReadOnlyList DisplayedMods => MetadataInfo.Mods.Value; + public new BatteryManager batteryManager => base.batteryManager; + public TestPlayerLoader(Func createPlayer) : base(createPlayer) { diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index 0e1f6f6b0c..df6d17f615 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -22,4 +22,4 @@ - \ No newline at end of file + diff --git a/osu.Game/Configuration/SessionStatics.cs b/osu.Game/Configuration/SessionStatics.cs index 36eb6964dd..e8ee04731c 100644 --- a/osu.Game/Configuration/SessionStatics.cs +++ b/osu.Game/Configuration/SessionStatics.cs @@ -16,6 +16,7 @@ namespace osu.Game.Configuration { SetDefault(Static.LoginOverlayDisplayed, false); SetDefault(Static.MutedAudioNotificationShownOnce, false); + SetDefault(Static.BatteryLowNotificationShownOnce, false); SetDefault(Static.LastHoverSoundPlaybackTime, (double?)null); SetDefault(Static.SeasonalBackgrounds, null); } @@ -25,6 +26,7 @@ namespace osu.Game.Configuration { LoginOverlayDisplayed, MutedAudioNotificationShownOnce, + BatteryLowNotificationShownOnce, /// /// Info about seasonal backgrounds available fetched from API - see . diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 679b3c7313..01a51e6e7a 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -26,6 +26,7 @@ using osu.Game.Screens.Play.PlayerSettings; using osu.Game.Users; using osuTK; using osuTK.Graphics; +using Xamarin.Essentials; namespace osu.Game.Screens.Play { @@ -121,6 +122,7 @@ namespace osu.Game.Screens.Play private void load(SessionStatics sessionStatics) { muteWarningShownOnce = sessionStatics.GetBindable(Static.MutedAudioNotificationShownOnce); + batteryWarningShownOnce = sessionStatics.GetBindable(Static.BatteryLowNotificationShownOnce); InternalChild = (content = new LogoTrackingContainer { @@ -196,6 +198,7 @@ namespace osu.Game.Screens.Play Scheduler.Add(new ScheduledDelegate(pushWhenLoaded, Clock.CurrentTime + 1800, 0)); showMuteWarningIfNeeded(); + showBatteryWarningIfNeeded(); } public override void OnResuming(IScreen last) @@ -470,5 +473,73 @@ namespace osu.Game.Screens.Play } #endregion + + #region Low battery warning + private Bindable batteryWarningShownOnce; + + // Send a warning if battery is less than 20% + public const double battery_tolerance = 0.2; + + private void showBatteryWarningIfNeeded() + { + if (!batteryWarningShownOnce.Value) + { + // Checks if the notification has not been shown yet, device is unplugged and if device battery is low. + if (!batteryManager.PluggedIn && batteryManager.ChargeLevel < battery_tolerance) + { + Console.WriteLine("Battery level: {0}", batteryManager.ChargeLevel); + notificationOverlay?.Post(new BatteryWarningNotification()); + batteryWarningShownOnce.Value = true; + } + } + } + public BatteryManager batteryManager = new BatteryManager(); + public class BatteryManager + { + public double ChargeLevel; + public bool PluggedIn; + public BatteryManager() + { + // Attempt to get battery information using Xamarin.Essentials + // Xamarin.Essentials throws a NotSupportedOrImplementedException on .NET standard so this only works on iOS/Android + // https://github.com/xamarin/Essentials/blob/7218ab88f7fbe00ec3379bd54e6c0ce35ffc0c22/Xamarin.Essentials/Battery/Battery.netstandard.tvos.cs + try + { + ChargeLevel = Battery.ChargeLevel; + PluggedIn = Battery.PowerSource == BatteryPowerSource.Battery; + } + catch (NotImplementedException e) + { + Console.WriteLine("Could not access battery info: {0}", e); + ChargeLevel = 1.0; + PluggedIn = false; + } + } + } + + private class BatteryWarningNotification : SimpleNotification + { + public override bool IsImportant => true; + + public BatteryWarningNotification() + { + Text = "Your battery level is low! Charge your device to prevent interruptions."; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours, NotificationOverlay notificationOverlay) + { + Icon = FontAwesome.Solid.BatteryQuarter; + IconBackgound.Colour = colours.RedDark; + + Activated = delegate + { + notificationOverlay.Hide(); + return true; + }; + } + } + + #endregion } } diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 292e5b932f..c68be5313d 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -1,4 +1,4 @@ - + netstandard2.1 Library @@ -35,5 +35,6 @@ + From 6bccb3aab6487e0edd0d8030378c2f18c2868194 Mon Sep 17 00:00:00 2001 From: Christine Chen Date: Thu, 8 Apr 2021 19:34:35 -0400 Subject: [PATCH 018/203] Use DI to implement battery detection, add BatteryCutoff property - Removed the Xamarin.Essentials package from osu.Game and added it to osu.iOS and osu.Android only. - iOS and Android implementations use Xamarin.Essentials.Battery, while the Desktop implementation only returns 100% battery for now. - Added a BatteryCutoff property to PowerStatus so it can be different for each platform (default 20%, 25% on iOS) --- osu.Android/OsuGameAndroid.cs | 9 +++ osu.Android/osu.Android.csproj | 4 + osu.Desktop/OsuGameDesktop.cs | 3 + .../Visual/Gameplay/TestScenePlayerLoader.cs | 76 +++++++------------ osu.Game/OsuGameBase.cs | 5 ++ osu.Game/Screens/Play/PlayerLoader.cs | 37 ++------- osu.Game/Tests/OsuTestBrowser.cs | 5 ++ osu.Game/Utils/PowerStatus.cs | 22 ++++++ osu.iOS/OsuGameIOS.cs | 17 +++++ osu.iOS/osu.iOS.csproj | 4 + 10 files changed, 103 insertions(+), 79 deletions(-) create mode 100644 osu.Game/Utils/PowerStatus.cs diff --git a/osu.Android/OsuGameAndroid.cs b/osu.Android/OsuGameAndroid.cs index 21d6336b2c..4c82a175fc 100644 --- a/osu.Android/OsuGameAndroid.cs +++ b/osu.Android/OsuGameAndroid.cs @@ -7,6 +7,8 @@ using Android.OS; using osu.Framework.Allocation; using osu.Game; using osu.Game.Updater; +using osu.Game.Utils; +using Xamarin.Essentials; namespace osu.Android { @@ -19,6 +21,7 @@ namespace osu.Android : base(null) { gameActivity = activity; + powerStatus = new AndroidPowerStatus(); } public override Version AssemblyVersion @@ -72,5 +75,11 @@ namespace osu.Android } protected override UpdateManager CreateUpdateManager() => new SimpleUpdateManager(); + public class AndroidPowerStatus : PowerStatus + { + public override double ChargeLevel => Battery.ChargeLevel; + + public override bool IsCharging => Battery.PowerSource != BatteryPowerSource.Battery; + } } } diff --git a/osu.Android/osu.Android.csproj b/osu.Android/osu.Android.csproj index 54857ac87d..64d5e5b1c8 100644 --- a/osu.Android/osu.Android.csproj +++ b/osu.Android/osu.Android.csproj @@ -63,5 +63,9 @@ 5.0.0 + + + + \ No newline at end of file diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index 0c21c75290..b6c57b219d 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -21,6 +21,8 @@ using osu.Game.Updater; using osu.Desktop.Windows; using osu.Framework.Threading; using osu.Game.IO; +using osu.Game.Utils; +using osu.Framework.Allocation; namespace osu.Desktop { @@ -33,6 +35,7 @@ namespace osu.Desktop : base(args) { noVersionOverlay = args?.Any(a => a == "--no-version-overlay") ?? false; + powerStatus = new DefaultPowerStatus(); } public override StableStorage GetStorageForStableInstall() diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs index a31d53e3b6..c5cc10993c 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs @@ -25,6 +25,7 @@ using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; using osu.Game.Screens.Play; using osu.Game.Screens.Play.PlayerSettings; +using osu.Game.Utils; using osuTK.Input; namespace osu.Game.Tests.Visual.Gameplay @@ -48,6 +49,9 @@ namespace osu.Game.Tests.Visual.Gameplay [Cached] private readonly VolumeOverlay volumeOverlay; + [Resolved] + private PowerStatus powerStatus { get; set; } + private readonly ChangelogOverlay changelogOverlay; public TestScenePlayerLoader() @@ -93,26 +97,6 @@ namespace osu.Game.Tests.Visual.Gameplay LoadScreen(loader = new TestPlayerLoader(() => player = new TestPlayer(interactive, interactive))); } - /// - /// Sets the input manager child to a new test player loader container instance with a custom battery level - /// - /// If the test player should behave like the production one. - /// If the player's device is plugged in. - /// A custom battery level for the test player. - private void resetPlayerWithBattery(bool interactive, bool pluggedIn, double batteryLevel) - { - Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo); - Beatmap.Value.BeatmapInfo.EpilepsyWarning = epilepsyWarning; - - foreach (var mod in SelectedMods.Value.OfType()) - mod.ApplyToTrack(Beatmap.Value.Track); - - loader = new TestPlayerLoader(() => player = new TestPlayer(interactive, interactive)); - loader.batteryManager.ChargeLevel = batteryLevel; - loader.batteryManager.PluggedIn = pluggedIn; - LoadScreen(loader); - } - [Test] public void TestEarlyExitBeforePlayerConstruction() { @@ -290,32 +274,6 @@ namespace osu.Game.Tests.Visual.Gameplay AddUntilStep("wait for player load", () => player.IsLoaded); } - [TestCase(false, 1.0)] // not plugged in, full battery, no notification - [TestCase(false, 0.2)] // not plugged in, at warning level, no notification - [TestCase(true, 0.1)] // plugged in, charging, below warning level, no notification - [TestCase(false, 0.1)] // not plugged in, below warning level, notification - public void TestLowBatteryNotification(bool pluggedIn, double batteryLevel) - { - AddStep("reset notification lock", () => sessionStatics.GetBindable(Static.BatteryLowNotificationShownOnce).Value = false); - - // mock phone on battery - AddStep("load player", () => resetPlayerWithBattery(false, pluggedIn, batteryLevel)); - AddUntilStep("wait for player", () => player?.LoadState == LoadState.Ready); - int notificationCount = !pluggedIn && batteryLevel < PlayerLoader.battery_tolerance ? 1 : 0; - AddAssert("check for notification", () => notificationOverlay.UnreadCount.Value == notificationCount); - AddStep("click notification", () => - { - var scrollContainer = (OsuScrollContainer)notificationOverlay.Children.Last(); - var flowContainer = scrollContainer.Children.OfType>().First(); - var notification = flowContainer.First(); - - InputManager.MoveMouseTo(notification); - InputManager.Click(MouseButton.Left); - }); - - AddUntilStep("wait for player load", () => player.IsLoaded); - } - [TestCase(true)] [TestCase(false)] public void TestEpilepsyWarning(bool warning) @@ -334,6 +292,30 @@ namespace osu.Game.Tests.Visual.Gameplay } } + [TestCase(false, 1.0)] // not charging, full battery --> no warning + [TestCase(false, 0.2)] // not charging, at cutoff --> warning + [TestCase(false, 0.1)] // charging, below cutoff --> warning + public void TestLowBatteryNotification(bool isCharging, double chargeLevel) + { + AddStep("reset notification lock", () => sessionStatics.GetBindable(Static.BatteryLowNotificationShownOnce).Value = false); + + // set charge status and level + AddStep("load player", () => resetPlayer(false, () => { powerStatus.IsCharging = isCharging; powerStatus.ChargeLevel = chargeLevel; })); + AddUntilStep("wait for player", () => player?.LoadState == LoadState.Ready); + int notificationCount = !isCharging && chargeLevel <= powerStatus.BatteryCutoff ? 1 : 0; + AddAssert("check for notification", () => notificationOverlay.UnreadCount.Value == notificationCount); + AddStep("click notification", () => + { + var scrollContainer = (OsuScrollContainer)notificationOverlay.Children.Last(); + var flowContainer = scrollContainer.Children.OfType>().First(); + var notification = flowContainer.First(); + + InputManager.MoveMouseTo(notification); + InputManager.Click(MouseButton.Left); + }); + AddUntilStep("wait for player load", () => player.IsLoaded); + } + [Test] public void TestEpilepsyWarningEarlyExit() { @@ -356,8 +338,6 @@ namespace osu.Game.Tests.Visual.Gameplay public IReadOnlyList DisplayedMods => MetadataInfo.Mods.Value; - public new BatteryManager batteryManager => base.batteryManager; - public TestPlayerLoader(Func createPlayer) : base(createPlayer) { diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 21313d0596..53873b0127 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -40,6 +40,7 @@ using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Scoring; using osu.Game.Skinning; +using osu.Game.Utils; using osuTK.Input; using RuntimeInfo = osu.Framework.RuntimeInfo; @@ -95,6 +96,8 @@ namespace osu.Game protected Storage Storage { get; set; } + protected PowerStatus powerStatus; + [Cached] [Cached(typeof(IBindable))] protected readonly Bindable Ruleset = new Bindable(); @@ -329,6 +332,8 @@ namespace osu.Game dependencies.CacheAs(MusicController); Ruleset.BindValueChanged(onRulesetChanged); + + dependencies.CacheAs(powerStatus); } private void onRulesetChanged(ValueChangedEvent r) diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 01a51e6e7a..9710fa7e46 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -24,9 +24,9 @@ using osu.Game.Overlays.Notifications; using osu.Game.Screens.Menu; using osu.Game.Screens.Play.PlayerSettings; using osu.Game.Users; +using osu.Game.Utils; using osuTK; using osuTK.Graphics; -using Xamarin.Essentials; namespace osu.Game.Screens.Play { @@ -113,6 +113,9 @@ namespace osu.Game.Screens.Play [Resolved] private AudioManager audioManager { get; set; } + [Resolved] + private PowerStatus powerStatus { get; set; } + public PlayerLoader(Func createPlayer) { this.createPlayer = createPlayer; @@ -265,7 +268,6 @@ namespace osu.Game.Screens.Play } #endregion - protected override void Update() { base.Update(); @@ -477,45 +479,18 @@ namespace osu.Game.Screens.Play #region Low battery warning private Bindable batteryWarningShownOnce; - // Send a warning if battery is less than 20% - public const double battery_tolerance = 0.2; - private void showBatteryWarningIfNeeded() { if (!batteryWarningShownOnce.Value) { - // Checks if the notification has not been shown yet, device is unplugged and if device battery is low. - if (!batteryManager.PluggedIn && batteryManager.ChargeLevel < battery_tolerance) + // Checks if the notification has not been shown yet, device is unplugged and if device battery is at or below the cutoff + if (!powerStatus.IsCharging && powerStatus.ChargeLevel <= powerStatus.BatteryCutoff) { - Console.WriteLine("Battery level: {0}", batteryManager.ChargeLevel); notificationOverlay?.Post(new BatteryWarningNotification()); batteryWarningShownOnce.Value = true; } } } - public BatteryManager batteryManager = new BatteryManager(); - public class BatteryManager - { - public double ChargeLevel; - public bool PluggedIn; - public BatteryManager() - { - // Attempt to get battery information using Xamarin.Essentials - // Xamarin.Essentials throws a NotSupportedOrImplementedException on .NET standard so this only works on iOS/Android - // https://github.com/xamarin/Essentials/blob/7218ab88f7fbe00ec3379bd54e6c0ce35ffc0c22/Xamarin.Essentials/Battery/Battery.netstandard.tvos.cs - try - { - ChargeLevel = Battery.ChargeLevel; - PluggedIn = Battery.PowerSource == BatteryPowerSource.Battery; - } - catch (NotImplementedException e) - { - Console.WriteLine("Could not access battery info: {0}", e); - ChargeLevel = 1.0; - PluggedIn = false; - } - } - } private class BatteryWarningNotification : SimpleNotification { diff --git a/osu.Game/Tests/OsuTestBrowser.cs b/osu.Game/Tests/OsuTestBrowser.cs index 71b0b02fa6..afce7dd38b 100644 --- a/osu.Game/Tests/OsuTestBrowser.cs +++ b/osu.Game/Tests/OsuTestBrowser.cs @@ -7,11 +7,16 @@ using osu.Framework.Screens; using osu.Framework.Testing; using osu.Game.Graphics; using osu.Game.Screens.Backgrounds; +using osu.Game.Utils; namespace osu.Game.Tests { public class OsuTestBrowser : OsuGameBase { + public OsuTestBrowser() + { + powerStatus = new DefaultPowerStatus(); + } protected override void LoadComplete() { base.LoadComplete(); diff --git a/osu.Game/Utils/PowerStatus.cs b/osu.Game/Utils/PowerStatus.cs new file mode 100644 index 0000000000..0657c98af1 --- /dev/null +++ b/osu.Game/Utils/PowerStatus.cs @@ -0,0 +1,22 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Utils +{ + public abstract class PowerStatus + { + /// + /// The maximum battery level before a warning notification + /// is sent. + /// + public virtual double BatteryCutoff { get; } = 0.2; + public virtual double ChargeLevel { get; set; } + public virtual bool IsCharging { get; set; } + } + + public class DefaultPowerStatus : PowerStatus + { + public override double ChargeLevel { get; set; } = 1; + public override bool IsCharging { get; set; } = true; + } +} diff --git a/osu.iOS/OsuGameIOS.cs b/osu.iOS/OsuGameIOS.cs index 5125ad81e0..21dce338dc 100644 --- a/osu.iOS/OsuGameIOS.cs +++ b/osu.iOS/OsuGameIOS.cs @@ -5,13 +5,30 @@ using System; using Foundation; using osu.Game; using osu.Game.Updater; +using osu.Game.Utils; +using Xamarin.Essentials; namespace osu.iOS { public class OsuGameIOS : OsuGame { + public OsuGameIOS() + : base(null) + { + powerStatus = new IOSPowerStatus(); + } public override Version AssemblyVersion => new Version(NSBundle.MainBundle.InfoDictionary["CFBundleVersion"].ToString()); protected override UpdateManager CreateUpdateManager() => new SimpleUpdateManager(); + + public class IOSPowerStatus : PowerStatus + { + // The low battery alert appears at 20% on iOS + // https://github.com/ppy/osu/issues/12239 + public override double BatteryCutoff => 0.25; + public override double ChargeLevel => Battery.ChargeLevel; + + public override bool IsCharging => Battery.PowerSource != BatteryPowerSource.Battery; + } } } diff --git a/osu.iOS/osu.iOS.csproj b/osu.iOS/osu.iOS.csproj index 1e9a21865d..ed6f52c60e 100644 --- a/osu.iOS/osu.iOS.csproj +++ b/osu.iOS/osu.iOS.csproj @@ -116,5 +116,9 @@ false + + + + From 493c095535c3acdddfada39ab29ecfd430c7afe9 Mon Sep 17 00:00:00 2001 From: Christine Chen Date: Thu, 8 Apr 2021 20:28:23 -0400 Subject: [PATCH 019/203] Fixed code style --- osu.Android/OsuGameAndroid.cs | 2 +- osu.Desktop/OsuGameDesktop.cs | 3 +-- osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs | 6 +++++- osu.Game/OsuGameBase.cs | 4 ++-- osu.Game/Screens/Play/PlayerLoader.cs | 1 + osu.Game/Tests/OsuTestBrowser.cs | 3 ++- osu.Game/Utils/PowerStatus.cs | 1 + osu.iOS/OsuGameIOS.cs | 2 +- 8 files changed, 14 insertions(+), 8 deletions(-) diff --git a/osu.Android/OsuGameAndroid.cs b/osu.Android/OsuGameAndroid.cs index 4c82a175fc..eb0d499c8f 100644 --- a/osu.Android/OsuGameAndroid.cs +++ b/osu.Android/OsuGameAndroid.cs @@ -21,7 +21,7 @@ namespace osu.Android : base(null) { gameActivity = activity; - powerStatus = new AndroidPowerStatus(); + PowerStatus = new AndroidPowerStatus(); } public override Version AssemblyVersion diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index b6c57b219d..48e28fa251 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -22,7 +22,6 @@ using osu.Desktop.Windows; using osu.Framework.Threading; using osu.Game.IO; using osu.Game.Utils; -using osu.Framework.Allocation; namespace osu.Desktop { @@ -35,7 +34,7 @@ namespace osu.Desktop : base(args) { noVersionOverlay = args?.Any(a => a == "--no-version-overlay") ?? false; - powerStatus = new DefaultPowerStatus(); + PowerStatus = new DefaultPowerStatus(); } public override StableStorage GetStorageForStableInstall() diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs index c5cc10993c..b885f0a9be 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs @@ -300,7 +300,11 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("reset notification lock", () => sessionStatics.GetBindable(Static.BatteryLowNotificationShownOnce).Value = false); // set charge status and level - AddStep("load player", () => resetPlayer(false, () => { powerStatus.IsCharging = isCharging; powerStatus.ChargeLevel = chargeLevel; })); + AddStep("load player", () => resetPlayer(false, () => + { + powerStatus.IsCharging = isCharging; + powerStatus.ChargeLevel = chargeLevel; + })); AddUntilStep("wait for player", () => player?.LoadState == LoadState.Ready); int notificationCount = !isCharging && chargeLevel <= powerStatus.BatteryCutoff ? 1 : 0; AddAssert("check for notification", () => notificationOverlay.UnreadCount.Value == notificationCount); diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 53873b0127..a907afd8af 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -96,7 +96,7 @@ namespace osu.Game protected Storage Storage { get; set; } - protected PowerStatus powerStatus; + protected PowerStatus PowerStatus; [Cached] [Cached(typeof(IBindable))] @@ -333,7 +333,7 @@ namespace osu.Game Ruleset.BindValueChanged(onRulesetChanged); - dependencies.CacheAs(powerStatus); + dependencies.CacheAs(PowerStatus); } private void onRulesetChanged(ValueChangedEvent r) diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 9710fa7e46..6ba99d65d5 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -477,6 +477,7 @@ namespace osu.Game.Screens.Play #endregion #region Low battery warning + private Bindable batteryWarningShownOnce; private void showBatteryWarningIfNeeded() diff --git a/osu.Game/Tests/OsuTestBrowser.cs b/osu.Game/Tests/OsuTestBrowser.cs index afce7dd38b..d4fa2a11d3 100644 --- a/osu.Game/Tests/OsuTestBrowser.cs +++ b/osu.Game/Tests/OsuTestBrowser.cs @@ -15,8 +15,9 @@ namespace osu.Game.Tests { public OsuTestBrowser() { - powerStatus = new DefaultPowerStatus(); + PowerStatus = new DefaultPowerStatus(); } + protected override void LoadComplete() { base.LoadComplete(); diff --git a/osu.Game/Utils/PowerStatus.cs b/osu.Game/Utils/PowerStatus.cs index 0657c98af1..1caed5b6fa 100644 --- a/osu.Game/Utils/PowerStatus.cs +++ b/osu.Game/Utils/PowerStatus.cs @@ -10,6 +10,7 @@ namespace osu.Game.Utils /// is sent. /// public virtual double BatteryCutoff { get; } = 0.2; + public virtual double ChargeLevel { get; set; } public virtual bool IsCharging { get; set; } } diff --git a/osu.iOS/OsuGameIOS.cs b/osu.iOS/OsuGameIOS.cs index 21dce338dc..d417fa8f8d 100644 --- a/osu.iOS/OsuGameIOS.cs +++ b/osu.iOS/OsuGameIOS.cs @@ -15,7 +15,7 @@ namespace osu.iOS public OsuGameIOS() : base(null) { - powerStatus = new IOSPowerStatus(); + PowerStatus = new IOSPowerStatus(); } public override Version AssemblyVersion => new Version(NSBundle.MainBundle.InfoDictionary["CFBundleVersion"].ToString()); From 6b6a71d3c38e47f2e6441cb6a03dcb04c43b51ac Mon Sep 17 00:00:00 2001 From: Christine Chen Date: Thu, 8 Apr 2021 20:38:16 -0400 Subject: [PATCH 020/203] trim whitespace --- osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs index b885f0a9be..2a0f46f5ff 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs @@ -301,7 +301,7 @@ namespace osu.Game.Tests.Visual.Gameplay // set charge status and level AddStep("load player", () => resetPlayer(false, () => - { + { powerStatus.IsCharging = isCharging; powerStatus.ChargeLevel = chargeLevel; })); From 59d13b0dd3faa9baca04c6664437294a2ed820f6 Mon Sep 17 00:00:00 2001 From: Christine Chen Date: Thu, 8 Apr 2021 21:53:42 -0400 Subject: [PATCH 021/203] Fixed indentation sorry about the style fixes... I'm using JetBrains Rider from now on. --- osu.Android/OsuGameAndroid.cs | 1 + .../Visual/Gameplay/TestScenePlayerLoader.cs | 18 +++++++++--------- osu.Game/Screens/Play/PlayerLoader.cs | 1 + 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/osu.Android/OsuGameAndroid.cs b/osu.Android/OsuGameAndroid.cs index eb0d499c8f..a0c6a1e354 100644 --- a/osu.Android/OsuGameAndroid.cs +++ b/osu.Android/OsuGameAndroid.cs @@ -75,6 +75,7 @@ namespace osu.Android } protected override UpdateManager CreateUpdateManager() => new SimpleUpdateManager(); + public class AndroidPowerStatus : PowerStatus { public override double ChargeLevel => Battery.ChargeLevel; diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs index 2a0f46f5ff..1377459c62 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs @@ -302,21 +302,21 @@ namespace osu.Game.Tests.Visual.Gameplay // set charge status and level AddStep("load player", () => resetPlayer(false, () => { - powerStatus.IsCharging = isCharging; - powerStatus.ChargeLevel = chargeLevel; + powerStatus.IsCharging = isCharging; + powerStatus.ChargeLevel = chargeLevel; })); AddUntilStep("wait for player", () => player?.LoadState == LoadState.Ready); int notificationCount = !isCharging && chargeLevel <= powerStatus.BatteryCutoff ? 1 : 0; AddAssert("check for notification", () => notificationOverlay.UnreadCount.Value == notificationCount); AddStep("click notification", () => - { - var scrollContainer = (OsuScrollContainer)notificationOverlay.Children.Last(); - var flowContainer = scrollContainer.Children.OfType>().First(); - var notification = flowContainer.First(); + { + var scrollContainer = (OsuScrollContainer)notificationOverlay.Children.Last(); + var flowContainer = scrollContainer.Children.OfType>().First(); + var notification = flowContainer.First(); - InputManager.MoveMouseTo(notification); - InputManager.Click(MouseButton.Left); - }); + InputManager.MoveMouseTo(notification); + InputManager.Click(MouseButton.Left); + }); AddUntilStep("wait for player load", () => player.IsLoaded); } diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 6ba99d65d5..e1ab0b8ef5 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -268,6 +268,7 @@ namespace osu.Game.Screens.Play } #endregion + protected override void Update() { base.Update(); From bb15baf118f6e49306acc9688cd6790b39dc102c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 9 Apr 2021 17:31:14 +0900 Subject: [PATCH 022/203] Add initial multiplayer spectator leaderboard --- .../MultiplayerSpectatorLeaderboard.cs | 82 +++++++++++++++++++ .../HUD/MultiplayerGameplayLeaderboard.cs | 19 +++-- 2 files changed, 94 insertions(+), 7 deletions(-) create mode 100644 osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiplayerSpectatorLeaderboard.cs diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiplayerSpectatorLeaderboard.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiplayerSpectatorLeaderboard.cs new file mode 100644 index 0000000000..35c3471ce2 --- /dev/null +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiplayerSpectatorLeaderboard.cs @@ -0,0 +1,82 @@ +// 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 osu.Framework.Timing; +using osu.Game.Online.Spectator; +using osu.Game.Rulesets.Scoring; +using osu.Game.Screens.Play.HUD; + +namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate +{ + public class MultiplayerSpectatorLeaderboard : MultiplayerGameplayLeaderboard + { + private readonly Dictionary trackedData = new Dictionary(); + + public MultiplayerSpectatorLeaderboard(ScoreProcessor scoreProcessor, int[] userIds) + : base(scoreProcessor, userIds) + { + } + + public void AddSource(int userId, IClock source) => trackedData[userId] = new TrackedUserData(source); + + public void RemoveSource(int userId, IClock source) => trackedData.Remove(userId); + + protected override void OnIncomingFrames(int userId, FrameDataBundle bundle) + { + if (!trackedData.TryGetValue(userId, out var data)) + return; + + data.Frames.Add(new TimedFrameHeader(bundle.Frames.First().Time, bundle.Header)); + } + + protected override void Update() + { + base.Update(); + + foreach (var (userId, data) in trackedData) + { + var targetTime = data.Clock.CurrentTime; + + int frameIndex = data.Frames.BinarySearch(new TimedFrameHeader(targetTime)); + if (frameIndex < 0) + frameIndex = ~frameIndex; + frameIndex = Math.Clamp(frameIndex - 1, 0, data.Frames.Count - 1); + + SetCurrentFrame(userId, data.Frames[frameIndex].Header); + } + } + + private class TrackedUserData + { + public readonly IClock Clock; + public readonly List Frames = new List(); + + public TrackedUserData(IClock clock) + { + Clock = clock; + } + } + + private class TimedFrameHeader : IComparable + { + public readonly double Time; + public readonly FrameHeader Header; + + public TimedFrameHeader(double time) + { + Time = time; + } + + public TimedFrameHeader(double time, FrameHeader header) + { + Time = time; + Header = header; + } + + public int CompareTo(TimedFrameHeader other) => Time.CompareTo(other.Time); + } + } +} diff --git a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs index a3d27c4e71..65ad8be3f0 100644 --- a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs @@ -20,7 +20,6 @@ namespace osu.Game.Screens.Play.HUD public class MultiplayerGameplayLeaderboard : GameplayLeaderboard { private readonly ScoreProcessor scoreProcessor; - private readonly Dictionary userScores = new Dictionary(); [Resolved] @@ -116,13 +115,19 @@ namespace osu.Game.Screens.Play.HUD trackedData.UpdateScore(scoreProcessor, mode.NewValue); } - private void handleIncomingFrames(int userId, FrameDataBundle bundle) + private void handleIncomingFrames(int userId, FrameDataBundle bundle) => Schedule(() => { - if (userScores.TryGetValue(userId, out var trackedData)) - { - trackedData.LastHeader = bundle.Header; - trackedData.UpdateScore(scoreProcessor, scoringMode.Value); - } + if (userScores.ContainsKey(userId)) + OnIncomingFrames(userId, bundle); + }); + + protected virtual void OnIncomingFrames(int userId, FrameDataBundle bundle) => SetCurrentFrame(userId, bundle.Header); + + protected void SetCurrentFrame(int userId, FrameHeader header) + { + var trackedScore = userScores[userId]; + trackedScore.LastHeader = header; + trackedScore.UpdateScore(scoreProcessor, scoringMode.Value); } protected override void Dispose(bool isDisposing) From 3b86f0eb2f55839c4301cba1aefcc971ba205149 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 9 Apr 2021 18:15:23 +0900 Subject: [PATCH 023/203] Fix exception with 0 frames --- .../Multiplayer/Spectate/MultiplayerSpectatorLeaderboard.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiplayerSpectatorLeaderboard.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiplayerSpectatorLeaderboard.cs index 35c3471ce2..08db5befa4 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiplayerSpectatorLeaderboard.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiplayerSpectatorLeaderboard.cs @@ -40,6 +40,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate { var targetTime = data.Clock.CurrentTime; + if (data.Frames.Count == 0) + continue; + int frameIndex = data.Frames.BinarySearch(new TimedFrameHeader(targetTime)); if (frameIndex < 0) frameIndex = ~frameIndex; From 90e243eea5404e1632fc3f63c679c8fc38874c60 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 9 Apr 2021 18:15:27 +0900 Subject: [PATCH 024/203] Rename methods --- .../Multiplayer/Spectate/MultiplayerSpectatorLeaderboard.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiplayerSpectatorLeaderboard.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiplayerSpectatorLeaderboard.cs index 08db5befa4..f16869b43d 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiplayerSpectatorLeaderboard.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiplayerSpectatorLeaderboard.cs @@ -20,9 +20,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate { } - public void AddSource(int userId, IClock source) => trackedData[userId] = new TrackedUserData(source); + public void AddClock(int userId, IClock source) => trackedData[userId] = new TrackedUserData(source); - public void RemoveSource(int userId, IClock source) => trackedData.Remove(userId); + public void RemoveClock(int userId, IClock source) => trackedData.Remove(userId); protected override void OnIncomingFrames(int userId, FrameDataBundle bundle) { From 589ce4bdc2b09c7f96d99582aea7e7f8ef430eb8 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 9 Apr 2021 18:16:10 +0900 Subject: [PATCH 025/203] Add test --- ...estSceneMultiplayerSpectatorLeaderboard.cs | 207 ++++++++++++++++++ 1 file changed, 207 insertions(+) create mode 100644 osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectatorLeaderboard.cs diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectatorLeaderboard.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectatorLeaderboard.cs new file mode 100644 index 0000000000..57e9fb37cc --- /dev/null +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectatorLeaderboard.cs @@ -0,0 +1,207 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Testing; +using osu.Framework.Timing; +using osu.Framework.Utils; +using osu.Game.Database; +using osu.Game.Online; +using osu.Game.Online.Spectator; +using osu.Game.Replays.Legacy; +using osu.Game.Rulesets.Osu.Scoring; +using osu.Game.Scoring; +using osu.Game.Screens.OnlinePlay.Multiplayer.Spectate; +using osu.Game.Screens.Play.HUD; +using osu.Game.Users; + +namespace osu.Game.Tests.Visual.Multiplayer +{ + public class TestSceneMultiplayerSpectatorLeaderboard : MultiplayerTestScene + { + [Cached(typeof(SpectatorStreamingClient))] + private TestSpectatorStreamingClient streamingClient = new TestSpectatorStreamingClient(); + + [Cached(typeof(UserLookupCache))] + private UserLookupCache lookupCache = new TestUserLookupCache(); + + protected override Container Content => content; + private readonly Container content; + + private readonly Dictionary clocks = new Dictionary + { + { 55, new ManualClock() }, + { 56, new ManualClock() } + }; + + public TestSceneMultiplayerSpectatorLeaderboard() + { + base.Content.AddRange(new Drawable[] + { + streamingClient, + lookupCache, + content = new Container { RelativeSizeAxes = Axes.Both } + }); + } + + [SetUpSteps] + public new void SetUpSteps() + { + MultiplayerSpectatorLeaderboard leaderboard = null; + + AddStep("reset", () => + { + Clear(); + + foreach (var (userId, clock) in clocks) + { + streamingClient.EndPlay(userId, 0); + clock.CurrentTime = 0; + } + }); + + AddStep("create leaderboard", () => + { + foreach (var (userId, _) in clocks) + streamingClient.StartPlay(userId, 0); + + Beatmap.Value = CreateWorkingBeatmap(Ruleset.Value); + + var playable = Beatmap.Value.GetPlayableBeatmap(Ruleset.Value); + var scoreProcessor = new OsuScoreProcessor(); + scoreProcessor.ApplyBeatmap(playable); + + LoadComponentAsync(leaderboard = new MultiplayerSpectatorLeaderboard(scoreProcessor, clocks.Keys.ToArray()) { Expanded = { Value = true } }, Add); + }); + + AddUntilStep("wait for load", () => leaderboard.IsLoaded); + + AddStep("add clock sources", () => + { + foreach (var (userId, clock) in clocks) + leaderboard.AddClock(userId, clock); + }); + } + + [Test] + public void TestLeaderboardTracksCurrentTime() + { + AddStep("send frames", () => + { + // For user 55, send 100 frames in sets of 1. + // For user 56, send 100 frames in sets of 10. + for (int i = 0; i < 100; i++) + { + streamingClient.SendFrames(55, i, 1); + + if (i % 10 == 0) + streamingClient.SendFrames(56, i, 10); + } + }); + + assertCombo(55, 1); + assertCombo(56, 10); + + setTime(500); + assertCombo(55, 5); + assertCombo(56, 10); + + setTime(1100); + assertCombo(55, 11); + assertCombo(56, 20); + } + + private void setTime(double time) => AddStep($"set time {time}", () => + { + foreach (var (_, clock) in clocks) + clock.CurrentTime = time; + }); + + private void assertCombo(int userId, int expectedCombo) + => AddUntilStep($"player {userId} has {expectedCombo} combo", () => this.ChildrenOfType().Single(s => s.User?.Id == userId).Combo.Value == expectedCombo); + + private class TestSpectatorStreamingClient : SpectatorStreamingClient + { + private readonly Dictionary userBeatmapDictionary = new Dictionary(); + private readonly Dictionary userSentStateDictionary = new Dictionary(); + + public TestSpectatorStreamingClient() + : base(new DevelopmentEndpointConfiguration()) + { + } + + public void StartPlay(int userId, int beatmapId) + { + userBeatmapDictionary[userId] = beatmapId; + userSentStateDictionary[userId] = false; + sendState(userId, beatmapId); + } + + public void EndPlay(int userId, int beatmapId) + { + ((ISpectatorClient)this).UserFinishedPlaying(userId, new SpectatorState + { + BeatmapID = beatmapId, + RulesetID = 0, + }); + userSentStateDictionary[userId] = false; + } + + public void SendFrames(int userId, int index, int count) + { + var frames = new List(); + + for (int i = index; i < index + count; i++) + { + var buttonState = i == index + count - 1 ? ReplayButtonState.None : ReplayButtonState.Left1; + frames.Add(new LegacyReplayFrame(i * 100, RNG.Next(0, 512), RNG.Next(0, 512), buttonState)); + } + + var bundle = new FrameDataBundle(new ScoreInfo { Combo = index + count }, frames); + ((ISpectatorClient)this).UserSentFrames(userId, bundle); + if (!userSentStateDictionary[userId]) + sendState(userId, userBeatmapDictionary[userId]); + } + + public override void WatchUser(int userId) + { + if (userSentStateDictionary[userId]) + { + // usually the server would do this. + sendState(userId, userBeatmapDictionary[userId]); + } + + base.WatchUser(userId); + } + + private void sendState(int userId, int beatmapId) + { + ((ISpectatorClient)this).UserBeganPlaying(userId, new SpectatorState + { + BeatmapID = beatmapId, + RulesetID = 0, + }); + userSentStateDictionary[userId] = true; + } + } + + private class TestUserLookupCache : UserLookupCache + { + protected override Task ComputeValueAsync(int lookup, CancellationToken token = default) + { + return Task.FromResult(new User + { + Id = lookup, + Username = $"User {lookup}" + }); + } + } + } +} From b49997f531dad5e80f50fa6cbe1f4349a99be38f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 9 Apr 2021 18:18:23 +0900 Subject: [PATCH 026/203] Add test for no frames --- .../TestSceneMultiplayerSpectatorLeaderboard.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectatorLeaderboard.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectatorLeaderboard.cs index 57e9fb37cc..53efb4392f 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectatorLeaderboard.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectatorLeaderboard.cs @@ -118,6 +118,13 @@ namespace osu.Game.Tests.Visual.Multiplayer assertCombo(56, 20); } + [Test] + public void TestNoFrames() + { + assertCombo(55, 0); + assertCombo(56, 1); + } + private void setTime(double time) => AddStep($"set time {time}", () => { foreach (var (_, clock) in clocks) From 9ddcd686ac09e3f0aa35c0f5a9c9ae01e7e44f0e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 9 Apr 2021 18:23:38 +0900 Subject: [PATCH 027/203] Fix incorrect assert --- .../Multiplayer/TestSceneMultiplayerSpectatorLeaderboard.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectatorLeaderboard.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectatorLeaderboard.cs index 53efb4392f..8589cffcc9 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectatorLeaderboard.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectatorLeaderboard.cs @@ -122,7 +122,7 @@ namespace osu.Game.Tests.Visual.Multiplayer public void TestNoFrames() { assertCombo(55, 0); - assertCombo(56, 1); + assertCombo(56, 0); } private void setTime(double time) => AddStep($"set time {time}", () => From e73f3f52d7730854a9f360dc2f1ae7df0ec834ea Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 9 Apr 2021 18:23:41 +0900 Subject: [PATCH 028/203] Add some more asserts --- ...estSceneMultiplayerSpectatorLeaderboard.cs | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectatorLeaderboard.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectatorLeaderboard.cs index 8589cffcc9..3b2cfb1c7b 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectatorLeaderboard.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectatorLeaderboard.cs @@ -95,8 +95,8 @@ namespace osu.Game.Tests.Visual.Multiplayer { AddStep("send frames", () => { - // For user 55, send 100 frames in sets of 1. - // For user 56, send 100 frames in sets of 10. + // For user 55, send frames in sets of 1. + // For user 56, send frames in sets of 10. for (int i = 0; i < 100; i++) { streamingClient.SendFrames(55, i, 1); @@ -109,13 +109,25 @@ namespace osu.Game.Tests.Visual.Multiplayer assertCombo(55, 1); assertCombo(56, 10); + // Advance to a point where only user 55's frame changes. setTime(500); assertCombo(55, 5); assertCombo(56, 10); + // Advance to a point where both user's frame changes. setTime(1100); assertCombo(55, 11); assertCombo(56, 20); + + // Advance user 56 only to a point where its frame changes. + setTime(56, 2100); + assertCombo(55, 11); + assertCombo(56, 30); + + // Advance both users beyond their last frame + setTime(101 * 100); + assertCombo(55, 100); + assertCombo(56, 100); } [Test] @@ -131,6 +143,9 @@ namespace osu.Game.Tests.Visual.Multiplayer clock.CurrentTime = time; }); + private void setTime(int userId, double time) + => AddStep($"set user {userId} time {time}", () => clocks[userId].CurrentTime = time); + private void assertCombo(int userId, int expectedCombo) => AddUntilStep($"player {userId} has {expectedCombo} combo", () => this.ChildrenOfType().Single(s => s.User?.Id == userId).Combo.Value == expectedCombo); From 7cbc8f2695b4868fa4be7a9b41ebd8ef42b1b0d4 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 9 Apr 2021 18:29:02 +0900 Subject: [PATCH 029/203] Add some xmldocs --- .../Play/HUD/MultiplayerGameplayLeaderboard.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs index 65ad8be3f0..f0d2ac4b7f 100644 --- a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs @@ -121,8 +121,21 @@ namespace osu.Game.Screens.Play.HUD OnIncomingFrames(userId, bundle); }); + /// + /// Invoked when new frames have arrived for a user. + /// + /// + /// By default, this immediately sets the current frame to be displayed for the user. + /// + /// The user which the frames arrived for. + /// The bundle of frames. protected virtual void OnIncomingFrames(int userId, FrameDataBundle bundle) => SetCurrentFrame(userId, bundle.Header); + /// + /// Sets the current frame to be displayed for a user. + /// + /// The user to set the frame of. + /// The frame to set. protected void SetCurrentFrame(int userId, FrameHeader header) { var trackedScore = userScores[userId]; From d2c37e6cf8828e5793c9aef36b76fd6ce986b537 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 9 Apr 2021 18:41:58 +0900 Subject: [PATCH 030/203] Remove unnecessary parameter --- .../Multiplayer/Spectate/MultiplayerSpectatorLeaderboard.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiplayerSpectatorLeaderboard.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiplayerSpectatorLeaderboard.cs index f16869b43d..aa9f162036 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiplayerSpectatorLeaderboard.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiplayerSpectatorLeaderboard.cs @@ -22,7 +22,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate public void AddClock(int userId, IClock source) => trackedData[userId] = new TrackedUserData(source); - public void RemoveClock(int userId, IClock source) => trackedData.Remove(userId); + public void RemoveClock(int userId) => trackedData.Remove(userId); protected override void OnIncomingFrames(int userId, FrameDataBundle bundle) { From 08311abc5ec93907337976e0d8ae0209684ef769 Mon Sep 17 00:00:00 2001 From: Christine Chen Date: Fri, 9 Apr 2021 17:55:41 -0400 Subject: [PATCH 031/203] Remove setters, cache CreatePowerStatus() and use a dummy LocalPowerStatus class in test scene --- osu.Android/OsuGameAndroid.cs | 7 ++- osu.Desktop/OsuGameDesktop.cs | 2 - .../Visual/Gameplay/TestScenePlayerLoader.cs | 44 +++++++++++++++---- osu.Game/Configuration/SessionStatics.cs | 4 +- osu.Game/OsuGame.cs | 5 +++ osu.Game/OsuGameBase.cs | 4 +- osu.Game/Screens/Play/PlayerLoader.cs | 11 ++--- osu.Game/Tests/OsuTestBrowser.cs | 6 --- osu.Game/Utils/PowerStatus.cs | 26 ++++++----- osu.iOS/OsuGameIOS.cs | 12 ++--- 10 files changed, 74 insertions(+), 47 deletions(-) diff --git a/osu.Android/OsuGameAndroid.cs b/osu.Android/OsuGameAndroid.cs index a0c6a1e354..02f4fa1ad6 100644 --- a/osu.Android/OsuGameAndroid.cs +++ b/osu.Android/OsuGameAndroid.cs @@ -21,7 +21,6 @@ namespace osu.Android : base(null) { gameActivity = activity; - PowerStatus = new AndroidPowerStatus(); } public override Version AssemblyVersion @@ -76,8 +75,12 @@ namespace osu.Android protected override UpdateManager CreateUpdateManager() => new SimpleUpdateManager(); - public class AndroidPowerStatus : PowerStatus + protected override PowerStatus CreatePowerStatus() => new AndroidPowerStatus(); + + private class AndroidPowerStatus : PowerStatus { + public override double BatteryCutoff => 0.20; + public override double ChargeLevel => Battery.ChargeLevel; public override bool IsCharging => Battery.PowerSource != BatteryPowerSource.Battery; diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index 48e28fa251..0c21c75290 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -21,7 +21,6 @@ using osu.Game.Updater; using osu.Desktop.Windows; using osu.Framework.Threading; using osu.Game.IO; -using osu.Game.Utils; namespace osu.Desktop { @@ -34,7 +33,6 @@ namespace osu.Desktop : base(args) { noVersionOverlay = args?.Any(a => a == "--no-version-overlay") ?? false; - PowerStatus = new DefaultPowerStatus(); } public override StableStorage GetStorageForStableInstall() diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs index 1377459c62..36958b0741 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs @@ -49,8 +49,8 @@ namespace osu.Game.Tests.Visual.Gameplay [Cached] private readonly VolumeOverlay volumeOverlay; - [Resolved] - private PowerStatus powerStatus { get; set; } + [Cached(typeof(PowerStatus))] + private readonly LocalPowerStatus powerStatus = new LocalPowerStatus(); private readonly ChangelogOverlay changelogOverlay; @@ -292,22 +292,22 @@ namespace osu.Game.Tests.Visual.Gameplay } } - [TestCase(false, 1.0)] // not charging, full battery --> no warning + [TestCase(false, 1.0)] // not charging, above cutoff --> no warning [TestCase(false, 0.2)] // not charging, at cutoff --> warning - [TestCase(false, 0.1)] // charging, below cutoff --> warning + [TestCase(true, 0.1)] // charging, below cutoff --> no warning public void TestLowBatteryNotification(bool isCharging, double chargeLevel) { - AddStep("reset notification lock", () => sessionStatics.GetBindable(Static.BatteryLowNotificationShownOnce).Value = false); + AddStep("reset notification lock", () => sessionStatics.GetBindable(Static.LowBatteryNotificationShownOnce).Value = false); // set charge status and level AddStep("load player", () => resetPlayer(false, () => { - powerStatus.IsCharging = isCharging; - powerStatus.ChargeLevel = chargeLevel; + powerStatus.SetCharging(isCharging); + powerStatus.SetChargeLevel(chargeLevel); })); AddUntilStep("wait for player", () => player?.LoadState == LoadState.Ready); - int notificationCount = !isCharging && chargeLevel <= powerStatus.BatteryCutoff ? 1 : 0; - AddAssert("check for notification", () => notificationOverlay.UnreadCount.Value == notificationCount); + int warning = !isCharging && chargeLevel <= powerStatus.BatteryCutoff ? 1 : 0; + AddAssert($"notification {(warning == 1 ? "triggered" : "not triggered")}", () => notificationOverlay.UnreadCount.Value == warning); AddStep("click notification", () => { var scrollContainer = (OsuScrollContainer)notificationOverlay.Children.Last(); @@ -380,5 +380,31 @@ namespace osu.Game.Tests.Visual.Gameplay throw new TimeoutException(); } } + + /// + /// Mutable dummy PowerStatus class for + /// + /// + private class LocalPowerStatus : PowerStatus + { + private bool isCharging = true; + private double chargeLevel = 1; + + public override double BatteryCutoff => 0.2; + + public override bool IsCharging => isCharging; + + public override double ChargeLevel => chargeLevel; + + public void SetCharging(bool value) + { + isCharging = value; + } + + public void SetChargeLevel(double value) + { + chargeLevel = value; + } + } } } diff --git a/osu.Game/Configuration/SessionStatics.cs b/osu.Game/Configuration/SessionStatics.cs index e8ee04731c..71e1a1efcc 100644 --- a/osu.Game/Configuration/SessionStatics.cs +++ b/osu.Game/Configuration/SessionStatics.cs @@ -16,7 +16,7 @@ namespace osu.Game.Configuration { SetDefault(Static.LoginOverlayDisplayed, false); SetDefault(Static.MutedAudioNotificationShownOnce, false); - SetDefault(Static.BatteryLowNotificationShownOnce, false); + SetDefault(Static.LowBatteryNotificationShownOnce, false); SetDefault(Static.LastHoverSoundPlaybackTime, (double?)null); SetDefault(Static.SeasonalBackgrounds, null); } @@ -26,7 +26,7 @@ namespace osu.Game.Configuration { LoginOverlayDisplayed, MutedAudioNotificationShownOnce, - BatteryLowNotificationShownOnce, + LowBatteryNotificationShownOnce, /// /// Info about seasonal backgrounds available fetched from API - see . diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 809e5d3c1b..277455b7d3 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -694,6 +694,11 @@ namespace osu.Game loadComponentSingleFile(new AccountCreationOverlay(), topMostOverlayContent.Add, true); loadComponentSingleFile(new DialogOverlay(), topMostOverlayContent.Add, true); + if (CreatePowerStatus() != null) + { + dependencies.CacheAs(CreatePowerStatus()); + } + chatOverlay.State.ValueChanged += state => channelManager.HighPollRate.Value = state.NewValue == Visibility.Visible; Add(externalLinkOpener = new ExternalLinkOpener()); diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index a907afd8af..1b10de614a 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -96,7 +96,7 @@ namespace osu.Game protected Storage Storage { get; set; } - protected PowerStatus PowerStatus; + protected virtual PowerStatus CreatePowerStatus() => null; [Cached] [Cached(typeof(IBindable))] @@ -332,8 +332,6 @@ namespace osu.Game dependencies.CacheAs(MusicController); Ruleset.BindValueChanged(onRulesetChanged); - - dependencies.CacheAs(PowerStatus); } private void onRulesetChanged(ValueChangedEvent r) diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index e1ab0b8ef5..d342bf8273 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -113,7 +113,7 @@ namespace osu.Game.Screens.Play [Resolved] private AudioManager audioManager { get; set; } - [Resolved] + [Resolved(CanBeNull = true)] private PowerStatus powerStatus { get; set; } public PlayerLoader(Func createPlayer) @@ -125,7 +125,7 @@ namespace osu.Game.Screens.Play private void load(SessionStatics sessionStatics) { muteWarningShownOnce = sessionStatics.GetBindable(Static.MutedAudioNotificationShownOnce); - batteryWarningShownOnce = sessionStatics.GetBindable(Static.BatteryLowNotificationShownOnce); + batteryWarningShownOnce = sessionStatics.GetBindable(Static.LowBatteryNotificationShownOnce); InternalChild = (content = new LogoTrackingContainer { @@ -483,10 +483,11 @@ namespace osu.Game.Screens.Play private void showBatteryWarningIfNeeded() { + if (powerStatus == null) return; + if (!batteryWarningShownOnce.Value) { - // Checks if the notification has not been shown yet, device is unplugged and if device battery is at or below the cutoff - if (!powerStatus.IsCharging && powerStatus.ChargeLevel <= powerStatus.BatteryCutoff) + if (powerStatus.IsLowBattery) { notificationOverlay?.Post(new BatteryWarningNotification()); batteryWarningShownOnce.Value = true; @@ -500,7 +501,7 @@ namespace osu.Game.Screens.Play public BatteryWarningNotification() { - Text = "Your battery level is low! Charge your device to prevent interruptions."; + Text = "Your battery level is low! Charge your device to prevent interruptions during gameplay."; } [BackgroundDependencyLoader] diff --git a/osu.Game/Tests/OsuTestBrowser.cs b/osu.Game/Tests/OsuTestBrowser.cs index d4fa2a11d3..71b0b02fa6 100644 --- a/osu.Game/Tests/OsuTestBrowser.cs +++ b/osu.Game/Tests/OsuTestBrowser.cs @@ -7,17 +7,11 @@ using osu.Framework.Screens; using osu.Framework.Testing; using osu.Game.Graphics; using osu.Game.Screens.Backgrounds; -using osu.Game.Utils; namespace osu.Game.Tests { public class OsuTestBrowser : OsuGameBase { - public OsuTestBrowser() - { - PowerStatus = new DefaultPowerStatus(); - } - protected override void LoadComplete() { base.LoadComplete(); diff --git a/osu.Game/Utils/PowerStatus.cs b/osu.Game/Utils/PowerStatus.cs index 1caed5b6fa..77d531710e 100644 --- a/osu.Game/Utils/PowerStatus.cs +++ b/osu.Game/Utils/PowerStatus.cs @@ -3,21 +3,27 @@ namespace osu.Game.Utils { + /// + /// Provides access to the system's power status. + /// Currently implemented on iOS and Android only. + /// public abstract class PowerStatus { /// - /// The maximum battery level before a warning notification - /// is sent. + /// The maximum battery level considered as low, from 0 to 1. /// - public virtual double BatteryCutoff { get; } = 0.2; + public virtual double BatteryCutoff { get; } = 0; - public virtual double ChargeLevel { get; set; } - public virtual bool IsCharging { get; set; } - } + /// + /// The charge level of the battery, from 0 to 1. + /// + public virtual double ChargeLevel { get; } = 0; - public class DefaultPowerStatus : PowerStatus - { - public override double ChargeLevel { get; set; } = 1; - public override bool IsCharging { get; set; } = true; + public virtual bool IsCharging { get; } = false; + + /// + /// Returns true if = false and <= . + /// + public bool IsLowBattery => !IsCharging && ChargeLevel <= BatteryCutoff; } } diff --git a/osu.iOS/OsuGameIOS.cs b/osu.iOS/OsuGameIOS.cs index d417fa8f8d..b53b594eae 100644 --- a/osu.iOS/OsuGameIOS.cs +++ b/osu.iOS/OsuGameIOS.cs @@ -12,20 +12,16 @@ namespace osu.iOS { public class OsuGameIOS : OsuGame { - public OsuGameIOS() - : base(null) - { - PowerStatus = new IOSPowerStatus(); - } public override Version AssemblyVersion => new Version(NSBundle.MainBundle.InfoDictionary["CFBundleVersion"].ToString()); protected override UpdateManager CreateUpdateManager() => new SimpleUpdateManager(); - public class IOSPowerStatus : PowerStatus + protected override PowerStatus CreatePowerStatus() => new IOSPowerStatus(); + + private class IOSPowerStatus : PowerStatus { - // The low battery alert appears at 20% on iOS - // https://github.com/ppy/osu/issues/12239 public override double BatteryCutoff => 0.25; + public override double ChargeLevel => Battery.ChargeLevel; public override bool IsCharging => Battery.PowerSource != BatteryPowerSource.Battery; From 43174b708c007e7bf8156fd3f6a01a6f8313a024 Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Sat, 10 Apr 2021 12:58:40 +0200 Subject: [PATCH 032/203] Remove visibility settings Can look into this later, not really important for a first iteration. --- osu.Game/Screens/Edit/Verify/IssueSettings.cs | 1 - .../Screens/Edit/Verify/VisibilitySettings.cs | 52 ------------------- 2 files changed, 53 deletions(-) delete mode 100644 osu.Game/Screens/Edit/Verify/VisibilitySettings.cs diff --git a/osu.Game/Screens/Edit/Verify/IssueSettings.cs b/osu.Game/Screens/Edit/Verify/IssueSettings.cs index 608be877de..4519231cd2 100644 --- a/osu.Game/Screens/Edit/Verify/IssueSettings.cs +++ b/osu.Game/Screens/Edit/Verify/IssueSettings.cs @@ -41,7 +41,6 @@ namespace osu.Game.Screens.Edit.Verify private IReadOnlyList createSections() => new Drawable[] { - new VisibilitySettings() }; } } diff --git a/osu.Game/Screens/Edit/Verify/VisibilitySettings.cs b/osu.Game/Screens/Edit/Verify/VisibilitySettings.cs deleted file mode 100644 index 6488c616e4..0000000000 --- a/osu.Game/Screens/Edit/Verify/VisibilitySettings.cs +++ /dev/null @@ -1,52 +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 osu.Framework.Allocation; -using osu.Framework.Bindables; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Graphics.UserInterfaceV2; -using osuTK; - -namespace osu.Game.Screens.Edit.Verify -{ - internal class VisibilitySettings : CompositeDrawable - { - [BackgroundDependencyLoader] - private void load() - { - RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; - - Padding = new MarginPadding(10); - - InternalChildren = new Drawable[] - { - new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Spacing = new Vector2(10), - Direction = FillDirection.Vertical, - Children = new Drawable[] - { - new LabelledSwitchButton - { - Label = "Show problems", - Current = new Bindable(true) - }, - new LabelledSwitchButton - { - Label = "Show warnings", - Current = new Bindable(true) - }, - new LabelledSwitchButton - { - Label = "Show negligibles" - } - } - }, - }; - } - } -} From d1007ff26aa9b2f9827e666125d0161dba6fd35f Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Sat, 10 Apr 2021 13:02:22 +0200 Subject: [PATCH 033/203] Move components to more appropriate spot --- osu.Game.Rulesets.Osu/Edit/OsuChecker.cs | 2 +- osu.Game/Rulesets/Edit/Checker.cs | 4 +-- .../Edit/Checks}/Components/BeatmapCheck.cs | 2 +- .../Edit/Checks}/Components/Check.cs | 2 +- .../Edit/Checks}/Components/CheckMetadata.cs | 2 +- .../Edit/Checks}/Components/Issue.cs | 25 +++++++++++-------- .../Edit/Checks}/Components/IssueTemplate.cs | 2 +- osu.Game/Screens/Edit/Verify/IssueTable.cs | 2 +- 8 files changed, 22 insertions(+), 19 deletions(-) rename osu.Game/{Screens/Edit/Verify => Rulesets/Edit/Checks}/Components/BeatmapCheck.cs (92%) rename osu.Game/{Screens/Edit/Verify => Rulesets/Edit/Checks}/Components/Check.cs (96%) rename osu.Game/{Screens/Edit/Verify => Rulesets/Edit/Checks}/Components/CheckMetadata.cs (97%) rename osu.Game/{Screens/Edit/Verify => Rulesets/Edit/Checks}/Components/Issue.cs (79%) rename osu.Game/{Screens/Edit/Verify => Rulesets/Edit/Checks}/Components/IssueTemplate.cs (98%) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuChecker.cs b/osu.Game.Rulesets.Osu/Edit/OsuChecker.cs index 9918b53c85..df3847f2fe 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuChecker.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuChecker.cs @@ -5,8 +5,8 @@ using System.Collections.Generic; using System.Linq; using osu.Game.Beatmaps; using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Edit.Checks.Components; using osu.Game.Rulesets.Osu.Edit.Checks; -using osu.Game.Screens.Edit.Verify.Components; namespace osu.Game.Rulesets.Osu.Edit { diff --git a/osu.Game/Rulesets/Edit/Checker.cs b/osu.Game/Rulesets/Edit/Checker.cs index 1c267c3435..9bebfc0668 100644 --- a/osu.Game/Rulesets/Edit/Checker.cs +++ b/osu.Game/Rulesets/Edit/Checker.cs @@ -4,8 +4,8 @@ using System.Collections.Generic; using System.Linq; using osu.Game.Beatmaps; -using osu.Game.Checks; -using osu.Game.Screens.Edit.Verify.Components; +using osu.Game.Rulesets.Edit.Checks; +using osu.Game.Rulesets.Edit.Checks.Components; namespace osu.Game.Rulesets.Edit { diff --git a/osu.Game/Screens/Edit/Verify/Components/BeatmapCheck.cs b/osu.Game/Rulesets/Edit/Checks/Components/BeatmapCheck.cs similarity index 92% rename from osu.Game/Screens/Edit/Verify/Components/BeatmapCheck.cs rename to osu.Game/Rulesets/Edit/Checks/Components/BeatmapCheck.cs index 7297dab60d..d0f93b5eef 100644 --- a/osu.Game/Screens/Edit/Verify/Components/BeatmapCheck.cs +++ b/osu.Game/Rulesets/Edit/Checks/Components/BeatmapCheck.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; using osu.Game.Beatmaps; -namespace osu.Game.Screens.Edit.Verify.Components +namespace osu.Game.Rulesets.Edit.Checks.Components { public abstract class BeatmapCheck : Check { diff --git a/osu.Game/Screens/Edit/Verify/Components/Check.cs b/osu.Game/Rulesets/Edit/Checks/Components/Check.cs similarity index 96% rename from osu.Game/Screens/Edit/Verify/Components/Check.cs rename to osu.Game/Rulesets/Edit/Checks/Components/Check.cs index 2ae21fd350..228c11f0f3 100644 --- a/osu.Game/Screens/Edit/Verify/Components/Check.cs +++ b/osu.Game/Rulesets/Edit/Checks/Components/Check.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; -namespace osu.Game.Screens.Edit.Verify.Components +namespace osu.Game.Rulesets.Edit.Checks.Components { public abstract class Check { diff --git a/osu.Game/Screens/Edit/Verify/Components/CheckMetadata.cs b/osu.Game/Rulesets/Edit/Checks/Components/CheckMetadata.cs similarity index 97% rename from osu.Game/Screens/Edit/Verify/Components/CheckMetadata.cs rename to osu.Game/Rulesets/Edit/Checks/Components/CheckMetadata.cs index 1cac99d47d..5a226729ad 100644 --- a/osu.Game/Screens/Edit/Verify/Components/CheckMetadata.cs +++ b/osu.Game/Rulesets/Edit/Checks/Components/CheckMetadata.cs @@ -1,7 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -namespace osu.Game.Screens.Edit.Verify +namespace osu.Game.Rulesets.Edit.Checks.Components { public class CheckMetadata { diff --git a/osu.Game/Screens/Edit/Verify/Components/Issue.cs b/osu.Game/Rulesets/Edit/Checks/Components/Issue.cs similarity index 79% rename from osu.Game/Screens/Edit/Verify/Components/Issue.cs rename to osu.Game/Rulesets/Edit/Checks/Components/Issue.cs index fe81cb9335..0f835202e7 100644 --- a/osu.Game/Screens/Edit/Verify/Components/Issue.cs +++ b/osu.Game/Rulesets/Edit/Checks/Components/Issue.cs @@ -7,7 +7,7 @@ using System.Linq; using osu.Game.Extensions; using osu.Game.Rulesets.Objects; -namespace osu.Game.Screens.Edit.Verify.Components +namespace osu.Game.Rulesets.Edit.Checks.Components { public class Issue { @@ -39,7 +39,7 @@ namespace osu.Game.Screens.Edit.Verify.Components public Issue(IssueTemplate template, params object[] args) { Time = null; - HitObjects = System.Array.Empty(); + HitObjects = Array.Empty(); Template = template; Arguments = args; @@ -57,11 +57,20 @@ namespace osu.Game.Screens.Edit.Verify.Components Time = time; } + public Issue(HitObject hitObject, IssueTemplate template, params object[] args) + : this(template, args) + { + Time = hitObject.StartTime; + HitObjects = new[] { hitObject }; + } + public Issue(IEnumerable hitObjects, IssueTemplate template, params object[] args) : this(template, args) { - Time = hitObjects.FirstOrDefault()?.StartTime; - HitObjects = hitObjects.ToArray(); + var hitObjectList = hitObjects.ToList(); + + Time = hitObjectList.FirstOrDefault()?.StartTime; + HitObjects = hitObjectList; } public override string ToString() @@ -71,13 +80,7 @@ namespace osu.Game.Screens.Edit.Verify.Components public string GetEditorTimestamp() { - // TODO: Editor timestamp formatting is handled in https://github.com/ppy/osu/pull/12030 - // We may be able to use that here too (if we decouple it from the HitObjectComposer class). - - if (Time == null) - return string.Empty; - - return Time.Value.ToEditorFormattedString(); + return Time == null ? string.Empty : Time.Value.ToEditorFormattedString(); } } } diff --git a/osu.Game/Screens/Edit/Verify/Components/IssueTemplate.cs b/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs similarity index 98% rename from osu.Game/Screens/Edit/Verify/Components/IssueTemplate.cs rename to osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs index b178fa7122..7eaabdc59b 100644 --- a/osu.Game/Screens/Edit/Verify/Components/IssueTemplate.cs +++ b/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs @@ -5,7 +5,7 @@ using Humanizer; using osu.Framework.Graphics; using osuTK.Graphics; -namespace osu.Game.Screens.Edit.Verify.Components +namespace osu.Game.Rulesets.Edit.Checks.Components { public class IssueTemplate { diff --git a/osu.Game/Screens/Edit/Verify/IssueTable.cs b/osu.Game/Screens/Edit/Verify/IssueTable.cs index c70695a849..20dceb5333 100644 --- a/osu.Game/Screens/Edit/Verify/IssueTable.cs +++ b/osu.Game/Screens/Edit/Verify/IssueTable.cs @@ -13,7 +13,7 @@ using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Input.Bindings; -using osu.Game.Screens.Edit.Verify.Components; +using osu.Game.Rulesets.Edit.Checks.Components; using osuTK.Graphics; namespace osu.Game.Screens.Edit.Verify From b30e41b805ffb7196851c42fc7c8b36c2828df0d Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Sat, 10 Apr 2021 13:02:36 +0200 Subject: [PATCH 034/203] Fix comment; mode -> ruleset --- osu.Game/Rulesets/Edit/Checker.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Edit/Checker.cs b/osu.Game/Rulesets/Edit/Checker.cs index 9bebfc0668..6687160b10 100644 --- a/osu.Game/Rulesets/Edit/Checker.cs +++ b/osu.Game/Rulesets/Edit/Checker.cs @@ -11,7 +11,7 @@ namespace osu.Game.Rulesets.Edit { public abstract class Checker { - // These are all mode-invariant, hence here instead of in e.g. `OsuChecker`. + // These are all ruleset-invariant, hence here instead of in e.g. `OsuChecker`. private readonly List beatmapChecks = new List { new CheckMetadataVowels() From bc4f3351f38bdf5d7e5812db554ceceeaa6ec2cc Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Sat, 10 Apr 2021 13:03:16 +0200 Subject: [PATCH 035/203] Replace checks with realistic ones --- .../Edit/Checks/CheckConsecutiveCircles.cs | 86 -------------- .../Edit/Checks/CheckOffscreenObjects.cs | 110 ++++++++++++++++++ osu.Game.Rulesets.Osu/Edit/OsuChecker.cs | 2 +- osu.Game/Checks/CheckMetadataVowels.cs | 65 ----------- osu.Game/Rulesets/Edit/Checker.cs | 2 +- .../Rulesets/Edit/Checks/CheckBackground.cs | 57 +++++++++ 6 files changed, 169 insertions(+), 153 deletions(-) delete mode 100644 osu.Game.Rulesets.Osu/Edit/Checks/CheckConsecutiveCircles.cs create mode 100644 osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs delete mode 100644 osu.Game/Checks/CheckMetadataVowels.cs create mode 100644 osu.Game/Rulesets/Edit/Checks/CheckBackground.cs diff --git a/osu.Game.Rulesets.Osu/Edit/Checks/CheckConsecutiveCircles.cs b/osu.Game.Rulesets.Osu/Edit/Checks/CheckConsecutiveCircles.cs deleted file mode 100644 index c41c0dac2b..0000000000 --- a/osu.Game.Rulesets.Osu/Edit/Checks/CheckConsecutiveCircles.cs +++ /dev/null @@ -1,86 +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.Collections.Generic; -using System.Linq; -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Osu.Objects; -using osu.Game.Screens.Edit.Verify; -using osu.Game.Screens.Edit.Verify.Components; - -namespace osu.Game.Rulesets.Osu.Edit.Checks -{ - public class CheckConsecutiveCircles : BeatmapCheck - { - private const double consecutive_threshold = 3; - private const double delta_time_min_expected = 300; - private const double delta_time_min_threshold = 100; - - public override CheckMetadata Metadata() => new CheckMetadata - ( - category: CheckMetadata.CheckCategory.Spread, - description: "Too many or fast consecutive circles." - ); - - private IssueTemplate templateManyInARow = new IssueTemplate - ( - type: IssueTemplate.IssueType.Problem, - unformattedMessage: "There are {0} circles in a row here, expected at most {1}." - ); - - private IssueTemplate templateTooFast = new IssueTemplate - ( - type: IssueTemplate.IssueType.Warning, - unformattedMessage: "These circles are too fast ({0:0} ms), expected at least {1:0} ms." - ); - - private IssueTemplate templateAlmostTooFast = new IssueTemplate - ( - type: IssueTemplate.IssueType.Negligible, - unformattedMessage: "These circles are almost too fast ({0:0} ms), expected at least {1:0} ms." - ); - - public override IEnumerable Templates() => new[] - { - templateManyInARow, - templateTooFast, - templateAlmostTooFast - }; - - public override IEnumerable Run(IBeatmap beatmap) - { - List prevCircles = new List(); - - foreach (HitObject hitobject in beatmap.HitObjects) - { - if (!(hitobject is HitCircle circle) || hitobject == beatmap.HitObjects.Last()) - { - if (prevCircles.Count > consecutive_threshold) - { - yield return new Issue( - prevCircles, - templateManyInARow, - prevCircles.Count, consecutive_threshold - ); - } - - prevCircles.Clear(); - continue; - } - - double? prevDeltaTime = circle.StartTime - prevCircles.LastOrDefault()?.StartTime; - prevCircles.Add(circle); - - if (prevDeltaTime == null || prevDeltaTime >= delta_time_min_expected) - continue; - - yield return new Issue( - prevCircles.TakeLast(2), - prevDeltaTime < delta_time_min_threshold ? templateTooFast : templateAlmostTooFast, - prevDeltaTime, delta_time_min_expected - ); - } - } - } -} diff --git a/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs b/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs new file mode 100644 index 0000000000..c47864855b --- /dev/null +++ b/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs @@ -0,0 +1,110 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Edit.Checks.Components; +using osu.Game.Rulesets.Osu.Objects; +using osuTK; + +namespace osu.Game.Rulesets.Osu.Edit.Checks +{ + public class CheckOffscreenObjects : BeatmapCheck + { + // These are close approximates to the edges of the screen + // in gameplay on a 4:3 aspect ratio for osu!stable. + private const int min_x = -67; + private const int min_y = -60; + private const int max_x = 579; + private const int max_y = 428; + + // The amount of milliseconds to step through a slider path at a time + // (higher = more performant, but higher false-negative chance). + private const int path_step_size = 5; + + public override CheckMetadata Metadata() => new CheckMetadata + ( + category: CheckMetadata.CheckCategory.Compose, + description: "Offscreen hitobjects." + ); + + public override IEnumerable Templates() => new[] + { + templateOffscreen, + templateOffscreenSliderPath + }; + + private readonly IssueTemplate templateOffscreen = new IssueTemplate + ( + type: IssueTemplate.IssueType.Problem, + unformattedMessage: "This object goes offscreen on a 4:3 aspect ratio." + ); + + private readonly IssueTemplate templateOffscreenSliderPath = new IssueTemplate + ( + type: IssueTemplate.IssueType.Problem, + unformattedMessage: "This slider goes offscreen here on a 4:3 aspect ratio." + ); + + public override IEnumerable Run(IBeatmap beatmap) + { + foreach (var hitobject in beatmap.HitObjects) + { + switch (hitobject) + { + case Slider slider: + { + foreach (var issue in sliderIssues(slider)) + yield return issue; + + break; + } + + case HitCircle circle: + { + if (isOffscreen(circle.StackedPosition, circle.Radius)) + yield return new Issue(circle, templateOffscreen); + + break; + } + } + } + } + + /// + /// Steps through points on the slider to ensure the entire path is on-screen. + /// Returns at most one issue. + /// + /// The slider whose path to check. + /// + private IEnumerable sliderIssues(Slider slider) + { + for (int i = 0; i < slider.Distance; i += path_step_size) + { + double progress = i / slider.Distance; + Vector2 position = slider.StackedPositionAt(progress); + + if (!isOffscreen(position, slider.Radius)) + continue; + + // `SpanDuration` ensures we don't include reverses. + double time = slider.StartTime + progress * slider.SpanDuration; + yield return new Issue(slider, templateOffscreenSliderPath) { Time = time }; + + yield break; + } + + // Above loop may skip the last position in the slider due to step size. + if (!isOffscreen(slider.StackedEndPosition, slider.Radius)) + yield break; + + yield return new Issue(slider, templateOffscreenSliderPath) { Time = slider.EndTime }; + } + + private bool isOffscreen(Vector2 position, double radius) + { + return position.X - radius < min_x || position.X + radius > max_x || + position.Y - radius < min_y || position.Y + radius > max_y; + } + } +} diff --git a/osu.Game.Rulesets.Osu/Edit/OsuChecker.cs b/osu.Game.Rulesets.Osu/Edit/OsuChecker.cs index df3847f2fe..ee3b230679 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuChecker.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuChecker.cs @@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Osu.Edit { public readonly List beatmapChecks = new List { - new CheckConsecutiveCircles() + new CheckOffscreenObjects() }; public override IEnumerable Run(IBeatmap beatmap) diff --git a/osu.Game/Checks/CheckMetadataVowels.cs b/osu.Game/Checks/CheckMetadataVowels.cs deleted file mode 100644 index 8bcfe89c3a..0000000000 --- a/osu.Game/Checks/CheckMetadataVowels.cs +++ /dev/null @@ -1,65 +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.Collections.Generic; -using System.Linq; -using osu.Game.Beatmaps; -using osu.Game.Screens.Edit.Verify; -using osu.Game.Screens.Edit.Verify.Components; - -namespace osu.Game.Checks -{ - public class CheckMetadataVowels : BeatmapCheck - { - private static readonly char[] vowels = { 'a', 'e', 'i', 'o', 'u' }; - - public override CheckMetadata Metadata() => new CheckMetadata - ( - category: CheckMetadata.CheckCategory.Metadata, - description: "Metadata fields contain vowels" - ); - - public override IEnumerable Templates() => new[] - { - templateArtistHasVowels - }; - - private IssueTemplate templateArtistHasVowels = new IssueTemplate - ( - type: IssueTemplate.IssueType.Warning, - unformattedMessage: "The {0} field \"{1}\" contains the vowel(s) {2}." - ); - - public override IEnumerable Run(IBeatmap beatmap) - { - foreach (var issue in GetVowelIssues("artist", beatmap.Metadata.Artist)) - yield return issue; - - foreach (var issue in GetVowelIssues("unicode artist", beatmap.Metadata.ArtistUnicode)) - yield return issue; - - foreach (var issue in GetVowelIssues("title", beatmap.Metadata.Title)) - yield return issue; - - foreach (var issue in GetVowelIssues("unicode title", beatmap.Metadata.TitleUnicode)) - yield return issue; - } - - private IEnumerable GetVowelIssues(string fieldName, string fieldValue) - { - if (fieldValue == null) - // Unicode fields can be null if same as respective romanized fields. - yield break; - - List matches = vowels.Where(c => fieldValue.ToLower().Contains(c)).ToList(); - - if (!matches.Any()) - yield break; - - yield return new Issue( - templateArtistHasVowels, - fieldName, fieldValue, string.Join(", ", matches) - ); - } - } -} diff --git a/osu.Game/Rulesets/Edit/Checker.cs b/osu.Game/Rulesets/Edit/Checker.cs index 6687160b10..65d7fc5913 100644 --- a/osu.Game/Rulesets/Edit/Checker.cs +++ b/osu.Game/Rulesets/Edit/Checker.cs @@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Edit // These are all ruleset-invariant, hence here instead of in e.g. `OsuChecker`. private readonly List beatmapChecks = new List { - new CheckMetadataVowels() + new CheckBackground() }; public virtual IEnumerable Run(IBeatmap beatmap) diff --git a/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs b/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs new file mode 100644 index 0000000000..9376f9568a --- /dev/null +++ b/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs @@ -0,0 +1,57 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using System.Linq; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Edit.Checks.Components; + +namespace osu.Game.Rulesets.Edit.Checks +{ + public class CheckBackground : BeatmapCheck + { + public override CheckMetadata Metadata() => new CheckMetadata + ( + category: CheckMetadata.CheckCategory.Resources, + description: "Missing background." + ); + + public override IEnumerable Templates() => new[] + { + templateNoneSet, + templateDoesNotExist + }; + + private readonly IssueTemplate templateNoneSet = new IssueTemplate + ( + type: IssueTemplate.IssueType.Problem, + unformattedMessage: "No background has been set." + ); + + private readonly IssueTemplate templateDoesNotExist = new IssueTemplate + ( + type: IssueTemplate.IssueType.Problem, + unformattedMessage: "The background file \"{0}\" is does not exist." + ); + + public override IEnumerable Run(IBeatmap beatmap) + { + if (beatmap.Metadata.BackgroundFile == null) + { + yield return new Issue(templateNoneSet); + + yield break; + } + + // If the background is set, also make sure it still exists. + + var set = beatmap.BeatmapInfo.BeatmapSet; + var file = set.Files.FirstOrDefault(f => f.Filename == beatmap.Metadata.BackgroundFile); + + if (file != null) + yield break; + + yield return new Issue(templateDoesNotExist, beatmap.Metadata.BackgroundFile); + } + } +} From 6d3cf78e4a4e50bd40c2b5f667d7e805e5bb93e5 Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Sat, 10 Apr 2021 13:04:39 +0200 Subject: [PATCH 036/203] Add issue selection This mainly helps with keeping track of which issue was clicked, since doing so switches tab. --- osu.Game/Screens/Edit/Verify/IssueTable.cs | 42 ++++++++++++++++++-- osu.Game/Screens/Edit/Verify/VerifyScreen.cs | 7 ++++ 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Edit/Verify/IssueTable.cs b/osu.Game/Screens/Edit/Verify/IssueTable.cs index 20dceb5333..458b0184b6 100644 --- a/osu.Game/Screens/Edit/Verify/IssueTable.cs +++ b/osu.Game/Screens/Edit/Verify/IssueTable.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -26,6 +27,9 @@ namespace osu.Game.Screens.Edit.Verify private readonly FillFlowContainer backgroundFlow; + [Resolved] + private Bindable selectedIssue { get; set; } + public IssueTable() { RelativeSizeAxes = Axes.X; @@ -115,6 +119,7 @@ namespace osu.Game.Screens.Edit.Verify public class RowBackground : OsuClickableContainer { + private readonly Issue issue; private const int fade_duration = 100; private readonly Box hoveredBackground; @@ -128,13 +133,16 @@ namespace osu.Game.Screens.Edit.Verify [Resolved] private EditorBeatmap editorBeatmap { get; set; } + [Resolved] + private Bindable selectedIssue { get; set; } + public RowBackground(Issue issue) { + this.issue = issue; + RelativeSizeAxes = Axes.X; Height = row_height; - AlwaysPresent = true; - CornerRadius = 3; Masking = true; @@ -152,6 +160,8 @@ namespace osu.Game.Screens.Edit.Verify // Supposed to work like clicking timestamps outside of the game. // TODO: Is there already defined behaviour for this I may be able to call? + selectedIssue.Value = issue; + if (issue.Time != null) { clock.Seek(issue.Time.Value); @@ -167,11 +177,35 @@ namespace osu.Game.Screens.Edit.Verify } private Color4 colourHover; + private Color4 colourSelected; [BackgroundDependencyLoader] private void load(OsuColour colours) { hoveredBackground.Colour = colourHover = colours.BlueDarker; + colourSelected = colours.YellowDarker; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + selectedIssue.BindValueChanged(change => { Selected = issue == change.NewValue; }, true); + } + + private bool selected; + + protected bool Selected + { + get => selected; + set + { + if (value == selected) + return; + + selected = value; + updateState(); + } } protected override bool OnHover(HoverEvent e) @@ -188,9 +222,9 @@ namespace osu.Game.Screens.Edit.Verify private void updateState() { - hoveredBackground.FadeColour(colourHover, 450, Easing.OutQuint); + hoveredBackground.FadeColour(selected ? colourSelected : colourHover, 450, Easing.OutQuint); - if (IsHovered) + if (selected || IsHovered) hoveredBackground.FadeIn(fade_duration, Easing.OutQuint); else hoveredBackground.FadeOut(fade_duration, Easing.OutQuint); diff --git a/osu.Game/Screens/Edit/Verify/VerifyScreen.cs b/osu.Game/Screens/Edit/Verify/VerifyScreen.cs index 88397fdbff..ea9f986eb6 100644 --- a/osu.Game/Screens/Edit/Verify/VerifyScreen.cs +++ b/osu.Game/Screens/Edit/Verify/VerifyScreen.cs @@ -13,6 +13,7 @@ using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets; using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Edit.Checks.Components; using osuTK; namespace osu.Game.Screens.Edit.Verify @@ -22,6 +23,9 @@ namespace osu.Game.Screens.Edit.Verify private Ruleset ruleset; private static Checker checker; // TODO: Should not be static, but apparently needs to be? + [Cached] + private Bindable selectedIssue = new Bindable(); + public VerifyScreen() : base(EditorScreenMode.Verify) { @@ -74,6 +78,9 @@ namespace osu.Game.Screens.Edit.Verify [Resolved] protected EditorBeatmap Beatmap { get; private set; } + [Resolved] + private Bindable selectedIssue { get; set; } + [BackgroundDependencyLoader] private void load(OsuColour colours) { From c995eca029f5f126e734507018b62fb3ea717bb5 Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Sat, 10 Apr 2021 13:05:24 +0200 Subject: [PATCH 037/203] Remove todo Doesn't really matter in the end, as only one checker will run at a time in this case. --- osu.Game/Screens/Edit/Verify/VerifyScreen.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Verify/VerifyScreen.cs b/osu.Game/Screens/Edit/Verify/VerifyScreen.cs index ea9f986eb6..b9fd93ac19 100644 --- a/osu.Game/Screens/Edit/Verify/VerifyScreen.cs +++ b/osu.Game/Screens/Edit/Verify/VerifyScreen.cs @@ -21,7 +21,7 @@ namespace osu.Game.Screens.Edit.Verify public class VerifyScreen : EditorScreen { private Ruleset ruleset; - private static Checker checker; // TODO: Should not be static, but apparently needs to be? + private static Checker checker; [Cached] private Bindable selectedIssue = new Bindable(); From 3a4f2e3d7e393d29d1f0f4ad8dcbf989f4f19073 Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Sat, 10 Apr 2021 13:09:16 +0200 Subject: [PATCH 038/203] Show table even if no issues --- osu.Game/Screens/Edit/Verify/IssueTable.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Verify/IssueTable.cs b/osu.Game/Screens/Edit/Verify/IssueTable.cs index 458b0184b6..e4612927fd 100644 --- a/osu.Game/Screens/Edit/Verify/IssueTable.cs +++ b/osu.Game/Screens/Edit/Verify/IssueTable.cs @@ -57,7 +57,7 @@ namespace osu.Game.Screens.Edit.Verify Content = null; backgroundFlow.Clear(); - if (value?.Any() != true) + if (value == null) return; foreach (var issue in value) From 747e0f00dc63e060feb246f69a5ffe6c33249604 Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Sat, 10 Apr 2021 13:10:05 +0200 Subject: [PATCH 039/203] Improve table formatting --- osu.Game/Screens/Edit/Verify/IssueTable.cs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Edit/Verify/IssueTable.cs b/osu.Game/Screens/Edit/Verify/IssueTable.cs index e4612927fd..40bd134551 100644 --- a/osu.Game/Screens/Edit/Verify/IssueTable.cs +++ b/osu.Game/Screens/Edit/Verify/IssueTable.cs @@ -74,9 +74,9 @@ namespace osu.Game.Screens.Edit.Verify { var columns = new List { - new TableColumn(string.Empty, Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), - new TableColumn("Type", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), - new TableColumn("Time", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)), + new TableColumn(string.Empty, Anchor.CentreLeft, new Dimension(GridSizeMode.AutoSize)), + new TableColumn("Type", Anchor.CentreLeft, new Dimension(GridSizeMode.AutoSize, minSize: 60)), + new TableColumn("Time", Anchor.CentreLeft, new Dimension(GridSizeMode.AutoSize, minSize: 60)), new TableColumn("Message", Anchor.CentreLeft), new TableColumn("Category", Anchor.CentreRight, new Dimension(GridSizeMode.AutoSize)), }; @@ -89,20 +89,21 @@ namespace osu.Game.Screens.Edit.Verify new OsuSpriteText { Text = $"#{index + 1}", - Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Medium) + Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Medium), + Margin = new MarginPadding { Right = 10 } }, new OsuSpriteText { Text = issue.Template.Type.ToString(), Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Bold), - Margin = new MarginPadding { Left = 10 }, + Margin = new MarginPadding { Right = 10 }, Colour = issue.Template.TypeColour() }, new OsuSpriteText { Text = issue.GetEditorTimestamp(), Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Bold), - Margin = new MarginPadding(10) + Margin = new MarginPadding { Right = 10 }, }, new OsuSpriteText { From 3289bb03791a11f0181d86a4c21ce13d1e214c2b Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Sat, 10 Apr 2021 14:56:30 +0200 Subject: [PATCH 040/203] Merge `Check` and `BeatmapCheck` We're probably not going to need GeneralChecks or BeatmapsetChecks. The verify tab is only available to a single difficulty at a time, and we already have access to the rest of the set through `IBeatmap`. --- .../Edit/Checks/CheckOffscreenObjects.cs | 2 +- osu.Game.Rulesets.Osu/Edit/OsuChecker.cs | 2 +- osu.Game/Rulesets/Edit/Checker.cs | 4 ++-- .../Rulesets/Edit/Checks/CheckBackground.cs | 2 +- .../Edit/Checks/Components/BeatmapCheck.cs | 19 ------------------ .../Rulesets/Edit/Checks/Components/Check.cs | 20 +++++++++---------- 6 files changed, 14 insertions(+), 35 deletions(-) delete mode 100644 osu.Game/Rulesets/Edit/Checks/Components/BeatmapCheck.cs diff --git a/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs b/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs index c47864855b..3d411d6e12 100644 --- a/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs +++ b/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs @@ -9,7 +9,7 @@ using osuTK; namespace osu.Game.Rulesets.Osu.Edit.Checks { - public class CheckOffscreenObjects : BeatmapCheck + public class CheckOffscreenObjects : Check { // These are close approximates to the edges of the screen // in gameplay on a 4:3 aspect ratio for osu!stable. diff --git a/osu.Game.Rulesets.Osu/Edit/OsuChecker.cs b/osu.Game.Rulesets.Osu/Edit/OsuChecker.cs index ee3b230679..d0dbc043d4 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuChecker.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuChecker.cs @@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Osu.Edit { public class OsuChecker : Checker { - public readonly List beatmapChecks = new List + public readonly List beatmapChecks = new List { new CheckOffscreenObjects() }; diff --git a/osu.Game/Rulesets/Edit/Checker.cs b/osu.Game/Rulesets/Edit/Checker.cs index 65d7fc5913..6ab6ed75e8 100644 --- a/osu.Game/Rulesets/Edit/Checker.cs +++ b/osu.Game/Rulesets/Edit/Checker.cs @@ -12,14 +12,14 @@ namespace osu.Game.Rulesets.Edit public abstract class Checker { // These are all ruleset-invariant, hence here instead of in e.g. `OsuChecker`. - private readonly List beatmapChecks = new List + private readonly IReadOnlyList checks = new List { new CheckBackground() }; public virtual IEnumerable Run(IBeatmap beatmap) { - return beatmapChecks.SelectMany(check => check.Run(beatmap)); + return checks.SelectMany(check => check.Run(beatmap)); } } } diff --git a/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs b/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs index 9376f9568a..aa10fad77b 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs @@ -8,7 +8,7 @@ using osu.Game.Rulesets.Edit.Checks.Components; namespace osu.Game.Rulesets.Edit.Checks { - public class CheckBackground : BeatmapCheck + public class CheckBackground : Check { public override CheckMetadata Metadata() => new CheckMetadata ( diff --git a/osu.Game/Rulesets/Edit/Checks/Components/BeatmapCheck.cs b/osu.Game/Rulesets/Edit/Checks/Components/BeatmapCheck.cs deleted file mode 100644 index d0f93b5eef..0000000000 --- a/osu.Game/Rulesets/Edit/Checks/Components/BeatmapCheck.cs +++ /dev/null @@ -1,19 +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.Collections.Generic; -using osu.Game.Beatmaps; - -namespace osu.Game.Rulesets.Edit.Checks.Components -{ - public abstract class BeatmapCheck : Check - { - /// - /// Returns zero, one, or several issues detected by this - /// check on the given beatmap. - /// - /// The beatmap to run the check on. - /// - public abstract override IEnumerable Run(IBeatmap beatmap); - } -} diff --git a/osu.Game/Rulesets/Edit/Checks/Components/Check.cs b/osu.Game/Rulesets/Edit/Checks/Components/Check.cs index 228c11f0f3..4e444cacbf 100644 --- a/osu.Game/Rulesets/Edit/Checks/Components/Check.cs +++ b/osu.Game/Rulesets/Edit/Checks/Components/Check.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; +using osu.Game.Beatmaps; namespace osu.Game.Rulesets.Edit.Checks.Components { @@ -21,21 +22,18 @@ namespace osu.Game.Rulesets.Edit.Checks.Components /// public abstract IEnumerable Templates(); + /// + /// Returns zero, one, or several issues detected by this + /// check on the given beatmap. + /// + /// The beatmap to run the check on. + /// + public abstract IEnumerable Run(IBeatmap beatmap); + protected Check() { foreach (var template in Templates()) template.Origin = this; } } - - public abstract class Check : Check - { - /// - /// Returns zero, one, or several issues detected by - /// this check on the given object. - /// - /// The object to run the check on. - /// - public abstract IEnumerable Run(T obj); - } } From 7d40b017223a008e17cf53cf0b05006e8dfe288f Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Sat, 10 Apr 2021 15:18:15 +0200 Subject: [PATCH 041/203] Remove old todo --- osu.Game/Screens/Edit/Verify/IssueTable.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game/Screens/Edit/Verify/IssueTable.cs b/osu.Game/Screens/Edit/Verify/IssueTable.cs index 40bd134551..5f04c7c4e8 100644 --- a/osu.Game/Screens/Edit/Verify/IssueTable.cs +++ b/osu.Game/Screens/Edit/Verify/IssueTable.cs @@ -158,9 +158,6 @@ namespace osu.Game.Screens.Edit.Verify Action = () => { - // Supposed to work like clicking timestamps outside of the game. - // TODO: Is there already defined behaviour for this I may be able to call? - selectedIssue.Value = issue; if (issue.Time != null) From dac733cced62e30089f52fa6147f5210197b3e20 Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Sat, 10 Apr 2021 15:49:57 +0200 Subject: [PATCH 042/203] Fix field name and accessibility --- osu.Game.Rulesets.Osu/Edit/OsuChecker.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuChecker.cs b/osu.Game.Rulesets.Osu/Edit/OsuChecker.cs index d0dbc043d4..10f0ecf0cf 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuChecker.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuChecker.cs @@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Osu.Edit { public class OsuChecker : Checker { - public readonly List beatmapChecks = new List + private readonly List checks = new List { new CheckOffscreenObjects() }; @@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Osu.Edit foreach (var issue in base.Run(beatmap)) yield return issue; - foreach (var issue in beatmapChecks.SelectMany(check => check.Run(beatmap))) + foreach (var issue in checks.SelectMany(check => check.Run(beatmap))) yield return issue; } } From 2b947a44da2d320669f61d13d6c1ee8c7c660001 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 11 Apr 2021 09:00:37 +0300 Subject: [PATCH 043/203] Cache power status at base instead --- osu.Game/OsuGame.cs | 5 ----- osu.Game/OsuGameBase.cs | 5 +++++ 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 277455b7d3..809e5d3c1b 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -694,11 +694,6 @@ namespace osu.Game loadComponentSingleFile(new AccountCreationOverlay(), topMostOverlayContent.Add, true); loadComponentSingleFile(new DialogOverlay(), topMostOverlayContent.Add, true); - if (CreatePowerStatus() != null) - { - dependencies.CacheAs(CreatePowerStatus()); - } - chatOverlay.State.ValueChanged += state => channelManager.HighPollRate.Value = state.NewValue == Visibility.Visible; Add(externalLinkOpener = new ExternalLinkOpener()); diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 41d790ea4a..fec8272076 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -284,6 +284,11 @@ namespace osu.Game dependencies.Cache(KeyBindingStore = new KeyBindingStore(contextFactory, RulesetStore)); dependencies.Cache(SettingsStore = new SettingsStore(contextFactory)); dependencies.Cache(RulesetConfigCache = new RulesetConfigCache(SettingsStore)); + + var powerStatus = CreatePowerStatus(); + if (powerStatus != null) + dependencies.CacheAs(powerStatus); + dependencies.Cache(new SessionStatics()); dependencies.Cache(new OsuColour()); From 3d85dc11c62aedccda43b373326463c9d5be5162 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 11 Apr 2021 09:02:32 +0300 Subject: [PATCH 044/203] Adjust documentation --- osu.Game/Utils/PowerStatus.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Utils/PowerStatus.cs b/osu.Game/Utils/PowerStatus.cs index 77d531710e..a53f50b620 100644 --- a/osu.Game/Utils/PowerStatus.cs +++ b/osu.Game/Utils/PowerStatus.cs @@ -22,7 +22,8 @@ namespace osu.Game.Utils public virtual bool IsCharging { get; } = false; /// - /// Returns true if = false and <= . + /// Whether the battery is currently low in charge. + /// Returns true if not charging and current charge level is lower than or equal to . /// public bool IsLowBattery => !IsCharging && ChargeLevel <= BatteryCutoff; } From 07ee1b4d0b73edebaaffc632acd13dc993f0871c Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 11 Apr 2021 09:11:28 +0300 Subject: [PATCH 045/203] Make power status properties abstract --- osu.Game/Utils/PowerStatus.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Utils/PowerStatus.cs b/osu.Game/Utils/PowerStatus.cs index a53f50b620..46f7e32b9e 100644 --- a/osu.Game/Utils/PowerStatus.cs +++ b/osu.Game/Utils/PowerStatus.cs @@ -12,14 +12,14 @@ namespace osu.Game.Utils /// /// The maximum battery level considered as low, from 0 to 1. /// - public virtual double BatteryCutoff { get; } = 0; + public abstract double BatteryCutoff { get; } /// /// The charge level of the battery, from 0 to 1. /// - public virtual double ChargeLevel { get; } = 0; + public abstract double ChargeLevel { get; } - public virtual bool IsCharging { get; } = false; + public abstract bool IsCharging { get; } /// /// Whether the battery is currently low in charge. From cb947a3b2710d1c22a55a1ac91262c83bc68591c Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 11 Apr 2021 10:02:51 +0300 Subject: [PATCH 046/203] Add expected output in test case rather than determining internally --- .../Visual/Gameplay/TestScenePlayerLoader.cs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs index 36958b0741..657c1dd47e 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs @@ -292,10 +292,10 @@ namespace osu.Game.Tests.Visual.Gameplay } } - [TestCase(false, 1.0)] // not charging, above cutoff --> no warning - [TestCase(false, 0.2)] // not charging, at cutoff --> warning - [TestCase(true, 0.1)] // charging, below cutoff --> no warning - public void TestLowBatteryNotification(bool isCharging, double chargeLevel) + [TestCase(false, 1.0, false)] // not charging, above cutoff --> no warning + [TestCase(true, 0.1, false)] // charging, below cutoff --> no warning + [TestCase(false, 0.2, true)] // not charging, at cutoff --> warning + public void TestLowBatteryNotification(bool isCharging, double chargeLevel, bool shouldWarn) { AddStep("reset notification lock", () => sessionStatics.GetBindable(Static.LowBatteryNotificationShownOnce).Value = false); @@ -306,8 +306,7 @@ namespace osu.Game.Tests.Visual.Gameplay powerStatus.SetChargeLevel(chargeLevel); })); AddUntilStep("wait for player", () => player?.LoadState == LoadState.Ready); - int warning = !isCharging && chargeLevel <= powerStatus.BatteryCutoff ? 1 : 0; - AddAssert($"notification {(warning == 1 ? "triggered" : "not triggered")}", () => notificationOverlay.UnreadCount.Value == warning); + AddAssert($"notification {(shouldWarn ? "triggered" : "not triggered")}", () => notificationOverlay.UnreadCount.Value == (shouldWarn ? 1 : 0)); AddStep("click notification", () => { var scrollContainer = (OsuScrollContainer)notificationOverlay.Children.Last(); From 419fd4470cbb818298cd5fe324d8ff528b2f3901 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 11 Apr 2021 08:57:44 +0300 Subject: [PATCH 047/203] Reorder method declaration --- osu.Game/OsuGameBase.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index fec8272076..96aabf0024 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -96,8 +96,6 @@ namespace osu.Game protected Storage Storage { get; set; } - protected virtual PowerStatus CreatePowerStatus() => null; - [Cached] [Cached(typeof(IBindable))] protected readonly Bindable Ruleset = new Bindable(); @@ -159,6 +157,8 @@ namespace osu.Game protected override UserInputManager CreateUserInputManager() => new OsuUserInputManager(); + protected virtual PowerStatus CreatePowerStatus() => null; + /// /// The maximum volume at which audio tracks should playback. This can be set lower than 1 to create some head-room for sound effects. /// From b7e16c2fcc8cad36ab2f40cc59c3f254088624e9 Mon Sep 17 00:00:00 2001 From: Christine Chen Date: Sun, 11 Apr 2021 15:47:38 -0400 Subject: [PATCH 048/203] Remove Xamarin.Essentials package from main project --- osu.Game/osu.Game.csproj | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index d0a918a8f5..471eced55a 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -35,6 +35,5 @@ - From fbd5195738bdd9843434ea7462a906e928285d5a Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 12 Apr 2021 03:37:03 +0300 Subject: [PATCH 049/203] Extract mod setting value handling to utils class --- .../API/ModSettingsDictionaryFormatter.cs | 34 ++---------------- osu.Game/Utils/ModUtils.cs | 36 +++++++++++++++++++ 2 files changed, 38 insertions(+), 32 deletions(-) diff --git a/osu.Game/Online/API/ModSettingsDictionaryFormatter.cs b/osu.Game/Online/API/ModSettingsDictionaryFormatter.cs index dd854acc32..81ecc74ddb 100644 --- a/osu.Game/Online/API/ModSettingsDictionaryFormatter.cs +++ b/osu.Game/Online/API/ModSettingsDictionaryFormatter.cs @@ -3,11 +3,10 @@ using System.Buffers; using System.Collections.Generic; -using System.Diagnostics; using System.Text; using MessagePack; using MessagePack.Formatters; -using osu.Framework.Bindables; +using osu.Game.Utils; namespace osu.Game.Online.API { @@ -24,36 +23,7 @@ namespace osu.Game.Online.API var stringBytes = new ReadOnlySequence(Encoding.UTF8.GetBytes(kvp.Key)); writer.WriteString(in stringBytes); - switch (kvp.Value) - { - case Bindable d: - primitiveFormatter.Serialize(ref writer, d.Value, options); - break; - - case Bindable i: - primitiveFormatter.Serialize(ref writer, i.Value, options); - break; - - case Bindable f: - primitiveFormatter.Serialize(ref writer, f.Value, options); - break; - - case Bindable b: - primitiveFormatter.Serialize(ref writer, b.Value, options); - break; - - case IBindable u: - // A mod with unknown (e.g. enum) generic type. - var valueMethod = u.GetType().GetProperty(nameof(IBindable.Value)); - Debug.Assert(valueMethod != null); - primitiveFormatter.Serialize(ref writer, valueMethod.GetValue(u), options); - break; - - default: - // fall back for non-bindable cases. - primitiveFormatter.Serialize(ref writer, kvp.Value, options); - break; - } + primitiveFormatter.Serialize(ref writer, ModUtils.GetSettingUnderlyingValue(kvp.Value), options); } } diff --git a/osu.Game/Utils/ModUtils.cs b/osu.Game/Utils/ModUtils.cs index c12b5a9fd4..596880f2e7 100644 --- a/osu.Game/Utils/ModUtils.cs +++ b/osu.Game/Utils/ModUtils.cs @@ -3,8 +3,11 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; +using osu.Framework.Bindables; +using osu.Game.Online.API; using osu.Game.Rulesets.Mods; #nullable enable @@ -129,5 +132,38 @@ namespace osu.Game.Utils else yield return mod; } + + /// + /// Returns the underlying value of the given mod setting object. + /// Used in for serialization and equality comparison purposes. + /// + /// The mod setting. + public static object GetSettingUnderlyingValue(object setting) + { + switch (setting) + { + case Bindable d: + return d.Value; + + case Bindable i: + return i.Value; + + case Bindable f: + return f.Value; + + case Bindable b: + return b.Value; + + case IBindable u: + // A mod with unknown (e.g. enum) generic type. + var valueMethod = u.GetType().GetProperty(nameof(IBindable.Value)); + Debug.Assert(valueMethod != null); + return valueMethod.GetValue(u); + + default: + // fall back for non-bindable cases. + return setting; + } + } } } From d6d8ea5b6b5ce956d81863136f5eff3f62bf5cd6 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 12 Apr 2021 11:17:56 +0900 Subject: [PATCH 050/203] Throw when getting a frame of an empty replay --- .../Gameplay/TestSceneSpectatorPlayback.cs | 30 +++++++++---------- osu.Game/Input/Handlers/ReplayInputHandler.cs | 2 -- .../Replays/FramedReplayInputHandler.cs | 9 ++++-- osu.Game/Rulesets/UI/RulesetInputManager.cs | 10 +++++++ 4 files changed, 32 insertions(+), 19 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs index 35b3bfc1f8..9c763814f3 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectatorPlayback.cs @@ -204,27 +204,27 @@ namespace osu.Game.Tests.Visual.Gameplay return; } - if (replayHandler.NextFrame != null) - { - var lastFrame = replay.Frames.LastOrDefault(); + if (!replayHandler.HasFrames) + return; - // this isn't perfect as we basically can't be aware of the rate-of-send here (the streamer is not sending data when not being moved). - // in gameplay playback, the case where NextFrame is null would pause gameplay and handle this correctly; it's strictly a test limitation / best effort implementation. - if (lastFrame != null) - latency = Math.Max(latency, Time.Current - lastFrame.Time); + var lastFrame = replay.Frames.LastOrDefault(); - latencyDisplay.Text = $"latency: {latency:N1}"; + // this isn't perfect as we basically can't be aware of the rate-of-send here (the streamer is not sending data when not being moved). + // in gameplay playback, the case where NextFrame is null would pause gameplay and handle this correctly; it's strictly a test limitation / best effort implementation. + if (lastFrame != null) + latency = Math.Max(latency, Time.Current - lastFrame.Time); - double proposedTime = Time.Current - latency + Time.Elapsed; + latencyDisplay.Text = $"latency: {latency:N1}"; - // this will either advance by one or zero frames. - double? time = replayHandler.SetFrameFromTime(proposedTime); + double proposedTime = Time.Current - latency + Time.Elapsed; - if (time == null) - return; + // this will either advance by one or zero frames. + double? time = replayHandler.SetFrameFromTime(proposedTime); - manualClock.CurrentTime = time.Value; - } + if (time == null) + return; + + manualClock.CurrentTime = time.Value; } [TearDownSteps] diff --git a/osu.Game/Input/Handlers/ReplayInputHandler.cs b/osu.Game/Input/Handlers/ReplayInputHandler.cs index fba1bee0b8..cd76000f98 100644 --- a/osu.Game/Input/Handlers/ReplayInputHandler.cs +++ b/osu.Game/Input/Handlers/ReplayInputHandler.cs @@ -32,8 +32,6 @@ namespace osu.Game.Input.Handlers public override bool Initialize(GameHost host) => true; - public override bool IsActive => true; - public class ReplayState : IInput where T : struct { diff --git a/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs b/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs index 0b41ca31ea..5eaccc766e 100644 --- a/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs +++ b/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs @@ -17,6 +17,8 @@ namespace osu.Game.Rulesets.Replays public abstract class FramedReplayInputHandler : ReplayInputHandler where TFrame : ReplayFrame { + public override bool IsActive => HasFrames; + private readonly Replay replay; protected List Frames => replay.Frames; @@ -25,7 +27,10 @@ namespace osu.Game.Rulesets.Replays { get { - if (!HasFrames || !currentFrameIndex.HasValue) + if (!HasFrames) + throw new InvalidOperationException($"Cannot get {nameof(CurrentFrame)} of the empty replay"); + + if (!currentFrameIndex.HasValue) return null; return (TFrame)Frames[currentFrameIndex.Value]; @@ -37,7 +42,7 @@ namespace osu.Game.Rulesets.Replays get { if (!HasFrames) - return null; + throw new InvalidOperationException($"Cannot get {nameof(NextFrame)} of the empty replay"); if (!currentFrameIndex.HasValue) return currentDirection > 0 ? (TFrame)Frames[0] : null; diff --git a/osu.Game/Rulesets/UI/RulesetInputManager.cs b/osu.Game/Rulesets/UI/RulesetInputManager.cs index d6f002ea2c..fb56a5d93d 100644 --- a/osu.Game/Rulesets/UI/RulesetInputManager.cs +++ b/osu.Game/Rulesets/UI/RulesetInputManager.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -10,6 +11,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Input; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; +using osu.Framework.Input.StateChanges; using osu.Framework.Input.StateChanges.Events; using osu.Framework.Input.States; using osu.Game.Configuration; @@ -100,6 +102,14 @@ namespace osu.Game.Rulesets.UI #endregion + protected override List GetPendingInputs() + { + if (replayInputHandler != null && !replayInputHandler.IsActive) + return new List(); + + return base.GetPendingInputs(); + } + #region Setting application (disables etc.) private Bindable mouseDisabled; From 4fcddfb44b7284dcf39720a685d2f34813e56ecd Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 12 Apr 2021 13:42:14 +0900 Subject: [PATCH 051/203] Fix multiplayer test failure --- .../Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs index 839118de2f..caa731f985 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSubScreen.cs @@ -134,7 +134,7 @@ namespace osu.Game.Tests.Visual.Multiplayer InputManager.Click(MouseButton.Left); }); - AddAssert("match started", () => Client.Room?.State == MultiplayerRoomState.WaitingForLoad); + AddUntilStep("match started", () => Client.Room?.State == MultiplayerRoomState.WaitingForLoad); } } } From 995c244ceef985c3b381b6ec6af54f8d5d940b0d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Apr 2021 14:00:26 +0900 Subject: [PATCH 052/203] Remove alt-mousewheel bindings for volume adjustment With the recent changes to the order of processing key bindings (`GlobalAction`s are handled first), having the alt-wheel bindings in here causes a regression as they are handled before `OnScroll` events. Specifically, this means editor alt-scroll functionality no longer works with the default bindings. Removing the bindings fixes this, while also still allowing alt-wheel adjustment of the volume via `VolumeControlReceptor`: https://github.com/ppy/osu/blob/a2f50af4243dfde95ec556859666b65e34f3007b/osu.Game/Overlays/Volume/VolumeControlReceptor.cs#L21-L26 In conjunction with the special case in `OsuScrollContainer`: https://github.com/ppy/osu/blob/02d5b1352b6c9971ea83a131c0d2637e167b56b3/osu.Game/Graphics/Containers/OsuScrollContainer.cs#L103-L105 --- .../Input/Bindings/GlobalActionContainer.cs | 2 - ...700_RefreshVolumeBindingsAgain.Designer.cs | 506 ++++++++++++++++++ ...210412045700_RefreshVolumeBindingsAgain.cs | 16 + 3 files changed, 522 insertions(+), 2 deletions(-) create mode 100644 osu.Game/Migrations/20210412045700_RefreshVolumeBindingsAgain.Designer.cs create mode 100644 osu.Game/Migrations/20210412045700_RefreshVolumeBindingsAgain.cs diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index 671c3bc8bc..ba4d757cd7 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -97,9 +97,7 @@ namespace osu.Game.Input.Bindings public IEnumerable AudioControlKeyBindings => new[] { new KeyBinding(new[] { InputKey.Alt, InputKey.Up }, GlobalAction.IncreaseVolume), - new KeyBinding(new[] { InputKey.Alt, InputKey.MouseWheelUp }, GlobalAction.IncreaseVolume), new KeyBinding(new[] { InputKey.Alt, InputKey.Down }, GlobalAction.DecreaseVolume), - new KeyBinding(new[] { InputKey.Alt, InputKey.MouseWheelDown }, GlobalAction.DecreaseVolume), new KeyBinding(new[] { InputKey.Control, InputKey.F4 }, GlobalAction.ToggleMute), diff --git a/osu.Game/Migrations/20210412045700_RefreshVolumeBindingsAgain.Designer.cs b/osu.Game/Migrations/20210412045700_RefreshVolumeBindingsAgain.Designer.cs new file mode 100644 index 0000000000..2c100d39b9 --- /dev/null +++ b/osu.Game/Migrations/20210412045700_RefreshVolumeBindingsAgain.Designer.cs @@ -0,0 +1,506 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using osu.Game.Database; + +namespace osu.Game.Migrations +{ + [DbContext(typeof(OsuDbContext))] + [Migration("20210412045700_RefreshVolumeBindingsAgain")] + partial class RefreshVolumeBindingsAgain + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.2.6-servicing-10079"); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("ApproachRate"); + + b.Property("CircleSize"); + + b.Property("DrainRate"); + + b.Property("OverallDifficulty"); + + b.Property("SliderMultiplier"); + + b.Property("SliderTickRate"); + + b.HasKey("ID"); + + b.ToTable("BeatmapDifficulty"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("AudioLeadIn"); + + b.Property("BPM"); + + b.Property("BaseDifficultyID"); + + b.Property("BeatDivisor"); + + b.Property("BeatmapSetInfoID"); + + b.Property("Countdown"); + + b.Property("DistanceSpacing"); + + b.Property("GridSize"); + + b.Property("Hash"); + + b.Property("Hidden"); + + b.Property("Length"); + + b.Property("LetterboxInBreaks"); + + b.Property("MD5Hash"); + + b.Property("MetadataID"); + + b.Property("OnlineBeatmapID"); + + b.Property("Path"); + + b.Property("RulesetID"); + + b.Property("SpecialStyle"); + + b.Property("StackLeniency"); + + b.Property("StarDifficulty"); + + b.Property("Status"); + + b.Property("StoredBookmarks"); + + b.Property("TimelineZoom"); + + b.Property("Version"); + + b.Property("WidescreenStoryboard"); + + b.HasKey("ID"); + + b.HasIndex("BaseDifficultyID"); + + b.HasIndex("BeatmapSetInfoID"); + + b.HasIndex("Hash"); + + b.HasIndex("MD5Hash"); + + b.HasIndex("MetadataID"); + + b.HasIndex("OnlineBeatmapID") + .IsUnique(); + + b.HasIndex("RulesetID"); + + b.ToTable("BeatmapInfo"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Artist"); + + b.Property("ArtistUnicode"); + + b.Property("AudioFile"); + + b.Property("AuthorString") + .HasColumnName("Author"); + + b.Property("BackgroundFile"); + + b.Property("PreviewTime"); + + b.Property("Source"); + + b.Property("Tags"); + + b.Property("Title"); + + b.Property("TitleUnicode"); + + b.Property("VideoFile"); + + b.HasKey("ID"); + + b.ToTable("BeatmapMetadata"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("BeatmapSetInfoID"); + + b.Property("FileInfoID"); + + b.Property("Filename") + .IsRequired(); + + b.HasKey("ID"); + + b.HasIndex("BeatmapSetInfoID"); + + b.HasIndex("FileInfoID"); + + b.ToTable("BeatmapSetFileInfo"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("DateAdded"); + + b.Property("DeletePending"); + + b.Property("Hash"); + + b.Property("MetadataID"); + + b.Property("OnlineBeatmapSetID"); + + b.Property("Protected"); + + b.Property("Status"); + + b.HasKey("ID"); + + b.HasIndex("DeletePending"); + + b.HasIndex("Hash") + .IsUnique(); + + b.HasIndex("MetadataID"); + + b.HasIndex("OnlineBeatmapSetID") + .IsUnique(); + + b.ToTable("BeatmapSetInfo"); + }); + + modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Key") + .HasColumnName("Key"); + + b.Property("RulesetID"); + + b.Property("SkinInfoID"); + + b.Property("StringValue") + .HasColumnName("Value"); + + b.Property("Variant"); + + b.HasKey("ID"); + + b.HasIndex("SkinInfoID"); + + b.HasIndex("RulesetID", "Variant"); + + b.ToTable("Settings"); + }); + + modelBuilder.Entity("osu.Game.IO.FileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Hash"); + + b.Property("ReferenceCount"); + + b.HasKey("ID"); + + b.HasIndex("Hash") + .IsUnique(); + + b.HasIndex("ReferenceCount"); + + b.ToTable("FileInfo"); + }); + + modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("IntAction") + .HasColumnName("Action"); + + b.Property("KeysString") + .HasColumnName("Keys"); + + b.Property("RulesetID"); + + b.Property("Variant"); + + b.HasKey("ID"); + + b.HasIndex("IntAction"); + + b.HasIndex("RulesetID", "Variant"); + + b.ToTable("KeyBinding"); + }); + + modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Available"); + + b.Property("InstantiationInfo"); + + b.Property("Name"); + + b.Property("ShortName"); + + b.HasKey("ID"); + + b.HasIndex("Available"); + + b.HasIndex("ShortName") + .IsUnique(); + + b.ToTable("RulesetInfo"); + }); + + modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("FileInfoID"); + + b.Property("Filename") + .IsRequired(); + + b.Property("ScoreInfoID"); + + b.HasKey("ID"); + + b.HasIndex("FileInfoID"); + + b.HasIndex("ScoreInfoID"); + + b.ToTable("ScoreFileInfo"); + }); + + modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Accuracy") + .HasColumnType("DECIMAL(1,4)"); + + b.Property("BeatmapInfoID"); + + b.Property("Combo"); + + b.Property("Date"); + + b.Property("DeletePending"); + + b.Property("Hash"); + + b.Property("MaxCombo"); + + b.Property("ModsJson") + .HasColumnName("Mods"); + + b.Property("OnlineScoreID"); + + b.Property("PP"); + + b.Property("Rank"); + + b.Property("RulesetID"); + + b.Property("StatisticsJson") + .HasColumnName("Statistics"); + + b.Property("TotalScore"); + + b.Property("UserID") + .HasColumnName("UserID"); + + b.Property("UserString") + .HasColumnName("User"); + + b.HasKey("ID"); + + b.HasIndex("BeatmapInfoID"); + + b.HasIndex("OnlineScoreID") + .IsUnique(); + + b.HasIndex("RulesetID"); + + b.ToTable("ScoreInfo"); + }); + + modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("FileInfoID"); + + b.Property("Filename") + .IsRequired(); + + b.Property("SkinInfoID"); + + b.HasKey("ID"); + + b.HasIndex("FileInfoID"); + + b.HasIndex("SkinInfoID"); + + b.ToTable("SkinFileInfo"); + }); + + modelBuilder.Entity("osu.Game.Skinning.SkinInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Creator"); + + b.Property("DeletePending"); + + b.Property("Hash"); + + b.Property("Name"); + + b.HasKey("ID"); + + b.HasIndex("DeletePending"); + + b.HasIndex("Hash") + .IsUnique(); + + b.ToTable("SkinInfo"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty") + .WithMany() + .HasForeignKey("BaseDifficultyID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet") + .WithMany("Beatmaps") + .HasForeignKey("BeatmapSetInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") + .WithMany("Beatmaps") + .HasForeignKey("MetadataID"); + + b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") + .WithMany() + .HasForeignKey("RulesetID") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo") + .WithMany("Files") + .HasForeignKey("BeatmapSetInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.IO.FileInfo", "FileInfo") + .WithMany() + .HasForeignKey("FileInfoID") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") + .WithMany("BeatmapSets") + .HasForeignKey("MetadataID"); + }); + + modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b => + { + b.HasOne("osu.Game.Skinning.SkinInfo") + .WithMany("Settings") + .HasForeignKey("SkinInfoID"); + }); + + modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b => + { + b.HasOne("osu.Game.IO.FileInfo", "FileInfo") + .WithMany() + .HasForeignKey("FileInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Scoring.ScoreInfo") + .WithMany("Files") + .HasForeignKey("ScoreInfoID"); + }); + + modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapInfo", "Beatmap") + .WithMany("Scores") + .HasForeignKey("BeatmapInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") + .WithMany() + .HasForeignKey("RulesetID") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => + { + b.HasOne("osu.Game.IO.FileInfo", "FileInfo") + .WithMany() + .HasForeignKey("FileInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Skinning.SkinInfo") + .WithMany("Files") + .HasForeignKey("SkinInfoID") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/osu.Game/Migrations/20210412045700_RefreshVolumeBindingsAgain.cs b/osu.Game/Migrations/20210412045700_RefreshVolumeBindingsAgain.cs new file mode 100644 index 0000000000..155d6670a8 --- /dev/null +++ b/osu.Game/Migrations/20210412045700_RefreshVolumeBindingsAgain.cs @@ -0,0 +1,16 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace osu.Game.Migrations +{ + public partial class RefreshVolumeBindingsAgain : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.Sql("DELETE FROM KeyBinding WHERE action in (6,7)"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + } + } +} From dd1925aaedc756a57e1aecadbe2e36992bd17e8a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Apr 2021 14:29:27 +0900 Subject: [PATCH 053/203] Remove temporary input ignore --- .../Screens/OnlinePlay/Multiplayer/Spectate/PlayerGrid_Cell.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/PlayerGrid_Cell.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/PlayerGrid_Cell.cs index 37d88693ee..2df05cb5ed 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/PlayerGrid_Cell.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/PlayerGrid_Cell.cs @@ -89,9 +89,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate private Vector2 getFinalSize() => facade.DrawSize; - // Todo: Temporary? - protected override bool ShouldBeConsideredForInput(Drawable child) => false; - protected override bool OnClick(ClickEvent e) { ToggleMaximisationState(this); From 1c553b5d481d21186eed0498160f90a6e51eedee Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Apr 2021 15:14:53 +0900 Subject: [PATCH 054/203] Checker -> BeatmapVerifier --- .../Edit/{OsuChecker.cs => OsuBeatmapVerifier.cs} | 2 +- osu.Game.Rulesets.Osu/OsuRuleset.cs | 2 +- .../Rulesets/Edit/{Checker.cs => BeatmapVerifier.cs} | 2 +- osu.Game/Rulesets/Ruleset.cs | 2 +- osu.Game/Screens/Edit/Verify/VerifyScreen.cs | 10 +++++----- 5 files changed, 9 insertions(+), 9 deletions(-) rename osu.Game.Rulesets.Osu/Edit/{OsuChecker.cs => OsuBeatmapVerifier.cs} (94%) rename osu.Game/Rulesets/Edit/{Checker.cs => BeatmapVerifier.cs} (94%) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuChecker.cs b/osu.Game.Rulesets.Osu/Edit/OsuBeatmapVerifier.cs similarity index 94% rename from osu.Game.Rulesets.Osu/Edit/OsuChecker.cs rename to osu.Game.Rulesets.Osu/Edit/OsuBeatmapVerifier.cs index 10f0ecf0cf..272612dbaf 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuChecker.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuBeatmapVerifier.cs @@ -10,7 +10,7 @@ using osu.Game.Rulesets.Osu.Edit.Checks; namespace osu.Game.Rulesets.Osu.Edit { - public class OsuChecker : Checker + public class OsuBeatmapVerifier : BeatmapVerifier { private readonly List checks = new List { diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 74679bd578..1658846e98 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -206,7 +206,7 @@ namespace osu.Game.Rulesets.Osu public override HitObjectComposer CreateHitObjectComposer() => new OsuHitObjectComposer(this); - public override Checker CreateChecker() => new OsuChecker(); + public override BeatmapVerifier CreateChecker() => new OsuBeatmapVerifier(); public override string Description => "osu!"; diff --git a/osu.Game/Rulesets/Edit/Checker.cs b/osu.Game/Rulesets/Edit/BeatmapVerifier.cs similarity index 94% rename from osu.Game/Rulesets/Edit/Checker.cs rename to osu.Game/Rulesets/Edit/BeatmapVerifier.cs index 6ab6ed75e8..230b128cc1 100644 --- a/osu.Game/Rulesets/Edit/Checker.cs +++ b/osu.Game/Rulesets/Edit/BeatmapVerifier.cs @@ -9,7 +9,7 @@ using osu.Game.Rulesets.Edit.Checks.Components; namespace osu.Game.Rulesets.Edit { - public abstract class Checker + public abstract class BeatmapVerifier { // These are all ruleset-invariant, hence here instead of in e.g. `OsuChecker`. private readonly IReadOnlyList checks = new List diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index 71f80c9982..088bcc7712 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -202,7 +202,7 @@ namespace osu.Game.Rulesets public virtual HitObjectComposer CreateHitObjectComposer() => null; - public virtual Checker CreateChecker() => null; + public virtual BeatmapVerifier CreateChecker() => null; public virtual Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.Solid.QuestionCircle }; diff --git a/osu.Game/Screens/Edit/Verify/VerifyScreen.cs b/osu.Game/Screens/Edit/Verify/VerifyScreen.cs index b9fd93ac19..7a520826f5 100644 --- a/osu.Game/Screens/Edit/Verify/VerifyScreen.cs +++ b/osu.Game/Screens/Edit/Verify/VerifyScreen.cs @@ -21,7 +21,7 @@ namespace osu.Game.Screens.Edit.Verify public class VerifyScreen : EditorScreen { private Ruleset ruleset; - private static Checker checker; + private static BeatmapVerifier beatmapVerifier; [Cached] private Bindable selectedIssue = new Bindable(); @@ -36,7 +36,7 @@ namespace osu.Game.Screens.Edit.Verify var dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); ruleset = parent.Get>().Value.BeatmapInfo.Ruleset?.CreateInstance(); - checker = ruleset?.CreateChecker(); + beatmapVerifier = ruleset?.CreateChecker(); return dependencies; } @@ -128,9 +128,9 @@ namespace osu.Game.Screens.Edit.Verify private void refresh() { - table.Issues = checker.Run(Beatmap) - .OrderByDescending(issue => issue.Template.Type) - .ThenByDescending(issue => issue.Template.Origin.Metadata().Category); + table.Issues = beatmapVerifier.Run(Beatmap) + .OrderByDescending(issue => issue.Template.Type) + .ThenByDescending(issue => issue.Template.Origin.Metadata().Category); } } } From 136627b9ac59697129ccd1ae94a67e50442d3d61 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Apr 2021 15:27:12 +0900 Subject: [PATCH 055/203] Wrap xmldoc less and make a few fixes --- osu.Game/Rulesets/Edit/BeatmapVerifier.cs | 9 ++++++--- osu.Game/Rulesets/Edit/Checks/Components/Check.cs | 12 +++--------- .../Rulesets/Edit/Checks/Components/CheckMetadata.cs | 6 ++---- osu.Game/Rulesets/Edit/Checks/Components/Issue.cs | 10 +++------- .../Rulesets/Edit/Checks/Components/IssueTemplate.cs | 5 +---- 5 files changed, 15 insertions(+), 27 deletions(-) diff --git a/osu.Game/Rulesets/Edit/BeatmapVerifier.cs b/osu.Game/Rulesets/Edit/BeatmapVerifier.cs index 230b128cc1..f67a068525 100644 --- a/osu.Game/Rulesets/Edit/BeatmapVerifier.cs +++ b/osu.Game/Rulesets/Edit/BeatmapVerifier.cs @@ -11,15 +11,18 @@ namespace osu.Game.Rulesets.Edit { public abstract class BeatmapVerifier { - // These are all ruleset-invariant, hence here instead of in e.g. `OsuChecker`. - private readonly IReadOnlyList checks = new List + /// + /// Checks which are performed regardless of ruleset. + /// These handle things like beatmap metadata, timing, and other ruleset agnostic elements. + /// + private readonly IReadOnlyList generalChecks = new List { new CheckBackground() }; public virtual IEnumerable Run(IBeatmap beatmap) { - return checks.SelectMany(check => check.Run(beatmap)); + return generalChecks.SelectMany(check => check.Run(beatmap)); } } } diff --git a/osu.Game/Rulesets/Edit/Checks/Components/Check.cs b/osu.Game/Rulesets/Edit/Checks/Components/Check.cs index 4e444cacbf..5a922fde56 100644 --- a/osu.Game/Rulesets/Edit/Checks/Components/Check.cs +++ b/osu.Game/Rulesets/Edit/Checks/Components/Check.cs @@ -9,25 +9,19 @@ namespace osu.Game.Rulesets.Edit.Checks.Components public abstract class Check { /// - /// Returns the for this check. - /// Basically, its information. + /// The metadata for this check. /// - /// public abstract CheckMetadata Metadata(); /// - /// The templates for issues that this check may use. - /// Basically, what issues this check can detect. + /// All possible templates for issues that this check may return. /// - /// public abstract IEnumerable Templates(); /// - /// Returns zero, one, or several issues detected by this - /// check on the given beatmap. + /// Runs this check and returns any issues detected for the provided beatmap. /// /// The beatmap to run the check on. - /// public abstract IEnumerable Run(IBeatmap beatmap); protected Check() diff --git a/osu.Game/Rulesets/Edit/Checks/Components/CheckMetadata.cs b/osu.Game/Rulesets/Edit/Checks/Components/CheckMetadata.cs index 5a226729ad..38a9a16325 100644 --- a/osu.Game/Rulesets/Edit/Checks/Components/CheckMetadata.cs +++ b/osu.Game/Rulesets/Edit/Checks/Components/CheckMetadata.cs @@ -42,14 +42,12 @@ namespace osu.Game.Rulesets.Edit.Checks.Components } /// - /// The category this check belongs to. E.g. , - /// , or . + /// The category this check belongs to. E.g. , , or . /// public readonly CheckCategory Category; /// - /// Describes the issue(s) that this check looks for. Keep this brief, such that - /// it fits into "No {description}". E.g. "Offscreen objects" / "Too short sliders". + /// Describes the issue(s) that this check looks for. Keep this brief, such that it fits into "No {description}". E.g. "Offscreen objects" / "Too short sliders". /// public readonly string Description; diff --git a/osu.Game/Rulesets/Edit/Checks/Components/Issue.cs b/osu.Game/Rulesets/Edit/Checks/Components/Issue.cs index 0f835202e7..6f6ba2a323 100644 --- a/osu.Game/Rulesets/Edit/Checks/Components/Issue.cs +++ b/osu.Game/Rulesets/Edit/Checks/Components/Issue.cs @@ -22,17 +22,13 @@ namespace osu.Game.Rulesets.Edit.Checks.Components public IReadOnlyList HitObjects; /// - /// The template which this issue is using. This provides properties - /// such as the , and the - /// . + /// The template which this issue is using. This provides properties such as the , and the . /// public IssueTemplate Template; /// - /// The arguments that give this issue its context, based on the - /// . These are then substituted into the - /// . - /// E.g. timestamps, which diff is being compared to, what some volume is, etc. + /// The arguments that give this issue its context, based on the . These are then substituted into the . + /// This could for instance include timestamps, which diff is being compared to, what some volume is, etc. /// public object[] Arguments; diff --git a/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs b/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs index 7eaabdc59b..69c421140b 100644 --- a/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs +++ b/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs @@ -35,8 +35,7 @@ namespace osu.Game.Rulesets.Edit.Checks.Components public Check Origin; /// - /// The type of the issue. E.g. , - /// , or . + /// The type of the issue. /// public readonly IssueType Type; @@ -57,7 +56,6 @@ namespace osu.Game.Rulesets.Edit.Checks.Components /// Returns the formatted message given the arguments used to format it. /// /// The arguments used to format the message. - /// public string Message(params object[] args) => UnformattedMessage.FormatWith(args); public static readonly Color4 PROBLEM_RED = new Colour4(1.0f, 0.4f, 0.4f, 1.0f); @@ -68,7 +66,6 @@ namespace osu.Game.Rulesets.Edit.Checks.Components /// /// Returns the colour corresponding to the type of this issue. /// - /// public Colour4 TypeColour() { return Type switch From 257acf9cd88f3826e6da336fe66e71d3a7834c60 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Apr 2021 15:28:13 +0900 Subject: [PATCH 056/203] Colour constants to private --- .../Edit/Checks/Components/IssueTemplate.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs b/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs index 69c421140b..5381d54600 100644 --- a/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs +++ b/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs @@ -9,6 +9,11 @@ namespace osu.Game.Rulesets.Edit.Checks.Components { public class IssueTemplate { + private static readonly Color4 problem_red = new Colour4(1.0f, 0.4f, 0.4f, 1.0f); + private static readonly Color4 warning_yellow = new Colour4(1.0f, 0.8f, 0.2f, 1.0f); + private static readonly Color4 negligible_green = new Colour4(0.33f, 0.8f, 0.5f, 1.0f); + private static readonly Color4 error_gray = new Colour4(0.5f, 0.5f, 0.5f, 1.0f); + /// /// The type, or severity, of an issue. This decides its priority. /// @@ -58,11 +63,6 @@ namespace osu.Game.Rulesets.Edit.Checks.Components /// The arguments used to format the message. public string Message(params object[] args) => UnformattedMessage.FormatWith(args); - public static readonly Color4 PROBLEM_RED = new Colour4(1.0f, 0.4f, 0.4f, 1.0f); - public static readonly Color4 WARNING_YELLOW = new Colour4(1.0f, 0.8f, 0.2f, 1.0f); - public static readonly Color4 NEGLIGIBLE_GREEN = new Colour4(0.33f, 0.8f, 0.5f, 1.0f); - public static readonly Color4 ERROR_GRAY = new Colour4(0.5f, 0.5f, 0.5f, 1.0f); - /// /// Returns the colour corresponding to the type of this issue. /// @@ -70,10 +70,10 @@ namespace osu.Game.Rulesets.Edit.Checks.Components { return Type switch { - IssueType.Problem => PROBLEM_RED, - IssueType.Warning => WARNING_YELLOW, - IssueType.Negligible => NEGLIGIBLE_GREEN, - IssueType.Error => ERROR_GRAY, + IssueType.Problem => problem_red, + IssueType.Warning => warning_yellow, + IssueType.Negligible => negligible_green, + IssueType.Error => error_gray, _ => Color4.White }; } From 3551322f1d95738e09c9e5f9c847abf0163d1d3b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Apr 2021 15:30:33 +0900 Subject: [PATCH 057/203] Fix formatting of colour getter --- .../Edit/Checks/Components/IssueTemplate.cs | 28 +++++++++++++------ osu.Game/Screens/Edit/Verify/IssueTable.cs | 2 +- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs b/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs index 5381d54600..6fe8b8de87 100644 --- a/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs +++ b/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs @@ -66,16 +66,28 @@ namespace osu.Game.Rulesets.Edit.Checks.Components /// /// Returns the colour corresponding to the type of this issue. /// - public Colour4 TypeColour() + public Colour4 Colour { - return Type switch + get { - IssueType.Problem => problem_red, - IssueType.Warning => warning_yellow, - IssueType.Negligible => negligible_green, - IssueType.Error => error_gray, - _ => Color4.White - }; + switch (Type) + { + case IssueType.Problem: + return problem_red; + + case IssueType.Warning: + return warning_yellow; + + case IssueType.Negligible: + return negligible_green; + + case IssueType.Error: + return error_gray; + + default: + return Color4.White; + } + } } } } diff --git a/osu.Game/Screens/Edit/Verify/IssueTable.cs b/osu.Game/Screens/Edit/Verify/IssueTable.cs index 5f04c7c4e8..1f275ca537 100644 --- a/osu.Game/Screens/Edit/Verify/IssueTable.cs +++ b/osu.Game/Screens/Edit/Verify/IssueTable.cs @@ -97,7 +97,7 @@ namespace osu.Game.Screens.Edit.Verify Text = issue.Template.Type.ToString(), Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Bold), Margin = new MarginPadding { Right = 10 }, - Colour = issue.Template.TypeColour() + Colour = issue.Template.Colour }, new OsuSpriteText { From f78239c7f238bd64a6272de1c3b5648511f8c581 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Apr 2021 15:32:52 +0900 Subject: [PATCH 058/203] Move enums out of nesting --- .../Edit/Checks/CheckOffscreenObjects.cs | 6 +-- .../Rulesets/Edit/Checks/CheckBackground.cs | 6 +-- .../Edit/Checks/Components/CheckCategory.cs | 41 +++++++++++++++++++ .../Edit/Checks/Components/CheckMetadata.cs | 36 ---------------- .../Rulesets/Edit/Checks/Components/Issue.cs | 2 +- .../Edit/Checks/Components/IssueTemplate.cs | 20 --------- .../Edit/Checks/Components/IssueType.cs | 25 +++++++++++ 7 files changed, 73 insertions(+), 63 deletions(-) create mode 100644 osu.Game/Rulesets/Edit/Checks/Components/CheckCategory.cs create mode 100644 osu.Game/Rulesets/Edit/Checks/Components/IssueType.cs diff --git a/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs b/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs index 3d411d6e12..910de76c5f 100644 --- a/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs +++ b/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs @@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Checks public override CheckMetadata Metadata() => new CheckMetadata ( - category: CheckMetadata.CheckCategory.Compose, + category: CheckCategory.Compose, description: "Offscreen hitobjects." ); @@ -36,13 +36,13 @@ namespace osu.Game.Rulesets.Osu.Edit.Checks private readonly IssueTemplate templateOffscreen = new IssueTemplate ( - type: IssueTemplate.IssueType.Problem, + type: IssueType.Problem, unformattedMessage: "This object goes offscreen on a 4:3 aspect ratio." ); private readonly IssueTemplate templateOffscreenSliderPath = new IssueTemplate ( - type: IssueTemplate.IssueType.Problem, + type: IssueType.Problem, unformattedMessage: "This slider goes offscreen here on a 4:3 aspect ratio." ); diff --git a/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs b/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs index aa10fad77b..2de9d9daae 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs @@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Edit.Checks { public override CheckMetadata Metadata() => new CheckMetadata ( - category: CheckMetadata.CheckCategory.Resources, + category: CheckCategory.Resources, description: "Missing background." ); @@ -24,13 +24,13 @@ namespace osu.Game.Rulesets.Edit.Checks private readonly IssueTemplate templateNoneSet = new IssueTemplate ( - type: IssueTemplate.IssueType.Problem, + type: IssueType.Problem, unformattedMessage: "No background has been set." ); private readonly IssueTemplate templateDoesNotExist = new IssueTemplate ( - type: IssueTemplate.IssueType.Problem, + type: IssueType.Problem, unformattedMessage: "The background file \"{0}\" is does not exist." ); diff --git a/osu.Game/Rulesets/Edit/Checks/Components/CheckCategory.cs b/osu.Game/Rulesets/Edit/Checks/Components/CheckCategory.cs new file mode 100644 index 0000000000..c37a580dd8 --- /dev/null +++ b/osu.Game/Rulesets/Edit/Checks/Components/CheckCategory.cs @@ -0,0 +1,41 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Rulesets.Edit.Checks.Components +{ + /// + /// The category of an issue. + /// + public enum CheckCategory + { + /// Anything to do with control points. + Timing, + + /// Anything to do with artist, title, creator, etc. + Metadata, + + /// Anything to do with non-audio files, e.g. background, skin, sprites, and video. + Resources, + + /// Anything to do with audio files, e.g. song and hitsounds. + Audio, + + /// Anything to do with files that don't fit into the above, e.g. unused, osu, or osb. + Files, + + /// Anything to do with hitobjects unrelated to spread. + Compose, + + /// Anything to do with difficulty levels or their progression. + Spread, + + /// Anything to do with variables like CS, OD, AR, HP, and global SV. + Settings, + + /// Anything to do with hitobject feedback. + Hitsounds, + + /// Anything to do with storyboarding, breaks, video offset, etc. + Events + } +} diff --git a/osu.Game/Rulesets/Edit/Checks/Components/CheckMetadata.cs b/osu.Game/Rulesets/Edit/Checks/Components/CheckMetadata.cs index 38a9a16325..cebb2f5455 100644 --- a/osu.Game/Rulesets/Edit/Checks/Components/CheckMetadata.cs +++ b/osu.Game/Rulesets/Edit/Checks/Components/CheckMetadata.cs @@ -5,42 +5,6 @@ namespace osu.Game.Rulesets.Edit.Checks.Components { public class CheckMetadata { - /// - /// The category of an issue. - /// - public enum CheckCategory - { - /// Anything to do with control points. - Timing, - - /// Anything to do with artist, title, creator, etc. - Metadata, - - /// Anything to do with non-audio files, e.g. background, skin, sprites, and video. - Resources, - - /// Anything to do with audio files, e.g. song and hitsounds. - Audio, - - /// Anything to do with files that don't fit into the above, e.g. unused, osu, or osb. - Files, - - /// Anything to do with hitobjects unrelated to spread. - Compose, - - /// Anything to do with difficulty levels or their progression. - Spread, - - /// Anything to do with variables like CS, OD, AR, HP, and global SV. - Settings, - - /// Anything to do with hitobject feedback. - Hitsounds, - - /// Anything to do with storyboarding, breaks, video offset, etc. - Events - } - /// /// The category this check belongs to. E.g. , , or . /// diff --git a/osu.Game/Rulesets/Edit/Checks/Components/Issue.cs b/osu.Game/Rulesets/Edit/Checks/Components/Issue.cs index 6f6ba2a323..f392fa8ef4 100644 --- a/osu.Game/Rulesets/Edit/Checks/Components/Issue.cs +++ b/osu.Game/Rulesets/Edit/Checks/Components/Issue.cs @@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Edit.Checks.Components public IReadOnlyList HitObjects; /// - /// The template which this issue is using. This provides properties such as the , and the . + /// The template which this issue is using. This provides properties such as the , and the . /// public IssueTemplate Template; diff --git a/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs b/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs index 6fe8b8de87..976341e41c 100644 --- a/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs +++ b/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs @@ -14,26 +14,6 @@ namespace osu.Game.Rulesets.Edit.Checks.Components private static readonly Color4 negligible_green = new Colour4(0.33f, 0.8f, 0.5f, 1.0f); private static readonly Color4 error_gray = new Colour4(0.5f, 0.5f, 0.5f, 1.0f); - /// - /// The type, or severity, of an issue. This decides its priority. - /// - public enum IssueType - { - /// A must-fix in the vast majority of cases. - Problem = 3, - - /// A possible mistake. Often requires critical thinking. - Warning = 2, - - // TODO: Try/catch all checks run and return error templates if exceptions occur. - /// An error occurred and a complete check could not be made. - Error = 1, - - // TODO: Negligible issues should be hidden by default. - /// A possible mistake so minor/unlikely that it can often be safely ignored. - Negligible = 0, - } - /// /// The check that this template originates from. /// diff --git a/osu.Game/Rulesets/Edit/Checks/Components/IssueType.cs b/osu.Game/Rulesets/Edit/Checks/Components/IssueType.cs new file mode 100644 index 0000000000..be43060cfc --- /dev/null +++ b/osu.Game/Rulesets/Edit/Checks/Components/IssueType.cs @@ -0,0 +1,25 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Rulesets.Edit.Checks.Components +{ + /// + /// The type, or severity, of an issue. This decides its priority. + /// + public enum IssueType + { + /// A must-fix in the vast majority of cases. + Problem = 3, + + /// A possible mistake. Often requires critical thinking. + Warning = 2, + + // TODO: Try/catch all checks run and return error templates if exceptions occur. + /// An error occurred and a complete check could not be made. + Error = 1, + + // TODO: Negligible issues should be hidden by default. + /// A possible mistake so minor/unlikely that it can often be safely ignored. + Negligible = 0, + } +} From 8c31e96cdfd657c537dce50a722438fdad7b63dc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Apr 2021 15:37:41 +0900 Subject: [PATCH 059/203] Change some methods to get properties --- osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs | 2 +- osu.Game/Rulesets/Edit/Checks/CheckBackground.cs | 2 +- osu.Game/Rulesets/Edit/Checks/Components/Check.cs | 4 ++-- osu.Game/Rulesets/Edit/Checks/Components/Issue.cs | 5 +---- osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs | 2 +- 5 files changed, 6 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs b/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs index 910de76c5f..b2a00da12a 100644 --- a/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs +++ b/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs @@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Checks description: "Offscreen hitobjects." ); - public override IEnumerable Templates() => new[] + public override IEnumerable PossibleTemplates => new[] { templateOffscreen, templateOffscreenSliderPath diff --git a/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs b/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs index 2de9d9daae..89b2e8293c 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs @@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Edit.Checks description: "Missing background." ); - public override IEnumerable Templates() => new[] + public override IEnumerable PossibleTemplates => new[] { templateNoneSet, templateDoesNotExist diff --git a/osu.Game/Rulesets/Edit/Checks/Components/Check.cs b/osu.Game/Rulesets/Edit/Checks/Components/Check.cs index 5a922fde56..550d2ea0a2 100644 --- a/osu.Game/Rulesets/Edit/Checks/Components/Check.cs +++ b/osu.Game/Rulesets/Edit/Checks/Components/Check.cs @@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Edit.Checks.Components /// /// All possible templates for issues that this check may return. /// - public abstract IEnumerable Templates(); + public abstract IEnumerable PossibleTemplates { get; } /// /// Runs this check and returns any issues detected for the provided beatmap. @@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Edit.Checks.Components protected Check() { - foreach (var template in Templates()) + foreach (var template in PossibleTemplates) template.Origin = this; } } diff --git a/osu.Game/Rulesets/Edit/Checks/Components/Issue.cs b/osu.Game/Rulesets/Edit/Checks/Components/Issue.cs index f392fa8ef4..0d5c5411fd 100644 --- a/osu.Game/Rulesets/Edit/Checks/Components/Issue.cs +++ b/osu.Game/Rulesets/Edit/Checks/Components/Issue.cs @@ -69,10 +69,7 @@ namespace osu.Game.Rulesets.Edit.Checks.Components HitObjects = hitObjectList; } - public override string ToString() - { - return Template.Message(Arguments); - } + public override string ToString() => Template.GetMessage(Arguments); public string GetEditorTimestamp() { diff --git a/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs b/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs index 976341e41c..a1156c7c72 100644 --- a/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs +++ b/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs @@ -41,7 +41,7 @@ namespace osu.Game.Rulesets.Edit.Checks.Components /// Returns the formatted message given the arguments used to format it. /// /// The arguments used to format the message. - public string Message(params object[] args) => UnformattedMessage.FormatWith(args); + public string GetMessage(params object[] args) => UnformattedMessage.FormatWith(args); /// /// Returns the colour corresponding to the type of this issue. From 78bbc8f5c824ffd35a8dab92ca0082c4e7244d7c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Apr 2021 15:47:32 +0900 Subject: [PATCH 060/203] Tidy some remaining code --- .../Edit/Checks/Components/CheckCategory.cs | 42 ++++++++++++++----- .../Rulesets/Edit/Checks/Components/Issue.cs | 6 +-- 2 files changed, 32 insertions(+), 16 deletions(-) diff --git a/osu.Game/Rulesets/Edit/Checks/Components/CheckCategory.cs b/osu.Game/Rulesets/Edit/Checks/Components/CheckCategory.cs index c37a580dd8..ae943cfda9 100644 --- a/osu.Game/Rulesets/Edit/Checks/Components/CheckCategory.cs +++ b/osu.Game/Rulesets/Edit/Checks/Components/CheckCategory.cs @@ -8,34 +8,54 @@ namespace osu.Game.Rulesets.Edit.Checks.Components /// public enum CheckCategory { - /// Anything to do with control points. + /// + /// Anything to do with control points. + /// Timing, - /// Anything to do with artist, title, creator, etc. + /// + /// Anything to do with artist, title, creator, etc. + /// Metadata, - /// Anything to do with non-audio files, e.g. background, skin, sprites, and video. + /// + /// Anything to do with non-audio files, e.g. background, skin, sprites, and video. + /// Resources, - /// Anything to do with audio files, e.g. song and hitsounds. + /// + /// Anything to do with audio files, e.g. song and hitsounds. + /// Audio, - /// Anything to do with files that don't fit into the above, e.g. unused, osu, or osb. + /// + /// Anything to do with files that don't fit into the above, e.g. unused, osu, or osb. + /// Files, - /// Anything to do with hitobjects unrelated to spread. + /// + /// Anything to do with hitobjects unrelated to spread. + /// Compose, - /// Anything to do with difficulty levels or their progression. + /// + /// Anything to do with difficulty levels or their progression. + /// Spread, - /// Anything to do with variables like CS, OD, AR, HP, and global SV. + /// + /// Anything to do with variables like CS, OD, AR, HP, and global SV. + /// Settings, - /// Anything to do with hitobject feedback. - Hitsounds, + /// + /// Anything to do with hitobject feedback. + /// + HitObjects, - /// Anything to do with storyboarding, breaks, video offset, etc. + /// + /// Anything to do with storyboarding, breaks, video offset, etc. + /// Events } } diff --git a/osu.Game/Rulesets/Edit/Checks/Components/Issue.cs b/osu.Game/Rulesets/Edit/Checks/Components/Issue.cs index 0d5c5411fd..7241fabf5b 100644 --- a/osu.Game/Rulesets/Edit/Checks/Components/Issue.cs +++ b/osu.Game/Rulesets/Edit/Checks/Components/Issue.cs @@ -40,11 +40,7 @@ namespace osu.Game.Rulesets.Edit.Checks.Components Arguments = args; if (template.Origin == null) - { - throw new ArgumentException( - "A template had no origin. Make sure the `Templates()` method contains all templates used." - ); - } + throw new ArgumentException("A template had no origin. Make sure the `Templates()` method contains all templates used."); } public Issue(double? time, IssueTemplate template, params object[] args) From 8bf85d737c75a71ba9e388842770cfc987f2bed7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Apr 2021 15:52:29 +0900 Subject: [PATCH 061/203] Change Metadata into a get property --- osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs | 2 +- osu.Game/Rulesets/Edit/Checks/CheckBackground.cs | 2 +- osu.Game/Rulesets/Edit/Checks/Components/Check.cs | 2 +- osu.Game/Screens/Edit/Verify/IssueTable.cs | 2 +- osu.Game/Screens/Edit/Verify/VerifyScreen.cs | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs b/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs index b2a00da12a..252f65ab5a 100644 --- a/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs +++ b/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs @@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Checks // (higher = more performant, but higher false-negative chance). private const int path_step_size = 5; - public override CheckMetadata Metadata() => new CheckMetadata + public override CheckMetadata Metadata { get; } = new CheckMetadata ( category: CheckCategory.Compose, description: "Offscreen hitobjects." diff --git a/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs b/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs index 89b2e8293c..22f98b6fd7 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs @@ -10,7 +10,7 @@ namespace osu.Game.Rulesets.Edit.Checks { public class CheckBackground : Check { - public override CheckMetadata Metadata() => new CheckMetadata + public override CheckMetadata Metadata { get; } = new CheckMetadata ( category: CheckCategory.Resources, description: "Missing background." diff --git a/osu.Game/Rulesets/Edit/Checks/Components/Check.cs b/osu.Game/Rulesets/Edit/Checks/Components/Check.cs index 550d2ea0a2..7c039d1572 100644 --- a/osu.Game/Rulesets/Edit/Checks/Components/Check.cs +++ b/osu.Game/Rulesets/Edit/Checks/Components/Check.cs @@ -11,7 +11,7 @@ namespace osu.Game.Rulesets.Edit.Checks.Components /// /// The metadata for this check. /// - public abstract CheckMetadata Metadata(); + public abstract CheckMetadata Metadata { get; } /// /// All possible templates for issues that this check may return. diff --git a/osu.Game/Screens/Edit/Verify/IssueTable.cs b/osu.Game/Screens/Edit/Verify/IssueTable.cs index 1f275ca537..84dbabf300 100644 --- a/osu.Game/Screens/Edit/Verify/IssueTable.cs +++ b/osu.Game/Screens/Edit/Verify/IssueTable.cs @@ -112,7 +112,7 @@ namespace osu.Game.Screens.Edit.Verify }, new OsuSpriteText { - Text = issue.Template.Origin.Metadata().Category.ToString(), + Text = issue.Template.Origin.Metadata.Category.ToString(), Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Bold), Margin = new MarginPadding(10) } diff --git a/osu.Game/Screens/Edit/Verify/VerifyScreen.cs b/osu.Game/Screens/Edit/Verify/VerifyScreen.cs index 7a520826f5..0c4aa04ff3 100644 --- a/osu.Game/Screens/Edit/Verify/VerifyScreen.cs +++ b/osu.Game/Screens/Edit/Verify/VerifyScreen.cs @@ -130,7 +130,7 @@ namespace osu.Game.Screens.Edit.Verify { table.Issues = beatmapVerifier.Run(Beatmap) .OrderByDescending(issue => issue.Template.Type) - .ThenByDescending(issue => issue.Template.Origin.Metadata().Category); + .ThenByDescending(issue => issue.Template.Origin.Metadata.Category); } } } From 42604afcdcf9ed0ae922b07434b4587ad4b4c85d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Apr 2021 16:15:27 +0900 Subject: [PATCH 062/203] Add binding for verify mode (and move enum entry to end) --- osu.Game/Input/Bindings/GlobalActionContainer.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index 3ea7f5f5fa..36e465720a 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -70,6 +70,7 @@ namespace osu.Game.Input.Bindings new KeyBinding(new[] { InputKey.F2 }, GlobalAction.EditorDesignMode), new KeyBinding(new[] { InputKey.F3 }, GlobalAction.EditorTimingMode), new KeyBinding(new[] { InputKey.F4 }, GlobalAction.EditorSetupMode), + new KeyBinding(new[] { InputKey.Control, InputKey.Shift, InputKey.A }, GlobalAction.EditorVerifyMode), }; public IEnumerable InGameKeyBindings => new[] @@ -225,9 +226,6 @@ namespace osu.Game.Input.Bindings [Description("Timing mode")] EditorTimingMode, - [Description("Verify mode")] - EditorVerifyMode, - [Description("Hold for HUD")] HoldForHUD, @@ -252,5 +250,8 @@ namespace osu.Game.Input.Bindings [Description("Beatmap Options")] ToggleBeatmapOptions, + + [Description("Verify mode")] + EditorVerifyMode, } } From e19e8ff2a3252b9adde46dae589ce8d2a134f4dc Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 12 Apr 2021 15:52:43 +0900 Subject: [PATCH 063/203] Rewrite FramedReplayInputHandler for robustness This commit changes the semantics of `CurrentFrame` and `NextFrame` of the class. The ordering of `NextFrame.Time` and `CurrentFrame.Time` was dependent on the current direction. Now, it should always satisfy `CurrentFrame.Time <= CurrentTime <= NextFrame.Time` except at the start/end. This change, however, doesn't break existing deriving classes if the template code pattern usage of interpolation is used. The deriving class code can be simplified due to the elimination of nullable types. I didn't include those changes in this commit. I removed `StreamingFramedReplayInputHandlerTest` for now, as it is almost-duplicate of `FramedReplayInputHandlerTest`. I'll include more tests in later commits. This commit fixes #6150. --- .../NonVisual/FramedReplayInputHandlerTest.cs | 81 ++--- .../StreamingFramedReplayInputHandlerTest.cs | 296 ------------------ .../Visual/Gameplay/TestSceneSpectator.cs | 4 +- .../Replays/FramedReplayInputHandler.cs | 167 +++++----- 4 files changed, 109 insertions(+), 439 deletions(-) delete mode 100644 osu.Game.Tests/NonVisual/StreamingFramedReplayInputHandlerTest.cs diff --git a/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs b/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs index 92a60663de..b4fc081a2a 100644 --- a/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs +++ b/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs @@ -37,11 +37,6 @@ namespace osu.Game.Tests.NonVisual [Test] public void TestNormalPlayback() { - Assert.IsNull(handler.CurrentFrame); - - confirmCurrentFrame(null); - confirmNextFrame(0); - setTime(0, 0); confirmCurrentFrame(0); confirmNextFrame(1); @@ -97,22 +92,22 @@ namespace osu.Game.Tests.NonVisual // exited important section setTime(8200, 8000); confirmCurrentFrame(7); - confirmNextFrame(null); + confirmNextFrame(7); setTime(8200, 8200); confirmCurrentFrame(7); - confirmNextFrame(null); + confirmNextFrame(7); } [Test] public void TestIntroTime() { setTime(-1000, -1000); - confirmCurrentFrame(null); + confirmCurrentFrame(0); confirmNextFrame(0); setTime(-500, -500); - confirmCurrentFrame(null); + confirmCurrentFrame(0); confirmNextFrame(0); setTime(0, 0); @@ -133,29 +128,29 @@ namespace osu.Game.Tests.NonVisual // pivot without crossing a frame boundary setTime(2700, 2700); confirmCurrentFrame(2); - confirmNextFrame(1); + confirmNextFrame(3); - // cross current frame boundary; should not yet update frame - setTime(1980, 1980); + // cross current frame boundary + setTime(1980, 2000); confirmCurrentFrame(2); - confirmNextFrame(1); + confirmNextFrame(3); setTime(1200, 1200); - confirmCurrentFrame(2); - confirmNextFrame(1); + confirmCurrentFrame(1); + confirmNextFrame(2); // ensure each frame plays out until start setTime(-500, 1000); confirmCurrentFrame(1); - confirmNextFrame(0); + confirmNextFrame(2); setTime(-500, 0); confirmCurrentFrame(0); - confirmNextFrame(null); + confirmNextFrame(1); setTime(-500, -500); confirmCurrentFrame(0); - confirmNextFrame(null); + confirmNextFrame(0); } [Test] @@ -168,12 +163,12 @@ namespace osu.Game.Tests.NonVisual confirmNextFrame(5); setTime(3500, null); - confirmCurrentFrame(4); - confirmNextFrame(3); + confirmCurrentFrame(3); + confirmNextFrame(4); setTime(3000, 3000); confirmCurrentFrame(3); - confirmNextFrame(2); + confirmNextFrame(4); setTime(3500, null); confirmCurrentFrame(3); @@ -187,17 +182,17 @@ namespace osu.Game.Tests.NonVisual confirmCurrentFrame(4); confirmNextFrame(5); - setTime(4000, null); + setTime(4000, 4000); confirmCurrentFrame(4); confirmNextFrame(5); setTime(3500, null); - confirmCurrentFrame(4); - confirmNextFrame(3); + confirmCurrentFrame(3); + confirmNextFrame(4); setTime(3000, 3000); confirmCurrentFrame(3); - confirmNextFrame(2); + confirmNextFrame(4); } [Test] @@ -209,24 +204,24 @@ namespace osu.Game.Tests.NonVisual confirmNextFrame(4); setTime(3200, null); - // next frame doesn't change even though direction reversed, because of important section. confirmCurrentFrame(3); confirmNextFrame(4); - setTime(3000, null); + setTime(3000, 3000); confirmCurrentFrame(3); confirmNextFrame(4); setTime(2800, 2800); - confirmCurrentFrame(3); - confirmNextFrame(2); + confirmCurrentFrame(2); + confirmNextFrame(3); } private void fastForwardToPoint(double destination) { for (int i = 0; i < 1000; i++) { - if (handler.SetFrameFromTime(destination) == null) + var time = handler.SetFrameFromTime(destination); + if (time == null || time == destination) return; } @@ -235,33 +230,17 @@ namespace osu.Game.Tests.NonVisual private void setTime(double set, double? expect) { - Assert.AreEqual(expect, handler.SetFrameFromTime(set)); + Assert.AreEqual(expect, handler.SetFrameFromTime(set), "Unexpected return value"); } - private void confirmCurrentFrame(int? frame) + private void confirmCurrentFrame(int frame) { - if (frame.HasValue) - { - Assert.IsNotNull(handler.CurrentFrame); - Assert.AreEqual(replay.Frames[frame.Value].Time, handler.CurrentFrame.Time); - } - else - { - Assert.IsNull(handler.CurrentFrame); - } + Assert.AreEqual(replay.Frames[frame].Time, handler.CurrentFrame.Time, "Unexpected current frame"); } - private void confirmNextFrame(int? frame) + private void confirmNextFrame(int frame) { - if (frame.HasValue) - { - Assert.IsNotNull(handler.NextFrame); - Assert.AreEqual(replay.Frames[frame.Value].Time, handler.NextFrame.Time); - } - else - { - Assert.IsNull(handler.NextFrame); - } + Assert.AreEqual(replay.Frames[frame].Time, handler.NextFrame.Time, "Unexpected next frame"); } private class TestReplayFrame : ReplayFrame diff --git a/osu.Game.Tests/NonVisual/StreamingFramedReplayInputHandlerTest.cs b/osu.Game.Tests/NonVisual/StreamingFramedReplayInputHandlerTest.cs deleted file mode 100644 index 21ec29b10b..0000000000 --- a/osu.Game.Tests/NonVisual/StreamingFramedReplayInputHandlerTest.cs +++ /dev/null @@ -1,296 +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 NUnit.Framework; -using osu.Game.Replays; -using osu.Game.Rulesets.Replays; - -namespace osu.Game.Tests.NonVisual -{ - [TestFixture] - public class StreamingFramedReplayInputHandlerTest - { - private Replay replay; - private TestInputHandler handler; - - [SetUp] - public void SetUp() - { - handler = new TestInputHandler(replay = new Replay - { - HasReceivedAllFrames = false, - Frames = new List - { - new TestReplayFrame(0), - new TestReplayFrame(1000), - new TestReplayFrame(2000), - new TestReplayFrame(3000, true), - new TestReplayFrame(4000, true), - new TestReplayFrame(5000, true), - new TestReplayFrame(7000, true), - new TestReplayFrame(8000), - } - }); - } - - [Test] - public void TestNormalPlayback() - { - Assert.IsNull(handler.CurrentFrame); - - confirmCurrentFrame(null); - confirmNextFrame(0); - - setTime(0, 0); - confirmCurrentFrame(0); - confirmNextFrame(1); - - // if we hit the first frame perfectly, time should progress to it. - setTime(1000, 1000); - confirmCurrentFrame(1); - confirmNextFrame(2); - - // in between non-important frames should progress based on input. - setTime(1200, 1200); - confirmCurrentFrame(1); - - setTime(1400, 1400); - confirmCurrentFrame(1); - - // progressing beyond the next frame should force time to that frame once. - setTime(2200, 2000); - confirmCurrentFrame(2); - - // second attempt should progress to input time - setTime(2200, 2200); - confirmCurrentFrame(2); - - // entering important section - setTime(3000, 3000); - confirmCurrentFrame(3); - - // cannot progress within - setTime(3500, null); - confirmCurrentFrame(3); - - setTime(4000, 4000); - confirmCurrentFrame(4); - - // still cannot progress - setTime(4500, null); - confirmCurrentFrame(4); - - setTime(5200, 5000); - confirmCurrentFrame(5); - - // important section AllowedImportantTimeSpan allowance - setTime(5200, 5200); - confirmCurrentFrame(5); - - setTime(7200, 7000); - confirmCurrentFrame(6); - - setTime(7200, null); - confirmCurrentFrame(6); - - // exited important section - setTime(8200, 8000); - confirmCurrentFrame(7); - confirmNextFrame(null); - - setTime(8200, null); - confirmCurrentFrame(7); - confirmNextFrame(null); - - setTime(8400, null); - confirmCurrentFrame(7); - confirmNextFrame(null); - } - - [Test] - public void TestIntroTime() - { - setTime(-1000, -1000); - confirmCurrentFrame(null); - confirmNextFrame(0); - - setTime(-500, -500); - confirmCurrentFrame(null); - confirmNextFrame(0); - - setTime(0, 0); - confirmCurrentFrame(0); - confirmNextFrame(1); - } - - [Test] - public void TestBasicRewind() - { - setTime(2800, 0); - setTime(2800, 1000); - setTime(2800, 2000); - setTime(2800, 2800); - confirmCurrentFrame(2); - confirmNextFrame(3); - - // pivot without crossing a frame boundary - setTime(2700, 2700); - confirmCurrentFrame(2); - confirmNextFrame(1); - - // cross current frame boundary; should not yet update frame - setTime(1980, 1980); - confirmCurrentFrame(2); - confirmNextFrame(1); - - setTime(1200, 1200); - confirmCurrentFrame(2); - confirmNextFrame(1); - - // ensure each frame plays out until start - setTime(-500, 1000); - confirmCurrentFrame(1); - confirmNextFrame(0); - - setTime(-500, 0); - confirmCurrentFrame(0); - confirmNextFrame(null); - - setTime(-500, -500); - confirmCurrentFrame(0); - confirmNextFrame(null); - } - - [Test] - public void TestRewindInsideImportantSection() - { - fastForwardToPoint(3000); - - setTime(4000, 4000); - confirmCurrentFrame(4); - confirmNextFrame(5); - - setTime(3500, null); - confirmCurrentFrame(4); - confirmNextFrame(3); - - setTime(3000, 3000); - confirmCurrentFrame(3); - confirmNextFrame(2); - - setTime(3500, null); - confirmCurrentFrame(3); - confirmNextFrame(4); - - setTime(4000, 4000); - confirmCurrentFrame(4); - confirmNextFrame(5); - - setTime(4500, null); - confirmCurrentFrame(4); - confirmNextFrame(5); - - setTime(4000, null); - confirmCurrentFrame(4); - confirmNextFrame(5); - - setTime(3500, null); - confirmCurrentFrame(4); - confirmNextFrame(3); - - setTime(3000, 3000); - confirmCurrentFrame(3); - confirmNextFrame(2); - } - - [Test] - public void TestRewindOutOfImportantSection() - { - fastForwardToPoint(3500); - - confirmCurrentFrame(3); - confirmNextFrame(4); - - setTime(3200, null); - // next frame doesn't change even though direction reversed, because of important section. - confirmCurrentFrame(3); - confirmNextFrame(4); - - setTime(3000, null); - confirmCurrentFrame(3); - confirmNextFrame(4); - - setTime(2800, 2800); - confirmCurrentFrame(3); - confirmNextFrame(2); - } - - private void fastForwardToPoint(double destination) - { - for (int i = 0; i < 1000; i++) - { - if (handler.SetFrameFromTime(destination) == null) - return; - } - - throw new TimeoutException("Seek was never fulfilled"); - } - - private void setTime(double set, double? expect) - { - Assert.AreEqual(expect, handler.SetFrameFromTime(set)); - } - - private void confirmCurrentFrame(int? frame) - { - if (frame.HasValue) - { - Assert.IsNotNull(handler.CurrentFrame); - Assert.AreEqual(replay.Frames[frame.Value].Time, handler.CurrentFrame.Time); - } - else - { - Assert.IsNull(handler.CurrentFrame); - } - } - - private void confirmNextFrame(int? frame) - { - if (frame.HasValue) - { - Assert.IsNotNull(handler.NextFrame); - Assert.AreEqual(replay.Frames[frame.Value].Time, handler.NextFrame.Time); - } - else - { - Assert.IsNull(handler.NextFrame); - } - } - - private class TestReplayFrame : ReplayFrame - { - public readonly bool IsImportant; - - public TestReplayFrame(double time, bool isImportant = false) - : base(time) - { - IsImportant = isImportant; - } - } - - private class TestInputHandler : FramedReplayInputHandler - { - public TestInputHandler(Replay replay) - : base(replay) - { - FrameAccuratePlayback = true; - } - - protected override double AllowedImportantTimeSpan => 1000; - - protected override bool IsImportant(TestReplayFrame frame) => frame.IsImportant; - } - } -} diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs index 9d85a9995d..9f1faa8e26 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs @@ -83,7 +83,7 @@ namespace osu.Game.Tests.Visual.Gameplay waitForPlayer(); AddAssert("ensure frames arrived", () => replayHandler.HasFrames); - AddUntilStep("wait for frame starvation", () => replayHandler.NextFrame == null); + AddUntilStep("wait for frame starvation", () => replayHandler.WaitingNextFrame); checkPaused(true); double? pausedTime = null; @@ -92,7 +92,7 @@ namespace osu.Game.Tests.Visual.Gameplay sendFrames(); - AddUntilStep("wait for frame starvation", () => replayHandler.NextFrame == null); + AddUntilStep("wait for frame starvation", () => replayHandler.WaitingNextFrame); checkPaused(true); AddAssert("time advanced", () => currentFrameStableTime > pausedTime); diff --git a/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs b/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs index 5eaccc766e..f527c0e105 100644 --- a/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs +++ b/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs @@ -1,9 +1,10 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +#nullable enable + using System; using System.Collections.Generic; -using System.Diagnostics; using JetBrains.Annotations; using osu.Game.Input.Handlers; using osu.Game.Replays; @@ -17,70 +18,80 @@ namespace osu.Game.Rulesets.Replays public abstract class FramedReplayInputHandler : ReplayInputHandler where TFrame : ReplayFrame { - public override bool IsActive => HasFrames; + /// + /// Whether we have at least one replay frame. + /// + public bool HasFrames => Frames.Count != 0; - private readonly Replay replay; - - protected List Frames => replay.Frames; + /// + /// Whether we are waiting for new frames to be received. + /// + public bool WaitingNextFrame => !replay.HasReceivedAllFrames && currentFrameIndex == Frames.Count - 1; + /// + /// The current frame of the replay. + /// The current time is always between the start and the end time of the current frame. + /// + /// The replay is empty. public TFrame CurrentFrame { get { if (!HasFrames) - throw new InvalidOperationException($"Cannot get {nameof(CurrentFrame)} of the empty replay"); + throw new InvalidOperationException($"Attempted to get {nameof(CurrentFrame)} of an empty replay"); - if (!currentFrameIndex.HasValue) - return null; - - return (TFrame)Frames[currentFrameIndex.Value]; + return (TFrame)Frames[Math.Max(0, currentFrameIndex)]; } } + /// + /// The next frame of the replay. + /// The start time is always greater or equals to the start time of regardless of the seeking direction. + /// If it is before the first frame of the replay or the after the last frame of the replay, and agree. + /// + /// The replay is empty. public TFrame NextFrame { get { if (!HasFrames) - throw new InvalidOperationException($"Cannot get {nameof(NextFrame)} of the empty replay"); + throw new InvalidOperationException($"Attempted to get {nameof(NextFrame)} of an empty replay"); - if (!currentFrameIndex.HasValue) - return currentDirection > 0 ? (TFrame)Frames[0] : null; - - int nextFrame = clampedNextFrameIndex; - - if (nextFrame == currentFrameIndex.Value) - return null; - - return (TFrame)Frames[clampedNextFrameIndex]; + return (TFrame)Frames[Math.Min(currentFrameIndex + 1, Frames.Count - 1)]; } } - private int? currentFrameIndex; - - private int clampedNextFrameIndex => - currentFrameIndex.HasValue ? Math.Clamp(currentFrameIndex.Value + currentDirection, 0, Frames.Count - 1) : 0; - - protected FramedReplayInputHandler(Replay replay) - { - this.replay = replay; - } - - private const double sixty_frame_time = 1000.0 / 60; - - protected virtual double AllowedImportantTimeSpan => sixty_frame_time * 1.2; - - protected double? CurrentTime { get; private set; } - - private int currentDirection = 1; - /// /// When set, we will ensure frames executed by nested drawables are frame-accurate to replay data. /// Disabling this can make replay playback smoother (useful for autoplay, currently). /// public bool FrameAccuratePlayback; - public bool HasFrames => Frames.Count > 0; + // This input handler should be enabled only if there is at least one replay frame. + public override bool IsActive => HasFrames; + + // Can make it non-null but that is a breaking change. + protected double? CurrentTime { get; private set; } + + protected virtual double AllowedImportantTimeSpan => sixty_frame_time * 1.2; + + protected List Frames => replay.Frames; + + private readonly Replay replay; + + private int currentFrameIndex; + + private const double sixty_frame_time = 1000.0 / 60; + + protected FramedReplayInputHandler(Replay replay) + { + // This replay frame ordering should be enforced on the Replay type + replay.Frames.Sort((x, y) => x.Time.CompareTo(y.Time)); + + this.replay = replay; + currentFrameIndex = -1; + CurrentTime = double.NegativeInfinity; + } private bool inImportantSection { @@ -89,13 +100,8 @@ namespace osu.Game.Rulesets.Replays if (!HasFrames || !FrameAccuratePlayback) return false; - var frame = currentDirection > 0 ? CurrentFrame : NextFrame; - - if (frame == null) - return false; - - return IsImportant(frame) && // a button is in a pressed state - Math.Abs(CurrentTime - NextFrame?.Time ?? 0) <= AllowedImportantTimeSpan; // the next frame is within an allowable time span + return IsImportant(CurrentFrame) && // a button is in a pressed state + Math.Abs(CurrentTime - NextFrame.Time ?? 0) <= AllowedImportantTimeSpan; // the next frame is within an allowable time span } } @@ -110,71 +116,52 @@ namespace osu.Game.Rulesets.Replays /// The usable time value. If null, we should not advance time as we do not have enough data. public override double? SetFrameFromTime(double time) { - updateDirection(time); - - Debug.Assert(currentDirection != 0); - if (!HasFrames) { - // in the case all frames are received, allow time to progress regardless. + // In the case all frames are received, allow time to progress regardless. if (replay.HasReceivedAllFrames) return CurrentTime = time; return null; } - TFrame next = NextFrame; + double frameStart = getFrameTime(currentFrameIndex); + double frameEnd = getFrameTime(currentFrameIndex + 1); - // if we have a next frame, check if it is before or at the current time in playback, and advance time to it if so. - if (next != null) + // If the proposed time is after the current frame end time, we progress forwards. + // If the proposed time is before the current frame start time, and we are at the frame boundary, we progress backwards. + if (frameEnd <= time) { - int compare = time.CompareTo(next.Time); - - if (compare == 0 || compare == currentDirection) - { - currentFrameIndex = clampedNextFrameIndex; - return CurrentTime = CurrentFrame.Time; - } + time = frameEnd; + currentFrameIndex++; } + else if (time < frameStart && CurrentTime == frameStart) + currentFrameIndex--; - // at this point, the frame index can't be advanced. - // even so, we may be able to propose the clock progresses forward due to being at an extent of the replay, - // or moving towards the next valid frame (ie. interpolating in a non-important section). + frameStart = getFrameTime(currentFrameIndex); + frameEnd = getFrameTime(currentFrameIndex + 1); - // the exception is if currently in an important section, which is respected above all. - if (inImportantSection) + // Pause until more frames are arrived. + if (WaitingNextFrame && frameStart < time) { - Debug.Assert(next != null || !replay.HasReceivedAllFrames); + CurrentTime = frameStart; return null; } - // if a next frame does exist, allow interpolation. - if (next != null) - return CurrentTime = time; + CurrentTime = Math.Clamp(time, frameStart, frameEnd); - // if all frames have been received, allow playing beyond extents. - if (replay.HasReceivedAllFrames) - return CurrentTime = time; - - // if not all frames are received but we are before the first frame, allow playing. - if (time < Frames[0].Time) - return CurrentTime = time; - - // in the case we have no next frames and haven't received enough frame data, block. - return null; + // In an important section, a mid-frame time cannot be used and a null is returned instead. + return inImportantSection && frameStart < time && time < frameEnd ? null : CurrentTime; } - private void updateDirection(double time) + private double getFrameTime(int index) { - if (!CurrentTime.HasValue) - { - currentDirection = 1; - } - else - { - currentDirection = time.CompareTo(CurrentTime); - if (currentDirection == 0) currentDirection = 1; - } + if (index < 0) + return double.NegativeInfinity; + if (index >= Frames.Count) + return double.PositiveInfinity; + + return Frames[index].Time; } } } From 3c28c09ab5073c20a8eaebb8ce043a6cdc16b77f Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 12 Apr 2021 16:18:35 +0900 Subject: [PATCH 064/203] Add more FramedReplayInputHandler tests --- .../NonVisual/FramedReplayInputHandlerTest.cs | 100 ++++++++++++++++-- 1 file changed, 89 insertions(+), 11 deletions(-) diff --git a/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs b/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs index b4fc081a2a..64af4b6db6 100644 --- a/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs +++ b/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs @@ -20,23 +20,15 @@ namespace osu.Game.Tests.NonVisual { handler = new TestInputHandler(replay = new Replay { - Frames = new List - { - new TestReplayFrame(0), - new TestReplayFrame(1000), - new TestReplayFrame(2000), - new TestReplayFrame(3000, true), - new TestReplayFrame(4000, true), - new TestReplayFrame(5000, true), - new TestReplayFrame(7000, true), - new TestReplayFrame(8000), - } + HasReceivedAllFrames = false }); } [Test] public void TestNormalPlayback() { + setReplayFrames(); + setTime(0, 0); confirmCurrentFrame(0); confirmNextFrame(1); @@ -102,6 +94,8 @@ namespace osu.Game.Tests.NonVisual [Test] public void TestIntroTime() { + setReplayFrames(); + setTime(-1000, -1000); confirmCurrentFrame(0); confirmNextFrame(0); @@ -118,6 +112,8 @@ namespace osu.Game.Tests.NonVisual [Test] public void TestBasicRewind() { + setReplayFrames(); + setTime(2800, 0); setTime(2800, 1000); setTime(2800, 2000); @@ -156,6 +152,7 @@ namespace osu.Game.Tests.NonVisual [Test] public void TestRewindInsideImportantSection() { + setReplayFrames(); fastForwardToPoint(3000); setTime(4000, 4000); @@ -198,6 +195,7 @@ namespace osu.Game.Tests.NonVisual [Test] public void TestRewindOutOfImportantSection() { + setReplayFrames(); fastForwardToPoint(3500); confirmCurrentFrame(3); @@ -216,6 +214,86 @@ namespace osu.Game.Tests.NonVisual confirmNextFrame(3); } + [Test] + public void TestReplayStreaming() + { + // no frames are arrived yet + setTime(0, null); + setTime(1000, null); + Assert.IsTrue(handler.WaitingNextFrame, "Should be waiting for the first frame"); + + replay.Frames.Add(new TestReplayFrame(0)); + replay.Frames.Add(new TestReplayFrame(1000)); + + // should always play from beginning + setTime(1000, 0); + confirmCurrentFrame(0); + Assert.IsFalse(handler.WaitingNextFrame, "Should not be waiting yet"); + setTime(1000, 1000); + confirmCurrentFrame(1); + confirmNextFrame(1); + Assert.IsTrue(handler.WaitingNextFrame, "Should be waiting"); + + // cannot seek beyond the last frame + setTime(1500, null); + confirmCurrentFrame(1); + + setTime(-100, 0); + confirmCurrentFrame(0); + + // can seek to the point before the first frame, however + setTime(-100, -100); + confirmCurrentFrame(0); + confirmNextFrame(0); + + fastForwardToPoint(1000); + setTime(3000, null); + replay.Frames.Add(new TestReplayFrame(2000)); + confirmCurrentFrame(1); + setTime(1000, 1000); + setTime(3000, 2000); + } + + [Test] + public void TestMultipleFramesSameTime() + { + replay.Frames.Add(new TestReplayFrame(0)); + replay.Frames.Add(new TestReplayFrame(0)); + replay.Frames.Add(new TestReplayFrame(1000)); + replay.Frames.Add(new TestReplayFrame(1000)); + replay.Frames.Add(new TestReplayFrame(2000)); + + // forward direction is prioritized when multiple frames have the same time. + setTime(0, 0); + setTime(0, 0); + + setTime(2000, 1000); + setTime(2000, 1000); + + setTime(1000, 1000); + setTime(1000, 1000); + setTime(-100, 1000); + setTime(-100, 0); + setTime(-100, 0); + setTime(-100, -100); + } + + private void setReplayFrames() + { + replay.Frames = new List + { + new TestReplayFrame(0), + new TestReplayFrame(1000), + new TestReplayFrame(2000), + new TestReplayFrame(3000, true), + new TestReplayFrame(4000, true), + new TestReplayFrame(5000, true), + new TestReplayFrame(7000, true), + new TestReplayFrame(8000), + }; + replay.HasReceivedAllFrames = true; + } + private void fastForwardToPoint(double destination) { for (int i = 0; i < 1000; i++) From 0eab9daf13bb406d2b82c1f624dbb7e483f0ab40 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Apr 2021 16:41:36 +0900 Subject: [PATCH 065/203] Update existing overlay containers to not block scroll input --- osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs | 2 ++ .../Screens/OnlinePlay/Match/Components/MatchSettingsOverlay.cs | 2 ++ osu.Game/Screens/Play/GameplayMenuOverlay.cs | 2 ++ osu.Game/Screens/Play/SongProgress.cs | 2 ++ 4 files changed, 8 insertions(+) diff --git a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs index fbf2ffd4bd..e168f265dd 100644 --- a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs +++ b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs @@ -23,6 +23,8 @@ namespace osu.Game.Graphics.Containers protected virtual string PopInSampleName => "UI/overlay-pop-in"; protected virtual string PopOutSampleName => "UI/overlay-pop-out"; + protected override bool BlockScrollInput => false; + protected override bool BlockNonPositionalInput => true; /// diff --git a/osu.Game/Screens/OnlinePlay/Match/Components/MatchSettingsOverlay.cs b/osu.Game/Screens/OnlinePlay/Match/Components/MatchSettingsOverlay.cs index ea3951fc3b..5699da740c 100644 --- a/osu.Game/Screens/OnlinePlay/Match/Components/MatchSettingsOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/Match/Components/MatchSettingsOverlay.cs @@ -19,6 +19,8 @@ namespace osu.Game.Screens.OnlinePlay.Match.Components protected OnlinePlayComposite Settings { get; set; } + protected override bool BlockScrollInput => false; + [BackgroundDependencyLoader] private void load() { diff --git a/osu.Game/Screens/Play/GameplayMenuOverlay.cs b/osu.Game/Screens/Play/GameplayMenuOverlay.cs index f938839be3..4a28da0dde 100644 --- a/osu.Game/Screens/Play/GameplayMenuOverlay.cs +++ b/osu.Game/Screens/Play/GameplayMenuOverlay.cs @@ -31,6 +31,8 @@ namespace osu.Game.Screens.Play protected override bool BlockNonPositionalInput => true; + protected override bool BlockScrollInput => false; + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; public Action OnRetry; diff --git a/osu.Game/Screens/Play/SongProgress.cs b/osu.Game/Screens/Play/SongProgress.cs index acf4640aa4..6c7cb9376c 100644 --- a/osu.Game/Screens/Play/SongProgress.cs +++ b/osu.Game/Screens/Play/SongProgress.cs @@ -45,6 +45,8 @@ namespace osu.Game.Screens.Play public override bool HandleNonPositionalInput => AllowSeeking.Value; public override bool HandlePositionalInput => AllowSeeking.Value; + protected override bool BlockScrollInput => false; + private double firstHitTime => objects.First().StartTime; private IEnumerable objects; From 65ebdd8f7a48c57f31e46acd21ac3369f8521be9 Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Mon, 12 Apr 2021 10:08:08 +0200 Subject: [PATCH 066/203] Move check origin from `IssueTemplate` to `Issue` As a result we can also make check an interface, and need to provide the check itself when constructing an issue. --- .../Edit/Checks/CheckOffscreenObjects.cs | 14 +++++------ .../Edit/OsuBeatmapVerifier.cs | 2 +- osu.Game/Rulesets/Edit/BeatmapVerifier.cs | 2 +- .../Rulesets/Edit/Checks/CheckBackground.cs | 12 +++++----- .../Rulesets/Edit/Checks/Components/Check.cs | 14 ++++------- .../Rulesets/Edit/Checks/Components/Issue.cs | 23 +++++++++++-------- .../Edit/Checks/Components/IssueTemplate.cs | 5 ---- osu.Game/Screens/Edit/Verify/VerifyScreen.cs | 2 +- 8 files changed, 33 insertions(+), 41 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs b/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs index 252f65ab5a..b34c9966a4 100644 --- a/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs +++ b/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs @@ -9,7 +9,7 @@ using osuTK; namespace osu.Game.Rulesets.Osu.Edit.Checks { - public class CheckOffscreenObjects : Check + public class CheckOffscreenObjects : ICheck { // These are close approximates to the edges of the screen // in gameplay on a 4:3 aspect ratio for osu!stable. @@ -22,13 +22,13 @@ namespace osu.Game.Rulesets.Osu.Edit.Checks // (higher = more performant, but higher false-negative chance). private const int path_step_size = 5; - public override CheckMetadata Metadata { get; } = new CheckMetadata + public CheckMetadata Metadata { get; } = new CheckMetadata ( category: CheckCategory.Compose, description: "Offscreen hitobjects." ); - public override IEnumerable PossibleTemplates => new[] + public IEnumerable PossibleTemplates => new[] { templateOffscreen, templateOffscreenSliderPath @@ -46,7 +46,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Checks unformattedMessage: "This slider goes offscreen here on a 4:3 aspect ratio." ); - public override IEnumerable Run(IBeatmap beatmap) + public IEnumerable Run(IBeatmap beatmap) { foreach (var hitobject in beatmap.HitObjects) { @@ -63,7 +63,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Checks case HitCircle circle: { if (isOffscreen(circle.StackedPosition, circle.Radius)) - yield return new Issue(circle, templateOffscreen); + yield return new Issue(this, circle, templateOffscreen); break; } @@ -89,7 +89,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Checks // `SpanDuration` ensures we don't include reverses. double time = slider.StartTime + progress * slider.SpanDuration; - yield return new Issue(slider, templateOffscreenSliderPath) { Time = time }; + yield return new Issue(this, slider, templateOffscreenSliderPath) { Time = time }; yield break; } @@ -98,7 +98,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Checks if (!isOffscreen(slider.StackedEndPosition, slider.Radius)) yield break; - yield return new Issue(slider, templateOffscreenSliderPath) { Time = slider.EndTime }; + yield return new Issue(this, slider, templateOffscreenSliderPath) { Time = slider.EndTime }; } private bool isOffscreen(Vector2 position, double radius) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuBeatmapVerifier.cs b/osu.Game.Rulesets.Osu/Edit/OsuBeatmapVerifier.cs index 272612dbaf..2faa239720 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuBeatmapVerifier.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuBeatmapVerifier.cs @@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Osu.Edit { public class OsuBeatmapVerifier : BeatmapVerifier { - private readonly List checks = new List + private readonly List checks = new List { new CheckOffscreenObjects() }; diff --git a/osu.Game/Rulesets/Edit/BeatmapVerifier.cs b/osu.Game/Rulesets/Edit/BeatmapVerifier.cs index f67a068525..1d0508705a 100644 --- a/osu.Game/Rulesets/Edit/BeatmapVerifier.cs +++ b/osu.Game/Rulesets/Edit/BeatmapVerifier.cs @@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Edit /// Checks which are performed regardless of ruleset. /// These handle things like beatmap metadata, timing, and other ruleset agnostic elements. /// - private readonly IReadOnlyList generalChecks = new List + private readonly IReadOnlyList generalChecks = new List { new CheckBackground() }; diff --git a/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs b/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs index 22f98b6fd7..c922aa03c0 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs @@ -8,15 +8,15 @@ using osu.Game.Rulesets.Edit.Checks.Components; namespace osu.Game.Rulesets.Edit.Checks { - public class CheckBackground : Check + public class CheckBackground : ICheck { - public override CheckMetadata Metadata { get; } = new CheckMetadata + public CheckMetadata Metadata { get; } = new CheckMetadata ( category: CheckCategory.Resources, description: "Missing background." ); - public override IEnumerable PossibleTemplates => new[] + public IEnumerable PossibleTemplates => new[] { templateNoneSet, templateDoesNotExist @@ -34,11 +34,11 @@ namespace osu.Game.Rulesets.Edit.Checks unformattedMessage: "The background file \"{0}\" is does not exist." ); - public override IEnumerable Run(IBeatmap beatmap) + public IEnumerable Run(IBeatmap beatmap) { if (beatmap.Metadata.BackgroundFile == null) { - yield return new Issue(templateNoneSet); + yield return new Issue(this, templateNoneSet); yield break; } @@ -51,7 +51,7 @@ namespace osu.Game.Rulesets.Edit.Checks if (file != null) yield break; - yield return new Issue(templateDoesNotExist, beatmap.Metadata.BackgroundFile); + yield return new Issue(this, templateDoesNotExist, beatmap.Metadata.BackgroundFile); } } } diff --git a/osu.Game/Rulesets/Edit/Checks/Components/Check.cs b/osu.Game/Rulesets/Edit/Checks/Components/Check.cs index 7c039d1572..f355ae734e 100644 --- a/osu.Game/Rulesets/Edit/Checks/Components/Check.cs +++ b/osu.Game/Rulesets/Edit/Checks/Components/Check.cs @@ -6,28 +6,22 @@ using osu.Game.Beatmaps; namespace osu.Game.Rulesets.Edit.Checks.Components { - public abstract class Check + public interface ICheck { /// /// The metadata for this check. /// - public abstract CheckMetadata Metadata { get; } + public CheckMetadata Metadata { get; } /// /// All possible templates for issues that this check may return. /// - public abstract IEnumerable PossibleTemplates { get; } + public IEnumerable PossibleTemplates { get; } /// /// Runs this check and returns any issues detected for the provided beatmap. /// /// The beatmap to run the check on. - public abstract IEnumerable Run(IBeatmap beatmap); - - protected Check() - { - foreach (var template in PossibleTemplates) - template.Origin = this; - } + public IEnumerable Run(IBeatmap beatmap); } } diff --git a/osu.Game/Rulesets/Edit/Checks/Components/Issue.cs b/osu.Game/Rulesets/Edit/Checks/Components/Issue.cs index 7241fabf5b..d0f7df857b 100644 --- a/osu.Game/Rulesets/Edit/Checks/Components/Issue.cs +++ b/osu.Game/Rulesets/Edit/Checks/Components/Issue.cs @@ -26,38 +26,41 @@ namespace osu.Game.Rulesets.Edit.Checks.Components /// public IssueTemplate Template; + /// + /// The check that this issue originates from. + /// + public ICheck Check; + /// /// The arguments that give this issue its context, based on the . These are then substituted into the . /// This could for instance include timestamps, which diff is being compared to, what some volume is, etc. /// public object[] Arguments; - public Issue(IssueTemplate template, params object[] args) + public Issue(ICheck check, IssueTemplate template, params object[] args) { + Check = check; Time = null; HitObjects = Array.Empty(); Template = template; Arguments = args; - - if (template.Origin == null) - throw new ArgumentException("A template had no origin. Make sure the `Templates()` method contains all templates used."); } - public Issue(double? time, IssueTemplate template, params object[] args) - : this(template, args) + public Issue(ICheck check, double? time, IssueTemplate template, params object[] args) + : this(check, template, args) { Time = time; } - public Issue(HitObject hitObject, IssueTemplate template, params object[] args) - : this(template, args) + public Issue(ICheck check, HitObject hitObject, IssueTemplate template, params object[] args) + : this(check, template, args) { Time = hitObject.StartTime; HitObjects = new[] { hitObject }; } - public Issue(IEnumerable hitObjects, IssueTemplate template, params object[] args) - : this(template, args) + public Issue(ICheck check, IEnumerable hitObjects, IssueTemplate template, params object[] args) + : this(check, template, args) { var hitObjectList = hitObjects.ToList(); diff --git a/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs b/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs index a1156c7c72..4a5f98ca5f 100644 --- a/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs +++ b/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs @@ -14,11 +14,6 @@ namespace osu.Game.Rulesets.Edit.Checks.Components private static readonly Color4 negligible_green = new Colour4(0.33f, 0.8f, 0.5f, 1.0f); private static readonly Color4 error_gray = new Colour4(0.5f, 0.5f, 0.5f, 1.0f); - /// - /// The check that this template originates from. - /// - public Check Origin; - /// /// The type of the issue. /// diff --git a/osu.Game/Screens/Edit/Verify/VerifyScreen.cs b/osu.Game/Screens/Edit/Verify/VerifyScreen.cs index 0c4aa04ff3..c0c4a040f6 100644 --- a/osu.Game/Screens/Edit/Verify/VerifyScreen.cs +++ b/osu.Game/Screens/Edit/Verify/VerifyScreen.cs @@ -130,7 +130,7 @@ namespace osu.Game.Screens.Edit.Verify { table.Issues = beatmapVerifier.Run(Beatmap) .OrderByDescending(issue => issue.Template.Type) - .ThenByDescending(issue => issue.Template.Origin.Metadata.Category); + .ThenByDescending(issue => issue.Check.Metadata.Category); } } } From a2fc9c398fc731e36c3472bc37a5c153af7d5378 Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Mon, 12 Apr 2021 10:08:30 +0200 Subject: [PATCH 067/203] Rename `CreateChecker` -> `CreateBeatmapVerifier` --- osu.Game.Rulesets.Osu/OsuRuleset.cs | 2 +- osu.Game/Rulesets/Ruleset.cs | 2 +- osu.Game/Screens/Edit/Verify/VerifyScreen.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 1658846e98..63da100a04 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -206,7 +206,7 @@ namespace osu.Game.Rulesets.Osu public override HitObjectComposer CreateHitObjectComposer() => new OsuHitObjectComposer(this); - public override BeatmapVerifier CreateChecker() => new OsuBeatmapVerifier(); + public override BeatmapVerifier CreateBeatmapVerifier() => new OsuBeatmapVerifier(); public override string Description => "osu!"; diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index 088bcc7712..2a29d88c89 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -202,7 +202,7 @@ namespace osu.Game.Rulesets public virtual HitObjectComposer CreateHitObjectComposer() => null; - public virtual BeatmapVerifier CreateChecker() => null; + public virtual BeatmapVerifier CreateBeatmapVerifier() => null; public virtual Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.Solid.QuestionCircle }; diff --git a/osu.Game/Screens/Edit/Verify/VerifyScreen.cs b/osu.Game/Screens/Edit/Verify/VerifyScreen.cs index c0c4a040f6..806029df4d 100644 --- a/osu.Game/Screens/Edit/Verify/VerifyScreen.cs +++ b/osu.Game/Screens/Edit/Verify/VerifyScreen.cs @@ -36,7 +36,7 @@ namespace osu.Game.Screens.Edit.Verify var dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); ruleset = parent.Get>().Value.BeatmapInfo.Ruleset?.CreateInstance(); - beatmapVerifier = ruleset?.CreateChecker(); + beatmapVerifier = ruleset?.CreateBeatmapVerifier(); return dependencies; } From f1b8171e389e5df4eb67b567d4e48586a3001c73 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 12 Apr 2021 17:13:48 +0900 Subject: [PATCH 068/203] Remove `#nullable true` for now to suppress inspector --- osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs b/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs index f527c0e105..45bcd0bc19 100644 --- a/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs +++ b/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable enable - using System; using System.Collections.Generic; using JetBrains.Annotations; From b5954a55adc3af47c27ea4c2b354e320d9b6cf4c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Apr 2021 17:46:14 +0900 Subject: [PATCH 069/203] Remove empty xmldoc --- osu.Desktop/Program.cs | 1 - .../Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs | 1 - osu.Game.Tournament/TournamentGameBase.cs | 1 - osu.Game/Beatmaps/IBeatmap.cs | 1 - osu.Game/Configuration/SettingsStore.cs | 1 - osu.Game/Graphics/Backgrounds/Triangles.cs | 1 - osu.Game/IO/Serialization/IJsonSerializable.cs | 1 - osu.Game/Input/KeyBindingStore.cs | 1 - osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs | 1 - osu.Game/Rulesets/Objects/SliderPath.cs | 2 -- osu.Game/Rulesets/Ruleset.cs | 1 - osu.Game/Rulesets/Scoring/HitWindows.cs | 1 - osu.Game/Screens/Edit/Components/RadioButtons/RadioButton.cs | 1 - osu.Game/Screens/Ranking/ScorePanelList.cs | 1 - osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs | 1 - osu.Game/Tests/Beatmaps/LegacyModConversionTest.cs | 1 - osu.Game/Utils/Optional.cs | 1 - 17 files changed, 18 deletions(-) diff --git a/osu.Desktop/Program.cs b/osu.Desktop/Program.cs index d06c4b6746..5fb09c0cef 100644 --- a/osu.Desktop/Program.cs +++ b/osu.Desktop/Program.cs @@ -69,7 +69,6 @@ namespace osu.Desktop /// Allow a maximum of one unhandled exception, per second of execution. /// /// - /// private static bool handleException(Exception arg) { bool continueExecution = Interlocked.Decrement(ref allowableExceptions) >= 0; diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs index c81710ed18..26e5d381e2 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs @@ -482,7 +482,6 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy /// Retrieves the sample info list at a point in time. /// /// The time to retrieve the sample info list from. - /// private IList sampleInfoListAt(int time) => nodeSamplesAt(time)?.First() ?? HitObject.Samples; /// diff --git a/osu.Game.Tournament/TournamentGameBase.cs b/osu.Game.Tournament/TournamentGameBase.cs index 2ee52c35aa..92eb7ac713 100644 --- a/osu.Game.Tournament/TournamentGameBase.cs +++ b/osu.Game.Tournament/TournamentGameBase.cs @@ -141,7 +141,6 @@ namespace osu.Game.Tournament /// /// Add missing player info based on user IDs. /// - /// private bool addPlayers() { bool addedInfo = false; diff --git a/osu.Game/Beatmaps/IBeatmap.cs b/osu.Game/Beatmaps/IBeatmap.cs index 9847ea020a..769b33009a 100644 --- a/osu.Game/Beatmaps/IBeatmap.cs +++ b/osu.Game/Beatmaps/IBeatmap.cs @@ -44,7 +44,6 @@ namespace osu.Game.Beatmaps /// /// Returns statistics for the contained in this beatmap. /// - /// IEnumerable GetStatistics(); /// diff --git a/osu.Game/Configuration/SettingsStore.cs b/osu.Game/Configuration/SettingsStore.cs index f8c9bdeaf8..86e84b0732 100644 --- a/osu.Game/Configuration/SettingsStore.cs +++ b/osu.Game/Configuration/SettingsStore.cs @@ -22,7 +22,6 @@ namespace osu.Game.Configuration /// /// The ruleset's internal ID. /// An optional variant. - /// public List Query(int? rulesetId = null, int? variant = null) => ContextFactory.Get().DatabasedSetting.Where(b => b.RulesetID == rulesetId && b.Variant == variant).ToList(); diff --git a/osu.Game/Graphics/Backgrounds/Triangles.cs b/osu.Game/Graphics/Backgrounds/Triangles.cs index 0e9382279a..67cee883c8 100644 --- a/osu.Game/Graphics/Backgrounds/Triangles.cs +++ b/osu.Game/Graphics/Backgrounds/Triangles.cs @@ -346,7 +346,6 @@ namespace osu.Game.Graphics.Backgrounds /// such that the smaller triangles appear on top. /// /// - /// public int CompareTo(TriangleParticle other) => other.Scale.CompareTo(Scale); } } diff --git a/osu.Game/IO/Serialization/IJsonSerializable.cs b/osu.Game/IO/Serialization/IJsonSerializable.cs index ba188963ea..c8d5ce39a6 100644 --- a/osu.Game/IO/Serialization/IJsonSerializable.cs +++ b/osu.Game/IO/Serialization/IJsonSerializable.cs @@ -22,7 +22,6 @@ namespace osu.Game.IO.Serialization /// /// Creates the default that should be used for all s. /// - /// public static JsonSerializerSettings CreateGlobalSettings() => new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore, diff --git a/osu.Game/Input/KeyBindingStore.cs b/osu.Game/Input/KeyBindingStore.cs index b25b00eb84..9d0cfedc03 100644 --- a/osu.Game/Input/KeyBindingStore.cs +++ b/osu.Game/Input/KeyBindingStore.cs @@ -85,7 +85,6 @@ namespace osu.Game.Input /// /// The ruleset's internal ID. /// An optional variant. - /// public List Query(int? rulesetId = null, int? variant = null) => ContextFactory.Get().DatabasedKeyBinding.Where(b => b.RulesetID == rulesetId && b.Variant == variant).ToList(); diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 6da9f12b50..d95b246c96 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -574,7 +574,6 @@ namespace osu.Game.Rulesets.Objects.Drawables /// Calculate the position to be used for sample playback at a specified X position (0..1). /// /// The lookup X position. Generally should be . - /// protected double CalculateSamplePlaybackBalance(double position) { const float balance_adjust_amount = 0.4f; diff --git a/osu.Game/Rulesets/Objects/SliderPath.cs b/osu.Game/Rulesets/Objects/SliderPath.cs index 61f5f94142..e64298f98d 100644 --- a/osu.Game/Rulesets/Objects/SliderPath.cs +++ b/osu.Game/Rulesets/Objects/SliderPath.cs @@ -147,7 +147,6 @@ namespace osu.Game.Rulesets.Objects /// to 1 (end of the path). /// /// Ranges from 0 (beginning of the path) to 1 (end of the path). - /// public Vector2 PositionAt(double progress) { ensureValid(); @@ -161,7 +160,6 @@ namespace osu.Game.Rulesets.Objects /// The first point has a PathType which all other points inherit. /// /// One of the control points in the segment. - /// public List PointsInSegment(PathControlPoint controlPoint) { bool found = false; diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index 38d30a2e31..efc8b50e3c 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -146,7 +146,6 @@ namespace osu.Game.Rulesets /// The beatmap to create the hit renderer for. /// The s to apply. /// Unable to successfully load the beatmap to be usable with this ruleset. - /// public abstract DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList mods = null); /// diff --git a/osu.Game/Rulesets/Scoring/HitWindows.cs b/osu.Game/Rulesets/Scoring/HitWindows.cs index 018b50bd3d..410614de07 100644 --- a/osu.Game/Rulesets/Scoring/HitWindows.cs +++ b/osu.Game/Rulesets/Scoring/HitWindows.cs @@ -62,7 +62,6 @@ namespace osu.Game.Rulesets.Scoring /// /// Retrieves a mapping of s to their timing windows for all allowed s. /// - /// public IEnumerable<(HitResult result, double length)> GetAllAvailableWindows() { for (var result = HitResult.Meh; result <= HitResult.Perfect; ++result) diff --git a/osu.Game/Screens/Edit/Components/RadioButtons/RadioButton.cs b/osu.Game/Screens/Edit/Components/RadioButtons/RadioButton.cs index a7b0fb05e3..dcf5f8a788 100644 --- a/osu.Game/Screens/Edit/Components/RadioButtons/RadioButton.cs +++ b/osu.Game/Screens/Edit/Components/RadioButtons/RadioButton.cs @@ -12,7 +12,6 @@ namespace osu.Game.Screens.Edit.Components.RadioButtons /// /// Whether this is selected. /// - /// public readonly BindableBool Selected; /// diff --git a/osu.Game/Screens/Ranking/ScorePanelList.cs b/osu.Game/Screens/Ranking/ScorePanelList.cs index 77b3d8fc3b..441c9e048a 100644 --- a/osu.Game/Screens/Ranking/ScorePanelList.cs +++ b/osu.Game/Screens/Ranking/ScorePanelList.cs @@ -226,7 +226,6 @@ namespace osu.Game.Screens.Ranking /// /// Enumerates all s contained in this . /// - /// public IEnumerable GetScorePanels() => flow.Select(t => t.Panel); /// diff --git a/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs b/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs index fcf20a2eb2..5ef2458919 100644 --- a/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs +++ b/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs @@ -189,7 +189,6 @@ namespace osu.Game.Tests.Beatmaps /// /// Creates the applicable to this . /// - /// protected abstract Ruleset CreateRuleset(); private class ConvertResult diff --git a/osu.Game/Tests/Beatmaps/LegacyModConversionTest.cs b/osu.Game/Tests/Beatmaps/LegacyModConversionTest.cs index 76f97db59f..54a83f4305 100644 --- a/osu.Game/Tests/Beatmaps/LegacyModConversionTest.cs +++ b/osu.Game/Tests/Beatmaps/LegacyModConversionTest.cs @@ -17,7 +17,6 @@ namespace osu.Game.Tests.Beatmaps /// /// Creates the whose legacy mod conversion is to be tested. /// - /// protected abstract Ruleset CreateRuleset(); protected void TestFromLegacy(LegacyMods legacyMods, Type[] expectedMods) diff --git a/osu.Game/Utils/Optional.cs b/osu.Game/Utils/Optional.cs index 9f8a1c2e62..fdb7623be5 100644 --- a/osu.Game/Utils/Optional.cs +++ b/osu.Game/Utils/Optional.cs @@ -37,7 +37,6 @@ namespace osu.Game.Utils /// Shortcase for: optional.HasValue ? optional.Value : fallback. /// /// The fallback value to return if is false. - /// public T GetOr(T fallback) => HasValue ? Value : fallback; public static implicit operator Optional(T value) => new Optional(value); From 6d18b3db00a5f3f045dc35cdc6a740bae7156f90 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 12 Apr 2021 18:49:38 +0900 Subject: [PATCH 070/203] Avoid empty list allocation --- osu.Game/Rulesets/UI/RulesetInputManager.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/UI/RulesetInputManager.cs b/osu.Game/Rulesets/UI/RulesetInputManager.cs index fb56a5d93d..1c0d820a3d 100644 --- a/osu.Game/Rulesets/UI/RulesetInputManager.cs +++ b/osu.Game/Rulesets/UI/RulesetInputManager.cs @@ -102,10 +102,13 @@ namespace osu.Game.Rulesets.UI #endregion + // to avoid allocation + private readonly List emptyInputList = new List(); + protected override List GetPendingInputs() { if (replayInputHandler != null && !replayInputHandler.IsActive) - return new List(); + return emptyInputList; return base.GetPendingInputs(); } From 359fae895f843ff7b8775917d443a89556705d07 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 12 Apr 2021 18:50:25 +0900 Subject: [PATCH 071/203] Rename property --- osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs | 6 +++--- osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs | 4 ++-- osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs b/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs index 64af4b6db6..954871595e 100644 --- a/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs +++ b/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs @@ -220,7 +220,7 @@ namespace osu.Game.Tests.NonVisual // no frames are arrived yet setTime(0, null); setTime(1000, null); - Assert.IsTrue(handler.WaitingNextFrame, "Should be waiting for the first frame"); + Assert.IsTrue(handler.WaitingForFrame, "Should be waiting for the first frame"); replay.Frames.Add(new TestReplayFrame(0)); replay.Frames.Add(new TestReplayFrame(1000)); @@ -228,11 +228,11 @@ namespace osu.Game.Tests.NonVisual // should always play from beginning setTime(1000, 0); confirmCurrentFrame(0); - Assert.IsFalse(handler.WaitingNextFrame, "Should not be waiting yet"); + Assert.IsFalse(handler.WaitingForFrame, "Should not be waiting yet"); setTime(1000, 1000); confirmCurrentFrame(1); confirmNextFrame(1); - Assert.IsTrue(handler.WaitingNextFrame, "Should be waiting"); + Assert.IsTrue(handler.WaitingForFrame, "Should be waiting"); // cannot seek beyond the last frame setTime(1500, null); diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs index 9f1faa8e26..397b37718d 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs @@ -83,7 +83,7 @@ namespace osu.Game.Tests.Visual.Gameplay waitForPlayer(); AddAssert("ensure frames arrived", () => replayHandler.HasFrames); - AddUntilStep("wait for frame starvation", () => replayHandler.WaitingNextFrame); + AddUntilStep("wait for frame starvation", () => replayHandler.WaitingForFrame); checkPaused(true); double? pausedTime = null; @@ -92,7 +92,7 @@ namespace osu.Game.Tests.Visual.Gameplay sendFrames(); - AddUntilStep("wait for frame starvation", () => replayHandler.WaitingNextFrame); + AddUntilStep("wait for frame starvation", () => replayHandler.WaitingForFrame); checkPaused(true); AddAssert("time advanced", () => currentFrameStableTime > pausedTime); diff --git a/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs b/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs index 45bcd0bc19..23cc311d79 100644 --- a/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs +++ b/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs @@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Replays /// /// Whether we are waiting for new frames to be received. /// - public bool WaitingNextFrame => !replay.HasReceivedAllFrames && currentFrameIndex == Frames.Count - 1; + public bool WaitingForFrame => !replay.HasReceivedAllFrames && currentFrameIndex == Frames.Count - 1; /// /// The current frame of the replay. @@ -140,7 +140,7 @@ namespace osu.Game.Rulesets.Replays frameEnd = getFrameTime(currentFrameIndex + 1); // Pause until more frames are arrived. - if (WaitingNextFrame && frameStart < time) + if (WaitingForFrame && frameStart < time) { CurrentTime = frameStart; return null; From 31d36071052bf72f9a82256242cbb6fa7c2b0e38 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 12 Apr 2021 18:50:54 +0900 Subject: [PATCH 072/203] Add TODO comment --- osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs b/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs index 23cc311d79..c3cd957f0d 100644 --- a/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs +++ b/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs @@ -44,7 +44,7 @@ namespace osu.Game.Rulesets.Replays /// /// The next frame of the replay. - /// The start time is always greater or equals to the start time of regardless of the seeking direction. + /// The start time is always greater or equal to the start time of regardless of the seeking direction. /// If it is before the first frame of the replay or the after the last frame of the replay, and agree. /// /// The replay is empty. @@ -83,7 +83,8 @@ namespace osu.Game.Rulesets.Replays protected FramedReplayInputHandler(Replay replay) { - // This replay frame ordering should be enforced on the Replay type + // TODO: This replay frame ordering should be enforced on the Replay type. + // Currently, the ordering can be broken if the frames are added after this construction. replay.Frames.Sort((x, y) => x.Time.CompareTo(y.Time)); this.replay = replay; From cc2acf5e548fb11873f1d20d480b27e35ced57fb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Apr 2021 19:05:23 +0900 Subject: [PATCH 073/203] Fix ctrl-dragging on an existing selection causing deselection of the hovered object --- .../Compose/Components/BlueprintContainer.cs | 38 ++++++++++++++----- .../Compose/Components/SelectionHandler.cs | 37 +++++++++++++++--- 2 files changed, 60 insertions(+), 15 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index 5699be4560..64cf0e7512 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -135,11 +135,12 @@ namespace osu.Game.Screens.Edit.Compose.Components protected override bool OnMouseDown(MouseDownEvent e) { - if (!beginClickSelection(e)) return true; + bool selectionPerformed = beginClickSelection(e); + // even if a selection didn't occur, a drag event may still move the selection. prepareSelectionMovement(); - return e.Button == MouseButton.Left; + return selectionPerformed || e.Button == MouseButton.Left; } private SelectionBlueprint clickedBlueprint; @@ -154,7 +155,7 @@ namespace osu.Game.Screens.Edit.Compose.Components // Deselection should only occur if no selected blueprints are hovered // A special case for when a blueprint was selected via this click is added since OnClick() may occur outside the hitobject and should not trigger deselection - if (endClickSelection() || clickedBlueprint != null) + if (endClickSelection(e) || clickedBlueprint != null) return true; deselectAll(); @@ -177,7 +178,12 @@ namespace osu.Game.Screens.Edit.Compose.Components protected override void OnMouseUp(MouseUpEvent e) { // Special case for when a drag happened instead of a click - Schedule(() => endClickSelection()); + Schedule(() => + { + endClickSelection(e); + clickSelectionBegan = false; + isDraggingBlueprint = false; + }); finishSelectionMovement(); } @@ -226,7 +232,6 @@ namespace osu.Game.Screens.Edit.Compose.Components Beatmap.Update(obj); changeHandler?.EndChange(); - isDraggingBlueprint = false; } if (DragBox.State == Visibility.Visible) @@ -355,13 +360,28 @@ namespace osu.Game.Screens.Edit.Compose.Components /// /// Finishes the current blueprint selection. /// + /// The mouse event which triggered end of selection. /// Whether a click selection was active. - private bool endClickSelection() + private bool endClickSelection(MouseButtonEvent e) { - if (!clickSelectionBegan) - return false; + if (!clickSelectionBegan && !isDraggingBlueprint) + { + // if a selection didn't occur, we may want to trigger a deselection. + if (e.ControlPressed && e.Button == MouseButton.Left) + { + // Iterate from the top of the input stack (blueprints closest to the front of the screen first). + // Priority is given to already-selected blueprints. + foreach (SelectionBlueprint blueprint in SelectionBlueprints.AliveChildren.Reverse().OrderByDescending(b => b.IsSelected)) + { + if (!blueprint.IsHovered) continue; + + return clickSelectionBegan = SelectionHandler.HandleDeselectionRequested(blueprint, e); + } + } + + return false; + } - clickSelectionBegan = false; return true; } diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs index 018d4d081c..e5e1100797 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs @@ -228,12 +228,31 @@ namespace osu.Game.Screens.Edit.Compose.Components return false; } - if (e.ControlPressed && e.Button == MouseButton.Left) + // while holding control, we only want to add to selection, not replace an existing selection. + if (e.ControlPressed && e.Button == MouseButton.Left && !blueprint.IsSelected) + { blueprint.ToggleSelection(); - else - ensureSelected(blueprint); + return true; + } - return true; + return ensureSelected(blueprint); + } + + /// + /// Handle a blueprint requesting selection. + /// + /// The blueprint. + /// The mouse event responsible for deselection. + /// Whether a deselection was performed. + internal bool HandleDeselectionRequested(SelectionBlueprint blueprint, MouseButtonEvent e) + { + if (blueprint.IsSelected) + { + blueprint.ToggleSelection(); + return true; + } + + return false; } private void handleQuickDeletion(SelectionBlueprint blueprint) @@ -247,13 +266,19 @@ namespace osu.Game.Screens.Edit.Compose.Components deleteSelected(); } - private void ensureSelected(SelectionBlueprint blueprint) + /// + /// Ensure the blueprint is in a selected state. + /// + /// The blueprint to select. + /// Whether selection state was changed. + private bool ensureSelected(SelectionBlueprint blueprint) { if (blueprint.IsSelected) - return; + return false; DeselectAll?.Invoke(); blueprint.Select(); + return true; } private void deleteSelected() From b4c75ba3c6eb81a8cf88f36edc8738ee7e2e09df Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Apr 2021 19:19:25 +0900 Subject: [PATCH 074/203] Fix TestQuickDeleteRemovesObject failing on second run --- .../Visual/Editing/TestSceneEditorQuickDelete.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneEditorQuickDelete.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorQuickDelete.cs index 9efd299fba..8a0f27b851 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneEditorQuickDelete.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneEditorQuickDelete.cs @@ -51,7 +51,7 @@ namespace osu.Game.Tests.Visual.Editing [Test] public void TestQuickDeleteRemovesSliderControlPoint() { - Slider slider = new Slider { StartTime = 1000 }; + Slider slider = null; PathControlPoint[] points = { @@ -62,7 +62,12 @@ namespace osu.Game.Tests.Visual.Editing AddStep("add slider", () => { - slider.Path = new SliderPath(points); + slider = new Slider + { + StartTime = 1000, + Path = new SliderPath(points) + }; + EditorBeatmap.Add(slider); }); From 905cd7c8eb9e7c3785ffa88e63cce5b4b7f2c024 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Apr 2021 19:22:07 +0900 Subject: [PATCH 075/203] Update resources --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 196d122a2a..c78dfb6a55 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -51,7 +51,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 71a6f0e5cd..92e05cb4a6 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -30,7 +30,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index a389cc13dd..11124730c9 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -71,7 +71,7 @@ - + From d2d7f77430181ce55d76eb7e2ef24613cfe69ca4 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 12 Apr 2021 19:50:24 +0900 Subject: [PATCH 076/203] Fix mods not being serialised correctly in ScoreInfo --- .../Online/TestAPIModJsonSerialization.cs | 33 +++++++++++++ osu.Game/Scoring/ScoreInfo.cs | 49 +++++++++---------- 2 files changed, 57 insertions(+), 25 deletions(-) diff --git a/osu.Game.Tests/Online/TestAPIModJsonSerialization.cs b/osu.Game.Tests/Online/TestAPIModJsonSerialization.cs index 77f910c144..3afb7481b1 100644 --- a/osu.Game.Tests/Online/TestAPIModJsonSerialization.cs +++ b/osu.Game.Tests/Online/TestAPIModJsonSerialization.cs @@ -11,7 +11,10 @@ using osu.Game.Online.API; using osu.Game.Rulesets; using osu.Game.Rulesets.Difficulty; using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Osu; +using osu.Game.Rulesets.Osu.Mods; using osu.Game.Rulesets.UI; +using osu.Game.Scoring; namespace osu.Game.Tests.Online { @@ -84,6 +87,36 @@ namespace osu.Game.Tests.Online Assert.That(converted?.OverallDifficulty.Value, Is.EqualTo(11)); } + [Test] + public void TestDeserialiseScoreInfoWithEmptyMods() + { + var score = new ScoreInfo { Ruleset = new OsuRuleset().RulesetInfo }; + + var deserialised = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(score)); + + if (deserialised != null) + deserialised.Ruleset = new OsuRuleset().RulesetInfo; + + Assert.That(deserialised?.Mods.Length, Is.Zero); + } + + [Test] + public void TestDeserialiseScoreInfoWithCustomModSetting() + { + var score = new ScoreInfo + { + Ruleset = new OsuRuleset().RulesetInfo, + Mods = new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 2 } } } + }; + + var deserialised = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(score)); + + if (deserialised != null) + deserialised.Ruleset = new OsuRuleset().RulesetInfo; + + Assert.That(((OsuModDoubleTime)deserialised?.Mods[0])?.SpeedChange.Value, Is.EqualTo(2)); + } + private class TestRuleset : Ruleset { public override IEnumerable GetModsFor(ModType type) => new Mod[] diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index ef11c19e3f..df8f309ee0 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -10,6 +10,7 @@ using Newtonsoft.Json.Converters; using osu.Framework.Extensions; using osu.Game.Beatmaps; using osu.Game.Database; +using osu.Game.Online.API; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Scoring; @@ -55,9 +56,10 @@ namespace osu.Game.Scoring [JsonIgnore] public virtual RulesetInfo Ruleset { get; set; } + private APIMod[] localAPIMods; private Mod[] mods; - [JsonProperty("mods")] + [JsonIgnore] [NotMapped] public Mod[] Mods { @@ -66,45 +68,50 @@ namespace osu.Game.Scoring if (mods != null) return mods; - if (modsJson == null) + if (apiMods == null) return Array.Empty(); - return getModsFromRuleset(JsonConvert.DeserializeObject(modsJson)); + var rulesetInstance = Ruleset.CreateInstance(); + return apiMods.Select(m => m.ToMod(rulesetInstance)).ToArray(); } set { - modsJson = null; + localAPIMods = null; mods = value; } } - private Mod[] getModsFromRuleset(DeserializedMod[] mods) => Ruleset.CreateInstance().GetAllMods().Where(mod => mods.Any(d => d.Acronym == mod.Acronym)).ToArray(); - - private string modsJson; - - [JsonIgnore] - [Column("Mods")] - public string ModsJson + // Used for API serialisation/deserialisation. + [JsonProperty("mods")] + private APIMod[] apiMods { get { - if (modsJson != null) - return modsJson; + if (localAPIMods != null) + return localAPIMods; if (mods == null) - return null; + return Array.Empty(); - return modsJson = JsonConvert.SerializeObject(mods.Select(m => new DeserializedMod { Acronym = m.Acronym })); + return localAPIMods = mods.Select(m => new APIMod(m)).ToArray(); } set { - modsJson = value; + localAPIMods = value; - // we potentially can't update this yet due to Ruleset being late-bound, so instead update on read as necessary. + // We potentially can't update this yet due to Ruleset being late-bound, so instead update on read as necessary. mods = null; } } + // Used for database serialisation/deserialisation. + [Column("Mods")] + private string modsString + { + get => JsonConvert.SerializeObject(apiMods); + set => apiMods = JsonConvert.DeserializeObject(value); + } + [NotMapped] [JsonProperty("user")] public User User { get; set; } @@ -251,14 +258,6 @@ namespace osu.Game.Scoring } } - [Serializable] - protected class DeserializedMod : IMod - { - public string Acronym { get; set; } - - public bool Equals(IMod other) => Acronym == other?.Acronym; - } - public override string ToString() => $"{User} playing {Beatmap}"; public bool Equals(ScoreInfo other) From 982d8fa8b1ef8ce69a4456b8a31192f948314f09 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 12 Apr 2021 20:49:26 +0900 Subject: [PATCH 077/203] Fix incorrect reference --- osu.Game/Scoring/ScoreInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index df8f309ee0..1e438edeea 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -68,7 +68,7 @@ namespace osu.Game.Scoring if (mods != null) return mods; - if (apiMods == null) + if (localAPIMods == null) return Array.Empty(); var rulesetInstance = Ruleset.CreateInstance(); From 625484468e4ba788e04b73b0c6b55614d8e30bdf Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 12 Apr 2021 20:49:37 +0900 Subject: [PATCH 078/203] Fix DB serialisation --- osu.Game/Scoring/ScoreInfo.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index 1e438edeea..01b62f8b4f 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -105,8 +105,9 @@ namespace osu.Game.Scoring } // Used for database serialisation/deserialisation. + [JsonIgnore] [Column("Mods")] - private string modsString + public string ModsString { get => JsonConvert.SerializeObject(apiMods); set => apiMods = JsonConvert.DeserializeObject(value); From 8413b0a5d32a7aa159bfc971f10b2b11bd058c5c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 12 Apr 2021 20:49:44 +0900 Subject: [PATCH 079/203] Don't map api mods to DB --- osu.Game/Scoring/ScoreInfo.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index 01b62f8b4f..312b5f46f4 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -83,6 +83,7 @@ namespace osu.Game.Scoring // Used for API serialisation/deserialisation. [JsonProperty("mods")] + [NotMapped] private APIMod[] apiMods { get From e9a114a15c37bf498ff7e1e8b24775964f01a4b8 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 12 Apr 2021 20:50:18 +0900 Subject: [PATCH 080/203] Rename property back --- osu.Game/Scoring/ScoreInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index 312b5f46f4..222f69b025 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -108,7 +108,7 @@ namespace osu.Game.Scoring // Used for database serialisation/deserialisation. [JsonIgnore] [Column("Mods")] - public string ModsString + public string ModsJson { get => JsonConvert.SerializeObject(apiMods); set => apiMods = JsonConvert.DeserializeObject(value); From c531e38a369452f3be8664e0c2b6ff4bb079e64c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 12 Apr 2021 22:00:27 +0900 Subject: [PATCH 081/203] Rework to create a derived tracked user data instead --- ...TestSceneMultiplayerGameplayLeaderboard.cs | 2 +- .../MultiplayerSpectatorLeaderboard.cs | 85 +++++------ .../HUD/MultiplayerGameplayLeaderboard.cs | 137 +++++++++--------- 3 files changed, 107 insertions(+), 117 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs index 1ee848b902..272d547505 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs @@ -163,7 +163,7 @@ namespace osu.Game.Tests.Visual.Multiplayer break; } - ((ISpectatorClient)this).UserSentFrames(userId, new FrameDataBundle(header, Array.Empty())); + ((ISpectatorClient)this).UserSentFrames(userId, new FrameDataBundle(header, new[] { new LegacyReplayFrame(Time.Current, 0, 0, ReplayButtonState.None) })); } } } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiplayerSpectatorLeaderboard.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiplayerSpectatorLeaderboard.cs index aa9f162036..1b9e2bda2d 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiplayerSpectatorLeaderboard.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiplayerSpectatorLeaderboard.cs @@ -2,10 +2,8 @@ // 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.Timing; -using osu.Game.Online.Spectator; using osu.Game.Rulesets.Scoring; using osu.Game.Screens.Play.HUD; @@ -13,73 +11,62 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate { public class MultiplayerSpectatorLeaderboard : MultiplayerGameplayLeaderboard { - private readonly Dictionary trackedData = new Dictionary(); - public MultiplayerSpectatorLeaderboard(ScoreProcessor scoreProcessor, int[] userIds) : base(scoreProcessor, userIds) { } - public void AddClock(int userId, IClock source) => trackedData[userId] = new TrackedUserData(source); - - public void RemoveClock(int userId) => trackedData.Remove(userId); - - protected override void OnIncomingFrames(int userId, FrameDataBundle bundle) + public void AddClock(int userId, IClock clock) { - if (!trackedData.TryGetValue(userId, out var data)) + if (!UserScores.TryGetValue(userId, out var data)) return; - data.Frames.Add(new TimedFrameHeader(bundle.Frames.First().Time, bundle.Header)); + ((SpectatingTrackedUserData)data).Clock = clock; } + public void RemoveClock(int userId) + { + if (!UserScores.TryGetValue(userId, out var data)) + return; + + ((SpectatingTrackedUserData)data).Clock = null; + } + + protected override TrackedUserData CreateUserData(int userId, ScoreProcessor scoreProcessor) => new SpectatingTrackedUserData(userId, scoreProcessor); + protected override void Update() { base.Update(); - foreach (var (userId, data) in trackedData) + foreach (var (_, data) in UserScores) + data.UpdateScore(); + } + + private class SpectatingTrackedUserData : TrackedUserData + { + [CanBeNull] + public IClock Clock; + + public SpectatingTrackedUserData(int userId, ScoreProcessor scoreProcessor) + : base(userId, scoreProcessor) { - var targetTime = data.Clock.CurrentTime; + } - if (data.Frames.Count == 0) - continue; + public override void UpdateScore() + { + if (Frames.Count == 0) + return; - int frameIndex = data.Frames.BinarySearch(new TimedFrameHeader(targetTime)); + if (Clock == null) + return; + + int frameIndex = Frames.BinarySearch(new TimedFrame(Clock.CurrentTime)); if (frameIndex < 0) frameIndex = ~frameIndex; - frameIndex = Math.Clamp(frameIndex - 1, 0, data.Frames.Count - 1); + frameIndex = Math.Clamp(frameIndex - 1, 0, Frames.Count - 1); - SetCurrentFrame(userId, data.Frames[frameIndex].Header); + SetFrame(Frames[frameIndex]); } } - - private class TrackedUserData - { - public readonly IClock Clock; - public readonly List Frames = new List(); - - public TrackedUserData(IClock clock) - { - Clock = clock; - } - } - - private class TimedFrameHeader : IComparable - { - public readonly double Time; - public readonly FrameHeader Header; - - public TimedFrameHeader(double time) - { - Time = time; - } - - public TimedFrameHeader(double time, FrameHeader header) - { - Time = time; - Header = header; - } - - public int CompareTo(TimedFrameHeader other) => Time.CompareTo(other.Time); - } } } diff --git a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs index f0d2ac4b7f..70de067784 100644 --- a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs @@ -1,10 +1,10 @@ // 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.Collections.Specialized; using System.Linq; -using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Game.Configuration; @@ -19,8 +19,7 @@ namespace osu.Game.Screens.Play.HUD [LongRunningLoad] public class MultiplayerGameplayLeaderboard : GameplayLeaderboard { - private readonly ScoreProcessor scoreProcessor; - private readonly Dictionary userScores = new Dictionary(); + protected readonly Dictionary UserScores = new Dictionary(); [Resolved] private SpectatorStreamingClient streamingClient { get; set; } @@ -31,9 +30,9 @@ namespace osu.Game.Screens.Play.HUD [Resolved] private UserLookupCache userLookupCache { get; set; } - private Bindable scoringMode; - + private readonly ScoreProcessor scoreProcessor; private readonly BindableList playingUsers; + private Bindable scoringMode; /// /// Construct a new leaderboard. @@ -52,6 +51,8 @@ namespace osu.Game.Screens.Play.HUD [BackgroundDependencyLoader] private void load(OsuConfigManager config, IAPIProvider api) { + scoringMode = config.GetBindable(OsuSetting.ScoreDisplayMode); + foreach (var userId in playingUsers) { streamingClient.WatchUser(userId); @@ -59,19 +60,17 @@ namespace osu.Game.Screens.Play.HUD // probably won't be required in the final implementation. var resolvedUser = userLookupCache.GetUserAsync(userId).Result; - var trackedUser = new TrackedUserData(); + var trackedUser = CreateUserData(userId, scoreProcessor); + trackedUser.ScoringMode.BindTo(scoringMode); - userScores[userId] = trackedUser; var leaderboardScore = AddPlayer(resolvedUser, resolvedUser?.Id == api.LocalUser.Value.Id); + leaderboardScore.Accuracy.BindTo(trackedUser.Accuracy); + leaderboardScore.TotalScore.BindTo(trackedUser.Score); + leaderboardScore.Combo.BindTo(trackedUser.CurrentCombo); + leaderboardScore.HasQuit.BindTo(trackedUser.UserQuit); - ((IBindable)leaderboardScore.Accuracy).BindTo(trackedUser.Accuracy); - ((IBindable)leaderboardScore.TotalScore).BindTo(trackedUser.Score); - ((IBindable)leaderboardScore.Combo).BindTo(trackedUser.CurrentCombo); - ((IBindable)leaderboardScore.HasQuit).BindTo(trackedUser.UserQuit); + UserScores[userId] = trackedUser; } - - scoringMode = config.GetBindable(OsuSetting.ScoreDisplayMode); - scoringMode.BindValueChanged(updateAllScores, true); } protected override void LoadComplete() @@ -101,7 +100,7 @@ namespace osu.Game.Screens.Play.HUD { streamingClient.StopWatchingUser(userId); - if (userScores.TryGetValue(userId, out var trackedData)) + if (UserScores.TryGetValue(userId, out var trackedData)) trackedData.MarkUserQuit(); } @@ -109,39 +108,16 @@ namespace osu.Game.Screens.Play.HUD } } - private void updateAllScores(ValueChangedEvent mode) - { - foreach (var trackedData in userScores.Values) - trackedData.UpdateScore(scoreProcessor, mode.NewValue); - } - private void handleIncomingFrames(int userId, FrameDataBundle bundle) => Schedule(() => { - if (userScores.ContainsKey(userId)) - OnIncomingFrames(userId, bundle); + if (!UserScores.TryGetValue(userId, out var trackedData)) + return; + + trackedData.Frames.Add(new TimedFrame(bundle.Frames.First().Time, bundle.Header)); + trackedData.UpdateScore(); }); - /// - /// Invoked when new frames have arrived for a user. - /// - /// - /// By default, this immediately sets the current frame to be displayed for the user. - /// - /// The user which the frames arrived for. - /// The bundle of frames. - protected virtual void OnIncomingFrames(int userId, FrameDataBundle bundle) => SetCurrentFrame(userId, bundle.Header); - - /// - /// Sets the current frame to be displayed for a user. - /// - /// The user to set the frame of. - /// The frame to set. - protected void SetCurrentFrame(int userId, FrameHeader header) - { - var trackedScore = userScores[userId]; - trackedScore.LastHeader = header; - trackedScore.UpdateScore(scoreProcessor, scoringMode.Value); - } + protected virtual TrackedUserData CreateUserData(int userId, ScoreProcessor scoreProcessor) => new TrackedUserData(userId, scoreProcessor); protected override void Dispose(bool isDisposing) { @@ -158,38 +134,65 @@ namespace osu.Game.Screens.Play.HUD } } - private class TrackedUserData + protected class TrackedUserData { - public IBindableNumber Score => score; + public readonly int UserId; + public readonly ScoreProcessor ScoreProcessor; - private readonly BindableDouble score = new BindableDouble(); + public readonly BindableDouble Score = new BindableDouble(); + public readonly BindableDouble Accuracy = new BindableDouble(1); + public readonly BindableInt CurrentCombo = new BindableInt(); + public readonly BindableBool UserQuit = new BindableBool(); - public IBindableNumber Accuracy => accuracy; + public readonly IBindable ScoringMode = new Bindable(); - private readonly BindableDouble accuracy = new BindableDouble(1); + public readonly List Frames = new List(); - public IBindableNumber CurrentCombo => currentCombo; - - private readonly BindableInt currentCombo = new BindableInt(); - - public IBindable UserQuit => userQuit; - - private readonly BindableBool userQuit = new BindableBool(); - - [CanBeNull] - public FrameHeader LastHeader; - - public void MarkUserQuit() => userQuit.Value = true; - - public void UpdateScore(ScoreProcessor processor, ScoringMode mode) + public TrackedUserData(int userId, ScoreProcessor scoreProcessor) { - if (LastHeader == null) + UserId = userId; + ScoreProcessor = scoreProcessor; + + ScoringMode.BindValueChanged(_ => UpdateScore()); + } + + public void MarkUserQuit() => UserQuit.Value = true; + + public virtual void UpdateScore() + { + if (Frames.Count == 0) return; - score.Value = processor.GetImmediateScore(mode, LastHeader.MaxCombo, LastHeader.Statistics); - accuracy.Value = LastHeader.Accuracy; - currentCombo.Value = LastHeader.Combo; + SetFrame(Frames.Last()); } + + protected void SetFrame(TimedFrame frame) + { + var header = frame.Header; + + Score.Value = ScoreProcessor.GetImmediateScore(ScoringMode.Value, header.MaxCombo, header.Statistics); + Accuracy.Value = header.Accuracy; + CurrentCombo.Value = header.Combo; + } + } + + protected class TimedFrame : IComparable + { + public readonly double Time; + public readonly FrameHeader Header; + + public TimedFrame(double time) + { + Time = time; + } + + public TimedFrame(double time, FrameHeader header) + { + Time = time; + Header = header; + } + + public int CompareTo(TimedFrame other) => Time.CompareTo(other.Time); } } } From 1e002841cff4ffeac18a58c57c6a0769e33b05c9 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 12 Apr 2021 22:03:45 +0900 Subject: [PATCH 082/203] Add test for scoring mode changes --- .../TestSceneMultiplayerGameplayLeaderboard.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs index 272d547505..b6c06bb149 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs @@ -11,6 +11,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Testing; using osu.Framework.Utils; +using osu.Game.Configuration; using osu.Game.Database; using osu.Game.Online; using osu.Game.Online.API; @@ -38,6 +39,8 @@ namespace osu.Game.Tests.Visual.Multiplayer protected override Container Content { get; } = new Container { RelativeSizeAxes = Axes.Both }; + private OsuConfigManager config; + public TestSceneMultiplayerGameplayLeaderboard() { base.Content.Children = new Drawable[] @@ -48,6 +51,12 @@ namespace osu.Game.Tests.Visual.Multiplayer }; } + [BackgroundDependencyLoader] + private void load() + { + Dependencies.Cache(config = new OsuConfigManager(LocalStorage)); + } + [SetUpSteps] public override void SetUpSteps() { @@ -97,6 +106,14 @@ namespace osu.Game.Tests.Visual.Multiplayer AddRepeatStep("mark user quit", () => Client.CurrentMatchPlayingUserIds.RemoveAt(0), users); } + [Test] + public void TestChangeScoringMode() + { + AddRepeatStep("update state", () => streamingClient.RandomlyUpdateState(), 5); + AddStep("change to classic", () => config.SetValue(OsuSetting.ScoreDisplayMode, ScoringMode.Classic)); + AddStep("change to standardised", () => config.SetValue(OsuSetting.ScoreDisplayMode, ScoringMode.Standardised)); + } + public class TestMultiplayerStreaming : SpectatorStreamingClient { public new BindableList PlayingUsers => (BindableList)base.PlayingUsers; From 7c4f6d2b62635ef38c95754484f7bb82b2d2122d Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Mon, 12 Apr 2021 15:47:26 +0200 Subject: [PATCH 083/203] Rework template usage Includes moving the origin check back to templates, constructing nested template classes in each check, and making parameterized template usage. --- .../Edit/Checks/CheckOffscreenObjects.cs | 57 ++++++++++++------- .../Rulesets/Edit/Checks/CheckBackground.cs | 55 +++++++++++------- .../Rulesets/Edit/Checks/Components/Issue.cs | 17 +++--- .../Edit/Checks/Components/IssueTemplate.cs | 8 ++- 4 files changed, 88 insertions(+), 49 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs b/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs index b34c9966a4..c4f38e6d09 100644 --- a/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs +++ b/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs @@ -22,29 +22,46 @@ namespace osu.Game.Rulesets.Osu.Edit.Checks // (higher = more performant, but higher false-negative chance). private const int path_step_size = 5; + private readonly IssueTemplateOffscreenCircle templateOffscreenCircle; + private readonly IssueTemplateOffscreenSlider templateOffscreenSlider; + private readonly IssueTemplate[] templates; + + private class IssueTemplateOffscreenCircle : IssueTemplate + { + public IssueTemplateOffscreenCircle(ICheck checkOrigin) + : base(checkOrigin, IssueType.Problem, "This circle goes offscreen on a 4:3 aspect ratio.") + { + } + + public Issue Create(HitCircle circle) => new Issue(circle, this); + } + + private class IssueTemplateOffscreenSlider : IssueTemplate + { + public IssueTemplateOffscreenSlider(ICheck checkOrigin) + : base(checkOrigin, IssueType.Problem, "This slider goes offscreen here on a 4:3 aspect ratio.") + { + } + + public Issue Create(Slider slider, double offscreenTime) => new Issue(slider, this) { Time = offscreenTime }; + } + + public CheckOffscreenObjects() + { + templates = new IssueTemplate[] + { + templateOffscreenCircle = new IssueTemplateOffscreenCircle(this), + templateOffscreenSlider = new IssueTemplateOffscreenSlider(this) + }; + } + public CheckMetadata Metadata { get; } = new CheckMetadata ( category: CheckCategory.Compose, description: "Offscreen hitobjects." ); - public IEnumerable PossibleTemplates => new[] - { - templateOffscreen, - templateOffscreenSliderPath - }; - - private readonly IssueTemplate templateOffscreen = new IssueTemplate - ( - type: IssueType.Problem, - unformattedMessage: "This object goes offscreen on a 4:3 aspect ratio." - ); - - private readonly IssueTemplate templateOffscreenSliderPath = new IssueTemplate - ( - type: IssueType.Problem, - unformattedMessage: "This slider goes offscreen here on a 4:3 aspect ratio." - ); + public IEnumerable PossibleTemplates => templates; public IEnumerable Run(IBeatmap beatmap) { @@ -63,7 +80,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Checks case HitCircle circle: { if (isOffscreen(circle.StackedPosition, circle.Radius)) - yield return new Issue(this, circle, templateOffscreen); + yield return templateOffscreenCircle.Create(circle); break; } @@ -89,7 +106,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Checks // `SpanDuration` ensures we don't include reverses. double time = slider.StartTime + progress * slider.SpanDuration; - yield return new Issue(this, slider, templateOffscreenSliderPath) { Time = time }; + yield return templateOffscreenSlider.Create(slider, time); yield break; } @@ -98,7 +115,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Checks if (!isOffscreen(slider.StackedEndPosition, slider.Radius)) yield break; - yield return new Issue(this, slider, templateOffscreenSliderPath) { Time = slider.EndTime }; + yield return templateOffscreenSlider.Create(slider, slider.EndTime); } private bool isOffscreen(Vector2 position, double radius) diff --git a/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs b/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs index c922aa03c0..1e45ea261c 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs @@ -10,35 +10,52 @@ namespace osu.Game.Rulesets.Edit.Checks { public class CheckBackground : ICheck { + private readonly IssueTemplateNoneSet templateNoneSet; + private readonly IssueTemplateDoesNotExist templateDoesNotExist; + private readonly IssueTemplate[] templates; + + private class IssueTemplateNoneSet : IssueTemplate + { + public IssueTemplateNoneSet(ICheck checkOrigin) + : base(checkOrigin, IssueType.Problem, "No background has been set") + { + } + + public Issue Create() => new Issue(this); + } + + private class IssueTemplateDoesNotExist : IssueTemplate + { + public IssueTemplateDoesNotExist(ICheck checkOrigin) + : base(checkOrigin, IssueType.Problem, "The background file \"{0}\" does not exist.") + { + } + + public Issue Create(string filename) => new Issue(this, filename); + } + + public CheckBackground() + { + templates = new IssueTemplate[] + { + templateNoneSet = new IssueTemplateNoneSet(this), + templateDoesNotExist = new IssueTemplateDoesNotExist(this) + }; + } + public CheckMetadata Metadata { get; } = new CheckMetadata ( category: CheckCategory.Resources, description: "Missing background." ); - public IEnumerable PossibleTemplates => new[] - { - templateNoneSet, - templateDoesNotExist - }; - - private readonly IssueTemplate templateNoneSet = new IssueTemplate - ( - type: IssueType.Problem, - unformattedMessage: "No background has been set." - ); - - private readonly IssueTemplate templateDoesNotExist = new IssueTemplate - ( - type: IssueType.Problem, - unformattedMessage: "The background file \"{0}\" is does not exist." - ); + public IEnumerable PossibleTemplates => templates; public IEnumerable Run(IBeatmap beatmap) { if (beatmap.Metadata.BackgroundFile == null) { - yield return new Issue(this, templateNoneSet); + yield return templateNoneSet.Create(); yield break; } @@ -51,7 +68,7 @@ namespace osu.Game.Rulesets.Edit.Checks if (file != null) yield break; - yield return new Issue(this, templateDoesNotExist, beatmap.Metadata.BackgroundFile); + yield return templateDoesNotExist.Create(beatmap.Metadata.BackgroundFile); } } } diff --git a/osu.Game/Rulesets/Edit/Checks/Components/Issue.cs b/osu.Game/Rulesets/Edit/Checks/Components/Issue.cs index d0f7df857b..2bc9930e8f 100644 --- a/osu.Game/Rulesets/Edit/Checks/Components/Issue.cs +++ b/osu.Game/Rulesets/Edit/Checks/Components/Issue.cs @@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Edit.Checks.Components /// /// The check that this issue originates from. /// - public ICheck Check; + public ICheck Check => Template.Check; /// /// The arguments that give this issue its context, based on the . These are then substituted into the . @@ -37,30 +37,29 @@ namespace osu.Game.Rulesets.Edit.Checks.Components /// public object[] Arguments; - public Issue(ICheck check, IssueTemplate template, params object[] args) + public Issue(IssueTemplate template, params object[] args) { - Check = check; Time = null; HitObjects = Array.Empty(); Template = template; Arguments = args; } - public Issue(ICheck check, double? time, IssueTemplate template, params object[] args) - : this(check, template, args) + public Issue(double? time, IssueTemplate template, params object[] args) + : this(template, args) { Time = time; } - public Issue(ICheck check, HitObject hitObject, IssueTemplate template, params object[] args) - : this(check, template, args) + public Issue(HitObject hitObject, IssueTemplate template, params object[] args) + : this(template, args) { Time = hitObject.StartTime; HitObjects = new[] { hitObject }; } - public Issue(ICheck check, IEnumerable hitObjects, IssueTemplate template, params object[] args) - : this(check, template, args) + public Issue(IEnumerable hitObjects, IssueTemplate template, params object[] args) + : this(template, args) { var hitObjectList = hitObjects.ToList(); diff --git a/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs b/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs index 4a5f98ca5f..e746844879 100644 --- a/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs +++ b/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs @@ -14,6 +14,11 @@ namespace osu.Game.Rulesets.Edit.Checks.Components private static readonly Color4 negligible_green = new Colour4(0.33f, 0.8f, 0.5f, 1.0f); private static readonly Color4 error_gray = new Colour4(0.5f, 0.5f, 0.5f, 1.0f); + /// + /// The check that this template originates from. + /// + public ICheck Check; + /// /// The type of the issue. /// @@ -26,8 +31,9 @@ namespace osu.Game.Rulesets.Edit.Checks.Components /// public readonly string UnformattedMessage; - public IssueTemplate(IssueType type, string unformattedMessage) + public IssueTemplate(ICheck check, IssueType type, string unformattedMessage) { + Check = check; Type = type; UnformattedMessage = unformattedMessage; } From 1c69829ad488edf10b2aaaa347c5b3be28aeabc0 Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Mon, 12 Apr 2021 15:47:58 +0200 Subject: [PATCH 084/203] Fix `Template.Origin` -> `Check` --- osu.Game/Screens/Edit/Verify/IssueTable.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Verify/IssueTable.cs b/osu.Game/Screens/Edit/Verify/IssueTable.cs index 84dbabf300..516e1adf44 100644 --- a/osu.Game/Screens/Edit/Verify/IssueTable.cs +++ b/osu.Game/Screens/Edit/Verify/IssueTable.cs @@ -112,7 +112,7 @@ namespace osu.Game.Screens.Edit.Verify }, new OsuSpriteText { - Text = issue.Template.Origin.Metadata.Category.ToString(), + Text = issue.Check.Metadata.Category.ToString(), Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Bold), Margin = new MarginPadding(10) } From 008dbc7dd6f841ea245ec8b732696ebbbb2a73b4 Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Mon, 12 Apr 2021 15:49:13 +0200 Subject: [PATCH 085/203] Reverse `IssueType` ordering Reversed both in the enum and where it's displayed, so ends up the same in the end. --- osu.Game/Rulesets/Edit/Checks/Components/IssueType.cs | 10 +++++----- osu.Game/Screens/Edit/Verify/VerifyScreen.cs | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game/Rulesets/Edit/Checks/Components/IssueType.cs b/osu.Game/Rulesets/Edit/Checks/Components/IssueType.cs index be43060cfc..1241e058ad 100644 --- a/osu.Game/Rulesets/Edit/Checks/Components/IssueType.cs +++ b/osu.Game/Rulesets/Edit/Checks/Components/IssueType.cs @@ -4,22 +4,22 @@ namespace osu.Game.Rulesets.Edit.Checks.Components { /// - /// The type, or severity, of an issue. This decides its priority. + /// The type, or severity, of an issue. /// public enum IssueType { /// A must-fix in the vast majority of cases. - Problem = 3, + Problem, /// A possible mistake. Often requires critical thinking. - Warning = 2, + Warning, // TODO: Try/catch all checks run and return error templates if exceptions occur. /// An error occurred and a complete check could not be made. - Error = 1, + Error, // TODO: Negligible issues should be hidden by default. /// A possible mistake so minor/unlikely that it can often be safely ignored. - Negligible = 0, + Negligible, } } diff --git a/osu.Game/Screens/Edit/Verify/VerifyScreen.cs b/osu.Game/Screens/Edit/Verify/VerifyScreen.cs index 806029df4d..a3d808ce62 100644 --- a/osu.Game/Screens/Edit/Verify/VerifyScreen.cs +++ b/osu.Game/Screens/Edit/Verify/VerifyScreen.cs @@ -129,8 +129,8 @@ namespace osu.Game.Screens.Edit.Verify private void refresh() { table.Issues = beatmapVerifier.Run(Beatmap) - .OrderByDescending(issue => issue.Template.Type) - .ThenByDescending(issue => issue.Check.Metadata.Category); + .OrderBy(issue => issue.Template.Type) + .ThenBy(issue => issue.Check.Metadata.Category); } } } From caaaba59505f19f999a593a3c35f460c598c9065 Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Mon, 12 Apr 2021 16:20:53 +0200 Subject: [PATCH 086/203] Rename `Check` -> `ICheck` --- osu.Game/Rulesets/Edit/Checks/Components/{Check.cs => ICheck.cs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename osu.Game/Rulesets/Edit/Checks/Components/{Check.cs => ICheck.cs} (100%) diff --git a/osu.Game/Rulesets/Edit/Checks/Components/Check.cs b/osu.Game/Rulesets/Edit/Checks/Components/ICheck.cs similarity index 100% rename from osu.Game/Rulesets/Edit/Checks/Components/Check.cs rename to osu.Game/Rulesets/Edit/Checks/Components/ICheck.cs From 6d50d01186f24c342b0fc406803b2486cebbbf4b Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Mon, 12 Apr 2021 16:23:05 +0200 Subject: [PATCH 087/203] Make `IssueTemplate.Check` readonly --- osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs b/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs index e746844879..97df79ecd8 100644 --- a/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs +++ b/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs @@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Edit.Checks.Components /// /// The check that this template originates from. /// - public ICheck Check; + public readonly ICheck Check; /// /// The type of the issue. From 36bd235021d08202dd0c489b1bc664b91a31aca1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Apr 2021 23:36:10 +0900 Subject: [PATCH 088/203] Move nested classes to bottom of file --- .../Edit/Checks/CheckOffscreenObjects.cs | 40 +++++++++---------- .../Rulesets/Edit/Checks/CheckBackground.cs | 40 +++++++++---------- 2 files changed, 40 insertions(+), 40 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs b/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs index c4f38e6d09..f2a062b6d7 100644 --- a/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs +++ b/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs @@ -26,26 +26,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Checks private readonly IssueTemplateOffscreenSlider templateOffscreenSlider; private readonly IssueTemplate[] templates; - private class IssueTemplateOffscreenCircle : IssueTemplate - { - public IssueTemplateOffscreenCircle(ICheck checkOrigin) - : base(checkOrigin, IssueType.Problem, "This circle goes offscreen on a 4:3 aspect ratio.") - { - } - - public Issue Create(HitCircle circle) => new Issue(circle, this); - } - - private class IssueTemplateOffscreenSlider : IssueTemplate - { - public IssueTemplateOffscreenSlider(ICheck checkOrigin) - : base(checkOrigin, IssueType.Problem, "This slider goes offscreen here on a 4:3 aspect ratio.") - { - } - - public Issue Create(Slider slider, double offscreenTime) => new Issue(slider, this) { Time = offscreenTime }; - } - public CheckOffscreenObjects() { templates = new IssueTemplate[] @@ -123,5 +103,25 @@ namespace osu.Game.Rulesets.Osu.Edit.Checks return position.X - radius < min_x || position.X + radius > max_x || position.Y - radius < min_y || position.Y + radius > max_y; } + + private class IssueTemplateOffscreenCircle : IssueTemplate + { + public IssueTemplateOffscreenCircle(ICheck checkOrigin) + : base(checkOrigin, IssueType.Problem, "This circle goes offscreen on a 4:3 aspect ratio.") + { + } + + public Issue Create(HitCircle circle) => new Issue(circle, this); + } + + private class IssueTemplateOffscreenSlider : IssueTemplate + { + public IssueTemplateOffscreenSlider(ICheck checkOrigin) + : base(checkOrigin, IssueType.Problem, "This slider goes offscreen here on a 4:3 aspect ratio.") + { + } + + public Issue Create(Slider slider, double offscreenTime) => new Issue(slider, this) { Time = offscreenTime }; + } } } diff --git a/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs b/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs index 1e45ea261c..b48d19ae29 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs @@ -14,26 +14,6 @@ namespace osu.Game.Rulesets.Edit.Checks private readonly IssueTemplateDoesNotExist templateDoesNotExist; private readonly IssueTemplate[] templates; - private class IssueTemplateNoneSet : IssueTemplate - { - public IssueTemplateNoneSet(ICheck checkOrigin) - : base(checkOrigin, IssueType.Problem, "No background has been set") - { - } - - public Issue Create() => new Issue(this); - } - - private class IssueTemplateDoesNotExist : IssueTemplate - { - public IssueTemplateDoesNotExist(ICheck checkOrigin) - : base(checkOrigin, IssueType.Problem, "The background file \"{0}\" does not exist.") - { - } - - public Issue Create(string filename) => new Issue(this, filename); - } - public CheckBackground() { templates = new IssueTemplate[] @@ -70,5 +50,25 @@ namespace osu.Game.Rulesets.Edit.Checks yield return templateDoesNotExist.Create(beatmap.Metadata.BackgroundFile); } + + private class IssueTemplateNoneSet : IssueTemplate + { + public IssueTemplateNoneSet(ICheck checkOrigin) + : base(checkOrigin, IssueType.Problem, "No background has been set") + { + } + + public Issue Create() => new Issue(this); + } + + private class IssueTemplateDoesNotExist : IssueTemplate + { + public IssueTemplateDoesNotExist(ICheck checkOrigin) + : base(checkOrigin, IssueType.Problem, "The background file \"{0}\" does not exist.") + { + } + + public Issue Create(string filename) => new Issue(this, filename); + } } } From 62c181228284f034168b87c27879fa72b87369b9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 12 Apr 2021 23:37:47 +0900 Subject: [PATCH 089/203] Remove redundant parameter naming --- osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs | 6 +----- osu.Game/Rulesets/Edit/Checks/CheckBackground.cs | 6 +----- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs b/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs index f2a062b6d7..8d4cf2f4f0 100644 --- a/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs +++ b/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs @@ -35,11 +35,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Checks }; } - public CheckMetadata Metadata { get; } = new CheckMetadata - ( - category: CheckCategory.Compose, - description: "Offscreen hitobjects." - ); + public CheckMetadata Metadata { get; } = new CheckMetadata(CheckCategory.Compose, "Offscreen hitobjects"); public IEnumerable PossibleTemplates => templates; diff --git a/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs b/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs index b48d19ae29..1a766798cb 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs @@ -23,11 +23,7 @@ namespace osu.Game.Rulesets.Edit.Checks }; } - public CheckMetadata Metadata { get; } = new CheckMetadata - ( - category: CheckCategory.Resources, - description: "Missing background." - ); + public CheckMetadata Metadata { get; } = new CheckMetadata(CheckCategory.Resources, "Missing background"); public IEnumerable PossibleTemplates => templates; From 43b97fe0ad738854f1ef795bfd386db615c75458 Mon Sep 17 00:00:00 2001 From: Christine Chen Date: Mon, 12 Apr 2021 10:52:12 -0400 Subject: [PATCH 090/203] Refactor PowerStatus (now called BatteryInfo) --- osu.Android/OsuGameAndroid.cs | 6 ++---- osu.Android/osu.Android.csproj | 1 - .../Visual/Gameplay/TestScenePlayerLoader.cs | 14 ++++++-------- osu.Game/OsuGameBase.cs | 4 ++-- osu.Game/Screens/Play/PlayerLoader.cs | 6 +++--- osu.Game/Utils/{PowerStatus.cs => BatteryInfo.cs} | 12 +++--------- osu.iOS/OsuGameIOS.cs | 6 ++---- osu.iOS/osu.iOS.csproj | 1 - 8 files changed, 18 insertions(+), 32 deletions(-) rename osu.Game/Utils/{PowerStatus.cs => BatteryInfo.cs} (68%) diff --git a/osu.Android/OsuGameAndroid.cs b/osu.Android/OsuGameAndroid.cs index 02f4fa1ad6..050bf2b787 100644 --- a/osu.Android/OsuGameAndroid.cs +++ b/osu.Android/OsuGameAndroid.cs @@ -75,12 +75,10 @@ namespace osu.Android protected override UpdateManager CreateUpdateManager() => new SimpleUpdateManager(); - protected override PowerStatus CreatePowerStatus() => new AndroidPowerStatus(); + protected override BatteryInfo CreateBatteryInfo() => new AndroidBatteryInfo(); - private class AndroidPowerStatus : PowerStatus + private class AndroidBatteryInfo : BatteryInfo { - public override double BatteryCutoff => 0.20; - public override double ChargeLevel => Battery.ChargeLevel; public override bool IsCharging => Battery.PowerSource != BatteryPowerSource.Battery; diff --git a/osu.Android/osu.Android.csproj b/osu.Android/osu.Android.csproj index 64d5e5b1c8..582c856a47 100644 --- a/osu.Android/osu.Android.csproj +++ b/osu.Android/osu.Android.csproj @@ -64,7 +64,6 @@ - diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs index 657c1dd47e..311448a284 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs @@ -49,8 +49,8 @@ namespace osu.Game.Tests.Visual.Gameplay [Cached] private readonly VolumeOverlay volumeOverlay; - [Cached(typeof(PowerStatus))] - private readonly LocalPowerStatus powerStatus = new LocalPowerStatus(); + [Cached(typeof(BatteryInfo))] + private readonly LocalBatteryInfo batteryInfo = new LocalBatteryInfo(); private readonly ChangelogOverlay changelogOverlay; @@ -302,8 +302,8 @@ namespace osu.Game.Tests.Visual.Gameplay // set charge status and level AddStep("load player", () => resetPlayer(false, () => { - powerStatus.SetCharging(isCharging); - powerStatus.SetChargeLevel(chargeLevel); + batteryInfo.SetCharging(isCharging); + batteryInfo.SetChargeLevel(chargeLevel); })); AddUntilStep("wait for player", () => player?.LoadState == LoadState.Ready); AddAssert($"notification {(shouldWarn ? "triggered" : "not triggered")}", () => notificationOverlay.UnreadCount.Value == (shouldWarn ? 1 : 0)); @@ -381,16 +381,14 @@ namespace osu.Game.Tests.Visual.Gameplay } /// - /// Mutable dummy PowerStatus class for + /// Mutable dummy BatteryInfo class for /// /// - private class LocalPowerStatus : PowerStatus + private class LocalBatteryInfo : BatteryInfo { private bool isCharging = true; private double chargeLevel = 1; - public override double BatteryCutoff => 0.2; - public override bool IsCharging => isCharging; public override double ChargeLevel => chargeLevel; diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 96aabf0024..de8ba93106 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -157,7 +157,7 @@ namespace osu.Game protected override UserInputManager CreateUserInputManager() => new OsuUserInputManager(); - protected virtual PowerStatus CreatePowerStatus() => null; + protected virtual BatteryInfo CreateBatteryInfo() => null; /// /// The maximum volume at which audio tracks should playback. This can be set lower than 1 to create some head-room for sound effects. @@ -285,7 +285,7 @@ namespace osu.Game dependencies.Cache(SettingsStore = new SettingsStore(contextFactory)); dependencies.Cache(RulesetConfigCache = new RulesetConfigCache(SettingsStore)); - var powerStatus = CreatePowerStatus(); + var powerStatus = CreateBatteryInfo(); if (powerStatus != null) dependencies.CacheAs(powerStatus); diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index d342bf8273..964410f838 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -114,7 +114,7 @@ namespace osu.Game.Screens.Play private AudioManager audioManager { get; set; } [Resolved(CanBeNull = true)] - private PowerStatus powerStatus { get; set; } + private BatteryInfo batteryInfo { get; set; } public PlayerLoader(Func createPlayer) { @@ -483,11 +483,11 @@ namespace osu.Game.Screens.Play private void showBatteryWarningIfNeeded() { - if (powerStatus == null) return; + if (batteryInfo == null) return; if (!batteryWarningShownOnce.Value) { - if (powerStatus.IsLowBattery) + if (batteryInfo.IsLowBattery) { notificationOverlay?.Post(new BatteryWarningNotification()); batteryWarningShownOnce.Value = true; diff --git a/osu.Game/Utils/PowerStatus.cs b/osu.Game/Utils/BatteryInfo.cs similarity index 68% rename from osu.Game/Utils/PowerStatus.cs rename to osu.Game/Utils/BatteryInfo.cs index 46f7e32b9e..1a64213d8e 100644 --- a/osu.Game/Utils/PowerStatus.cs +++ b/osu.Game/Utils/BatteryInfo.cs @@ -5,15 +5,9 @@ namespace osu.Game.Utils { /// /// Provides access to the system's power status. - /// Currently implemented on iOS and Android only. /// - public abstract class PowerStatus + public abstract class BatteryInfo { - /// - /// The maximum battery level considered as low, from 0 to 1. - /// - public abstract double BatteryCutoff { get; } - /// /// The charge level of the battery, from 0 to 1. /// @@ -23,8 +17,8 @@ namespace osu.Game.Utils /// /// Whether the battery is currently low in charge. - /// Returns true if not charging and current charge level is lower than or equal to . + /// Returns true if not charging and current charge level is lower than or equal to 25%. /// - public bool IsLowBattery => !IsCharging && ChargeLevel <= BatteryCutoff; + public bool IsLowBattery => !IsCharging && ChargeLevel <= 0.25; } } diff --git a/osu.iOS/OsuGameIOS.cs b/osu.iOS/OsuGameIOS.cs index b53b594eae..702aef45f5 100644 --- a/osu.iOS/OsuGameIOS.cs +++ b/osu.iOS/OsuGameIOS.cs @@ -16,12 +16,10 @@ namespace osu.iOS protected override UpdateManager CreateUpdateManager() => new SimpleUpdateManager(); - protected override PowerStatus CreatePowerStatus() => new IOSPowerStatus(); + protected override BatteryInfo CreateBatteryInfo() => new IOSBatteryInfo(); - private class IOSPowerStatus : PowerStatus + private class IOSBatteryInfo : BatteryInfo { - public override double BatteryCutoff => 0.25; - public override double ChargeLevel => Battery.ChargeLevel; public override bool IsCharging => Battery.PowerSource != BatteryPowerSource.Battery; diff --git a/osu.iOS/osu.iOS.csproj b/osu.iOS/osu.iOS.csproj index ed6f52c60e..1cbe4422cc 100644 --- a/osu.iOS/osu.iOS.csproj +++ b/osu.iOS/osu.iOS.csproj @@ -117,7 +117,6 @@ - From bb720c23a01e6f27b376491abf29e578f4db5bc6 Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Mon, 12 Apr 2021 17:12:37 +0200 Subject: [PATCH 091/203] Remove check ctors and locals --- .../Edit/Checks/CheckOffscreenObjects.cs | 25 ++++++------------- .../Rulesets/Edit/Checks/CheckBackground.cs | 23 ++++++----------- 2 files changed, 15 insertions(+), 33 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs b/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs index 8d4cf2f4f0..adaabc0a1d 100644 --- a/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs +++ b/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs @@ -22,22 +22,13 @@ namespace osu.Game.Rulesets.Osu.Edit.Checks // (higher = more performant, but higher false-negative chance). private const int path_step_size = 5; - private readonly IssueTemplateOffscreenCircle templateOffscreenCircle; - private readonly IssueTemplateOffscreenSlider templateOffscreenSlider; - private readonly IssueTemplate[] templates; - - public CheckOffscreenObjects() - { - templates = new IssueTemplate[] - { - templateOffscreenCircle = new IssueTemplateOffscreenCircle(this), - templateOffscreenSlider = new IssueTemplateOffscreenSlider(this) - }; - } - public CheckMetadata Metadata { get; } = new CheckMetadata(CheckCategory.Compose, "Offscreen hitobjects"); - public IEnumerable PossibleTemplates => templates; + public IEnumerable PossibleTemplates => new IssueTemplate[] + { + new IssueTemplateOffscreenCircle(this), + new IssueTemplateOffscreenSlider(this) + }; public IEnumerable Run(IBeatmap beatmap) { @@ -56,7 +47,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Checks case HitCircle circle: { if (isOffscreen(circle.StackedPosition, circle.Radius)) - yield return templateOffscreenCircle.Create(circle); + yield return new IssueTemplateOffscreenCircle(this).Create(circle); break; } @@ -82,7 +73,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Checks // `SpanDuration` ensures we don't include reverses. double time = slider.StartTime + progress * slider.SpanDuration; - yield return templateOffscreenSlider.Create(slider, time); + yield return new IssueTemplateOffscreenSlider(this).Create(slider, time); yield break; } @@ -91,7 +82,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Checks if (!isOffscreen(slider.StackedEndPosition, slider.Radius)) yield break; - yield return templateOffscreenSlider.Create(slider, slider.EndTime); + yield return new IssueTemplateOffscreenSlider(this).Create(slider, slider.EndTime); } private bool isOffscreen(Vector2 position, double radius) diff --git a/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs b/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs index 1a766798cb..b0c1d6eb4b 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs @@ -10,28 +10,19 @@ namespace osu.Game.Rulesets.Edit.Checks { public class CheckBackground : ICheck { - private readonly IssueTemplateNoneSet templateNoneSet; - private readonly IssueTemplateDoesNotExist templateDoesNotExist; - private readonly IssueTemplate[] templates; - - public CheckBackground() - { - templates = new IssueTemplate[] - { - templateNoneSet = new IssueTemplateNoneSet(this), - templateDoesNotExist = new IssueTemplateDoesNotExist(this) - }; - } - public CheckMetadata Metadata { get; } = new CheckMetadata(CheckCategory.Resources, "Missing background"); - public IEnumerable PossibleTemplates => templates; + public IEnumerable PossibleTemplates => new IssueTemplate[] + { + new IssueTemplateNoneSet(this), + new IssueTemplateDoesNotExist(this) + }; public IEnumerable Run(IBeatmap beatmap) { if (beatmap.Metadata.BackgroundFile == null) { - yield return templateNoneSet.Create(); + yield return new IssueTemplateNoneSet(this).Create(); yield break; } @@ -44,7 +35,7 @@ namespace osu.Game.Rulesets.Edit.Checks if (file != null) yield break; - yield return templateDoesNotExist.Create(beatmap.Metadata.BackgroundFile); + yield return new IssueTemplateDoesNotExist(this).Create(beatmap.Metadata.BackgroundFile); } private class IssueTemplateNoneSet : IssueTemplate From f66306a81ad70868c29bc2a56d471a08f6bebbe5 Mon Sep 17 00:00:00 2001 From: Christine Chen Date: Mon, 12 Apr 2021 11:11:22 -0400 Subject: [PATCH 092/203] Remove IsLowBattery --- osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs | 2 +- osu.Game/Screens/Play/PlayerLoader.cs | 2 +- osu.Game/Utils/BatteryInfo.cs | 6 ------ 3 files changed, 2 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs index 311448a284..c56f2db0d0 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs @@ -294,7 +294,7 @@ namespace osu.Game.Tests.Visual.Gameplay [TestCase(false, 1.0, false)] // not charging, above cutoff --> no warning [TestCase(true, 0.1, false)] // charging, below cutoff --> no warning - [TestCase(false, 0.2, true)] // not charging, at cutoff --> warning + [TestCase(false, 0.25, true)] // not charging, at cutoff --> warning public void TestLowBatteryNotification(bool isCharging, double chargeLevel, bool shouldWarn) { AddStep("reset notification lock", () => sessionStatics.GetBindable(Static.LowBatteryNotificationShownOnce).Value = false); diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 964410f838..fc4659da97 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -487,7 +487,7 @@ namespace osu.Game.Screens.Play if (!batteryWarningShownOnce.Value) { - if (batteryInfo.IsLowBattery) + if (!batteryInfo.IsCharging && batteryInfo.ChargeLevel <= 0.25) { notificationOverlay?.Post(new BatteryWarningNotification()); batteryWarningShownOnce.Value = true; diff --git a/osu.Game/Utils/BatteryInfo.cs b/osu.Game/Utils/BatteryInfo.cs index 1a64213d8e..dd9b695e1f 100644 --- a/osu.Game/Utils/BatteryInfo.cs +++ b/osu.Game/Utils/BatteryInfo.cs @@ -14,11 +14,5 @@ namespace osu.Game.Utils public abstract double ChargeLevel { get; } public abstract bool IsCharging { get; } - - /// - /// Whether the battery is currently low in charge. - /// Returns true if not charging and current charge level is lower than or equal to 25%. - /// - public bool IsLowBattery => !IsCharging && ChargeLevel <= 0.25; } } From 19a154ddf15684f8a3429a642e3f71e555e0d27f Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Mon, 12 Apr 2021 17:28:12 +0200 Subject: [PATCH 093/203] Rename `checkOrigin` -> `check` More consistent with `Issue.ctor`'s "template". --- .../Edit/Checks/CheckOffscreenObjects.cs | 8 ++++---- osu.Game/Rulesets/Edit/Checks/CheckBackground.cs | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs b/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs index adaabc0a1d..0a682c4a83 100644 --- a/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs +++ b/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs @@ -93,8 +93,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Checks private class IssueTemplateOffscreenCircle : IssueTemplate { - public IssueTemplateOffscreenCircle(ICheck checkOrigin) - : base(checkOrigin, IssueType.Problem, "This circle goes offscreen on a 4:3 aspect ratio.") + public IssueTemplateOffscreenCircle(ICheck check) + : base(check, IssueType.Problem, "This circle goes offscreen on a 4:3 aspect ratio.") { } @@ -103,8 +103,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Checks private class IssueTemplateOffscreenSlider : IssueTemplate { - public IssueTemplateOffscreenSlider(ICheck checkOrigin) - : base(checkOrigin, IssueType.Problem, "This slider goes offscreen here on a 4:3 aspect ratio.") + public IssueTemplateOffscreenSlider(ICheck check) + : base(check, IssueType.Problem, "This slider goes offscreen here on a 4:3 aspect ratio.") { } diff --git a/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs b/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs index b0c1d6eb4b..463b596120 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs @@ -40,8 +40,8 @@ namespace osu.Game.Rulesets.Edit.Checks private class IssueTemplateNoneSet : IssueTemplate { - public IssueTemplateNoneSet(ICheck checkOrigin) - : base(checkOrigin, IssueType.Problem, "No background has been set") + public IssueTemplateNoneSet(ICheck check) + : base(check, IssueType.Problem, "No background has been set") { } @@ -50,8 +50,8 @@ namespace osu.Game.Rulesets.Edit.Checks private class IssueTemplateDoesNotExist : IssueTemplate { - public IssueTemplateDoesNotExist(ICheck checkOrigin) - : base(checkOrigin, IssueType.Problem, "The background file \"{0}\" does not exist.") + public IssueTemplateDoesNotExist(ICheck check) + : base(check, IssueType.Problem, "The background file \"{0}\" does not exist.") { } From d9e3276d0edb2f163efd5dcd5894a2881b89ab78 Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Mon, 12 Apr 2021 19:18:22 +0200 Subject: [PATCH 094/203] Don't update path type once immediately --- .../Blueprints/Sliders/Components/PathControlPointPiece.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs index 6b78cff33e..7686043c43 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs @@ -58,12 +58,13 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components { this.slider = slider; ControlPoint = controlPoint; + PointsInSegment = slider.Path.PointsInSegment(ControlPoint); slider.Path.Version.BindValueChanged(_ => { PointsInSegment = slider.Path.PointsInSegment(ControlPoint); updatePathType(); - }, runOnceImmediately: true); + }); controlPoint.Type.BindValueChanged(_ => updateMarkerDisplay()); From 92fab653e175b594327cbce5a7702a6068cf6a58 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 12 Apr 2021 20:10:22 +0300 Subject: [PATCH 095/203] Take current mod settings value into account on equality comparsion --- osu.Game/Online/API/APIMod.cs | 26 +++++++++++++++++++++++++- osu.Game/Rulesets/Mods/Mod.cs | 14 +++++++++++++- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/API/APIMod.cs b/osu.Game/Online/API/APIMod.cs index bad7e0af38..4427c82a8b 100644 --- a/osu.Game/Online/API/APIMod.cs +++ b/osu.Game/Online/API/APIMod.cs @@ -11,6 +11,7 @@ using osu.Framework.Bindables; using osu.Game.Configuration; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; +using osu.Game.Utils; namespace osu.Game.Online.API { @@ -64,7 +65,15 @@ namespace osu.Game.Online.API } public bool Equals(IMod other) => other is APIMod them && Equals(them); - public bool Equals(APIMod other) => Acronym == other?.Acronym; + + public bool Equals(APIMod other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + + return Acronym == other.Acronym && + Settings.SequenceEqual(other.Settings, ModSettingsEqualityComparer.Default); + } public override string ToString() { @@ -73,5 +82,20 @@ namespace osu.Game.Online.API return $"{Acronym}"; } + + private class ModSettingsEqualityComparer : IEqualityComparer> + { + public static ModSettingsEqualityComparer Default { get; } = new ModSettingsEqualityComparer(); + + public bool Equals(KeyValuePair x, KeyValuePair y) + { + object xValue = ModUtils.GetSettingUnderlyingValue(x.Value); + object yValue = ModUtils.GetSettingUnderlyingValue(y.Value); + + return x.Key == y.Key && EqualityComparer.Default.Equals(xValue, yValue); + } + + public int GetHashCode(KeyValuePair obj) => HashCode.Combine(obj.Key, ModUtils.GetSettingUnderlyingValue(obj.Value)); + } } } diff --git a/osu.Game/Rulesets/Mods/Mod.cs b/osu.Game/Rulesets/Mods/Mod.cs index 93a11e70f8..832a14ee1e 100644 --- a/osu.Game/Rulesets/Mods/Mod.cs +++ b/osu.Game/Rulesets/Mods/Mod.cs @@ -12,6 +12,7 @@ using osu.Framework.Testing; using osu.Game.Configuration; using osu.Game.IO.Serialization; using osu.Game.Rulesets.UI; +using osu.Game.Utils; namespace osu.Game.Rulesets.Mods { @@ -173,7 +174,18 @@ namespace osu.Game.Rulesets.Mods } public bool Equals(IMod other) => other is Mod them && Equals(them); - public bool Equals(Mod other) => GetType() == other?.GetType(); + + public bool Equals(Mod other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + + return GetType() == other.GetType() && + this.GetSettingsSourceProperties().All(pair => + EqualityComparer.Default.Equals( + ModUtils.GetSettingUnderlyingValue(pair.Item2.GetValue(this)), + ModUtils.GetSettingUnderlyingValue(pair.Item2.GetValue(other)))); + } /// /// Reset all custom settings for this mod back to their defaults. From 589e1a2a471ef69c49d24d42d5b56df9362f7b6c Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 12 Apr 2021 20:51:24 +0300 Subject: [PATCH 096/203] Add mod settings equality test --- .../Mods/ModSettingsEqualityComparsion.cs | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 osu.Game.Tests/Mods/ModSettingsEqualityComparsion.cs diff --git a/osu.Game.Tests/Mods/ModSettingsEqualityComparsion.cs b/osu.Game.Tests/Mods/ModSettingsEqualityComparsion.cs new file mode 100644 index 0000000000..7a5789f01a --- /dev/null +++ b/osu.Game.Tests/Mods/ModSettingsEqualityComparsion.cs @@ -0,0 +1,36 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Game.Online.API; +using osu.Game.Rulesets.Osu.Mods; + +namespace osu.Game.Tests.Mods +{ + [TestFixture] + public class ModSettingsEqualityComparison + { + [Test] + public void Test() + { + var mod1 = new OsuModDoubleTime { SpeedChange = { Value = 1.25 } }; + var mod2 = new OsuModDoubleTime { SpeedChange = { Value = 1.26 } }; + var mod3 = new OsuModDoubleTime { SpeedChange = { Value = 1.26 } }; + var apiMod1 = new APIMod(mod1); + var apiMod2 = new APIMod(mod2); + var apiMod3 = new APIMod(mod3); + + Assert.That(mod1, Is.Not.EqualTo(mod2)); + Assert.That(apiMod1, Is.Not.EqualTo(apiMod2)); + + Assert.That(mod2, Is.EqualTo(mod2)); + Assert.That(apiMod2, Is.EqualTo(apiMod2)); + + Assert.That(mod2, Is.EqualTo(mod3)); + Assert.That(apiMod2, Is.EqualTo(apiMod3)); + + Assert.That(mod3, Is.EqualTo(mod2)); + Assert.That(apiMod3, Is.EqualTo(apiMod2)); + } + } +} From 8f84abf34807bae9679b2f70cdfd8c082eb25e03 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 12 Apr 2021 21:51:04 +0300 Subject: [PATCH 097/203] Display "replays watched" tooltip for replays subsection --- .../Visual/Online/TestSceneUserHistoryGraph.cs | 3 +-- .../Sections/Historical/ChartProfileSubsection.cs | 7 ++++++- .../Sections/Historical/PlayHistorySubsection.cs | 2 ++ .../Profile/Sections/Historical/ProfileLineChart.cs | 4 ++-- .../Profile/Sections/Historical/ReplaysSubsection.cs | 2 ++ .../Profile/Sections/Historical/UserHistoryGraph.cs | 12 +++++++----- 6 files changed, 20 insertions(+), 10 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserHistoryGraph.cs b/osu.Game.Tests/Visual/Online/TestSceneUserHistoryGraph.cs index 57ce4c41e7..484c59695e 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneUserHistoryGraph.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneUserHistoryGraph.cs @@ -19,13 +19,12 @@ namespace osu.Game.Tests.Visual.Online { UserHistoryGraph graph; - Add(graph = new UserHistoryGraph + Add(graph = new UserHistoryGraph("Test") { RelativeSizeAxes = Axes.X, Height = 200, Anchor = Anchor.Centre, Origin = Anchor.Centre, - TooltipCounterName = "Test" }); var values = new[] diff --git a/osu.Game/Overlays/Profile/Sections/Historical/ChartProfileSubsection.cs b/osu.Game/Overlays/Profile/Sections/Historical/ChartProfileSubsection.cs index b82773155d..a48036dcbb 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/ChartProfileSubsection.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/ChartProfileSubsection.cs @@ -15,6 +15,11 @@ namespace osu.Game.Overlays.Profile.Sections.Historical { private ProfileLineChart chart; + /// + /// Text describing the value being plotted on the graph, which will be displayed as a prefix to the value in the history graph tooltip. + /// + protected abstract string GraphCounterName { get; } + protected ChartProfileSubsection(Bindable user, string headerText) : base(user, headerText) { @@ -30,7 +35,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical Left = 20, Right = 40 }, - Child = chart = new ProfileLineChart() + Child = chart = new ProfileLineChart(GraphCounterName) }; protected override void LoadComplete() diff --git a/osu.Game/Overlays/Profile/Sections/Historical/PlayHistorySubsection.cs b/osu.Game/Overlays/Profile/Sections/Historical/PlayHistorySubsection.cs index 2f15886c3a..dfd29db693 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/PlayHistorySubsection.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/PlayHistorySubsection.cs @@ -9,6 +9,8 @@ namespace osu.Game.Overlays.Profile.Sections.Historical { public class PlayHistorySubsection : ChartProfileSubsection { + protected override string GraphCounterName => "Plays"; + public PlayHistorySubsection(Bindable user) : base(user, "Play History") { diff --git a/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs b/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs index f02aa36b6c..eb5deb2802 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs @@ -42,7 +42,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical private readonly Container rowLinesContainer; private readonly Container columnLinesContainer; - public ProfileLineChart() + public ProfileLineChart(string graphCounterName) { RelativeSizeAxes = Axes.X; Height = 250; @@ -88,7 +88,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical } } }, - graph = new UserHistoryGraph + graph = new UserHistoryGraph(graphCounterName) { RelativeSizeAxes = Axes.Both } diff --git a/osu.Game/Overlays/Profile/Sections/Historical/ReplaysSubsection.cs b/osu.Game/Overlays/Profile/Sections/Historical/ReplaysSubsection.cs index e594e8d020..1c28306f17 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/ReplaysSubsection.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/ReplaysSubsection.cs @@ -9,6 +9,8 @@ namespace osu.Game.Overlays.Profile.Sections.Historical { public class ReplaysSubsection : ChartProfileSubsection { + protected override string GraphCounterName => "Replays Watched"; + public ReplaysSubsection(Bindable user) : base(user, "Replays Watched History") { diff --git a/osu.Game/Overlays/Profile/Sections/Historical/UserHistoryGraph.cs b/osu.Game/Overlays/Profile/Sections/Historical/UserHistoryGraph.cs index b1e8c8f0ca..f9e5ccf618 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/UserHistoryGraph.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/UserHistoryGraph.cs @@ -11,20 +11,22 @@ namespace osu.Game.Overlays.Profile.Sections.Historical { public class UserHistoryGraph : UserGraph { + private readonly string tooltipCounterName; + [CanBeNull] public UserHistoryCount[] Values { set => Data = value?.Select(v => new KeyValuePair(v.Date, v.Count)).ToArray(); } - /// - /// Text describing the value being plotted on the graph, which will be displayed as a prefix to the value in the . - /// - public string TooltipCounterName { get; set; } = "Plays"; + public UserHistoryGraph(string tooltipCounterName) + { + this.tooltipCounterName = tooltipCounterName; + } protected override float GetDataPointHeight(long playCount) => playCount; - protected override UserGraphTooltip GetTooltip() => new HistoryGraphTooltip(TooltipCounterName); + protected override UserGraphTooltip GetTooltip() => new HistoryGraphTooltip(tooltipCounterName); protected override object GetTooltipContent(DateTime date, long playCount) { From d8088777ea0a1177fb3e205530fdab7e04165458 Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Tue, 13 Apr 2021 01:21:34 +0200 Subject: [PATCH 098/203] Add `Equals` method to `IssueTemplate` This will be useful in tests. --- osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs b/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs index 97df79ecd8..e21f14f4bc 100644 --- a/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs +++ b/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs @@ -70,5 +70,7 @@ namespace osu.Game.Rulesets.Edit.Checks.Components } } } + + public bool Equals(IssueTemplate other) => other.Type == Type && other.UnformattedMessage == UnformattedMessage; } } From 47cf4bcf2595c672c0f70a85da4c55a36beea182 Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Tue, 13 Apr 2021 01:22:24 +0200 Subject: [PATCH 099/203] Add `CheckBackground` tests --- .../Editing/Checks/CheckBackgroundTest.cs | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 osu.Game.Tests/Editing/Checks/CheckBackgroundTest.cs diff --git a/osu.Game.Tests/Editing/Checks/CheckBackgroundTest.cs b/osu.Game.Tests/Editing/Checks/CheckBackgroundTest.cs new file mode 100644 index 0000000000..54d1a116c7 --- /dev/null +++ b/osu.Game.Tests/Editing/Checks/CheckBackgroundTest.cs @@ -0,0 +1,73 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Edit.Checks; +using osu.Game.Rulesets.Objects; + +namespace osu.Game.Tests.Editing.Checks +{ + [TestFixture] + public class CheckBackgroundTest + { + private CheckBackground check; + private IBeatmap beatmap; + + [SetUp] + public void Setup() + { + check = new CheckBackground(); + beatmap = new Beatmap + { + BeatmapInfo = new BeatmapInfo + { + Metadata = new BeatmapMetadata { BackgroundFile = "abc123.jpg" }, + BeatmapSet = new BeatmapSetInfo + { + Files = new List(new [] + { + new BeatmapSetFileInfo { Filename = "abc123.jpg" } + }) + } + } + }; + } + + [Test] + public void TestBackgroundSetAndInFiles() + { + var issues = check.Run(beatmap); + + Assert.That(!issues.Any()); + } + + [Test] + public void TestBackgroundSetAndNotInFiles() + { + beatmap.BeatmapInfo.BeatmapSet.Files.Clear(); + + var issues = check.Run(beatmap).ToList(); + var issue = issues.FirstOrDefault(); + + Assert.That(issues.Count == 1); + Assert.That(issue != null); + Assert.That(issue.Template.Equals(check.PossibleTemplates.ElementAt(1))); + } + + [Test] + public void TestBackgroundNotSet() + { + beatmap.Metadata.BackgroundFile = null; + + var issues = check.Run(beatmap).ToList(); + var issue = issues.FirstOrDefault(); + + Assert.That(issues.Count == 1); + Assert.That(issue != null); + Assert.That(issue.Template.Equals(check.PossibleTemplates.ElementAt(0))); + } + } +} From 8a6dfcfae1cb9c8b50e04777648098ef271a8d56 Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Tue, 13 Apr 2021 01:22:36 +0200 Subject: [PATCH 100/203] Add `CheckOffscreenObjects` tests --- .../Checks/CheckOffscreenObjectsTest.cs | 263 ++++++++++++++++++ 1 file changed, 263 insertions(+) create mode 100644 osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckOffscreenObjectsTest.cs diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckOffscreenObjectsTest.cs b/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckOffscreenObjectsTest.cs new file mode 100644 index 0000000000..fa7854765f --- /dev/null +++ b/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckOffscreenObjectsTest.cs @@ -0,0 +1,263 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.Osu.Edit.Checks; +using osu.Game.Rulesets.Osu.Objects; +using osuTK; + +namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks +{ + [TestFixture] + public class CheckOffscreenObjectsTest + { + private CheckOffscreenObjects check; + + [SetUp] + public void Setup() + { + check = new CheckOffscreenObjects(); + } + + [Test] + public void TestCircleInCenter() + { + var beatmap = new Beatmap + { + HitObjects = new List + { + new HitCircle + { + StartTime = 3000, + Position = new Vector2(320, 240) // Playfield is 640 x 480. + } + } + }; + + var issues = check.Run(beatmap); + + Assert.That(!issues.Any()); + } + + [Test] + public void TestCircleNearEdge() + { + var beatmap = new Beatmap + { + HitObjects = new List + { + new HitCircle + { + StartTime = 3000, + Position = new Vector2(5, 5) + } + } + }; + + var issues = check.Run(beatmap); + + Assert.That(!issues.Any()); + } + + [Test] + public void TestCircleNearEdgeStackedOffscreen() + { + var beatmap = new Beatmap + { + HitObjects = new List + { + new HitCircle + { + StartTime = 3000, + Position = new Vector2(5, 5), + StackHeight = 5 + } + } + }; + + assertOffscreenCircle(beatmap); + } + + [Test] + public void TestCircleOffscreen() + { + var beatmap = new Beatmap + { + HitObjects = new List + { + new HitCircle + { + StartTime = 3000, + Position = new Vector2(0, 0) + } + } + }; + + assertOffscreenCircle(beatmap); + } + + [Test] + public void TestSliderInCenter() + { + var beatmap = new Beatmap + { + HitObjects = new List + { + new Slider + { + StartTime = 3000, + Position = new Vector2(420, 240), + Path = new SliderPath(new[] + { + new PathControlPoint(new Vector2(0, 0), PathType.Linear), + new PathControlPoint(new Vector2(-100, 0)) + }), + } + } + }; + + var issues = check.Run(beatmap); + + Assert.That(!issues.Any()); + } + + [Test] + public void TestSliderNearEdge() + { + var beatmap = new Beatmap + { + HitObjects = new List + { + new Slider + { + StartTime = 3000, + Position = new Vector2(320, 240), + Path = new SliderPath(new[] + { + new PathControlPoint(new Vector2(0, 0), PathType.Linear), + new PathControlPoint(new Vector2(0, -235)) + }), + } + } + }; + + var issues = check.Run(beatmap); + + Assert.That(!issues.Any()); + } + + [Test] + public void TestSliderNearEdgeStackedOffscreen() + { + var beatmap = new Beatmap + { + HitObjects = new List + { + new Slider + { + StartTime = 3000, + Position = new Vector2(320, 240), + Path = new SliderPath(new[] + { + new PathControlPoint(new Vector2(0, 0), PathType.Linear), + new PathControlPoint(new Vector2(0, -235)) + }), + StackHeight = 5 + } + } + }; + + assertOffscreenSlider(beatmap); + } + + [Test] + public void TestSliderOffscreenStart() + { + var beatmap = new Beatmap + { + HitObjects = new List + { + new Slider + { + StartTime = 3000, + Position = new Vector2(0, 0), + Path = new SliderPath(new[] + { + new PathControlPoint(new Vector2(0, 0), PathType.Linear), + new PathControlPoint(new Vector2(320, 240)) + }), + } + } + }; + + assertOffscreenSlider(beatmap); + } + + [Test] + public void TestSliderOffscreenEnd() + { + var beatmap = new Beatmap + { + HitObjects = new List + { + new Slider + { + StartTime = 3000, + Position = new Vector2(320, 240), + Path = new SliderPath(new[] + { + new PathControlPoint(new Vector2(0, 0), PathType.Linear), + new PathControlPoint(new Vector2(-320, -240)) + }), + } + } + }; + + assertOffscreenSlider(beatmap); + } + + [Test] + public void TestSliderOffscreenPath() + { + var beatmap = new Beatmap + { + HitObjects = new List + { + new Slider + { + StartTime = 3000, + Position = new Vector2(320, 240), + Path = new SliderPath(new[] + { + // Circular arc shoots over the top of the screen. + new PathControlPoint(new Vector2(0, 0), PathType.PerfectCurve), + new PathControlPoint(new Vector2(-100, -200)), + new PathControlPoint(new Vector2(100, -200)) + }), + } + } + }; + + assertOffscreenSlider(beatmap); + } + + private void assertOneIssue(IBeatmap beatmap, int templateIndex) + { + var issues = check.Run(beatmap).ToList(); + var issue = issues.FirstOrDefault(); + + Assert.That(issues.Count == 1); + Assert.That(issue != null); + Assert.That(issue.Template.Equals(check.PossibleTemplates.ElementAt(templateIndex))); + } + + private void assertOffscreenCircle(IBeatmap beatmap) => assertOneIssue(beatmap, 0); + + private void assertOffscreenSlider(IBeatmap beatmap) => assertOneIssue(beatmap, 1); + } +} From 0bcc39bd36a9a5893f57fcaa8b94f9d3e31356e9 Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Tue, 13 Apr 2021 02:17:35 +0200 Subject: [PATCH 101/203] Remove redundant space --- osu.Game.Tests/Editing/Checks/CheckBackgroundTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Editing/Checks/CheckBackgroundTest.cs b/osu.Game.Tests/Editing/Checks/CheckBackgroundTest.cs index 54d1a116c7..2f309afb6c 100644 --- a/osu.Game.Tests/Editing/Checks/CheckBackgroundTest.cs +++ b/osu.Game.Tests/Editing/Checks/CheckBackgroundTest.cs @@ -27,7 +27,7 @@ namespace osu.Game.Tests.Editing.Checks Metadata = new BeatmapMetadata { BackgroundFile = "abc123.jpg" }, BeatmapSet = new BeatmapSetInfo { - Files = new List(new [] + Files = new List(new[] { new BeatmapSetFileInfo { Filename = "abc123.jpg" } }) From 6d3f9fa9cefdd776d937e0c6b809df8624457e50 Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Tue, 13 Apr 2021 02:29:25 +0200 Subject: [PATCH 102/203] Use `is` class instead of `Equals` with template index Ensures ordering of `PossibleTemplates` does not affect tests. --- .../Editor/Checks/CheckOffscreenObjectsTest.cs | 14 ++++++++++---- .../Edit/Checks/CheckOffscreenObjects.cs | 4 ++-- .../Editing/Checks/CheckBackgroundTest.cs | 4 ++-- osu.Game/Rulesets/Edit/Checks/CheckBackground.cs | 4 ++-- .../Edit/Checks/Components/IssueTemplate.cs | 2 -- 5 files changed, 16 insertions(+), 12 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckOffscreenObjectsTest.cs b/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckOffscreenObjectsTest.cs index fa7854765f..33b839650f 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckOffscreenObjectsTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckOffscreenObjectsTest.cs @@ -246,18 +246,24 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks assertOffscreenSlider(beatmap); } - private void assertOneIssue(IBeatmap beatmap, int templateIndex) + private void assertOffscreenCircle(IBeatmap beatmap) { var issues = check.Run(beatmap).ToList(); var issue = issues.FirstOrDefault(); Assert.That(issues.Count == 1); Assert.That(issue != null); - Assert.That(issue.Template.Equals(check.PossibleTemplates.ElementAt(templateIndex))); + Assert.That(issue.Template is CheckOffscreenObjects.IssueTemplateOffscreenCircle); } - private void assertOffscreenCircle(IBeatmap beatmap) => assertOneIssue(beatmap, 0); + private void assertOffscreenSlider(IBeatmap beatmap) + { + var issues = check.Run(beatmap).ToList(); + var issue = issues.FirstOrDefault(); - private void assertOffscreenSlider(IBeatmap beatmap) => assertOneIssue(beatmap, 1); + Assert.That(issues.Count == 1); + Assert.That(issue != null); + Assert.That(issue.Template is CheckOffscreenObjects.IssueTemplateOffscreenSlider); + } } } diff --git a/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs b/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs index 0a682c4a83..c241db7e06 100644 --- a/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs +++ b/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs @@ -91,7 +91,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Checks position.Y - radius < min_y || position.Y + radius > max_y; } - private class IssueTemplateOffscreenCircle : IssueTemplate + public class IssueTemplateOffscreenCircle : IssueTemplate { public IssueTemplateOffscreenCircle(ICheck check) : base(check, IssueType.Problem, "This circle goes offscreen on a 4:3 aspect ratio.") @@ -101,7 +101,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Checks public Issue Create(HitCircle circle) => new Issue(circle, this); } - private class IssueTemplateOffscreenSlider : IssueTemplate + public class IssueTemplateOffscreenSlider : IssueTemplate { public IssueTemplateOffscreenSlider(ICheck check) : base(check, IssueType.Problem, "This slider goes offscreen here on a 4:3 aspect ratio.") diff --git a/osu.Game.Tests/Editing/Checks/CheckBackgroundTest.cs b/osu.Game.Tests/Editing/Checks/CheckBackgroundTest.cs index 2f309afb6c..327abcab6c 100644 --- a/osu.Game.Tests/Editing/Checks/CheckBackgroundTest.cs +++ b/osu.Game.Tests/Editing/Checks/CheckBackgroundTest.cs @@ -54,7 +54,7 @@ namespace osu.Game.Tests.Editing.Checks Assert.That(issues.Count == 1); Assert.That(issue != null); - Assert.That(issue.Template.Equals(check.PossibleTemplates.ElementAt(1))); + Assert.That(issue.Template is CheckBackground.IssueTemplateDoesNotExist); } [Test] @@ -67,7 +67,7 @@ namespace osu.Game.Tests.Editing.Checks Assert.That(issues.Count == 1); Assert.That(issue != null); - Assert.That(issue.Template.Equals(check.PossibleTemplates.ElementAt(0))); + Assert.That(issue.Template is CheckBackground.IssueTemplateNoneSet); } } } diff --git a/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs b/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs index 463b596120..4d5069f446 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs @@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Edit.Checks yield return new IssueTemplateDoesNotExist(this).Create(beatmap.Metadata.BackgroundFile); } - private class IssueTemplateNoneSet : IssueTemplate + public class IssueTemplateNoneSet : IssueTemplate { public IssueTemplateNoneSet(ICheck check) : base(check, IssueType.Problem, "No background has been set") @@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.Edit.Checks public Issue Create() => new Issue(this); } - private class IssueTemplateDoesNotExist : IssueTemplate + public class IssueTemplateDoesNotExist : IssueTemplate { public IssueTemplateDoesNotExist(ICheck check) : base(check, IssueType.Problem, "The background file \"{0}\" does not exist.") diff --git a/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs b/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs index e21f14f4bc..97df79ecd8 100644 --- a/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs +++ b/osu.Game/Rulesets/Edit/Checks/Components/IssueTemplate.cs @@ -70,7 +70,5 @@ namespace osu.Game.Rulesets.Edit.Checks.Components } } } - - public bool Equals(IssueTemplate other) => other.Type == Type && other.UnformattedMessage == UnformattedMessage; } } From 17c2c4e885b63761fa179ddc2ee193b6078a4b03 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 13 Apr 2021 05:31:56 +0300 Subject: [PATCH 103/203] Fix test case filename not matching --- ...ingsEqualityComparsion.cs => ModSettingsEqualityComparison.cs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename osu.Game.Tests/Mods/{ModSettingsEqualityComparsion.cs => ModSettingsEqualityComparison.cs} (100%) diff --git a/osu.Game.Tests/Mods/ModSettingsEqualityComparsion.cs b/osu.Game.Tests/Mods/ModSettingsEqualityComparison.cs similarity index 100% rename from osu.Game.Tests/Mods/ModSettingsEqualityComparsion.cs rename to osu.Game.Tests/Mods/ModSettingsEqualityComparison.cs From 66e74da2b74a10ed695b073bf1a92c5c1b7b7d32 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Apr 2021 13:03:14 +0900 Subject: [PATCH 104/203] Fix regression in quick delete mouse action blocking --- .../Visual/Editing/TestSceneEditorQuickDelete.cs | 3 ++- .../Screens/Edit/Compose/Components/BlueprintContainer.cs | 8 ++++---- .../Screens/Edit/Compose/Components/SelectionHandler.cs | 6 +++--- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneEditorQuickDelete.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorQuickDelete.cs index 8a0f27b851..25e12b7a88 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneEditorQuickDelete.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneEditorQuickDelete.cs @@ -83,8 +83,9 @@ namespace osu.Game.Tests.Visual.Editing AddStep("right click", () => InputManager.Click(MouseButton.Right)); AddAssert("slider has 2 points", () => slider.Path.ControlPoints.Count == 2); - // second click should nuke the object completely. AddStep("right click", () => InputManager.Click(MouseButton.Right)); + + // second click should nuke the object completely. AddAssert("no hitobjects in beatmap", () => EditorBeatmap.HitObjects.Count == 0); AddStep("release shift", () => InputManager.ReleaseKey(Key.ShiftLeft)); diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index 64cf0e7512..b5a28dc022 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -135,7 +135,7 @@ namespace osu.Game.Screens.Edit.Compose.Components protected override bool OnMouseDown(MouseDownEvent e) { - bool selectionPerformed = beginClickSelection(e); + bool selectionPerformed = performMouseDownActions(e); // even if a selection didn't occur, a drag event may still move the selection. prepareSelectionMovement(); @@ -343,7 +343,7 @@ namespace osu.Game.Screens.Edit.Compose.Components /// /// The input event that triggered this selection. /// Whether a selection was performed. - private bool beginClickSelection(MouseButtonEvent e) + private bool performMouseDownActions(MouseButtonEvent e) { // Iterate from the top of the input stack (blueprints closest to the front of the screen first). // Priority is given to already-selected blueprints. @@ -351,7 +351,7 @@ namespace osu.Game.Screens.Edit.Compose.Components { if (!blueprint.IsHovered) continue; - return clickSelectionBegan = SelectionHandler.HandleSelectionRequested(blueprint, e); + return clickSelectionBegan = SelectionHandler.MouseDownSelectionRequested(blueprint, e); } return false; @@ -375,7 +375,7 @@ namespace osu.Game.Screens.Edit.Compose.Components { if (!blueprint.IsHovered) continue; - return clickSelectionBegan = SelectionHandler.HandleDeselectionRequested(blueprint, e); + return clickSelectionBegan = SelectionHandler.MouseUpSelectionRequested(blueprint, e); } } diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs index e5e1100797..389ef78ed5 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs @@ -220,12 +220,12 @@ namespace osu.Game.Screens.Edit.Compose.Components /// The blueprint. /// The mouse event responsible for selection. /// Whether a selection was performed. - internal bool HandleSelectionRequested(SelectionBlueprint blueprint, MouseButtonEvent e) + internal bool MouseDownSelectionRequested(SelectionBlueprint blueprint, MouseButtonEvent e) { if (e.ShiftPressed && e.Button == MouseButton.Right) { handleQuickDeletion(blueprint); - return false; + return true; } // while holding control, we only want to add to selection, not replace an existing selection. @@ -244,7 +244,7 @@ namespace osu.Game.Screens.Edit.Compose.Components /// The blueprint. /// The mouse event responsible for deselection. /// Whether a deselection was performed. - internal bool HandleDeselectionRequested(SelectionBlueprint blueprint, MouseButtonEvent e) + internal bool MouseUpSelectionRequested(SelectionBlueprint blueprint, MouseButtonEvent e) { if (blueprint.IsSelected) { From 05d7fe289f3e6a48b855edc629e163fe7ea26fff Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Apr 2021 13:09:18 +0900 Subject: [PATCH 105/203] Rename test scene in preparation for increasing scope --- ...estSceneEditorQuickDelete.cs => TestSceneEditorSelection.cs} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename osu.Game.Tests/Visual/Editing/{TestSceneEditorQuickDelete.cs => TestSceneEditorSelection.cs} (98%) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneEditorQuickDelete.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorSelection.cs similarity index 98% rename from osu.Game.Tests/Visual/Editing/TestSceneEditorQuickDelete.cs rename to osu.Game.Tests/Visual/Editing/TestSceneEditorSelection.cs index 25e12b7a88..36c4357432 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneEditorQuickDelete.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneEditorSelection.cs @@ -18,7 +18,7 @@ using osuTK.Input; namespace osu.Game.Tests.Visual.Editing { - public class TestSceneEditorQuickDelete : EditorTestScene + public class TestSceneEditorSelection : EditorTestScene { protected override Ruleset CreateEditorRuleset() => new OsuRuleset(); From 7c975359d9b6b06a797a7121c38700e09c2f08b8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Apr 2021 13:29:37 +0900 Subject: [PATCH 106/203] Add basic select/deselect tests --- .../Editing/TestSceneEditorSelection.cs | 98 ++++++++++++++++++- 1 file changed, 93 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneEditorSelection.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorSelection.cs index 36c4357432..4f386972fa 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneEditorSelection.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneEditorSelection.cs @@ -27,6 +27,97 @@ namespace osu.Game.Tests.Visual.Editing private BlueprintContainer blueprintContainer => Editor.ChildrenOfType().First(); + private void moveMouseToObject(HitObject obj) + { + AddStep("move mouse to object", () => + { + var pos = blueprintContainer.SelectionBlueprints + .First(s => s.HitObject == obj) + .ChildrenOfType() + .First().ScreenSpaceDrawQuad.Centre; + + InputManager.MoveMouseTo(pos); + }); + } + + [Test] + public void TestBasicSelect() + { + var addedObject = new HitCircle { StartTime = 100 }; + AddStep("add hitobject", () => EditorBeatmap.Add(addedObject)); + + moveMouseToObject(addedObject); + AddStep("left click", () => InputManager.Click(MouseButton.Left)); + + AddAssert("hitobject selected", () => EditorBeatmap.SelectedHitObjects.Single() == addedObject); + + var addedObject2 = new HitCircle + { + StartTime = 100, + Position = new Vector2(100), + }; + + AddStep("add one more hitobject", () => EditorBeatmap.Add(addedObject2)); + AddAssert("selection unchanged", () => EditorBeatmap.SelectedHitObjects.Single() == addedObject); + + moveMouseToObject(addedObject2); + AddStep("left click", () => InputManager.Click(MouseButton.Left)); + AddAssert("hitobject selected", () => EditorBeatmap.SelectedHitObjects.Single() == addedObject2); + } + + [Test] + public void TestMultiSelect() + { + var addedObjects = new[] + { + new HitCircle { StartTime = 100 }, + new HitCircle { StartTime = 200, Position = new Vector2(50) }, + new HitCircle { StartTime = 300, Position = new Vector2(100) }, + new HitCircle { StartTime = 400, Position = new Vector2(150) }, + }; + + AddStep("add hitobjects", () => EditorBeatmap.AddRange(addedObjects)); + + moveMouseToObject(addedObjects[0]); + AddStep("click first", () => InputManager.Click(MouseButton.Left)); + + AddAssert("hitobject selected", () => EditorBeatmap.SelectedHitObjects.Single() == addedObjects[0]); + + AddStep("hold control", () => InputManager.PressKey(Key.ControlLeft)); + + moveMouseToObject(addedObjects[1]); + AddStep("click second", () => InputManager.Click(MouseButton.Left)); + AddAssert("2 hitobjects selected", () => EditorBeatmap.SelectedHitObjects.Count == 2 && EditorBeatmap.SelectedHitObjects.Contains(addedObjects[1])); + + moveMouseToObject(addedObjects[2]); + AddStep("click third", () => InputManager.Click(MouseButton.Left)); + AddAssert("3 hitobjects selected", () => EditorBeatmap.SelectedHitObjects.Count == 3 && EditorBeatmap.SelectedHitObjects.Contains(addedObjects[2])); + + moveMouseToObject(addedObjects[1]); + AddStep("click second", () => InputManager.Click(MouseButton.Left)); + AddAssert("2 hitobjects selected", () => EditorBeatmap.SelectedHitObjects.Count == 2 && !EditorBeatmap.SelectedHitObjects.Contains(addedObjects[1])); + } + + [Test] + public void TestBasicDeselect() + { + var addedObject = new HitCircle { StartTime = 100 }; + AddStep("add hitobject", () => EditorBeatmap.Add(addedObject)); + + moveMouseToObject(addedObject); + AddStep("left click", () => InputManager.Click(MouseButton.Left)); + + AddAssert("hitobject selected", () => EditorBeatmap.SelectedHitObjects.Single() == addedObject); + + AddStep("click away", () => + { + InputManager.MoveMouseTo(blueprintContainer.ScreenSpaceDrawQuad.Centre); + InputManager.Click(MouseButton.Left); + }); + + AddAssert("selection lost", () => EditorBeatmap.SelectedHitObjects.Count == 0); + } + [Test] public void TestQuickDeleteRemovesObject() { @@ -36,11 +127,8 @@ namespace osu.Game.Tests.Visual.Editing AddStep("select added object", () => EditorBeatmap.SelectedHitObjects.Add(addedObject)); - AddStep("move mouse to object", () => - { - var pos = blueprintContainer.ChildrenOfType().First().ScreenSpaceDrawQuad.Centre; - InputManager.MoveMouseTo(pos); - }); + moveMouseToObject(addedObject); + AddStep("hold shift", () => InputManager.PressKey(Key.ShiftLeft)); AddStep("right click", () => InputManager.Click(MouseButton.Right)); AddStep("release shift", () => InputManager.ReleaseKey(Key.ShiftLeft)); From 516bd138e32ba1bd3e4ae35a150d4ae1e9b0d8ab Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Apr 2021 13:46:38 +0900 Subject: [PATCH 107/203] Add (previously failing) test coverage of drag from selection --- .../Editing/TestSceneEditorSelection.cs | 57 +++++++++++++++---- 1 file changed, 47 insertions(+), 10 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneEditorSelection.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorSelection.cs index 4f386972fa..99f31b0c2a 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneEditorSelection.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneEditorSelection.cs @@ -1,6 +1,7 @@ // 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 NUnit.Framework; using osu.Framework.Testing; @@ -27,12 +28,12 @@ namespace osu.Game.Tests.Visual.Editing private BlueprintContainer blueprintContainer => Editor.ChildrenOfType().First(); - private void moveMouseToObject(HitObject obj) + private void moveMouseToObject(Func targetFunc) { AddStep("move mouse to object", () => { var pos = blueprintContainer.SelectionBlueprints - .First(s => s.HitObject == obj) + .First(s => s.HitObject == targetFunc()) .ChildrenOfType() .First().ScreenSpaceDrawQuad.Centre; @@ -46,7 +47,7 @@ namespace osu.Game.Tests.Visual.Editing var addedObject = new HitCircle { StartTime = 100 }; AddStep("add hitobject", () => EditorBeatmap.Add(addedObject)); - moveMouseToObject(addedObject); + moveMouseToObject(() => addedObject); AddStep("left click", () => InputManager.Click(MouseButton.Left)); AddAssert("hitobject selected", () => EditorBeatmap.SelectedHitObjects.Single() == addedObject); @@ -60,7 +61,7 @@ namespace osu.Game.Tests.Visual.Editing AddStep("add one more hitobject", () => EditorBeatmap.Add(addedObject2)); AddAssert("selection unchanged", () => EditorBeatmap.SelectedHitObjects.Single() == addedObject); - moveMouseToObject(addedObject2); + moveMouseToObject(() => addedObject2); AddStep("left click", () => InputManager.Click(MouseButton.Left)); AddAssert("hitobject selected", () => EditorBeatmap.SelectedHitObjects.Single() == addedObject2); } @@ -78,33 +79,69 @@ namespace osu.Game.Tests.Visual.Editing AddStep("add hitobjects", () => EditorBeatmap.AddRange(addedObjects)); - moveMouseToObject(addedObjects[0]); + moveMouseToObject(() => addedObjects[0]); AddStep("click first", () => InputManager.Click(MouseButton.Left)); AddAssert("hitobject selected", () => EditorBeatmap.SelectedHitObjects.Single() == addedObjects[0]); AddStep("hold control", () => InputManager.PressKey(Key.ControlLeft)); - moveMouseToObject(addedObjects[1]); + moveMouseToObject(() => addedObjects[1]); AddStep("click second", () => InputManager.Click(MouseButton.Left)); AddAssert("2 hitobjects selected", () => EditorBeatmap.SelectedHitObjects.Count == 2 && EditorBeatmap.SelectedHitObjects.Contains(addedObjects[1])); - moveMouseToObject(addedObjects[2]); + moveMouseToObject(() => addedObjects[2]); AddStep("click third", () => InputManager.Click(MouseButton.Left)); AddAssert("3 hitobjects selected", () => EditorBeatmap.SelectedHitObjects.Count == 3 && EditorBeatmap.SelectedHitObjects.Contains(addedObjects[2])); - moveMouseToObject(addedObjects[1]); + moveMouseToObject(() => addedObjects[1]); AddStep("click second", () => InputManager.Click(MouseButton.Left)); AddAssert("2 hitobjects selected", () => EditorBeatmap.SelectedHitObjects.Count == 2 && !EditorBeatmap.SelectedHitObjects.Contains(addedObjects[1])); } + [TestCase(false)] + [TestCase(true)] + public void TestMultiSelectFromDrag(bool alreadySelectedBeforeDrag) + { + HitCircle[] addedObjects = null; + + AddStep("add hitobjects", () => EditorBeatmap.AddRange(addedObjects = new[] + { + new HitCircle { StartTime = 100 }, + new HitCircle { StartTime = 200, Position = new Vector2(50) }, + new HitCircle { StartTime = 300, Position = new Vector2(100) }, + new HitCircle { StartTime = 400, Position = new Vector2(150) }, + })); + + moveMouseToObject(() => addedObjects[0]); + AddStep("click first", () => InputManager.Click(MouseButton.Left)); + + AddStep("hold control", () => InputManager.PressKey(Key.ControlLeft)); + + moveMouseToObject(() => addedObjects[1]); + + if (alreadySelectedBeforeDrag) + AddStep("click second", () => InputManager.Click(MouseButton.Left)); + + AddStep("mouse down on second", () => InputManager.PressButton(MouseButton.Left)); + + AddAssert("2 hitobjects selected", () => EditorBeatmap.SelectedHitObjects.Count == 2 && EditorBeatmap.SelectedHitObjects.Contains(addedObjects[1])); + + AddStep("drag to centre", () => InputManager.MoveMouseTo(blueprintContainer.ScreenSpaceDrawQuad.Centre)); + + AddAssert("positions changed", () => addedObjects[0].Position != Vector2.Zero && addedObjects[1].Position != new Vector2(50)); + + AddStep("release control", () => InputManager.ReleaseKey(Key.ControlLeft)); + AddStep("mouse up", () => InputManager.ReleaseButton(MouseButton.Left)); + } + [Test] public void TestBasicDeselect() { var addedObject = new HitCircle { StartTime = 100 }; AddStep("add hitobject", () => EditorBeatmap.Add(addedObject)); - moveMouseToObject(addedObject); + moveMouseToObject(() => addedObject); AddStep("left click", () => InputManager.Click(MouseButton.Left)); AddAssert("hitobject selected", () => EditorBeatmap.SelectedHitObjects.Single() == addedObject); @@ -127,7 +164,7 @@ namespace osu.Game.Tests.Visual.Editing AddStep("select added object", () => EditorBeatmap.SelectedHitObjects.Add(addedObject)); - moveMouseToObject(addedObject); + moveMouseToObject(() => addedObject); AddStep("hold shift", () => InputManager.PressKey(Key.ShiftLeft)); AddStep("right click", () => InputManager.Click(MouseButton.Right)); From a664efe12bc4e0fb38d71a43b25e59335cb8c7c1 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 13 Apr 2021 07:59:13 +0300 Subject: [PATCH 108/203] Fix history graph tooltips leaking to others Since there was no check about which tooltip content came from which graph, all history graphs use the "Replays Watched" tooltip, as it is the latest created one. --- .../Profile/Sections/Historical/UserHistoryGraph.cs | 7 ++++++- osu.Game/Overlays/Profile/UserGraph.cs | 3 ++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Profile/Sections/Historical/UserHistoryGraph.cs b/osu.Game/Overlays/Profile/Sections/Historical/UserHistoryGraph.cs index f9e5ccf618..52831b4243 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/UserHistoryGraph.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/UserHistoryGraph.cs @@ -32,6 +32,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical { return new TooltipDisplayContent { + Name = tooltipCounterName, Count = playCount.ToString("N0"), Date = date.ToString("MMMM yyyy") }; @@ -39,14 +40,17 @@ namespace osu.Game.Overlays.Profile.Sections.Historical protected class HistoryGraphTooltip : UserGraphTooltip { + private readonly string tooltipCounterName; + public HistoryGraphTooltip(string tooltipCounterName) : base(tooltipCounterName) { + this.tooltipCounterName = tooltipCounterName; } public override bool SetContent(object content) { - if (!(content is TooltipDisplayContent info)) + if (!(content is TooltipDisplayContent info) || info.Name != tooltipCounterName) return false; Counter.Text = info.Count; @@ -57,6 +61,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical private class TooltipDisplayContent { + public string Name; public string Count; public string Date; } diff --git a/osu.Game/Overlays/Profile/UserGraph.cs b/osu.Game/Overlays/Profile/UserGraph.cs index cdfd722d68..e2eb5d5180 100644 --- a/osu.Game/Overlays/Profile/UserGraph.cs +++ b/osu.Game/Overlays/Profile/UserGraph.cs @@ -208,6 +208,7 @@ namespace osu.Game.Overlays.Profile protected abstract class UserGraphTooltip : VisibilityContainer, ITooltip { + protected new readonly OsuSpriteText Name; protected readonly OsuSpriteText Counter, BottomText; private readonly Box background; @@ -237,7 +238,7 @@ namespace osu.Game.Overlays.Profile Spacing = new Vector2(3, 0), Children = new Drawable[] { - new OsuSpriteText + Name = new OsuSpriteText { Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold), Text = tooltipCounterName From 4852630c93564dd7af90ab18ea67c55b416e2655 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Apr 2021 14:03:42 +0900 Subject: [PATCH 109/203] Fix import multiple file types via drag potentially reaching the wrong importer --- osu.Game/OsuGameBase.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index ca132df552..e285faab11 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -433,12 +433,15 @@ namespace osu.Game if (paths.Length == 0) return; - var extension = Path.GetExtension(paths.First())?.ToLowerInvariant(); + var filesPerExtension = paths.GroupBy(p => Path.GetExtension(p).ToLowerInvariant()); - foreach (var importer in fileImporters) + foreach (var groups in filesPerExtension) { - if (importer.HandledExtensions.Contains(extension)) - await importer.Import(paths).ConfigureAwait(false); + foreach (var importer in fileImporters) + { + if (importer.HandledExtensions.Contains(groups.Key)) + await importer.Import(groups.ToArray()).ConfigureAwait(false); + } } } From d0f30b7b422afe385fd1fc0de7a28684bf0ab7ef Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 13 Apr 2021 14:29:47 +0900 Subject: [PATCH 110/203] Delay map completion one frame after the last judgment This is a workaround of a timing issue. KeyCounter is disabled while break time (`HasCompleted == true`). When the last keypress is exactly at the same time the map ends, the last frame was considered in a break time while forward play but considered not in a break time while rewinding. This inconsistency made the last keypress not decremented in the key counter when a replay is rewound. The situation regularly happens in osu!standard because the map ends right after the player hits the last hit circle. It was caught by `TestSceneGameplayRewinding`. This commit makes the update of the map completion delayed one frame. The problematic keypress frame is now processed strictly before the map completion, and the map completion status is correctly rewound before the keypress frame. --- osu.Game/Rulesets/Scoring/JudgementProcessor.cs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/osu.Game/Rulesets/Scoring/JudgementProcessor.cs b/osu.Game/Rulesets/Scoring/JudgementProcessor.cs index 8aef615b5f..201a05e569 100644 --- a/osu.Game/Rulesets/Scoring/JudgementProcessor.cs +++ b/osu.Game/Rulesets/Scoring/JudgementProcessor.cs @@ -28,6 +28,8 @@ namespace osu.Game.Rulesets.Scoring /// public int JudgedHits { get; private set; } + private JudgementResult lastAppliedResult; + private readonly BindableBool hasCompleted = new BindableBool(); /// @@ -53,12 +55,11 @@ namespace osu.Game.Rulesets.Scoring public void ApplyResult(JudgementResult result) { JudgedHits++; + lastAppliedResult = result; ApplyResultInternal(result); NewJudgement?.Invoke(result); - - updateHasCompleted(); } /// @@ -69,8 +70,6 @@ namespace osu.Game.Rulesets.Scoring { JudgedHits--; - updateHasCompleted(); - RevertResultInternal(result); } @@ -134,6 +133,10 @@ namespace osu.Game.Rulesets.Scoring } } - private void updateHasCompleted() => hasCompleted.Value = JudgedHits == MaxHits; + protected override void Update() + { + base.Update(); + hasCompleted.Value = JudgedHits == MaxHits && (JudgedHits == 0 || lastAppliedResult.TimeAbsolute < Clock.CurrentTime); + } } } From 273099d53c6b0dfab4a694055fc12bf45d5611f2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Apr 2021 14:31:41 +0900 Subject: [PATCH 111/203] Don't store online IDs from score submission responses for now Closes remaining portion of https://github.com/ppy/osu/issues/12372. --- osu.Game/Screens/Play/SubmittingPlayer.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/SubmittingPlayer.cs b/osu.Game/Screens/Play/SubmittingPlayer.cs index d22199447d..b64d835c58 100644 --- a/osu.Game/Screens/Play/SubmittingPlayer.cs +++ b/osu.Game/Screens/Play/SubmittingPlayer.cs @@ -109,7 +109,12 @@ namespace osu.Game.Screens.Play request.Success += s => { - score.ScoreInfo.OnlineScoreID = s.ID; + // For the time being, online ID responses are not really useful for anything. + // In addition, the IDs provided via new (lazer) endpoints are based on a different autoincrement from legacy (stable) scores. + // + // Until we better define the server-side logic behind this, let's not store the online ID to avoid potential unique constraint + // conflicts across various systems (ie. solo and multiplayer). + // score.ScoreInfo.OnlineScoreID = s.ID; tcs.SetResult(true); }; From 4837cef095558c2ed301881471f3eaf76d6eb209 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Apr 2021 14:44:52 +0900 Subject: [PATCH 112/203] Use static for playfield centre positioning --- .../Checks/CheckOffscreenObjectsTest.cs | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckOffscreenObjectsTest.cs b/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckOffscreenObjectsTest.cs index 33b839650f..4a5e4e18c0 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckOffscreenObjectsTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckOffscreenObjectsTest.cs @@ -9,6 +9,7 @@ using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu.Edit.Checks; using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.UI; using osuTK; namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks @@ -16,6 +17,8 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks [TestFixture] public class CheckOffscreenObjectsTest { + private static readonly Vector2 playfield_centre = OsuPlayfield.BASE_SIZE * 0.5f; + private CheckOffscreenObjects check; [SetUp] @@ -34,7 +37,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks new HitCircle { StartTime = 3000, - Position = new Vector2(320, 240) // Playfield is 640 x 480. + Position = playfield_centre // Playfield is 640 x 480. } } }; @@ -136,11 +139,11 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks new Slider { StartTime = 3000, - Position = new Vector2(320, 240), + Position = playfield_centre, Path = new SliderPath(new[] { new PathControlPoint(new Vector2(0, 0), PathType.Linear), - new PathControlPoint(new Vector2(0, -235)) + new PathControlPoint(new Vector2(0, -playfield_centre.Y + 5)) }), } } @@ -161,11 +164,11 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks new Slider { StartTime = 3000, - Position = new Vector2(320, 240), + Position = playfield_centre, Path = new SliderPath(new[] { new PathControlPoint(new Vector2(0, 0), PathType.Linear), - new PathControlPoint(new Vector2(0, -235)) + new PathControlPoint(new Vector2(0, -playfield_centre.Y + 5)) }), StackHeight = 5 } @@ -189,7 +192,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks Path = new SliderPath(new[] { new PathControlPoint(new Vector2(0, 0), PathType.Linear), - new PathControlPoint(new Vector2(320, 240)) + new PathControlPoint(playfield_centre) }), } } @@ -208,11 +211,11 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks new Slider { StartTime = 3000, - Position = new Vector2(320, 240), + Position = playfield_centre, Path = new SliderPath(new[] { new PathControlPoint(new Vector2(0, 0), PathType.Linear), - new PathControlPoint(new Vector2(-320, -240)) + new PathControlPoint(-playfield_centre) }), } } @@ -231,7 +234,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks new Slider { StartTime = 3000, - Position = new Vector2(320, 240), + Position = playfield_centre, Path = new SliderPath(new[] { // Circular arc shoots over the top of the screen. From b45d7de4eccbd9aecbf621afefc6661ca986fa9e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Apr 2021 15:04:01 +0900 Subject: [PATCH 113/203] Update asserts to use better nunit specifications --- .../Checks/CheckOffscreenObjectsTest.cs | 29 ++++++------------- .../Editing/Checks/CheckBackgroundTest.cs | 16 ++++------ 2 files changed, 14 insertions(+), 31 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckOffscreenObjectsTest.cs b/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckOffscreenObjectsTest.cs index 4a5e4e18c0..9f9ba0e8db 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckOffscreenObjectsTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckOffscreenObjectsTest.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Linq; using NUnit.Framework; using osu.Game.Beatmaps; +using osu.Game.Rulesets.Edit.Checks.Components; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu.Edit.Checks; @@ -42,9 +43,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks } }; - var issues = check.Run(beatmap); - - Assert.That(!issues.Any()); + Assert.That(check.Run(beatmap), Is.Empty); } [Test] @@ -62,9 +61,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks } }; - var issues = check.Run(beatmap); - - Assert.That(!issues.Any()); + Assert.That(check.Run(beatmap), Is.Empty); } [Test] @@ -124,9 +121,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks } }; - var issues = check.Run(beatmap); - - Assert.That(!issues.Any()); + Assert.That(check.Run(beatmap), Is.Empty); } [Test] @@ -149,9 +144,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks } }; - var issues = check.Run(beatmap); - - Assert.That(!issues.Any()); + Assert.That(check.Run(beatmap), Is.Empty); } [Test] @@ -252,21 +245,17 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks private void assertOffscreenCircle(IBeatmap beatmap) { var issues = check.Run(beatmap).ToList(); - var issue = issues.FirstOrDefault(); - Assert.That(issues.Count == 1); - Assert.That(issue != null); - Assert.That(issue.Template is CheckOffscreenObjects.IssueTemplateOffscreenCircle); + Assert.That(issues, Has.Count.EqualTo(1)); + Assert.That(issues.Single().Template is CheckOffscreenObjects.IssueTemplateOffscreenCircle); } private void assertOffscreenSlider(IBeatmap beatmap) { var issues = check.Run(beatmap).ToList(); - var issue = issues.FirstOrDefault(); - Assert.That(issues.Count == 1); - Assert.That(issue != null); - Assert.That(issue.Template is CheckOffscreenObjects.IssueTemplateOffscreenSlider); + Assert.That(issues, Has.Count.EqualTo(1)); + Assert.That(issues.Single().Template is CheckOffscreenObjects.IssueTemplateOffscreenSlider); } } } diff --git a/osu.Game.Tests/Editing/Checks/CheckBackgroundTest.cs b/osu.Game.Tests/Editing/Checks/CheckBackgroundTest.cs index 327abcab6c..635e3bb0f3 100644 --- a/osu.Game.Tests/Editing/Checks/CheckBackgroundTest.cs +++ b/osu.Game.Tests/Editing/Checks/CheckBackgroundTest.cs @@ -39,9 +39,7 @@ namespace osu.Game.Tests.Editing.Checks [Test] public void TestBackgroundSetAndInFiles() { - var issues = check.Run(beatmap); - - Assert.That(!issues.Any()); + Assert.That(check.Run(beatmap), Is.Empty); } [Test] @@ -50,11 +48,9 @@ namespace osu.Game.Tests.Editing.Checks beatmap.BeatmapInfo.BeatmapSet.Files.Clear(); var issues = check.Run(beatmap).ToList(); - var issue = issues.FirstOrDefault(); - Assert.That(issues.Count == 1); - Assert.That(issue != null); - Assert.That(issue.Template is CheckBackground.IssueTemplateDoesNotExist); + Assert.That(issues, Has.Count.EqualTo(1)); + Assert.That(issues.Single().Template is CheckBackground.IssueTemplateDoesNotExist); } [Test] @@ -63,11 +59,9 @@ namespace osu.Game.Tests.Editing.Checks beatmap.Metadata.BackgroundFile = null; var issues = check.Run(beatmap).ToList(); - var issue = issues.FirstOrDefault(); - Assert.That(issues.Count == 1); - Assert.That(issue != null); - Assert.That(issue.Template is CheckBackground.IssueTemplateNoneSet); + Assert.That(issues, Has.Count.EqualTo(1)); + Assert.That(issues.Single().Template is CheckBackground.IssueTemplateNoneSet); } } } From 9f8af03a70633d32fa11842c69ea73b1ccf72f11 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 13 Apr 2021 09:28:58 +0300 Subject: [PATCH 114/203] Remove irrelevant change --- osu.Game/Overlays/Profile/UserGraph.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Profile/UserGraph.cs b/osu.Game/Overlays/Profile/UserGraph.cs index e2eb5d5180..cdfd722d68 100644 --- a/osu.Game/Overlays/Profile/UserGraph.cs +++ b/osu.Game/Overlays/Profile/UserGraph.cs @@ -208,7 +208,6 @@ namespace osu.Game.Overlays.Profile protected abstract class UserGraphTooltip : VisibilityContainer, ITooltip { - protected new readonly OsuSpriteText Name; protected readonly OsuSpriteText Counter, BottomText; private readonly Box background; @@ -238,7 +237,7 @@ namespace osu.Game.Overlays.Profile Spacing = new Vector2(3, 0), Children = new Drawable[] { - Name = new OsuSpriteText + new OsuSpriteText { Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold), Text = tooltipCounterName From fbc6fb8fc55ad431173ad61072a84e26629469e7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Apr 2021 15:35:57 +0900 Subject: [PATCH 115/203] Split out common logic into private method and add inline comment for future visitors --- .../Sliders/Components/PathControlPointPiece.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs index 7686043c43..ce9580d0f4 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs @@ -58,11 +58,13 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components { this.slider = slider; ControlPoint = controlPoint; - PointsInSegment = slider.Path.PointsInSegment(ControlPoint); + + // we don't want to run the path type update on construction as it may inadvertently change the slider. + cachePoints(slider); slider.Path.Version.BindValueChanged(_ => { - PointsInSegment = slider.Path.PointsInSegment(ControlPoint); + cachePoints(slider); updatePathType(); }); @@ -206,6 +208,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components protected override void OnDragEnd(DragEndEvent e) => changeHandler?.EndChange(); + private void cachePoints(Slider slider) => PointsInSegment = slider.Path.PointsInSegment(ControlPoint); + /// /// Handles correction of invalid path types. /// From 57ba7b7cbbbd93870978d047b6edb957cd49d8cf Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 13 Apr 2021 15:55:23 +0900 Subject: [PATCH 116/203] Partially revert the changes of `CurrentFrame` and `NextFrame` for compatibility Making those always non-null is postponed as when a replay's frame contains keypress the behavior is changed. Previously, the key is pressed at the time of the first frame. But using non-null frames means the key is pressed at negative infinity. However, I think the new way of always using non-null frames makes the client code so I plan to bundle the change to more breaking changes. --- .../NonVisual/FramedReplayInputHandlerTest.cs | 22 +++++++++---------- .../Replays/FramedReplayInputHandler.cs | 9 ++++---- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs b/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs index 954871595e..a42b7d54ee 100644 --- a/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs +++ b/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs @@ -84,11 +84,11 @@ namespace osu.Game.Tests.NonVisual // exited important section setTime(8200, 8000); confirmCurrentFrame(7); - confirmNextFrame(7); + confirmNextFrame(null); setTime(8200, 8200); confirmCurrentFrame(7); - confirmNextFrame(7); + confirmNextFrame(null); } [Test] @@ -97,11 +97,11 @@ namespace osu.Game.Tests.NonVisual setReplayFrames(); setTime(-1000, -1000); - confirmCurrentFrame(0); + confirmCurrentFrame(null); confirmNextFrame(0); setTime(-500, -500); - confirmCurrentFrame(0); + confirmCurrentFrame(null); confirmNextFrame(0); setTime(0, 0); @@ -145,7 +145,7 @@ namespace osu.Game.Tests.NonVisual confirmNextFrame(1); setTime(-500, -500); - confirmCurrentFrame(0); + confirmCurrentFrame(null); confirmNextFrame(0); } @@ -231,7 +231,7 @@ namespace osu.Game.Tests.NonVisual Assert.IsFalse(handler.WaitingForFrame, "Should not be waiting yet"); setTime(1000, 1000); confirmCurrentFrame(1); - confirmNextFrame(1); + confirmNextFrame(null); Assert.IsTrue(handler.WaitingForFrame, "Should be waiting"); // cannot seek beyond the last frame @@ -243,7 +243,7 @@ namespace osu.Game.Tests.NonVisual // can seek to the point before the first frame, however setTime(-100, -100); - confirmCurrentFrame(0); + confirmCurrentFrame(null); confirmNextFrame(0); fastForwardToPoint(1000); @@ -311,14 +311,14 @@ namespace osu.Game.Tests.NonVisual Assert.AreEqual(expect, handler.SetFrameFromTime(set), "Unexpected return value"); } - private void confirmCurrentFrame(int frame) + private void confirmCurrentFrame(int? frame) { - Assert.AreEqual(replay.Frames[frame].Time, handler.CurrentFrame.Time, "Unexpected current frame"); + Assert.AreEqual(frame is int x ? replay.Frames[x].Time : (double?)null, handler.CurrentFrame?.Time, "Unexpected current frame"); } - private void confirmNextFrame(int frame) + private void confirmNextFrame(int? frame) { - Assert.AreEqual(replay.Frames[frame].Time, handler.NextFrame.Time, "Unexpected next frame"); + Assert.AreEqual(frame is int x ? replay.Frames[x].Time : (double?)null, handler.NextFrame?.Time, "Unexpected next frame"); } private class TestReplayFrame : ReplayFrame diff --git a/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs b/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs index c3cd957f0d..a7f11b1e6f 100644 --- a/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs +++ b/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs @@ -30,6 +30,7 @@ namespace osu.Game.Rulesets.Replays /// The current frame of the replay. /// The current time is always between the start and the end time of the current frame. /// + /// Returns null if the current time is strictly before the first frame. /// The replay is empty. public TFrame CurrentFrame { @@ -38,15 +39,15 @@ namespace osu.Game.Rulesets.Replays if (!HasFrames) throw new InvalidOperationException($"Attempted to get {nameof(CurrentFrame)} of an empty replay"); - return (TFrame)Frames[Math.Max(0, currentFrameIndex)]; + return currentFrameIndex == -1 ? null : (TFrame)Frames[currentFrameIndex]; } } /// /// The next frame of the replay. /// The start time is always greater or equal to the start time of regardless of the seeking direction. - /// If it is before the first frame of the replay or the after the last frame of the replay, and agree. /// + /// Returns null if the current frame is the last frame. /// The replay is empty. public TFrame NextFrame { @@ -55,7 +56,7 @@ namespace osu.Game.Rulesets.Replays if (!HasFrames) throw new InvalidOperationException($"Attempted to get {nameof(NextFrame)} of an empty replay"); - return (TFrame)Frames[Math.Min(currentFrameIndex + 1, Frames.Count - 1)]; + return currentFrameIndex == Frames.Count - 1 ? null : (TFrame)Frames[currentFrameIndex + 1]; } } @@ -96,7 +97,7 @@ namespace osu.Game.Rulesets.Replays { get { - if (!HasFrames || !FrameAccuratePlayback) + if (!HasFrames || !FrameAccuratePlayback || CurrentFrame == null) return false; return IsImportant(CurrentFrame) && // a button is in a pressed state From a9652b7b259b2921648c220e1d7f2ccf98276afa Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Apr 2021 16:05:12 +0900 Subject: [PATCH 117/203] Start TimelineTestScene in a more visible place --- osu.Game.Tests/Visual/Editing/TimelineTestScene.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/osu.Game.Tests/Visual/Editing/TimelineTestScene.cs b/osu.Game.Tests/Visual/Editing/TimelineTestScene.cs index 1da6433707..88b4614791 100644 --- a/osu.Game.Tests/Visual/Editing/TimelineTestScene.cs +++ b/osu.Game.Tests/Visual/Editing/TimelineTestScene.cs @@ -64,6 +64,13 @@ namespace osu.Game.Tests.Visual.Editing }); } + protected override void LoadComplete() + { + base.LoadComplete(); + + Clock.Seek(2500); + } + public abstract Drawable CreateTestComponent(); private class AudioVisualiser : CompositeDrawable From ebf97ff48ff43a5879b19fcb9365c84c8369fe21 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Apr 2021 16:25:43 +0900 Subject: [PATCH 118/203] Update timeline ticks to use width as a differentiation method, rather than height --- .../Visualisations/PointVisualisation.cs | 10 ++-- .../Timeline/TimelineTickDisplay.cs | 56 +++++++++++-------- 2 files changed, 39 insertions(+), 27 deletions(-) diff --git a/osu.Game/Screens/Edit/Components/Timelines/Summary/Visualisations/PointVisualisation.cs b/osu.Game/Screens/Edit/Components/Timelines/Summary/Visualisations/PointVisualisation.cs index b0ecffdd24..b5c4cd6dda 100644 --- a/osu.Game/Screens/Edit/Components/Timelines/Summary/Visualisations/PointVisualisation.cs +++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/Visualisations/PointVisualisation.cs @@ -3,7 +3,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Shapes; -using osuTK; namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Visualisations { @@ -12,7 +11,7 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Visualisations /// public class PointVisualisation : Box { - public const float WIDTH = 1; + public const float MAX_WIDTH = 5; public PointVisualisation(double startTime) : this() @@ -27,8 +26,11 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Visualisations RelativePositionAxes = Axes.X; RelativeSizeAxes = Axes.Y; - Width = WIDTH; - EdgeSmoothness = new Vector2(WIDTH, 0); + Anchor = Anchor.CentreLeft; + Origin = Anchor.Centre; + + Width = MAX_WIDTH; + Height = 0.75f; } } } diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineTickDisplay.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineTickDisplay.cs index c070c833f8..7ec5bb7197 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineTickDisplay.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineTickDisplay.cs @@ -6,9 +6,7 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Caching; -using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; -using osu.Framework.Graphics.Colour; using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Screens.Edit.Components.Timelines.Summary.Parts; @@ -33,6 +31,8 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline [Resolved] private OsuColour colours { get; set; } + private static readonly int highest_divisor = BindableBeatDivisor.VALID_DIVISORS.Last(); + public TimelineTickDisplay() { RelativeSizeAxes = Axes.Both; @@ -80,8 +80,8 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline if (timeline != null) { var newRange = ( - (ToLocalSpace(timeline.ScreenSpaceDrawQuad.TopLeft).X - PointVisualisation.WIDTH * 2) / DrawWidth * Content.RelativeChildSize.X, - (ToLocalSpace(timeline.ScreenSpaceDrawQuad.TopRight).X + PointVisualisation.WIDTH * 2) / DrawWidth * Content.RelativeChildSize.X); + (ToLocalSpace(timeline.ScreenSpaceDrawQuad.TopLeft).X - PointVisualisation.MAX_WIDTH * 2) / DrawWidth * Content.RelativeChildSize.X, + (ToLocalSpace(timeline.ScreenSpaceDrawQuad.TopRight).X + PointVisualisation.MAX_WIDTH * 2) / DrawWidth * Content.RelativeChildSize.X); if (visibleRange != newRange) { @@ -100,7 +100,6 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline private void createTicks() { int drawableIndex = 0; - int highestDivisor = BindableBeatDivisor.VALID_DIVISORS.Last(); nextMinTick = null; nextMaxTick = null; @@ -131,25 +130,12 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline var divisor = BindableBeatDivisor.GetDivisorForBeatIndex(beat, beatDivisor.Value); var colour = BindableBeatDivisor.GetColourFor(divisor, colours); - bool isMainBeat = indexInBar == 0; - // even though "bar lines" take up the full vertical space, we render them in two pieces because it allows for less anchor/origin churn. - float height = isMainBeat ? 0.5f : 0.4f - (float)divisor / highestDivisor * 0.2f; - float gradientOpacity = isMainBeat ? 1 : 0; - var topPoint = getNextUsablePoint(); - topPoint.X = xPos; - topPoint.Height = height; - topPoint.Colour = ColourInfo.GradientVertical(colour, colour.Opacity(gradientOpacity)); - topPoint.Anchor = Anchor.TopLeft; - topPoint.Origin = Anchor.TopCentre; - - var bottomPoint = getNextUsablePoint(); - bottomPoint.X = xPos; - bottomPoint.Anchor = Anchor.BottomLeft; - bottomPoint.Colour = ColourInfo.GradientVertical(colour.Opacity(gradientOpacity), colour); - bottomPoint.Origin = Anchor.BottomCentre; - bottomPoint.Height = height; + var line = getNextUsableLine(); + line.X = xPos; + line.Width = PointVisualisation.MAX_WIDTH * getWidth(indexInBar, divisor); + line.Colour = colour; } beat++; @@ -168,7 +154,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline tickCache.Validate(); - Drawable getNextUsablePoint() + Drawable getNextUsableLine() { PointVisualisation point; if (drawableIndex >= Count) @@ -183,6 +169,30 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline } } + private static float getWidth(int indexInBar, int divisor) + { + if (indexInBar == 0) + return 1; + + switch (divisor) + { + case 1: + case 2: + return 0.6f; + + case 3: + case 4: + return 0.5f; + + case 6: + case 8: + return 0.4f; + + default: + return 0.3f; + } + } + protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); From 27e851c2eed2b0edc1833a620e7ec4e9e85973f0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Apr 2021 16:41:29 +0900 Subject: [PATCH 119/203] Also adjust height --- .../Visualisations/PointVisualisation.cs | 2 +- .../Timeline/TimelineTickDisplay.cs | 25 +++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Components/Timelines/Summary/Visualisations/PointVisualisation.cs b/osu.Game/Screens/Edit/Components/Timelines/Summary/Visualisations/PointVisualisation.cs index b5c4cd6dda..53a1f94731 100644 --- a/osu.Game/Screens/Edit/Components/Timelines/Summary/Visualisations/PointVisualisation.cs +++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/Visualisations/PointVisualisation.cs @@ -11,7 +11,7 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Visualisations /// public class PointVisualisation : Box { - public const float MAX_WIDTH = 5; + public const float MAX_WIDTH = 4; public PointVisualisation(double startTime) : this() diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineTickDisplay.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineTickDisplay.cs index 7ec5bb7197..3aaf0451c8 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineTickDisplay.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineTickDisplay.cs @@ -135,6 +135,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline var line = getNextUsableLine(); line.X = xPos; line.Width = PointVisualisation.MAX_WIDTH * getWidth(indexInBar, divisor); + line.Height = 0.9f * getHeight(indexInBar, divisor); line.Colour = colour; } @@ -193,6 +194,30 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline } } + private static float getHeight(int indexInBar, int divisor) + { + if (indexInBar == 0) + return 1; + + switch (divisor) + { + case 1: + case 2: + return 0.9f; + + case 3: + case 4: + return 0.8f; + + case 6: + case 8: + return 0.7f; + + default: + return 0.6f; + } + } + protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); From 5a06db8a113dfd012a46b304f9967508f0ca609e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Apr 2021 16:48:05 +0900 Subject: [PATCH 120/203] Change default editor waveform opacity to 25% The previous setting felt way too high. --- osu.Game/Configuration/OsuConfigManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index 387cfbb193..0525e84077 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -143,7 +143,7 @@ namespace osu.Game.Configuration SetDefault(OsuSetting.DiscordRichPresence, DiscordRichPresenceMode.Full); - SetDefault(OsuSetting.EditorWaveformOpacity, 1f); + SetDefault(OsuSetting.EditorWaveformOpacity, 0.25f); } public OsuConfigManager(Storage storage) From 0932daeaa8564fe9fd9e25560abe1bfa5a8b20b3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Apr 2021 16:50:03 +0900 Subject: [PATCH 121/203] Force the new default on update --- osu.Game/Configuration/OsuConfigManager.cs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index 0525e84077..21a1a1d430 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -169,14 +169,9 @@ namespace osu.Game.Configuration int combined = (year * 10000) + monthDay; - if (combined < 20200305) + if (combined < 20210413) { - // the maximum value of this setting was changed. - // if we don't manually increase this, it causes song select to filter out beatmaps the user expects to see. - var maxStars = (BindableDouble)GetOriginalBindable(OsuSetting.DisplayStarsMaximum); - - if (maxStars.Value == 10) - maxStars.Value = maxStars.MaxValue; + SetValue(OsuSetting.EditorWaveformOpacity, 0.25f); } } From 36510309d10105ddcdbea2b45f36e941f6be5415 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 13 Apr 2021 09:24:35 +0300 Subject: [PATCH 122/203] Merge `EnableUserDim` and `IgnoreUserSettings` to one bindable --- .../Visual/Background/TestSceneUserDimBackgrounds.cs | 12 ++++++------ osu.Game/Graphics/Containers/UserDimContainer.cs | 8 +------- osu.Game/Rulesets/Mods/ModCinema.cs | 3 +-- .../Screens/Backgrounds/BackgroundScreenBeatmap.cs | 8 ++++---- osu.Game/Screens/Edit/Editor.cs | 2 +- osu.Game/Screens/Play/Player.cs | 4 ++-- osu.Game/Screens/Play/PlayerLoader.cs | 6 +++--- 7 files changed, 18 insertions(+), 25 deletions(-) diff --git a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs index ba4d12b19f..a4c28651df 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs @@ -142,9 +142,9 @@ namespace osu.Game.Tests.Visual.Background { performFullSetup(); AddUntilStep("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied()); - AddStep("Enable user dim", () => songSelect.DimEnabled.Value = false); + AddStep("Disable user dim", () => songSelect.IgnoreUserSettings.Value = true); AddUntilStep("Screen is undimmed and user blur removed", () => songSelect.IsBackgroundUndimmed() && songSelect.IsUserBlurDisabled()); - AddStep("Disable user dim", () => songSelect.DimEnabled.Value = true); + AddStep("Enable user dim", () => songSelect.IgnoreUserSettings.Value = false); AddUntilStep("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied()); } @@ -161,10 +161,10 @@ namespace osu.Game.Tests.Visual.Background player.ReplacesBackground.Value = true; player.StoryboardEnabled.Value = true; }); - AddStep("Enable user dim", () => player.DimmableStoryboard.EnableUserDim.Value = true); + AddStep("Enable user dim", () => player.DimmableStoryboard.IgnoreUserSettings.Value = false); AddStep("Set dim level to 1", () => songSelect.DimLevel.Value = 1f); AddUntilStep("Storyboard is invisible", () => !player.IsStoryboardVisible); - AddStep("Disable user dim", () => player.DimmableStoryboard.EnableUserDim.Value = false); + AddStep("Disable user dim", () => player.DimmableStoryboard.IgnoreUserSettings.Value = true); AddUntilStep("Storyboard is visible", () => player.IsStoryboardVisible); } @@ -281,11 +281,11 @@ namespace osu.Game.Tests.Visual.Background protected override BackgroundScreen CreateBackground() { background = new FadeAccessibleBackground(Beatmap.Value); - DimEnabled.BindTo(background.EnableUserDim); + IgnoreUserSettings.BindTo(background.IgnoreUserSettings); return background; } - public readonly Bindable DimEnabled = new Bindable(); + public readonly Bindable IgnoreUserSettings = new Bindable(); public readonly Bindable DimLevel = new BindableDouble(); public readonly Bindable BlurLevel = new BindableDouble(); diff --git a/osu.Game/Graphics/Containers/UserDimContainer.cs b/osu.Game/Graphics/Containers/UserDimContainer.cs index 39c1fdad52..4e555ac1eb 100644 --- a/osu.Game/Graphics/Containers/UserDimContainer.cs +++ b/osu.Game/Graphics/Containers/UserDimContainer.cs @@ -23,11 +23,6 @@ namespace osu.Game.Graphics.Containers protected const double BACKGROUND_FADE_DURATION = 800; - /// - /// Whether or not user-configured dim levels should be applied to the container. - /// - public readonly Bindable EnableUserDim = new Bindable(true); - /// /// Whether or not user-configured settings relating to brightness of elements should be ignored /// @@ -57,7 +52,7 @@ namespace osu.Game.Graphics.Containers private float breakLightening => LightenDuringBreaks.Value && IsBreakTime.Value ? BREAK_LIGHTEN_AMOUNT : 0; - protected float DimLevel => Math.Max(EnableUserDim.Value && !IgnoreUserSettings.Value ? (float)UserDimLevel.Value - breakLightening : 0, 0); + protected float DimLevel => Math.Max(!IgnoreUserSettings.Value ? (float)UserDimLevel.Value - breakLightening : 0, 0); protected override Container Content => dimContent; @@ -78,7 +73,6 @@ namespace osu.Game.Graphics.Containers LightenDuringBreaks = config.GetBindable(OsuSetting.LightenDuringBreaks); ShowStoryboard = config.GetBindable(OsuSetting.ShowStoryboard); - EnableUserDim.ValueChanged += _ => UpdateVisuals(); UserDimLevel.ValueChanged += _ => UpdateVisuals(); LightenDuringBreaks.ValueChanged += _ => UpdateVisuals(); IsBreakTime.ValueChanged += _ => UpdateVisuals(); diff --git a/osu.Game/Rulesets/Mods/ModCinema.cs b/osu.Game/Rulesets/Mods/ModCinema.cs index eb0473016a..c78088ba2d 100644 --- a/osu.Game/Rulesets/Mods/ModCinema.cs +++ b/osu.Game/Rulesets/Mods/ModCinema.cs @@ -37,8 +37,7 @@ namespace osu.Game.Rulesets.Mods public void ApplyToPlayer(Player player) { - player.ApplyToBackground(b => b.EnableUserDim.Value = false); - + player.ApplyToBackground(b => b.IgnoreUserSettings.Value = true); player.DimmableStoryboard.IgnoreUserSettings.Value = true; player.BreakOverlay.Hide(); diff --git a/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs b/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs index b08455be95..d27211144e 100644 --- a/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs +++ b/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs @@ -27,9 +27,9 @@ namespace osu.Game.Screens.Backgrounds private WorkingBeatmap beatmap; /// - /// Whether or not user dim settings should be applied to this Background. + /// Whether or not user-configured settings relating to brightness of elements should be ignored /// - public readonly Bindable EnableUserDim = new Bindable(); + public readonly Bindable IgnoreUserSettings = new Bindable(); public readonly Bindable StoryboardReplacesBackground = new Bindable(); @@ -50,7 +50,7 @@ namespace osu.Game.Screens.Backgrounds InternalChild = dimmable = CreateFadeContainer(); - dimmable.EnableUserDim.BindTo(EnableUserDim); + dimmable.IgnoreUserSettings.BindTo(IgnoreUserSettings); dimmable.IsBreakTime.BindTo(IsBreakTime); dimmable.BlurAmount.BindTo(BlurAmount); @@ -148,7 +148,7 @@ namespace osu.Game.Screens.Backgrounds /// /// As an optimisation, we add the two blur portions to be applied rather than actually applying two separate blurs. /// - private Vector2 blurTarget => EnableUserDim.Value + private Vector2 blurTarget => !IgnoreUserSettings.Value ? new Vector2(BlurAmount.Value + (float)userBlurLevel.Value * USER_BLUR_FACTOR) : new Vector2(BlurAmount.Value); diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 0759e21382..64350fb56e 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -462,7 +462,7 @@ namespace osu.Game.Screens.Edit // todo: temporary. we want to be applying dim using the UserDimContainer eventually. b.FadeColour(Color4.DarkGray, 500); - b.EnableUserDim.Value = false; + b.IgnoreUserSettings.Value = true; b.BlurAmount.Value = 0; }); diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index efe5d26409..dd3f58439b 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -764,7 +764,7 @@ namespace osu.Game.Screens.Play ApplyToBackground(b => { - b.EnableUserDim.Value = true; + b.IgnoreUserSettings.Value = false; b.BlurAmount.Value = 0; // bind component bindables. @@ -913,7 +913,7 @@ namespace osu.Game.Screens.Play float fadeOutDuration = instant ? 0 : 250; this.FadeOut(fadeOutDuration); - ApplyToBackground(b => b.EnableUserDim.Value = false); + ApplyToBackground(b => b.IgnoreUserSettings.Value = true); storyboardReplacesBackground.Value = false; } diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 679b3c7313..cf15104809 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -229,7 +229,7 @@ namespace osu.Game.Screens.Play content.ScaleTo(0.7f, 150, Easing.InQuint); this.FadeOut(150); - ApplyToBackground(b => b.EnableUserDim.Value = false); + ApplyToBackground(b => b.IgnoreUserSettings.Value = true); BackgroundBrightnessReduction = false; Beatmap.Value.Track.RemoveAdjustment(AdjustableProperty.Volume, volumeAdjustment); @@ -277,7 +277,7 @@ namespace osu.Game.Screens.Play // Preview user-defined background dim and blur when hovered on the visual settings panel. ApplyToBackground(b => { - b.EnableUserDim.Value = true; + b.IgnoreUserSettings.Value = false; b.BlurAmount.Value = 0; }); @@ -288,7 +288,7 @@ namespace osu.Game.Screens.Play ApplyToBackground(b => { // Returns background dim and blur to the values specified by PlayerLoader. - b.EnableUserDim.Value = false; + b.IgnoreUserSettings.Value = true; b.BlurAmount.Value = BACKGROUND_BLUR; }); From 98c25b2e71c1aef8ad2973c0cb7ce96b33faff42 Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Tue, 13 Apr 2021 10:33:08 +0200 Subject: [PATCH 123/203] Remove unused import --- .../Editor/Checks/CheckOffscreenObjectsTest.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckOffscreenObjectsTest.cs b/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckOffscreenObjectsTest.cs index 9f9ba0e8db..f9445a9a96 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckOffscreenObjectsTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckOffscreenObjectsTest.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using System.Linq; using NUnit.Framework; using osu.Game.Beatmaps; -using osu.Game.Rulesets.Edit.Checks.Components; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu.Edit.Checks; From c8cb4286f6e61eeba14b4b0256d5a17d781f6bd9 Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Tue, 13 Apr 2021 10:35:06 +0200 Subject: [PATCH 124/203] Add reference for screen bounding box numbers --- osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs b/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs index c241db7e06..27cae2ecc1 100644 --- a/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs +++ b/osu.Game.Rulesets.Osu/Edit/Checks/CheckOffscreenObjects.cs @@ -11,8 +11,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Checks { public class CheckOffscreenObjects : ICheck { - // These are close approximates to the edges of the screen - // in gameplay on a 4:3 aspect ratio for osu!stable. + // A close approximation for the bounding box of the screen in gameplay on 4:3 aspect ratio. + // Uses gameplay space coordinates (512 x 384 playfield / 640 x 480 screen area). + // See https://github.com/ppy/osu/pull/12361#discussion_r612199777 for reference. private const int min_x = -67; private const int min_y = -60; private const int max_x = 579; From b41e3a2e7a84046981007ebc6a6d2ecd85d1b42b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Apr 2021 17:38:13 +0900 Subject: [PATCH 125/203] Remove unused using statement --- osu.Game/Configuration/OsuConfigManager.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index 21a1a1d430..f9b1c9618b 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -3,7 +3,6 @@ using System; using System.Diagnostics; -using osu.Framework.Bindables; using osu.Framework.Configuration; using osu.Framework.Configuration.Tracking; using osu.Framework.Extensions; From 60c2494b316827c691327a55793750871753a190 Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Tue, 13 Apr 2021 10:40:56 +0200 Subject: [PATCH 126/203] Make `BeatmapVerifier` an interface --- .../Edit/OsuBeatmapVerifier.cs | 12 ++---------- osu.Game.Rulesets.Osu/OsuRuleset.cs | 2 +- osu.Game/Rulesets/Edit/BeatmapVerifier.cs | 18 ++---------------- osu.Game/Rulesets/Ruleset.cs | 2 +- osu.Game/Screens/Edit/Verify/VerifyScreen.cs | 2 +- 5 files changed, 7 insertions(+), 29 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuBeatmapVerifier.cs b/osu.Game.Rulesets.Osu/Edit/OsuBeatmapVerifier.cs index 2faa239720..1c7ab00bbb 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuBeatmapVerifier.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuBeatmapVerifier.cs @@ -10,21 +10,13 @@ using osu.Game.Rulesets.Osu.Edit.Checks; namespace osu.Game.Rulesets.Osu.Edit { - public class OsuBeatmapVerifier : BeatmapVerifier + public class OsuBeatmapVerifier : IBeatmapVerifier { private readonly List checks = new List { new CheckOffscreenObjects() }; - public override IEnumerable Run(IBeatmap beatmap) - { - // Also run mode-invariant checks. - foreach (var issue in base.Run(beatmap)) - yield return issue; - - foreach (var issue in checks.SelectMany(check => check.Run(beatmap))) - yield return issue; - } + public IEnumerable Run(IBeatmap beatmap) => checks.SelectMany(check => check.Run(beatmap)); } } diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 63da100a04..d6375fa6e3 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -206,7 +206,7 @@ namespace osu.Game.Rulesets.Osu public override HitObjectComposer CreateHitObjectComposer() => new OsuHitObjectComposer(this); - public override BeatmapVerifier CreateBeatmapVerifier() => new OsuBeatmapVerifier(); + public override IBeatmapVerifier CreateBeatmapVerifier() => new OsuBeatmapVerifier(); public override string Description => "osu!"; diff --git a/osu.Game/Rulesets/Edit/BeatmapVerifier.cs b/osu.Game/Rulesets/Edit/BeatmapVerifier.cs index 1d0508705a..2bafacefa3 100644 --- a/osu.Game/Rulesets/Edit/BeatmapVerifier.cs +++ b/osu.Game/Rulesets/Edit/BeatmapVerifier.cs @@ -2,27 +2,13 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; -using System.Linq; using osu.Game.Beatmaps; -using osu.Game.Rulesets.Edit.Checks; using osu.Game.Rulesets.Edit.Checks.Components; namespace osu.Game.Rulesets.Edit { - public abstract class BeatmapVerifier + public interface IBeatmapVerifier { - /// - /// Checks which are performed regardless of ruleset. - /// These handle things like beatmap metadata, timing, and other ruleset agnostic elements. - /// - private readonly IReadOnlyList generalChecks = new List - { - new CheckBackground() - }; - - public virtual IEnumerable Run(IBeatmap beatmap) - { - return generalChecks.SelectMany(check => check.Run(beatmap)); - } + public IEnumerable Run(IBeatmap beatmap); } } diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index 2a29d88c89..b501c55aef 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -202,7 +202,7 @@ namespace osu.Game.Rulesets public virtual HitObjectComposer CreateHitObjectComposer() => null; - public virtual BeatmapVerifier CreateBeatmapVerifier() => null; + public virtual IBeatmapVerifier CreateBeatmapVerifier() => null; public virtual Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.Solid.QuestionCircle }; diff --git a/osu.Game/Screens/Edit/Verify/VerifyScreen.cs b/osu.Game/Screens/Edit/Verify/VerifyScreen.cs index a3d808ce62..a733c9c176 100644 --- a/osu.Game/Screens/Edit/Verify/VerifyScreen.cs +++ b/osu.Game/Screens/Edit/Verify/VerifyScreen.cs @@ -21,7 +21,7 @@ namespace osu.Game.Screens.Edit.Verify public class VerifyScreen : EditorScreen { private Ruleset ruleset; - private static BeatmapVerifier beatmapVerifier; + private static IBeatmapVerifier beatmapVerifier; [Cached] private Bindable selectedIssue = new Bindable(); From 304fe5cd341027b19da18b82f394e5fc444a2883 Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Tue, 13 Apr 2021 10:41:02 +0200 Subject: [PATCH 127/203] Add `CheckBackground` to `OsuBeatmapVerifier` --- osu.Game.Rulesets.Osu/Edit/OsuBeatmapVerifier.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuBeatmapVerifier.cs b/osu.Game.Rulesets.Osu/Edit/OsuBeatmapVerifier.cs index 1c7ab00bbb..66ef74ab08 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuBeatmapVerifier.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuBeatmapVerifier.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Linq; using osu.Game.Beatmaps; using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Edit.Checks; using osu.Game.Rulesets.Edit.Checks.Components; using osu.Game.Rulesets.Osu.Edit.Checks; @@ -14,6 +15,10 @@ namespace osu.Game.Rulesets.Osu.Edit { private readonly List checks = new List { + // General checks + new CheckBackground(), + + // Ruleset-specific checks new CheckOffscreenObjects() }; From 15658eda554f0574dd85f8d9e4b557ace3229c2d Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 13 Apr 2021 09:56:43 +0300 Subject: [PATCH 128/203] Add failing test case --- .../Background/TestSceneUserDimBackgrounds.cs | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs index a4c28651df..655b426e43 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs @@ -168,6 +168,29 @@ namespace osu.Game.Tests.Visual.Background AddUntilStep("Storyboard is visible", () => player.IsStoryboardVisible); } + [Test] + public void TestStoryboardIgnoreUserSettings() + { + performFullSetup(); + createFakeStoryboard(); + AddStep("Enable replacing background", () => player.ReplacesBackground.Value = true); + + AddUntilStep("Storyboard is invisible", () => !player.IsStoryboardVisible); + AddUntilStep("Background is visible", () => songSelect.IsBackgroundVisible()); + + AddStep("Ignore user settings", () => + { + player.ApplyToBackground(b => b.IgnoreUserSettings.Value = true); + player.DimmableStoryboard.IgnoreUserSettings.Value = true; + }); + AddUntilStep("Storyboard is visible", () => player.IsStoryboardVisible); + AddUntilStep("Background is invisible", () => songSelect.IsBackgroundInvisible()); + + AddStep("Disable background replacement", () => player.ReplacesBackground.Value = false); + AddUntilStep("Storyboard is visible", () => player.IsStoryboardVisible); + AddUntilStep("Background is visible", () => songSelect.IsBackgroundVisible()); + } + /// /// Check if the visual settings container retains dim and blur when pausing /// From 7c53bebfd4a5e1ac99b62ec12f0521616b6994dd Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 13 Apr 2021 09:25:08 +0300 Subject: [PATCH 129/203] Fix beatmap background not hiding when user settings ignored and storyboard replaces background --- osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs b/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs index d27211144e..10d381b8b7 100644 --- a/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs +++ b/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs @@ -166,7 +166,9 @@ namespace osu.Game.Screens.Backgrounds BlurAmount.ValueChanged += _ => UpdateVisuals(); } - protected override bool ShowDimContent => !ShowStoryboard.Value || !StoryboardReplacesBackground.Value; // The background needs to be hidden in the case of it being replaced by the storyboard + protected override bool ShowDimContent + // The background needs to be hidden in the case of it being replaced by the storyboard + => (!ShowStoryboard.Value && !IgnoreUserSettings.Value) || !StoryboardReplacesBackground.Value; protected override void UpdateVisuals() { From aa5fe2e9fcdbe3f2dd6ac1d63328c4f5f4af477d Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Tue, 13 Apr 2021 11:02:01 +0200 Subject: [PATCH 130/203] Rename `BeatmapVerifier` -> `IBeatmapVerifier` --- .../Rulesets/Edit/{BeatmapVerifier.cs => IBeatmapVerifier.cs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename osu.Game/Rulesets/Edit/{BeatmapVerifier.cs => IBeatmapVerifier.cs} (100%) diff --git a/osu.Game/Rulesets/Edit/BeatmapVerifier.cs b/osu.Game/Rulesets/Edit/IBeatmapVerifier.cs similarity index 100% rename from osu.Game/Rulesets/Edit/BeatmapVerifier.cs rename to osu.Game/Rulesets/Edit/IBeatmapVerifier.cs From 4618728bf016fc3253d0cce337c230a2ed0f2fac Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Tue, 13 Apr 2021 11:35:12 +0200 Subject: [PATCH 131/203] Add test case --- .../TestSceneOsuEditorSelectInvalidPath.cs | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorSelectInvalidPath.cs diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorSelectInvalidPath.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorSelectInvalidPath.cs new file mode 100644 index 0000000000..d0348c1b6b --- /dev/null +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorSelectInvalidPath.cs @@ -0,0 +1,46 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Tests.Beatmaps; +using osu.Game.Tests.Visual; +using osuTK; + +namespace osu.Game.Rulesets.Osu.Tests.Editor +{ + public class TestSceneOsuEditorSelectInvalidPath : EditorTestScene + { + protected override Ruleset CreateEditorRuleset() => new OsuRuleset(); + + protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => new TestBeatmap(ruleset, false); + + [Test] + public void TestSelectDoesNotModify() + { + Slider slider = new Slider { StartTime = 0, Position = new Vector2(320, 40) }; + + PathControlPoint[] points = + { + new PathControlPoint(new Vector2(0), PathType.PerfectCurve), + new PathControlPoint(new Vector2(-100, 0)), + new PathControlPoint(new Vector2(100, 20)) + }; + + int preSelectVersion = -1; + AddStep("add slider", () => + { + slider.Path = new SliderPath(points); + EditorBeatmap.Add(slider); + preSelectVersion = slider.Path.Version.Value; + }); + + AddStep("select added slider", () => EditorBeatmap.SelectedHitObjects.Add(slider)); + + AddAssert("slider same path", () => slider.Path.Version.Value == preSelectVersion); + } + } +} From fca9c70c1b47bd986fe089e675f85ea44d9b6690 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Apr 2021 17:39:12 +0900 Subject: [PATCH 132/203] Move timeline hit object test to immediately viewable area --- .../Visual/Editing/TestSceneTimelineHitObjectBlueprint.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneTimelineHitObjectBlueprint.cs b/osu.Game.Tests/Visual/Editing/TestSceneTimelineHitObjectBlueprint.cs index 35f394fe1d..88246381bf 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneTimelineHitObjectBlueprint.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneTimelineHitObjectBlueprint.cs @@ -29,7 +29,7 @@ namespace osu.Game.Tests.Visual.Editing EditorBeatmap.Add(new Spinner { Position = new Vector2(256, 256), - StartTime = 150, + StartTime = 2700, Duration = 500 }); }); From 00f235760d237e6902162c4f1675cbedbc0063ba Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Apr 2021 18:12:46 +0900 Subject: [PATCH 133/203] Update visual appearance of timeline blueprints to close match new designs --- .../TestSceneTimelineHitObjectBlueprint.cs | 6 +- .../Timeline/TimelineBlueprintContainer.cs | 3 +- .../Timeline/TimelineHitObjectBlueprint.cs | 166 +++++++++--------- 3 files changed, 92 insertions(+), 83 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneTimelineHitObjectBlueprint.cs b/osu.Game.Tests/Visual/Editing/TestSceneTimelineHitObjectBlueprint.cs index 88246381bf..e6fad33a51 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneTimelineHitObjectBlueprint.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneTimelineHitObjectBlueprint.cs @@ -21,7 +21,7 @@ namespace osu.Game.Tests.Visual.Editing [Test] public void TestDisallowZeroDurationObjects() { - DragBar dragBar; + DragArea dragArea; AddStep("add spinner", () => { @@ -37,8 +37,8 @@ namespace osu.Game.Tests.Visual.Editing AddStep("hold down drag bar", () => { // distinguishes between the actual drag bar and its "underlay shadow". - dragBar = this.ChildrenOfType().Single(bar => bar.HandlePositionalInput); - InputManager.MoveMouseTo(dragBar); + dragArea = this.ChildrenOfType().Single(bar => bar.HandlePositionalInput); + InputManager.MoveMouseTo(dragArea); InputManager.PressButton(MouseButton.Left); }); diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs index be34c8d57e..7427473a35 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs @@ -63,7 +63,8 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline { AddInternal(backgroundBox = new SelectableAreaBackground { - Colour = Color4.Black + Colour = Color4.Black, + Depth = float.MaxValue, }); } diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs index d24614299c..179abd0a26 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; @@ -28,9 +29,8 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline { public class TimelineHitObjectBlueprint : SelectionBlueprint { - private const float thickness = 5; private const float shadow_radius = 5; - private const float circle_size = 34; + private const float circle_size = 38; public Action OnDragHandled; @@ -40,8 +40,8 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline private Bindable indexInCurrentComboBindable; private Bindable comboIndexBindable; - private readonly Circle circle; - private readonly DragBar dragBar; + private readonly Container circle; + private readonly DragArea dragArea; private readonly List shadowComponents = new List(); private readonly Container mainComponents; private readonly OsuSpriteText comboIndexText; @@ -76,71 +76,27 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline { Anchor = Anchor.CentreLeft, Origin = Anchor.Centre, - Font = OsuFont.Numeric.With(size: circle_size / 2, weight: FontWeight.Black), + Y = -1, + Font = OsuFont.Default.With(size: circle_size * 0.5f, weight: FontWeight.Regular), }, }); - circle = new Circle + circle = new ExtendableCircle { - Size = new Vector2(circle_size), + RelativeSizeAxes = Axes.X, + Size = new Vector2(1, circle_size), Anchor = Anchor.CentreLeft, - Origin = Anchor.Centre, - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, - Radius = shadow_radius, - Colour = Color4.Black - }, + Origin = Anchor.CentreLeft, }; - shadowComponents.Add(circle); + mainComponents.Add(circle); if (hitObject is IHasDuration) { - DragBar dragBarUnderlay; - Container extensionBar; - - mainComponents.AddRange(new Drawable[] + mainComponents.Add(dragArea = new DragArea(hitObject) { - extensionBar = new Container - { - Masking = true, - Size = new Vector2(1, thickness), - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - RelativePositionAxes = Axes.X, - RelativeSizeAxes = Axes.X, - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, - Radius = shadow_radius, - Colour = Color4.Black - }, - Child = new Box - { - RelativeSizeAxes = Axes.Both, - } - }, - circle, - // only used for drawing the shadow - dragBarUnderlay = new DragBar(null), - // cover up the shadow on the join - new Box - { - Height = thickness, - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - RelativeSizeAxes = Axes.X, - }, - dragBar = new DragBar(hitObject) { OnDragHandled = e => OnDragHandled?.Invoke(e) }, + OnDragHandled = e => OnDragHandled?.Invoke(e) }); - - shadowComponents.Add(dragBarUnderlay); - shadowComponents.Add(extensionBar); - } - else - { - mainComponents.Add(circle); } updateShadows(); @@ -173,7 +129,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline var comboColour = combo.GetComboColour(comboColours); if (HitObject is IHasDuration) - mainComponents.Colour = ColourInfo.GradientHorizontal(comboColour, Color4.White); + mainComponents.Colour = ColourInfo.GradientHorizontal(comboColour, comboColour.Lighten(0.4f)); else mainComponents.Colour = comboColour; @@ -227,10 +183,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline protected override bool ShouldBeConsideredForInput(Drawable child) => true; - public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => - base.ReceivePositionalInputAt(screenSpacePos) || - circle.ReceivePositionalInputAt(screenSpacePos) || - dragBar?.ReceivePositionalInputAt(screenSpacePos) == true; + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => circle.Contains(screenSpacePos); protected override void OnSelected() { @@ -256,7 +209,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline { Type = EdgeEffectType.Shadow, Radius = shadow_radius, - Colour = State == SelectionState.Selected ? Color4.Orange : Color4.Black + Colour = Color4.Black.Opacity(0.4f) }; } } @@ -267,22 +220,11 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline updateShadows(); } - public override Quad SelectionQuad - { - get - { - // correctly include the circle in the selection quad region, as it is usually outside the blueprint itself. - var leftQuad = circle.ScreenSpaceDrawQuad; - var rightQuad = dragBar?.ScreenSpaceDrawQuad ?? ScreenSpaceDrawQuad; - - return new Quad(leftQuad.TopLeft, Vector2.ComponentMax(rightQuad.TopRight, leftQuad.TopRight), - leftQuad.BottomLeft, Vector2.ComponentMax(rightQuad.BottomRight, leftQuad.BottomRight)); - } - } + public override Quad SelectionQuad => circle.ScreenSpaceDrawQuad; public override Vector2 ScreenSpaceSelectionPoint => ScreenSpaceDrawQuad.TopLeft; - public class DragBar : Container + public class DragArea : Container { private readonly HitObject hitObject; @@ -293,13 +235,13 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline public override bool HandlePositionalInput => hitObject != null; - public DragBar(HitObject hitObject) + public DragArea(HitObject hitObject) { this.hitObject = hitObject; - CornerRadius = 2; + CornerRadius = circle_size / 2; Masking = true; - Size = new Vector2(5, 1); + Size = new Vector2(circle_size, 1); Anchor = Anchor.CentreRight; Origin = Anchor.Centre; RelativePositionAxes = Axes.X; @@ -406,5 +348,71 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline changeHandler?.EndChange(); } } + + /// + /// A circle with externalised end caps so it can take up the full width of a relative width area. + /// + public class ExtendableCircle : Container + { + private readonly Circle rightCircle; + private readonly Circle leftCircle; + + public override Quad ScreenSpaceDrawQuad + { + get + { + var leftQuad = leftCircle.ScreenSpaceDrawQuad; + + if (Width == 0) + { + return leftQuad; + } + + var rightQuad = rightCircle.ScreenSpaceDrawQuad; + + return new Quad(leftQuad.TopLeft, rightQuad.TopRight, leftQuad.BottomLeft, rightQuad.BottomRight); + } + } + + public ExtendableCircle() + { + var effect = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Radius = shadow_radius, + Colour = Color4.Black.Opacity(0.4f) + }; + + InternalChildren = new Drawable[] + { + new Container + { + Height = circle_size, + RelativeSizeAxes = Axes.X, + Masking = true, + AlwaysPresent = true, + EdgeEffect = effect, + }, + leftCircle = new Circle + { + EdgeEffect = effect, + Origin = Anchor.TopCentre, + Size = new Vector2(circle_size) + }, + rightCircle = new Circle + { + EdgeEffect = effect, + Anchor = Anchor.TopRight, + Origin = Anchor.TopCentre, + Size = new Vector2(circle_size) + }, + new Box + { + Height = circle_size, + RelativeSizeAxes = Axes.X, + }, + }; + } + } } } From 109ee395bf397fc73b13e10f70bf8dedae67b20f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Apr 2021 18:47:07 +0900 Subject: [PATCH 134/203] Fix input and remove outdated hover logic --- .../Timeline/TimelineHitObjectBlueprint.cs | 42 +++++++------------ 1 file changed, 14 insertions(+), 28 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs index 179abd0a26..89a9095d22 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs @@ -17,7 +17,6 @@ using osu.Framework.Input.Events; using osu.Framework.Utils; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; -using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; @@ -40,9 +39,8 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline private Bindable indexInCurrentComboBindable; private Bindable comboIndexBindable; - private readonly Container circle; - private readonly DragArea dragArea; - private readonly List shadowComponents = new List(); + private readonly Drawable circle; + private readonly Container mainComponents; private readonly OsuSpriteText comboIndexText; @@ -93,7 +91,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline if (hitObject is IHasDuration) { - mainComponents.Add(dragArea = new DragArea(hitObject) + mainComponents.Add(new DragArea(hitObject) { OnDragHandled = e => OnDragHandled?.Invoke(e) }); @@ -183,8 +181,6 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline protected override bool ShouldBeConsideredForInput(Drawable child) => true; - public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => circle.Contains(screenSpacePos); - protected override void OnSelected() { updateShadows(); @@ -192,27 +188,6 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline private void updateShadows() { - foreach (var s in shadowComponents) - { - if (State == SelectionState.Selected) - { - s.EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, - Radius = shadow_radius / 2, - Colour = Color4.Orange, - }; - } - else - { - s.EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, - Radius = shadow_radius, - Colour = Color4.Black.Opacity(0.4f) - }; - } - } } protected override void OnDeselected() @@ -220,6 +195,9 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline updateShadows(); } + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => + circle.ReceivePositionalInputAt(screenSpacePos); + public override Quad SelectionQuad => circle.ScreenSpaceDrawQuad; public override Vector2 ScreenSpaceSelectionPoint => ScreenSpaceDrawQuad.TopLeft; @@ -351,12 +329,20 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline /// /// A circle with externalised end caps so it can take up the full width of a relative width area. + /// TODO: figure how to do this with a single circle to avoid pixel-misaligned edges. /// public class ExtendableCircle : Container { private readonly Circle rightCircle; private readonly Circle leftCircle; + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) + { + return base.ReceivePositionalInputAt(screenSpacePos) + || leftCircle.ReceivePositionalInputAt(screenSpacePos) + || rightCircle.ReceivePositionalInputAt(screenSpacePos); + } + public override Quad ScreenSpaceDrawQuad { get From 495fdd8d65d99d7818726e29829fcd6ff8fed660 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Apr 2021 18:58:51 +0900 Subject: [PATCH 135/203] Update drag area display to match new design logic --- .../Timeline/TimelineHitObjectBlueprint.cs | 40 ++++++++++++++++--- 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs index 89a9095d22..493f62921b 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs @@ -202,7 +202,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline public override Vector2 ScreenSpaceSelectionPoint => ScreenSpaceDrawQuad.TopLeft; - public class DragArea : Container + public class DragArea : Circle { private readonly HitObject hitObject; @@ -224,6 +224,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline Origin = Anchor.Centre; RelativePositionAxes = Axes.X; RelativeSizeAxes = Axes.Y; + Colour = OsuColour.Gray(0.2f); InternalChildren = new Drawable[] { @@ -234,6 +235,14 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline }; } + protected override void LoadComplete() + { + base.LoadComplete(); + + updateState(); + FinishTransforms(); + } + protected override bool OnHover(HoverEvent e) { updateState(); @@ -265,7 +274,20 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline private void updateState() { - Colour = IsHovered || hasMouseDown ? Color4.OrangeRed : Color4.White; + if (hasMouseDown) + { + this.ScaleTo(0.7f, 200, Easing.OutQuint); + } + else if (IsHovered) + { + this.ScaleTo(0.8f, 200, Easing.OutQuint); + } + else + { + this.ScaleTo(0.6f, 200, Easing.OutQuint); + } + + this.FadeTo(IsHovered || hasMouseDown ? 0.8f : 0.2f, 200, Easing.OutQuint); } [Resolved] @@ -369,12 +391,16 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline Colour = Color4.Black.Opacity(0.4f) }; + const float fudge = 0.97f; + InternalChildren = new Drawable[] { new Container { - Height = circle_size, - RelativeSizeAxes = Axes.X, + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Height = fudge, Masking = true, AlwaysPresent = true, EdgeEffect = effect, @@ -394,8 +420,10 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline }, new Box { - Height = circle_size, - RelativeSizeAxes = Axes.X, + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Height = fudge, }, }; } From b2c17979defca0bd018244129c9b9e1d029fdc84 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Apr 2021 19:24:20 +0900 Subject: [PATCH 136/203] Update colours of all overlay components in one swoop (based off combo colour) --- .../Timeline/TimelineHitObjectBlueprint.cs | 79 ++++++++++--------- 1 file changed, 41 insertions(+), 38 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs index 493f62921b..6463f1a200 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs @@ -31,6 +31,8 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline private const float shadow_radius = 5; private const float circle_size = 38; + private Container repeatsContainer; + public Action OnDragHandled; [UsedImplicitly] @@ -41,7 +43,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline private readonly Drawable circle; - private readonly Container mainComponents; + private readonly Container colouredComponents; private readonly OsuSpriteText comboIndexText; [Resolved] @@ -59,39 +61,37 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline RelativePositionAxes = Axes.X; RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; + Height = circle_size; - AddRangeInternal(new Drawable[] + AddRangeInternal(new[] { - mainComponents = new Container + circle = new ExtendableCircle + { + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + }, + colouredComponents = new Container { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - }, - comboIndexText = new OsuSpriteText - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.Centre, - Y = -1, - Font = OsuFont.Default.With(size: circle_size * 0.5f, weight: FontWeight.Regular), + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + comboIndexText = new OsuSpriteText + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.Centre, + Y = -1, + Font = OsuFont.Default.With(size: circle_size * 0.5f, weight: FontWeight.Regular), + }, + } }, }); - circle = new ExtendableCircle - { - RelativeSizeAxes = Axes.X, - Size = new Vector2(1, circle_size), - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - }; - - mainComponents.Add(circle); - if (hitObject is IHasDuration) { - mainComponents.Add(new DragArea(hitObject) + colouredComponents.Add(new DragArea(hitObject) { OnDragHandled = e => OnDragHandled?.Invoke(e) }); @@ -127,15 +127,15 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline var comboColour = combo.GetComboColour(comboColours); if (HitObject is IHasDuration) - mainComponents.Colour = ColourInfo.GradientHorizontal(comboColour, comboColour.Lighten(0.4f)); + circle.Colour = ColourInfo.GradientHorizontal(comboColour, comboColour.Lighten(0.4f)); else - mainComponents.Colour = comboColour; + circle.Colour = comboColour; - var col = mainComponents.Colour.TopLeft.Linear; + var col = circle.Colour.TopLeft.Linear; float brightness = col.R + col.G + col.B; // decide the combo index colour based on brightness? - comboIndexText.Colour = brightness > 0.5f ? Color4.Black : Color4.White; + colouredComponents.Colour = OsuColour.Gray(brightness > 0.5f ? 0.2f : 0.9f); } protected override void Update() @@ -155,13 +155,11 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline } } - private Container repeatsContainer; - private void updateRepeats(IHasRepeats repeats) { repeatsContainer?.Expire(); - mainComponents.Add(repeatsContainer = new Container + colouredComponents.Add(repeatsContainer = new Container { RelativeSizeAxes = Axes.Both, }); @@ -170,7 +168,8 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline { repeatsContainer.Add(new Circle { - Size = new Vector2(circle_size / 2), + Size = new Vector2(circle_size / 3), + Alpha = 0.2f, Anchor = Anchor.CentreLeft, Origin = Anchor.Centre, RelativePositionAxes = Axes.X, @@ -224,7 +223,6 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline Origin = Anchor.Centre; RelativePositionAxes = Axes.X; RelativeSizeAxes = Axes.Y; - Colour = OsuColour.Gray(0.2f); InternalChildren = new Drawable[] { @@ -351,7 +349,6 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline /// /// A circle with externalised end caps so it can take up the full width of a relative width area. - /// TODO: figure how to do this with a single circle to avoid pixel-misaligned edges. /// public class ExtendableCircle : Container { @@ -391,7 +388,9 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline Colour = Color4.Black.Opacity(0.4f) }; - const float fudge = 0.97f; + // TODO: figure how to do this whole thing with a single circle to avoid pixel-misaligned edges. + // just working with what i can make work for the time being.. + const float fudge = 0.4f; InternalChildren = new Drawable[] { @@ -400,7 +399,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline RelativeSizeAxes = Axes.Both, Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, - Height = fudge, + Padding = new MarginPadding { Vertical = fudge }, Masking = true, AlwaysPresent = true, EdgeEffect = effect, @@ -418,12 +417,16 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline Origin = Anchor.TopCentre, Size = new Vector2(circle_size) }, - new Box + new Container { RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Vertical = fudge }, Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, - Height = fudge, + Children = new Drawable[] + { + new Box { RelativeSizeAxes = Axes.Both, } + } }, }; } From e7b0042a608d09e1f6a01e53860a01595905177f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Apr 2021 19:25:03 +0900 Subject: [PATCH 137/203] Remove unnecessary hover / shadow logic --- .../Timeline/TimelineHitObjectBlueprint.cs | 29 +++++++------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs index 6463f1a200..67be298567 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs @@ -28,7 +28,6 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline { public class TimelineHitObjectBlueprint : SelectionBlueprint { - private const float shadow_radius = 5; private const float circle_size = 38; private Container repeatsContainer; @@ -96,8 +95,6 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline OnDragHandled = e => OnDragHandled?.Invoke(e) }); } - - updateShadows(); } protected override void LoadComplete() @@ -116,6 +113,16 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline } } + protected override void OnSelected() + { + // base logic hides selected blueprints when not selected, but timeline doesn't do that. + } + + protected override void OnDeselected() + { + // base logic hides selected blueprints when not selected, but timeline doesn't do that. + } + private void updateComboIndex() => comboIndexText.Text = (indexInCurrentComboBindable.Value + 1).ToString(); private void updateComboColour() @@ -180,20 +187,6 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline protected override bool ShouldBeConsideredForInput(Drawable child) => true; - protected override void OnSelected() - { - updateShadows(); - } - - private void updateShadows() - { - } - - protected override void OnDeselected() - { - updateShadows(); - } - public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => circle.ReceivePositionalInputAt(screenSpacePos); @@ -384,7 +377,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline var effect = new EdgeEffectParameters { Type = EdgeEffectType.Shadow, - Radius = shadow_radius, + Radius = 5, Colour = Color4.Black.Opacity(0.4f) }; From bcd41417b3cb7701985d8db8614ded82cc9c7034 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Apr 2021 19:36:29 +0900 Subject: [PATCH 138/203] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index c78dfb6a55..b5315c3616 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,6 +52,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 92e05cb4a6..45b3d5c161 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -29,7 +29,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 11124730c9..105a6e59c2 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -93,7 +93,7 @@ - + From 03ba04e8cec2b335355916d477712e84305ce00e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Apr 2021 19:50:22 +0900 Subject: [PATCH 139/203] Split out general checks into its own verifier class (and remove `static` usage) --- .../Edit/OsuBeatmapVerifier.cs | 5 --- osu.Game/Rulesets/Edit/BeatmapVerifier.cs | 24 ++++++++++++++ osu.Game/Screens/Edit/Verify/VerifyScreen.cs | 32 ++++++++----------- 3 files changed, 38 insertions(+), 23 deletions(-) create mode 100644 osu.Game/Rulesets/Edit/BeatmapVerifier.cs diff --git a/osu.Game.Rulesets.Osu/Edit/OsuBeatmapVerifier.cs b/osu.Game.Rulesets.Osu/Edit/OsuBeatmapVerifier.cs index 66ef74ab08..1c7ab00bbb 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuBeatmapVerifier.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuBeatmapVerifier.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using System.Linq; using osu.Game.Beatmaps; using osu.Game.Rulesets.Edit; -using osu.Game.Rulesets.Edit.Checks; using osu.Game.Rulesets.Edit.Checks.Components; using osu.Game.Rulesets.Osu.Edit.Checks; @@ -15,10 +14,6 @@ namespace osu.Game.Rulesets.Osu.Edit { private readonly List checks = new List { - // General checks - new CheckBackground(), - - // Ruleset-specific checks new CheckOffscreenObjects() }; diff --git a/osu.Game/Rulesets/Edit/BeatmapVerifier.cs b/osu.Game/Rulesets/Edit/BeatmapVerifier.cs new file mode 100644 index 0000000000..b1d538bf04 --- /dev/null +++ b/osu.Game/Rulesets/Edit/BeatmapVerifier.cs @@ -0,0 +1,24 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using System.Linq; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Edit.Checks; +using osu.Game.Rulesets.Edit.Checks.Components; + +namespace osu.Game.Rulesets.Edit +{ + /// + /// A ruleset-agnostic beatmap converter that identifies issues in common metadata or mapping standards. + /// + public class BeatmapVerifier : IBeatmapVerifier + { + private readonly List checks = new List + { + new CheckBackground(), + }; + + public IEnumerable Run(IBeatmap beatmap) => checks.SelectMany(check => check.Run(beatmap)); + } +} diff --git a/osu.Game/Screens/Edit/Verify/VerifyScreen.cs b/osu.Game/Screens/Edit/Verify/VerifyScreen.cs index a733c9c176..550fbe2950 100644 --- a/osu.Game/Screens/Edit/Verify/VerifyScreen.cs +++ b/osu.Game/Screens/Edit/Verify/VerifyScreen.cs @@ -7,11 +7,9 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; -using osu.Game.Rulesets; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit.Checks.Components; using osuTK; @@ -20,9 +18,6 @@ namespace osu.Game.Screens.Edit.Verify { public class VerifyScreen : EditorScreen { - private Ruleset ruleset; - private static IBeatmapVerifier beatmapVerifier; - [Cached] private Bindable selectedIssue = new Bindable(); @@ -31,16 +26,6 @@ namespace osu.Game.Screens.Edit.Verify { } - protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) - { - var dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); - - ruleset = parent.Get>().Value.BeatmapInfo.Ruleset?.CreateInstance(); - beatmapVerifier = ruleset?.CreateBeatmapVerifier(); - - return dependencies; - } - [BackgroundDependencyLoader] private void load() { @@ -81,9 +66,15 @@ namespace osu.Game.Screens.Edit.Verify [Resolved] private Bindable selectedIssue { get; set; } + private IBeatmapVerifier rulesetVerifier; + private BeatmapVerifier generalVerifier; + [BackgroundDependencyLoader] private void load(OsuColour colours) { + generalVerifier = new BeatmapVerifier(); + rulesetVerifier = Beatmap.BeatmapInfo.Ruleset?.CreateInstance()?.CreateBeatmapVerifier(); + RelativeSizeAxes = Axes.Both; InternalChildren = new Drawable[] @@ -128,9 +119,14 @@ namespace osu.Game.Screens.Edit.Verify private void refresh() { - table.Issues = beatmapVerifier.Run(Beatmap) - .OrderBy(issue => issue.Template.Type) - .ThenBy(issue => issue.Check.Metadata.Category); + var issues = generalVerifier.Run(Beatmap); + + if (rulesetVerifier != null) + issues = issues.Concat(rulesetVerifier.Run(Beatmap)); + + table.Issues = issues + .OrderBy(issue => issue.Template.Type) + .ThenBy(issue => issue.Check.Metadata.Category); } } } From 464fc02875f067aef4bd55f3244aa1df97cc774b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Apr 2021 19:55:17 +0900 Subject: [PATCH 140/203] Fix some styling issues with the verify screen layout --- osu.Game/Screens/Edit/Verify/IssueTable.cs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Edit/Verify/IssueTable.cs b/osu.Game/Screens/Edit/Verify/IssueTable.cs index 516e1adf44..042d6d84e3 100644 --- a/osu.Game/Screens/Edit/Verify/IssueTable.cs +++ b/osu.Game/Screens/Edit/Verify/IssueTable.cs @@ -38,9 +38,6 @@ namespace osu.Game.Screens.Edit.Verify Padding = new MarginPadding { Horizontal = horizontal_inset }; RowSize = new Dimension(GridSizeMode.Absolute, row_height); - Masking = true; - CornerRadius = 6; - AddInternal(backgroundFlow = new FillFlowContainer { RelativeSizeAxes = Axes.Both, @@ -118,6 +115,17 @@ namespace osu.Game.Screens.Edit.Verify } }; + protected override Drawable CreateHeader(int index, TableColumn column) => new HeaderText(column?.Header ?? string.Empty); + + private class HeaderText : OsuSpriteText + { + public HeaderText(string text) + { + Text = text.ToUpper(); + Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold); + } + } + public class RowBackground : OsuClickableContainer { private readonly Issue issue; From 0d6890243fc7b6308ffdd87e7f87c1551d4ae2fd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Apr 2021 20:18:18 +0900 Subject: [PATCH 141/203] Fix typo in xmldoc --- osu.Game/Rulesets/Edit/BeatmapVerifier.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Edit/BeatmapVerifier.cs b/osu.Game/Rulesets/Edit/BeatmapVerifier.cs index b1d538bf04..f9bced7beb 100644 --- a/osu.Game/Rulesets/Edit/BeatmapVerifier.cs +++ b/osu.Game/Rulesets/Edit/BeatmapVerifier.cs @@ -10,7 +10,7 @@ using osu.Game.Rulesets.Edit.Checks.Components; namespace osu.Game.Rulesets.Edit { /// - /// A ruleset-agnostic beatmap converter that identifies issues in common metadata or mapping standards. + /// A ruleset-agnostic beatmap verifier that identifies issues in common metadata or mapping standards. /// public class BeatmapVerifier : IBeatmapVerifier { From e601141be2a70e1a1f004c205b360212a9fe2605 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Tue, 13 Apr 2021 14:57:02 +0300 Subject: [PATCH 142/203] Simplify ExtendableCircle component --- .../Timeline/TimelineHitObjectBlueprint.cs | 83 ++++--------------- 1 file changed, 15 insertions(+), 68 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs index 67be298567..1c0d6e5ab6 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs @@ -343,84 +343,31 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline /// /// A circle with externalised end caps so it can take up the full width of a relative width area. /// - public class ExtendableCircle : Container + public class ExtendableCircle : CompositeDrawable { - private readonly Circle rightCircle; - private readonly Circle leftCircle; + private readonly CircularContainer content; - public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) - { - return base.ReceivePositionalInputAt(screenSpacePos) - || leftCircle.ReceivePositionalInputAt(screenSpacePos) - || rightCircle.ReceivePositionalInputAt(screenSpacePos); - } + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => content.ReceivePositionalInputAt(screenSpacePos); - public override Quad ScreenSpaceDrawQuad - { - get - { - var leftQuad = leftCircle.ScreenSpaceDrawQuad; - - if (Width == 0) - { - return leftQuad; - } - - var rightQuad = rightCircle.ScreenSpaceDrawQuad; - - return new Quad(leftQuad.TopLeft, rightQuad.TopRight, leftQuad.BottomLeft, rightQuad.BottomRight); - } - } + public override Quad ScreenSpaceDrawQuad => content.ScreenSpaceDrawQuad; public ExtendableCircle() { - var effect = new EdgeEffectParameters + Padding = new MarginPadding { Horizontal = -circle_size / 2f }; + InternalChild = content = new CircularContainer { - Type = EdgeEffectType.Shadow, - Radius = 5, - Colour = Color4.Black.Opacity(0.4f) - }; - - // TODO: figure how to do this whole thing with a single circle to avoid pixel-misaligned edges. - // just working with what i can make work for the time being.. - const float fudge = 0.4f; - - InternalChildren = new Drawable[] - { - new Container + RelativeSizeAxes = Axes.Both, + Masking = true, + EdgeEffect = new EdgeEffectParameters { - RelativeSizeAxes = Axes.Both, - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Padding = new MarginPadding { Vertical = fudge }, - Masking = true, - AlwaysPresent = true, - EdgeEffect = effect, + Type = EdgeEffectType.Shadow, + Radius = 5, + Colour = Color4.Black.Opacity(0.4f) }, - leftCircle = new Circle + Child = new Box { - EdgeEffect = effect, - Origin = Anchor.TopCentre, - Size = new Vector2(circle_size) - }, - rightCircle = new Circle - { - EdgeEffect = effect, - Anchor = Anchor.TopRight, - Origin = Anchor.TopCentre, - Size = new Vector2(circle_size) - }, - new Container - { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Vertical = fudge }, - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Children = new Drawable[] - { - new Box { RelativeSizeAxes = Axes.Both, } - } - }, + RelativeSizeAxes = Axes.Both + } }; } } From 69da804f817dac304b7bc36587070d5f129b5eae Mon Sep 17 00:00:00 2001 From: Naxess <30292137+Naxesss@users.noreply.github.com> Date: Tue, 13 Apr 2021 13:57:56 +0200 Subject: [PATCH 143/203] Add missing period --- osu.Game/Rulesets/Edit/Checks/CheckBackground.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs b/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs index 4d5069f446..93da42425c 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckBackground.cs @@ -41,7 +41,7 @@ namespace osu.Game.Rulesets.Edit.Checks public class IssueTemplateNoneSet : IssueTemplate { public IssueTemplateNoneSet(ICheck check) - : base(check, IssueType.Problem, "No background has been set") + : base(check, IssueType.Problem, "No background has been set.") { } From 0edc1a850d95c1c8bd94c873f12be8b40bcb33d5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Apr 2021 23:05:58 +0900 Subject: [PATCH 144/203] Split out common EditorTable base class --- osu.Game/Screens/Edit/EditorTable.cs | 49 +++++++++++++++++ .../Screens/Edit/Timing/ControlPointTable.cs | 44 ++-------------- osu.Game/Screens/Edit/Verify/IssueTable.cs | 52 ++++--------------- 3 files changed, 63 insertions(+), 82 deletions(-) create mode 100644 osu.Game/Screens/Edit/EditorTable.cs diff --git a/osu.Game/Screens/Edit/EditorTable.cs b/osu.Game/Screens/Edit/EditorTable.cs new file mode 100644 index 0000000000..e5e2add384 --- /dev/null +++ b/osu.Game/Screens/Edit/EditorTable.cs @@ -0,0 +1,49 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; + +namespace osu.Game.Screens.Edit +{ + public abstract class EditorTable : TableContainer + { + private const float horizontal_inset = 20; + + protected const float ROW_HEIGHT = 25; + + protected const int TEXT_SIZE = 14; + + protected readonly FillFlowContainer BackgroundFlow; + + protected EditorTable() + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + + Padding = new MarginPadding { Horizontal = horizontal_inset }; + RowSize = new Dimension(GridSizeMode.Absolute, ROW_HEIGHT); + + AddInternal(BackgroundFlow = new FillFlowContainer + { + RelativeSizeAxes = Axes.Both, + Depth = 1f, + Padding = new MarginPadding { Horizontal = -horizontal_inset }, + Margin = new MarginPadding { Top = ROW_HEIGHT } + }); + } + + protected override Drawable CreateHeader(int index, TableColumn column) => new HeaderText(column?.Header ?? string.Empty); + + private class HeaderText : OsuSpriteText + { + public HeaderText(string text) + { + Text = text.ToUpper(); + Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold); + } + } + } +} diff --git a/osu.Game/Screens/Edit/Timing/ControlPointTable.cs b/osu.Game/Screens/Edit/Timing/ControlPointTable.cs index a17b431fcc..75e2bb1f5c 100644 --- a/osu.Game/Screens/Edit/Timing/ControlPointTable.cs +++ b/osu.Game/Screens/Edit/Timing/ControlPointTable.cs @@ -20,47 +20,24 @@ using osuTK.Graphics; namespace osu.Game.Screens.Edit.Timing { - public class ControlPointTable : TableContainer + public class ControlPointTable : EditorTable { - private const float horizontal_inset = 20; - private const float row_height = 25; - private const int text_size = 14; - - private readonly FillFlowContainer backgroundFlow; - [Resolved] private Bindable selectedGroup { get; set; } - public ControlPointTable() - { - RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; - - Padding = new MarginPadding { Horizontal = horizontal_inset }; - RowSize = new Dimension(GridSizeMode.Absolute, row_height); - - AddInternal(backgroundFlow = new FillFlowContainer - { - RelativeSizeAxes = Axes.Both, - Depth = 1f, - Padding = new MarginPadding { Horizontal = -horizontal_inset }, - Margin = new MarginPadding { Top = row_height } - }); - } - public IEnumerable ControlGroups { set { Content = null; - backgroundFlow.Clear(); + BackgroundFlow.Clear(); if (value?.Any() != true) return; foreach (var group in value) { - backgroundFlow.Add(new RowBackground(group)); + BackgroundFlow.Add(new RowBackground(group)); } Columns = createHeaders(); @@ -86,13 +63,13 @@ namespace osu.Game.Screens.Edit.Timing new OsuSpriteText { Text = $"#{index + 1}", - Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Bold), + Font = OsuFont.GetFont(size: TEXT_SIZE, weight: FontWeight.Bold), Margin = new MarginPadding(10) }, new OsuSpriteText { Text = group.Time.ToEditorFormattedString(), - Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Bold) + Font = OsuFont.GetFont(size: TEXT_SIZE, weight: FontWeight.Bold) }, null, new ControlGroupAttributes(group), @@ -164,17 +141,6 @@ namespace osu.Game.Screens.Edit.Timing } } - protected override Drawable CreateHeader(int index, TableColumn column) => new HeaderText(column?.Header ?? string.Empty); - - private class HeaderText : OsuSpriteText - { - public HeaderText(string text) - { - Text = text.ToUpper(); - Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold); - } - } - public class RowBackground : OsuClickableContainer { private readonly ControlPointGroup controlGroup; diff --git a/osu.Game/Screens/Edit/Verify/IssueTable.cs b/osu.Game/Screens/Edit/Verify/IssueTable.cs index 042d6d84e3..00570d363b 100644 --- a/osu.Game/Screens/Edit/Verify/IssueTable.cs +++ b/osu.Game/Screens/Edit/Verify/IssueTable.cs @@ -19,47 +19,24 @@ using osuTK.Graphics; namespace osu.Game.Screens.Edit.Verify { - public class IssueTable : TableContainer + public class IssueTable : EditorTable { - private const float horizontal_inset = 20; - private const float row_height = 25; - private const int text_size = 14; - - private readonly FillFlowContainer backgroundFlow; - [Resolved] private Bindable selectedIssue { get; set; } - public IssueTable() - { - RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; - - Padding = new MarginPadding { Horizontal = horizontal_inset }; - RowSize = new Dimension(GridSizeMode.Absolute, row_height); - - AddInternal(backgroundFlow = new FillFlowContainer - { - RelativeSizeAxes = Axes.Both, - Depth = 1f, - Padding = new MarginPadding { Horizontal = -horizontal_inset }, - Margin = new MarginPadding { Top = row_height } - }); - } - public IEnumerable Issues { set { Content = null; - backgroundFlow.Clear(); + BackgroundFlow.Clear(); if (value == null) return; foreach (var issue in value) { - backgroundFlow.Add(new RowBackground(issue)); + BackgroundFlow.Add(new RowBackground(issue)); } Columns = createHeaders(); @@ -86,46 +63,35 @@ namespace osu.Game.Screens.Edit.Verify new OsuSpriteText { Text = $"#{index + 1}", - Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Medium), + Font = OsuFont.GetFont(size: TEXT_SIZE, weight: FontWeight.Medium), Margin = new MarginPadding { Right = 10 } }, new OsuSpriteText { Text = issue.Template.Type.ToString(), - Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Bold), + Font = OsuFont.GetFont(size: TEXT_SIZE, weight: FontWeight.Bold), Margin = new MarginPadding { Right = 10 }, Colour = issue.Template.Colour }, new OsuSpriteText { Text = issue.GetEditorTimestamp(), - Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Bold), + Font = OsuFont.GetFont(size: TEXT_SIZE, weight: FontWeight.Bold), Margin = new MarginPadding { Right = 10 }, }, new OsuSpriteText { Text = issue.ToString(), - Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Medium) + Font = OsuFont.GetFont(size: TEXT_SIZE, weight: FontWeight.Medium) }, new OsuSpriteText { Text = issue.Check.Metadata.Category.ToString(), - Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Bold), + Font = OsuFont.GetFont(size: TEXT_SIZE, weight: FontWeight.Bold), Margin = new MarginPadding(10) } }; - protected override Drawable CreateHeader(int index, TableColumn column) => new HeaderText(column?.Header ?? string.Empty); - - private class HeaderText : OsuSpriteText - { - public HeaderText(string text) - { - Text = text.ToUpper(); - Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold); - } - } - public class RowBackground : OsuClickableContainer { private readonly Issue issue; @@ -150,7 +116,7 @@ namespace osu.Game.Screens.Edit.Verify this.issue = issue; RelativeSizeAxes = Axes.X; - Height = row_height; + Height = ROW_HEIGHT; AlwaysPresent = true; CornerRadius = 3; Masking = true; From 21e8e5fbcacf799b0485ab0203e66473a5d6e881 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Apr 2021 23:26:19 +0900 Subject: [PATCH 145/203] Move common table layout logic into `EditorTable` abstract class --- osu.Game/Screens/Edit/EditorTable.cs | 95 ++++++++++- .../Screens/Edit/Timing/ControlPointTable.cs | 120 +++----------- osu.Game/Screens/Edit/Verify/IssueTable.cs | 154 +++++------------- 3 files changed, 152 insertions(+), 217 deletions(-) diff --git a/osu.Game/Screens/Edit/EditorTable.cs b/osu.Game/Screens/Edit/EditorTable.cs index e5e2add384..ef1c88db9a 100644 --- a/osu.Game/Screens/Edit/EditorTable.cs +++ b/osu.Game/Screens/Edit/EditorTable.cs @@ -1,10 +1,15 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Input.Events; using osu.Game.Graphics; +using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; +using osuTK.Graphics; namespace osu.Game.Screens.Edit { @@ -16,7 +21,7 @@ namespace osu.Game.Screens.Edit protected const int TEXT_SIZE = 14; - protected readonly FillFlowContainer BackgroundFlow; + protected readonly FillFlowContainer BackgroundFlow; protected EditorTable() { @@ -26,7 +31,7 @@ namespace osu.Game.Screens.Edit Padding = new MarginPadding { Horizontal = horizontal_inset }; RowSize = new Dimension(GridSizeMode.Absolute, ROW_HEIGHT); - AddInternal(BackgroundFlow = new FillFlowContainer + AddInternal(BackgroundFlow = new FillFlowContainer { RelativeSizeAxes = Axes.Both, Depth = 1f, @@ -45,5 +50,91 @@ namespace osu.Game.Screens.Edit Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold); } } + + public class RowBackground : OsuClickableContainer + { + public readonly object Item; + + private const int fade_duration = 100; + + private readonly Box hoveredBackground; + + [Resolved] + private EditorClock clock { get; set; } + + public RowBackground(object item) + { + Item = item; + + RelativeSizeAxes = Axes.X; + Height = 25; + + AlwaysPresent = true; + + CornerRadius = 3; + Masking = true; + + Children = new Drawable[] + { + hoveredBackground = new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0, + }, + }; + + // todo delete + Action = () => + { + }; + } + + private Color4 colourHover; + private Color4 colourSelected; + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + hoveredBackground.Colour = colourHover = colours.BlueDarker; + colourSelected = colours.YellowDarker; + } + + private bool selected; + + public bool Selected + { + get => selected; + set + { + if (value == selected) + return; + + selected = value; + updateState(); + } + } + + protected override bool OnHover(HoverEvent e) + { + updateState(); + return base.OnHover(e); + } + + protected override void OnHoverLost(HoverLostEvent e) + { + updateState(); + base.OnHoverLost(e); + } + + private void updateState() + { + hoveredBackground.FadeColour(selected ? colourSelected : colourHover, 450, Easing.OutQuint); + + if (selected || IsHovered) + hoveredBackground.FadeIn(fade_duration, Easing.OutQuint); + else + hoveredBackground.FadeOut(fade_duration, Easing.OutQuint); + } + } } } diff --git a/osu.Game/Screens/Edit/Timing/ControlPointTable.cs b/osu.Game/Screens/Edit/Timing/ControlPointTable.cs index 75e2bb1f5c..dd51056bf1 100644 --- a/osu.Game/Screens/Edit/Timing/ControlPointTable.cs +++ b/osu.Game/Screens/Edit/Timing/ControlPointTable.cs @@ -8,12 +8,9 @@ using osu.Framework.Bindables; using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Input.Events; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Extensions; using osu.Game.Graphics; -using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osuTK; using osuTK.Graphics; @@ -25,6 +22,9 @@ namespace osu.Game.Screens.Edit.Timing [Resolved] private Bindable selectedGroup { get; set; } + [Resolved] + private EditorClock clock { get; set; } + public IEnumerable ControlGroups { set @@ -37,7 +37,14 @@ namespace osu.Game.Screens.Edit.Timing foreach (var group in value) { - BackgroundFlow.Add(new RowBackground(group)); + BackgroundFlow.Add(new RowBackground(group) + { + Action = () => + { + selectedGroup.Value = group; + clock.SeekSmoothlyTo(group.Time); + } + }); } Columns = createHeaders(); @@ -45,6 +52,16 @@ namespace osu.Game.Screens.Edit.Timing } } + protected override void LoadComplete() + { + base.LoadComplete(); + + selectedGroup.BindValueChanged(group => + { + foreach (var b in BackgroundFlow) b.Selected = b.Item == group.NewValue; + }, true); + } + private TableColumn[] createHeaders() { var columns = new List @@ -140,100 +157,5 @@ namespace osu.Game.Screens.Edit.Timing return null; } } - - public class RowBackground : OsuClickableContainer - { - private readonly ControlPointGroup controlGroup; - private const int fade_duration = 100; - - private readonly Box hoveredBackground; - - [Resolved] - private EditorClock clock { get; set; } - - [Resolved] - private Bindable selectedGroup { get; set; } - - public RowBackground(ControlPointGroup controlGroup) - { - this.controlGroup = controlGroup; - RelativeSizeAxes = Axes.X; - Height = 25; - - AlwaysPresent = true; - - CornerRadius = 3; - Masking = true; - - Children = new Drawable[] - { - hoveredBackground = new Box - { - RelativeSizeAxes = Axes.Both, - Alpha = 0, - }, - }; - - Action = () => - { - selectedGroup.Value = controlGroup; - clock.SeekSmoothlyTo(controlGroup.Time); - }; - } - - private Color4 colourHover; - private Color4 colourSelected; - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - hoveredBackground.Colour = colourHover = colours.BlueDarker; - colourSelected = colours.YellowDarker; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - selectedGroup.BindValueChanged(group => { Selected = controlGroup == group.NewValue; }, true); - } - - private bool selected; - - protected bool Selected - { - get => selected; - set - { - if (value == selected) - return; - - selected = value; - updateState(); - } - } - - protected override bool OnHover(HoverEvent e) - { - updateState(); - return base.OnHover(e); - } - - protected override void OnHoverLost(HoverLostEvent e) - { - updateState(); - base.OnHoverLost(e); - } - - private void updateState() - { - hoveredBackground.FadeColour(selected ? colourSelected : colourHover, 450, Easing.OutQuint); - - if (selected || IsHovered) - hoveredBackground.FadeIn(fade_duration, Easing.OutQuint); - else - hoveredBackground.FadeOut(fade_duration, Easing.OutQuint); - } - } } } diff --git a/osu.Game/Screens/Edit/Verify/IssueTable.cs b/osu.Game/Screens/Edit/Verify/IssueTable.cs index 00570d363b..44244028c9 100644 --- a/osu.Game/Screens/Edit/Verify/IssueTable.cs +++ b/osu.Game/Screens/Edit/Verify/IssueTable.cs @@ -8,14 +8,10 @@ using osu.Framework.Bindables; using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Input.Events; using osu.Game.Graphics; -using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Input.Bindings; using osu.Game.Rulesets.Edit.Checks.Components; -using osuTK.Graphics; namespace osu.Game.Screens.Edit.Verify { @@ -24,6 +20,15 @@ namespace osu.Game.Screens.Edit.Verify [Resolved] private Bindable selectedIssue { get; set; } + [Resolved] + private EditorClock clock { get; set; } + + [Resolved] + private EditorBeatmap editorBeatmap { get; set; } + + [Resolved] + private Editor editor { get; set; } + public IEnumerable Issues { set @@ -36,7 +41,25 @@ namespace osu.Game.Screens.Edit.Verify foreach (var issue in value) { - BackgroundFlow.Add(new RowBackground(issue)); + BackgroundFlow.Add(new RowBackground(issue) + { + Action = () => + { + selectedIssue.Value = issue; + + if (issue.Time != null) + { + clock.Seek(issue.Time.Value); + editor.OnPressed(GlobalAction.EditorComposeMode); + } + + if (!issue.HitObjects.Any()) + return; + + editorBeatmap.SelectedHitObjects.Clear(); + editorBeatmap.SelectedHitObjects.AddRange(issue.HitObjects); + }, + }); } Columns = createHeaders(); @@ -44,6 +67,16 @@ namespace osu.Game.Screens.Edit.Verify } } + protected override void LoadComplete() + { + base.LoadComplete(); + + selectedIssue.BindValueChanged(issue => + { + foreach (var b in BackgroundFlow) b.Selected = b.Item == issue.NewValue; + }, true); + } + private TableColumn[] createHeaders() { var columns = new List @@ -91,116 +124,5 @@ namespace osu.Game.Screens.Edit.Verify Margin = new MarginPadding(10) } }; - - public class RowBackground : OsuClickableContainer - { - private readonly Issue issue; - private const int fade_duration = 100; - - private readonly Box hoveredBackground; - - [Resolved] - private EditorClock clock { get; set; } - - [Resolved] - private Editor editor { get; set; } - - [Resolved] - private EditorBeatmap editorBeatmap { get; set; } - - [Resolved] - private Bindable selectedIssue { get; set; } - - public RowBackground(Issue issue) - { - this.issue = issue; - - RelativeSizeAxes = Axes.X; - Height = ROW_HEIGHT; - AlwaysPresent = true; - CornerRadius = 3; - Masking = true; - - Children = new Drawable[] - { - hoveredBackground = new Box - { - RelativeSizeAxes = Axes.Both, - Alpha = 0, - }, - }; - - Action = () => - { - selectedIssue.Value = issue; - - if (issue.Time != null) - { - clock.Seek(issue.Time.Value); - editor.OnPressed(GlobalAction.EditorComposeMode); - } - - if (!issue.HitObjects.Any()) - return; - - editorBeatmap.SelectedHitObjects.Clear(); - editorBeatmap.SelectedHitObjects.AddRange(issue.HitObjects); - }; - } - - private Color4 colourHover; - private Color4 colourSelected; - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - hoveredBackground.Colour = colourHover = colours.BlueDarker; - colourSelected = colours.YellowDarker; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - selectedIssue.BindValueChanged(change => { Selected = issue == change.NewValue; }, true); - } - - private bool selected; - - protected bool Selected - { - get => selected; - set - { - if (value == selected) - return; - - selected = value; - updateState(); - } - } - - protected override bool OnHover(HoverEvent e) - { - updateState(); - return base.OnHover(e); - } - - protected override void OnHoverLost(HoverLostEvent e) - { - updateState(); - base.OnHoverLost(e); - } - - private void updateState() - { - hoveredBackground.FadeColour(selected ? colourSelected : colourHover, 450, Easing.OutQuint); - - if (selected || IsHovered) - hoveredBackground.FadeIn(fade_duration, Easing.OutQuint); - else - hoveredBackground.FadeOut(fade_duration, Easing.OutQuint); - } - } } } From cb4f64133eedde15134e64b9b0a0c584256df3aa Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 13 Apr 2021 23:30:20 +0900 Subject: [PATCH 146/203] Add xmldoc to interfaces --- osu.Game/Rulesets/Edit/Checks/Components/ICheck.cs | 3 +++ osu.Game/Rulesets/Edit/IBeatmapVerifier.cs | 3 +++ 2 files changed, 6 insertions(+) diff --git a/osu.Game/Rulesets/Edit/Checks/Components/ICheck.cs b/osu.Game/Rulesets/Edit/Checks/Components/ICheck.cs index f355ae734e..f284240092 100644 --- a/osu.Game/Rulesets/Edit/Checks/Components/ICheck.cs +++ b/osu.Game/Rulesets/Edit/Checks/Components/ICheck.cs @@ -6,6 +6,9 @@ using osu.Game.Beatmaps; namespace osu.Game.Rulesets.Edit.Checks.Components { + /// + /// A specific check that can be run on a beatmap to verify or find issues. + /// public interface ICheck { /// diff --git a/osu.Game/Rulesets/Edit/IBeatmapVerifier.cs b/osu.Game/Rulesets/Edit/IBeatmapVerifier.cs index 2bafacefa3..61d8119635 100644 --- a/osu.Game/Rulesets/Edit/IBeatmapVerifier.cs +++ b/osu.Game/Rulesets/Edit/IBeatmapVerifier.cs @@ -7,6 +7,9 @@ using osu.Game.Rulesets.Edit.Checks.Components; namespace osu.Game.Rulesets.Edit { + /// + /// A class which can run against a beatmap and surface issues to the user which could go against known criteria or hinder gameplay. + /// public interface IBeatmapVerifier { public IEnumerable Run(IBeatmap beatmap); From bf5ed12b757663216d7e5c1a92aa4e26e8371f2a Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 14 Apr 2021 06:33:53 +0300 Subject: [PATCH 147/203] Add support for legacy skin `CursorCentre` setting --- osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursor.cs | 5 +++-- osu.Game.Rulesets.Osu/Skinning/OsuSkinConfiguration.cs | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursor.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursor.cs index 314139d02a..8fe40f801b 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursor.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursor.cs @@ -24,6 +24,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy [BackgroundDependencyLoader] private void load(ISkinSource skin) { + bool centre = skin.GetConfig(OsuSkinConfiguration.CursorCentre)?.Value ?? false; spin = skin.GetConfig(OsuSkinConfiguration.CursorRotate)?.Value ?? true; InternalChildren = new[] @@ -32,13 +33,13 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy { Texture = skin.GetTexture("cursor"), Anchor = Anchor.Centre, - Origin = Anchor.Centre, + Origin = centre ? Anchor.Centre : Anchor.TopLeft, }, new NonPlayfieldSprite { Texture = skin.GetTexture("cursormiddle"), Anchor = Anchor.Centre, - Origin = Anchor.Centre, + Origin = centre ? Anchor.Centre : Anchor.TopLeft, }, }; } diff --git a/osu.Game.Rulesets.Osu/Skinning/OsuSkinConfiguration.cs b/osu.Game.Rulesets.Osu/Skinning/OsuSkinConfiguration.cs index 75a62a6f8e..6953e66b5c 100644 --- a/osu.Game.Rulesets.Osu/Skinning/OsuSkinConfiguration.cs +++ b/osu.Game.Rulesets.Osu/Skinning/OsuSkinConfiguration.cs @@ -8,6 +8,7 @@ namespace osu.Game.Rulesets.Osu.Skinning SliderBorderSize, SliderPathRadius, AllowSliderBallTint, + CursorCentre, CursorExpand, CursorRotate, HitCircleOverlayAboveNumber, From df991bc0affa64d7a0deaf680e57b24cb03cbc26 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 14 Apr 2021 06:36:31 +0300 Subject: [PATCH 148/203] Refactor gameplay cursor test scene and add visual coverage --- .../TestSceneGameplayCursor.cs | 62 +++++++++++++++---- 1 file changed, 49 insertions(+), 13 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs index e3ccf83715..ed44bc7309 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs @@ -4,13 +4,22 @@ using System; using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Audio.Sample; +using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.OpenGL.Textures; using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Textures; +using osu.Framework.Input; using osu.Framework.Testing.Input; using osu.Framework.Utils; +using osu.Game.Audio; using osu.Game.Configuration; +using osu.Game.Rulesets.Osu.Skinning; using osu.Game.Rulesets.Osu.UI.Cursor; using osu.Game.Screens.Play; +using osu.Game.Skinning; using osuTK; namespace osu.Game.Rulesets.Osu.Tests @@ -21,7 +30,7 @@ namespace osu.Game.Rulesets.Osu.Tests [Cached] private GameplayBeatmap gameplayBeatmap; - private ClickingCursorContainer lastContainer; + private OsuCursorContainer lastContainer; [Resolved] private OsuConfigManager config { get; set; } @@ -48,12 +57,10 @@ namespace osu.Game.Rulesets.Osu.Tests { config.SetValue(OsuSetting.AutoCursorSize, true); gameplayBeatmap.BeatmapInfo.BaseDifficulty.CircleSize = val; - Scheduler.AddOnce(recreate); + Scheduler.AddOnce(() => loadContent(false)); }); - AddStep("test cursor container", recreate); - - void recreate() => SetContents(() => new OsuInputManager(new OsuRuleset().RulesetInfo) { Child = new OsuCursorContainer() }); + AddStep("test cursor container", () => loadContent(false)); } [TestCase(1, 1)] @@ -68,7 +75,7 @@ namespace osu.Game.Rulesets.Osu.Tests AddStep($"adjust cs to {circleSize}", () => gameplayBeatmap.BeatmapInfo.BaseDifficulty.CircleSize = circleSize); AddStep("turn on autosizing", () => config.SetValue(OsuSetting.AutoCursorSize, true)); - AddStep("load content", loadContent); + AddStep("load content", () => loadContent()); AddUntilStep("cursor size correct", () => lastContainer.ActiveCursor.Scale.X == OsuCursorContainer.GetScaleForCircleSize(circleSize) * userScale); @@ -82,18 +89,47 @@ namespace osu.Game.Rulesets.Osu.Tests AddUntilStep("cursor size correct", () => lastContainer.ActiveCursor.Scale.X == userScale); } - private void loadContent() + [Test] + public void TestTopLeftOrigin() { - SetContents(() => new MovingCursorInputManager + AddStep("load content", () => loadContent(false, () => new SkinProvidingContainer(new TopLeftCursorSkin()))); + AddAssert("cursor top left", () => lastContainer.ActiveCursor.Origin == Anchor.TopLeft); + } + + private void loadContent(bool automated = true, Func skinProvider = null) + { + SetContents(() => { - Child = lastContainer = new ClickingCursorContainer - { - RelativeSizeAxes = Axes.Both, - Masking = true, - } + var inputManager = automated ? (InputManager)new MovingCursorInputManager() : new OsuInputManager(new OsuRuleset().RulesetInfo); + var skinContainer = skinProvider?.Invoke() ?? new SkinProvidingContainer(null); + + lastContainer = automated ? new ClickingCursorContainer() : new OsuCursorContainer(); + + return inputManager.WithChild(skinContainer.WithChild(lastContainer)); }); } + private class TopLeftCursorSkin : ISkin + { + public Drawable GetDrawableComponent(ISkinComponent component) => null; + public Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => null; + public ISample GetSample(ISampleInfo sampleInfo) => null; + + public IBindable GetConfig(TLookup lookup) + { + switch (lookup) + { + case OsuSkinConfiguration osuLookup: + if (osuLookup == OsuSkinConfiguration.CursorCentre) + return SkinUtils.As(new BindableBool(false)); + + break; + } + + return null; + } + } + private class ClickingCursorContainer : OsuCursorContainer { private bool pressed; From 89ce8f290f3e074d6b762cbd6e30aeae5d35cf9c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 14 Apr 2021 12:28:37 +0900 Subject: [PATCH 149/203] Add simple acceleration to volume metre adjustments --- osu.Game/Overlays/Volume/VolumeMeter.cs | 34 +++++++++++++++++++++---- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/Volume/VolumeMeter.cs b/osu.Game/Overlays/Volume/VolumeMeter.cs index 5b997bbd05..57485946c1 100644 --- a/osu.Game/Overlays/Volume/VolumeMeter.cs +++ b/osu.Game/Overlays/Volume/VolumeMeter.cs @@ -13,6 +13,7 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.UserInterface; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; +using osu.Framework.Threading; using osu.Framework.Utils; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; @@ -225,7 +226,7 @@ namespace osu.Game.Overlays.Volume private set => Bindable.Value = value; } - private const double adjust_step = 0.05; + private const double adjust_step = 0.01; public void Increase(double amount = 1, bool isPrecise = false) => adjust(amount, isPrecise); public void Decrease(double amount = 1, bool isPrecise = false) => adjust(-amount, isPrecise); @@ -233,16 +234,39 @@ namespace osu.Game.Overlays.Volume // because volume precision is set to 0.01, this local is required to keep track of more precise adjustments and only apply when possible. private double scrollAccumulation; + private double accelerationModifier = 1; + + private const double max_acceleration = 5; + + private ScheduledDelegate accelerationDebounce; + + private void resetAcceleration() => accelerationModifier = 1; + private void adjust(double delta, bool isPrecise) { - scrollAccumulation += delta * adjust_step * (isPrecise ? 0.1 : 1); + // every adjust increment increases the rate at which adjustments happen up to a cutoff. + // this debounce will reset on inactivity. + accelerationDebounce?.Cancel(); + accelerationDebounce = Scheduler.AddDelayed(resetAcceleration, 150); + + delta *= accelerationModifier; + accelerationModifier = Math.Min(max_acceleration, accelerationModifier * 1.2f); var precision = Bindable.Precision; - while (Precision.AlmostBigger(Math.Abs(scrollAccumulation), precision)) + if (isPrecise) { - Volume += Math.Sign(scrollAccumulation) * precision; - scrollAccumulation = scrollAccumulation < 0 ? Math.Min(0, scrollAccumulation + precision) : Math.Max(0, scrollAccumulation - precision); + scrollAccumulation += delta * adjust_step * 0.1; + + while (Precision.AlmostBigger(Math.Abs(scrollAccumulation), precision)) + { + Volume += Math.Sign(scrollAccumulation) * precision; + scrollAccumulation = scrollAccumulation < 0 ? Math.Min(0, scrollAccumulation + precision) : Math.Max(0, scrollAccumulation - precision); + } + } + else + { + Volume += Math.Sign(delta) * Math.Max(precision, Math.Abs(delta * adjust_step)); } } From 8282f38eb708884e765e14148aa254b21db5f4fd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 14 Apr 2021 13:10:45 +0900 Subject: [PATCH 150/203] Fix volume controls not supporting key repeat --- osu.Game/Extensions/DrawableExtensions.cs | 8 +++-- .../Overlays/Volume/VolumeControlReceptor.cs | 32 +++++++++++++++---- 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/osu.Game/Extensions/DrawableExtensions.cs b/osu.Game/Extensions/DrawableExtensions.cs index 1790eb608e..67b9e727a5 100644 --- a/osu.Game/Extensions/DrawableExtensions.cs +++ b/osu.Game/Extensions/DrawableExtensions.cs @@ -9,6 +9,9 @@ namespace osu.Game.Extensions { public static class DrawableExtensions { + public const double REPEAT_INTERVAL = 70; + public const double INITIAL_DELAY = 250; + /// /// Helper method that is used while doesn't support repetitions of . /// Simulates repetitions by continually invoking a delegate according to the default key repeat rate. @@ -19,12 +22,13 @@ namespace osu.Game.Extensions /// The which is handling the repeat. /// The to schedule repetitions on. /// The to be invoked once immediately and with every repetition. + /// The delay imposed on the first repeat. Defaults to . /// A which can be cancelled to stop the repeat events from firing. - public static ScheduledDelegate BeginKeyRepeat(this IKeyBindingHandler handler, Scheduler scheduler, Action action) + public static ScheduledDelegate BeginKeyRepeat(this IKeyBindingHandler handler, Scheduler scheduler, Action action, double initialRepeatDelay = INITIAL_DELAY) { action(); - ScheduledDelegate repeatDelegate = new ScheduledDelegate(action, handler.Time.Current + 250, 70); + ScheduledDelegate repeatDelegate = new ScheduledDelegate(action, handler.Time.Current + initialRepeatDelay, REPEAT_INTERVAL); scheduler.Add(repeatDelegate); return repeatDelegate; } diff --git a/osu.Game/Overlays/Volume/VolumeControlReceptor.cs b/osu.Game/Overlays/Volume/VolumeControlReceptor.cs index 3b39b74e00..34b86b2f81 100644 --- a/osu.Game/Overlays/Volume/VolumeControlReceptor.cs +++ b/osu.Game/Overlays/Volume/VolumeControlReceptor.cs @@ -6,6 +6,8 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Input; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; +using osu.Framework.Threading; +using osu.Game.Extensions; using osu.Game.Input.Bindings; namespace osu.Game.Overlays.Volume @@ -15,8 +17,30 @@ namespace osu.Game.Overlays.Volume public Func ActionRequested; public Func ScrollActionRequested; - public bool OnPressed(GlobalAction action) => - ActionRequested?.Invoke(action) ?? false; + private ScheduledDelegate keyRepeat; + + public bool OnPressed(GlobalAction action) + { + switch (action) + { + case GlobalAction.DecreaseVolume: + case GlobalAction.IncreaseVolume: + keyRepeat?.Cancel(); + keyRepeat = this.BeginKeyRepeat(Scheduler, () => ActionRequested?.Invoke(action), 150); + return true; + + case GlobalAction.ToggleMute: + ActionRequested?.Invoke(action); + return true; + } + + return false; + } + + public void OnReleased(GlobalAction action) + { + keyRepeat?.Cancel(); + } protected override bool OnScroll(ScrollEvent e) { @@ -27,9 +51,5 @@ namespace osu.Game.Overlays.Volume public bool OnScroll(GlobalAction action, float amount, bool isPrecise) => ScrollActionRequested?.Invoke(action, amount, isPrecise) ?? false; - - public void OnReleased(GlobalAction action) - { - } } } From 65a1270f9a4dc5d6ba1680dad869736e8714f31f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 14 Apr 2021 14:16:18 +0900 Subject: [PATCH 151/203] Hide top-right HUD overlay elements as part of HUD visibility --- osu.Game/Screens/Play/HUDOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index 3dffab8102..31b49dfbe9 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -74,7 +74,7 @@ namespace osu.Game.Screens.Play private bool holdingForHUD; - private IEnumerable hideTargets => new Drawable[] { visibilityContainer, KeyCounter }; + private IEnumerable hideTargets => new Drawable[] { visibilityContainer, KeyCounter, topRightElements }; public HUDOverlay(ScoreProcessor scoreProcessor, HealthProcessor healthProcessor, DrawableRuleset drawableRuleset, IReadOnlyList mods) { From ad53ababe89f49a2650a4bcd4eb5368052f9413f Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 14 Apr 2021 08:16:45 +0300 Subject: [PATCH 152/203] Fix wrong default Ah, soz --- osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursor.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursor.cs index 8fe40f801b..7a8555d991 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursor.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursor.cs @@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy [BackgroundDependencyLoader] private void load(ISkinSource skin) { - bool centre = skin.GetConfig(OsuSkinConfiguration.CursorCentre)?.Value ?? false; + bool centre = skin.GetConfig(OsuSkinConfiguration.CursorCentre)?.Value ?? true; spin = skin.GetConfig(OsuSkinConfiguration.CursorRotate)?.Value ?? true; InternalChildren = new[] From b060b59dcf0c2f0bf73979b398aa2000007d6fd7 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 14 Apr 2021 08:17:35 +0300 Subject: [PATCH 153/203] Return null values instead of throwing NIE --- osu.Game.Rulesets.Osu.Tests/TestSceneCursorTrail.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneCursorTrail.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneCursorTrail.cs index 8fd13c7417..0ba97fac54 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneCursorTrail.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneCursorTrail.cs @@ -78,7 +78,7 @@ namespace osu.Game.Rulesets.Osu.Tests RelativeSizeAxes = Axes.Both; } - public Drawable GetDrawableComponent(ISkinComponent component) => throw new NotImplementedException(); + public Drawable GetDrawableComponent(ISkinComponent component) => null; public Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) { @@ -98,9 +98,9 @@ namespace osu.Game.Rulesets.Osu.Tests return null; } - public ISample GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException(); + public ISample GetSample(ISampleInfo sampleInfo) => null; - public IBindable GetConfig(TLookup lookup) => throw new NotImplementedException(); + public IBindable GetConfig(TLookup lookup) => null; public event Action SourceChanged { From daf198fa77f18a6872213477c37abcc66f933ad2 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 14 Apr 2021 08:18:24 +0300 Subject: [PATCH 154/203] Add osu! 2007 skin cursor for testing purposes --- .../Resources/old-skin/cursor.png | Bin 0 -> 10496 bytes .../Resources/old-skin/cursortrail.png | Bin 0 -> 3763 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100755 osu.Game.Rulesets.Osu.Tests/Resources/old-skin/cursor.png create mode 100755 osu.Game.Rulesets.Osu.Tests/Resources/old-skin/cursortrail.png diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/old-skin/cursor.png b/osu.Game.Rulesets.Osu.Tests/Resources/old-skin/cursor.png new file mode 100755 index 0000000000000000000000000000000000000000..fe305468fe9efc47be6e9e793baabdab04aab4da GIT binary patch literal 10496 zcmV+bDgV}qP)j{00004XF*Lt006O$ zeEU(80000WV@Og>004&%004{+008|`004nN004b?008NW002DY000@xb3BE2000U( zX+uL$P-t&-Z*ypGa3D!TLm+T+Z)Rz1WdHz3$DNjUR8-d%htIutdZEoQ(iwV_E---f zE+8EQQ5a?h7|H;{3{7l^s6a#!5dlSzpnw6Rp-8NVVj(D~U=K(TP+~BOsHkK{)=GSN zdGF=r_s6~8+Gp=`_t|@&wJrc8PaiHX1(pIJnJ3@}dN|Wpg-6h_{Qw4dfB~ieFj?uT zzCrH6KqN0W7kawL3H*!R3;{^|zGdj?Pp5H0=h0sk8Wyh&7ga7GLtw0fuTQ>mB{3?=`JbBsZ3rr0E=h-EE#ca>7pWA znp#_08k!lIeo?6Zy7)IG?(HJI3i#YJh}QRq?XUb&>HuKOifXg#4_nNB06Mk;Ab0-{ zo8}<^Bt?B|zwyO+XySQ^7YI^qjEyrhGmW?$mXWxizw3WG{0)8aJtOgUzn6#Z%86wP zlLT~e-B>9}DMCIyJ(bDg&<+1Q#Q!+(uk%&0*raG}W_n!s* z`>t?__>spaFD&Aut10z!o?HH?RWufnX30 z)&drY2g!gBGC?lb3<^LI*ah~2N>BspK_h4ZCqM@{4K9Go;5xVo?tlki1dM~{UdPU)xj{ZqAQTQoLvauf5<ZgZNI6o6v>;tbFLDbRL8g&+C=7~%qN5B^ zwkS_j2#SSDLv276qbgBHQSGQ6)GgE~Y6kTQO-3uB4bV1dFZ3#O96A$SfG$Tjpxe-w z(09<|=rSYbRd;g|%>I!rO<0Hzgl9y5R$!^~o_Sb3}g)(-23Wnu-`0_=Y5 zG3+_)Aa)%47DvRX;>>XFxCk5%mxn9IHQ~!?W?(_!4|Qz6*Z? zKaQU#NE37jc7$L;0%0?ug3v;^M0iMeMI;i{iPppbBA2*{SV25ayh0o$z9Y$y^hqwH zNRp7WlXQf1o^+4&icBVJlO4$sWC3|6xsiO4{FwY!f+Arg;U&SA*eFpY(JnD4@j?SR-`K0DzX#{6;CMMSAv!Fl>(L4DIHeoQ<_y) zQT9+yRo<_BQF&U0rsAlQpi-uCR%J?+qH3?oRV`CJr}~U8OLw9t(JSaZ^cgiJHBU96 zTCG~Y+Pu1sdWd?SdaL>)4T1(kBUYnKqg!J}Q&rPfGgq@&^S%~di=h>-wNI;8Yff87 zJ4}0Dt zz%@8vFt8N8)OsmzY2DIcLz1DBVTNI|;iwVK$j2zpsKe-mv8Hi^@owW@<4-0QCP^ms zCJ#(yOjnrZnRc1}YNl_-GOIGXZB90KH{WR9Y5sDV!7|RWgUjw(P%L~cwpnyre6+N( zHrY-t*ICY4 zUcY?IPTh`aS8F$7Pq&Y@KV(1Rpyt4IsB?JYsNu+VY;c@#(sN31I_C7k*~FRe+~z#z zV&k&j<-9B6>fu`G+V3Xg7UEXv_SjwBJ8G6!a$8Ik+VFL5OaMFr+(FGBh%@F?24>HLNsjWR>x%^{cLj zD}-~yJ0q|Wp%D!cv#Z@!?_E6}X%SfvIkZM+P1c&LYZcZetvwSZ8O4k`8I6t(i*Abk z!1QC*F=u1EVya_iST3x6tmkY;b{Tt$W5+4wOvKv7mc~xT*~RUNn~HacFOQ$*x^OGG zFB3cyY7*uW{SuEPE+mB|wI<_|qmxhZWO#|Zo)ndotdxONgVci5ku;mMy=gOiZ+=5M zl)fgtQ$Q8{O!WzMgPUHd;& z##i2{a;|EvR;u1nJ$Hb8VDO;h!Im23nxdNbhq#CC)_T;o*J;<4AI2QcIQ+Cew7&Oi z#@CGv3JpaKACK^kj2sO-+S6#&*x01hRMHGL3!A5oMIO8Pjq5j^Eru<%t+dvnoA$o+&v?IGcZV;atwS+4HIAr!T}^80(JeesFQs#oIjrJ^h!wFI~Cpe)(drQ}4Me zc2`bcwYhrg8sl2Wb<6AReHMLfKUnZUby9Y>+)@{ z+t=@`yfZKqGIV!1a(Lt}`|jkuqXC)@%*Rcr{xo>6OEH*lc%TLr*1x5{cQYs>ht;Of}f>-u708W z;=5lQf9ac9H8cK_|8n8i;#cyoj=Wy>x_j1t_VJtKH}i9aZ{^<}eaCp$`#$Xb#C+xl z?1zevdLO$!d4GDiki4+)8~23s`{L#u!T$Qp02p*d zSaefwW^{L9a%BK;VQFr3E^cLXAT%y8E;js(W8VM(9t}xEK~!i%?OO*}RM)mX(ND+`O3Zh~`6ct59MFc?wQ4~dqM(hZ zX(ln2X!NRa-oGZ0OY(UWljuv{_r3Fd%fl&qt^eO^?X}n5XJr2WcpZnYmZcM)cCoHz zGm>OBW1P?CC#zkc43Rba>dQ=|}HcF61)mVttlr7M~^UFJ_xW%X!9uDm~+#R(eK0wtPts zd%ncKE5wm64RYX1dfVe{%a;aQ>4^g@^u&HDp4dmu5&0OiL_R{67@c9Zx=7&a4oZCBWY(dZ9aZ@~dWQ?(Q8JKG65Fcrx3icB# z{2dL&?q+&IjhM@~HDL6uxC~dyZb$Z-4@okM5e2dtQGg!HI8wxx#G3LX@d)ifo?^3% z5V>_uQWy8a5$?eixnapGi$+bUUATV7l{csq{5hmP9$-(P6AT)v@Q*LLaq<*PThE{uqonD6O3aFnBKOuUs{sJ~3*;~+3`HPsW? z3t5I;dBDYaJ$ZnL8*0$Qjs{$jmqgFFmyJ*s=_@r)je_0?B(IM+RV`0x;& zaZUGk({sGH2k+N^bLUb0o%_#se|ukB^X)zD>O1#-SbE^#$vF`*Sra_{ho#zSVuBTB zy_`i74|6?{ixF4krpE}}IgHT%6?Q;}$3dJUaFObXeeJ}`uwG_1{RcU@kIU^5QcylV zuYTo)tB*H5daT{9)oRJZd%yqw`+AV6y>;u>j!!@Rbo2G=*VkUTa%J_!ix*d2x^!vv zM<0E(4$tawUQgG!UybLh@ZNg7zXi`~s578vH}vg*-i`lyqOJP)lgFibMVqDt^cgkO z*}Z?5nN^UxvBFQSCvp>WgdPSA9~~eLdj%ap$xTPx*?=o_ljs?F*^6XBK_H&)(PR9q z$fN};x7L2J^^?!FyKmpsB9xyV0@wzCuKVn>&$eE>c5UtX^XHep^UgcvhYlT@vwQdM z!aaNT6g4z7%xi9LUW9k{(HYls={}wp;Jvx@KH62G{c60k5&E`5Z_U5{^_23$im$%a z&fk0RQeI?S?g$Uxv?z1y2u~xaALvNej~W5;0Ko3}@WT(+o<4ng$$ zODE2nHEYzwi4)Tc3kx%sEnAjTQ&Us$`RAWE;yjbCaep*D$9tLdKH5!3`y%LB2z|?; zcMbG!fSnD`o;_Rn#W&jdC(qx`PfDGY>ESap#?mgbyIAgz4)8(;c<2N7pGbmKNbp_t zScXnw17mL+u_7=)Z5unhd#^ENgGMgdbngA{_kDUxd-U6T&yRoi-FHnGxBC&YThE?7 zTeWZBzS*l*t(uabpPxB$v zM=bAYWgzwtv-rpXd{0M!*xfScv5QzDmpJ!7iS|OA-f_Zfd*qpyBhc zwM`g{r;wnI!z=qQU%tGyv9WRCx^?UF0D4SHN=mAyr)Qi%AP9%XAV^P$AFd;!qM}l! zPMtdDpa1;lF`Tn-ol5ud+!xXd5=iaPJ{EcgLSF{-=0JZD>@0!3YS>*1`>W6ai{HKa zU14HM!B`iMv^b?@xHs@x@I>w+mcWC@i2J@|N!(GV>)WxB;P@PYyHa4{=b=!CCA)YJ zn-vjXu<>y7Cx?*DTfV*fv<(1GfWSVC&$aMQX=P>QM0hOS$Hyn3opd0?AL0k;4)KPB zM?^#r`LSr*g7Y|Br{X@5r+0iH^nMRWFX#z{zF3L`=pO?+`LI`xwku(O{oVUd*L;0P zyQJylCq;eZa>u%OrS(&&L*0Sfj3e|GFh)VNKzd2y$h- zEOy44^*hd!$6GKLpM~Mm0MKyZ!i9Bpb#-$~OH0QmB_$0~tJVFWB^VL_AreG}NOedD z`SECfg3iAn-yswQ)SmPNL2qB^?+-f}us02MsRJkiHUZDtTer1KHtjrH6c&@4Y3tNK z9Jn=lMt*XRaS#?pF&xG;`!_m(ye2pdb0JgB$1ZG4uS@5p5ZbGPjK^ecG$ z-m`Paylr28`Q=;izy<&*L6RC58ylO7F&hOfga^t zloMbl3HGQ1@?d`vaI6KM&3EoUTMx%n7M5%*aQ7WH6u1LKijXcmaj+4~D1yhxNBjnP zTAk^78HefNy0)Gy_l}R0o{ffp=wm;_z7x&WULz%25gDdY`3#Ck#xjm#oE*}*ftETrG0y3fCQ)Cy&n z+P(gEYisLT6dnZ#yCIl{BGIxV!MzCVi{#Ic?+}VGng%KSQ-LQJxGI5fCvYAF-uf@T zdb9!f=Q(&~4wG3X^x%mj)GToVUzgW@c2JfV`s;D|u5tsBub;VX^l0y(ac@p4Sl@_M z)j5o%%jng!SFc{(hmvzKd0nH?B%&ovX*AAX1n@t}s~}H8b_0Qfyq^VJrNFltI1dBw zkvrdMcTXx<_h$EyNx5qK0a5x!;SL;eKQW`o;{H_6=nh?BW3A6IbhQwf^a!x(mppmr;iw$JDYFHTUfF^z>ovUhh1YzpT6}@*Upq3q0w-H52$&1LuC= zJ$CnjwxQwJKQ>48FDmWomOfZ0iS}ZPqU4M`L+_{UblpLJ7&;4h0%tp8c|eTDJ$Y6_ z%Jf|y{^P;P2j6QiqL#jZW#$pAwyKd~^H7HmKr2dezt`&|uZlbg*$14EWZ=pLzNNsq z2Y8zwKGMGZ!9O0>C8ZTsxp<8jW28t3SBJ571QKn_>nD&Y zGhNgD7rp;m0vyqVr?|LyQf+PRLXh7D^6vomS>SKlytQ_>qi5dqYD*#j<6~repn` zfo13*cr3MD=;v7ZS@|#MwTnOjv8*LO!DNI43IL)BCc-S>ke@;m&5mv3gm&_s;(&` zyaPv6w4S-V2aQ`EKYrW>Wi20l^wF;7=H@qQYHDVem6c6FxQ>O4gN&!peOVz-Lq{2= zrZ=GXDxK+??!V~$ml5EZhEAM|xqj7+8#fNzy?gf*a9;raQ)ka#-R~csy;!9gGlH+| z@69$IAZL_iyq}X7_LK`0VczCW>BD_PvMSELdyUA`hQ-;34ad_tro#~qHzv%tn5VA>lP6FRK%<=Cb(_e<( zt61i~PvkFL`shGV^pr|7=dmLNs)5}>Ucs1E=>40#ESt*~g-Hdb(O%}x8N<8-$1H^B zYtgtFJ=h9>P4N6qc)ki=D*Bsxz7(Ec1JCb==UajMJtBYR{0IAchUY9XbIH!okvAD8 zXUwZQd0sx9#}P+M1*-nuN~f%J*B+zhA355(8;y^l7uzuA-^Q5VhB3boV}2UO*vP-x zn9szR&u<^|_2|@-z80qqqYW&7Wi@n2K6Per!RLAI|zwy#6B zZv}3h=QnPt+pqD>D3>@)7|J&v>B*L5$r!7hoyc47w1pElINQ6Of$QQY;sbqU56FL#1Gy^o=4(1hl;5c zmD3(nP_(F_3NH>uslub><%co*x{Vu;Iaj2fDR9GU)kF zKKbMzYUdTGp{G&U!)PDKAF1+Qb!{ICJSo7H4}7bD^C<9A$X^2fmXOf64VF%6Q^6~m zYno$+AuiJK{fWF6E-cF*^sT6YsHgH4|4D*93MMq@rvRF+YjD*psVjMVh%n1;fEjAVQQjX zoNTP4lVH4K-|j`={*$~a^12lra3llIB;cw9KKf309(XTfQPWyHf6M;p0VS38?o&tb zOeXoV6?04(+gbx1-=E0);KJdc0V7|e;EHE@+k2G_iAvkBFm?3uT{pjc+=38#mv(Xh z_AQ!AtE;PLw(sN+;J;!ghwRZ;ooL`l2cAOUS`U54fRo1hr7ymI)RdC3s3v0I8?)^^ z3KO{EDNc;M+=S_Rgx2%ztGXX|HQ0<(6OU1?6LTc9?UkC+X!nQ}(}Lp5s_XWjYJ{@3 zAAb1Z0pJafbvH!B0mn46dJlP?8VV<6zS}@8Ld`9yQ?c{XY%KEVbFz&bU*^0#~QI}F*bFU@o zPCW}s5>v)+gTiJgynsaVF74i)K7D%4>eZ`fjl`i=t7-R^wzFR6?rn#35D$Ufc$m)w zj$+{11YFI)r}H}Swj`xwZPN6}nPKKu94E5QcVf%tU^TZ-NmY`1V`7XX)&?M8y`G)bd}S4xyA<41V(%qTA| zSw^*f)3ayK&LOji(NPM}GOq>P194lYf~nHk90S#L44CoU%LY zE~KdqI8Ff172u+|y}7t#=?;&c>G_tL%ow36+leD7FkzGnd5mq%uMqDzoSN8-#b!OW zyi#JQp66`pvM|oJd;XMw(4ouQ+S(4lzzM4Q+P~5s!o79W0r13x_OG-t&_YR&DnJ7M z%PR0?$#;Y~%_dCsKU9#*U&uQ zlr5dB&zQgYlE2maaqM##v*iMw*?cpR)m$HQOF{6j)wjj zuu}nhJ3+h&_AdhmW%t%=H*W2Z8<_iscTjGYxn^dN$ghW~b0tN#iZ1zq?tybN`^3*KOBp%^D* zF8O`kdghG|>^re!=QFS3XUzPxR#^joR6A& z7JX0vk#V%`jq~{B%a_y6O9|Sap&b}pQ%B+P#L+nC&>{ux=Aivj=-C2&hoJW)^uG@~ z*IKbcNAk0*;xY0W z0=DsZrARs2*|yunaNpqRBLc$4<;NzCS=w~`{8j*|hrER_JO;14jY{hXLV6EC*I<0E z!)mLFJ}6+lJ-4>Dw*2tn!<8Ua4Kf#SUWMyrxW5?B7vQ}ryuSwR=qGK5pyvei(cauO z=>G_Iu0l?}-F%@we!!%PkeIpI!Ex)NU3#r{7F)d`WhqvG_&Sv9Z~Z~y9S3y)Wq(x> zkC9FgaHUxap)$i!)g`O9bN8G9!Ts`cB4bC-t*BVM5+2zCP<4*8nMbc zh-Ky;th0Avyw*^CaP{idT{mvr*b73JY0nMUb+}&(sl$8peiPcAL3{d5z=zOx1A4DR z-h-W{#fum3h)tee7L`(+=^auXVd1i-tJwNYDNnslA5|)wu{-c5iFX`+=m7e^FQ$1s zMlzPqG0Bn|%0}5(I!*R=?^O`x;XigrcmJeG{o;nqJO0jzY5=89ph!3ZPtlJPDH@L5 zyLYb%9;B#v`}XbIEqCwUr4J%kXtNR5r*Qu?;FJDEwkI2)$2`I%B^~g<=)ry^K<%-KPvh^Gvf`|vI#~)RgRh5 zda9$^aas?1ue^S)-G>eh=pCAqn3y;Z=uM`bzqE{w74w%kkQxY<(NWvda#qE?Tw zKJc1|cO29K7E2KYGxabF@Hxgs5+lnJ3$t!ZUCkVG{48xlLp9yPQ~d+`PmGEiKC5u{ z(u&h(udMj??t}FpLfc|AEE*tGDj$cOgq()ZceOJR`pR2RYn_p3w|JFezuw6ZiQLjQ7yh>l+ za9Z%q#1EMc&&|!t%d6b9Y168U z7cXw5G5)~^AMC~XAg&v6|1CT}i1+sNi%Z>+kUDLBWdGvj-9rmz+j`}Xk=ss<5h(LL z1?nnGk?nR9{Vx0USyq^i?c4sBh<84^VUa+yfpsIMLjW+q_3m1Z(=u(UPC>*2aoF&7(F92BB8Wzbn>FGi1^BYz^G-O-2)eO z^XNIx(YgC9Prrm|Au)xMLXuXD?G~~k#oTjckdf_bSGHoaily8mWK>6ZOqW(RnR*tBwC?=K|%%Y&vwuX}8!+ouS_D?G;HoIYdPV#t!z^I6i3LXPqc zG1q*)Oy8>5jBhj3R$w#5RcMp#Ewmo%2k9lS$_n6Hjq}$xAM4FC&356)Cfl*a1(s~_ zLOCN{W6Y@b@R=?r^%<9sIgID+zY+1y2Ym?epe?a)P+i^RFxFQ%jO7VPJ%_Q_$YraS z7;@EfL_G666P{|aoTnVGgs6C`Y$aEfqhOm(ld?<;jTz-4A){Ptz?knt7C!|~U*_oa z>OM65-4BWcI%rsU>pBF(<8vg+_t=aTR*dQzB+E@a#%!&gPE^%;jC!XYW7EiEy0#Og zcGRA9b?Ey)f4GB)+s}lp6S_~GMwG^ZCvhQu%lQ0000004&%004{+008|`004nN004b?008NW002DY000@xb3BE2000U( zX+uL$P-t&-Z*ypGa3D!TLm+T+Z)Rz1WdHz3$DNjUR8-d%htIutdZEoQ0#b(FyTAa_ zdy`&8VVD_UC<6{NG_fI~0ue<-nj%P0#DLLIBvwSR5EN9f2P6n6F&ITuEN@2Ei>|D^ z_ww@lRz|vC zuzLs)$;-`!o*{AqUjza0dRV*yaMRE;fKCVhpQKsoe1Yhg01=zBIT!&C1$=TK@rP|Ibo3vKKm@PqnO#LJhq6%Ij6Hz*<$V$@wQAMN5qJ)hzm2h zoGcOF60t^#FqJFfH{#e-4l@G)6iI9sa9D{VHW4w29}?su;^hF~NC{tY+*d5%WDCTX za!E_i;d2ub1#}&jF5T4HnnCyEWTkKf0>c0%E1Ah>(_PY1)0w;+02c53Su*0<(nUqK zG_|(0G&D0Z{i;y^b@OjZ+}lNZ8Th$p5Uu}MTtq^NHl*T1?CO*}7&0ztZsv2j*bmJyf3G7=Z`5B*PvzoDiKdLpOAxi2$L0#SX*@cY z_n(^h55xYX#km%V()bZjV~l{*bt*u9?FT3d5g^g~#a;iSZ@&02Abxq_DwB(I|L-^b zXThc7C4-yrInE_0gw7K3GZ**7&k~>k0Z0NWkO#^@9q0fwx1%qj zZ=)yBuQ3=54Wo^*!gyjLF-e%Um=erBOdIALW)L%unZshS@>qSW9o8Sq#0s#5*edK% z>{;v(b^`kbN5rY%%y90wC>#%$kE_5P!JWYk;U;klcqzOl-UjcFXXA75rT9jCH~u<) z0>40zCTJ7v2qAyk54cquI@7b&LHdZ`+zlTss6bJ7%PQ)z$cROu4wBhpu-r)01) zS~6}jY?%U?gEALn#wiFzo#H}aQ8rT=DHkadR18&{>P1bW7E`~Y4p3)hWn`DhhRJ5j z*2tcg9i<^OEt(fCg;q*CP8+7ZTcWhYX$fb^_9d-LhL+6BEtPYWVlfKTBusSTASKKb%HuWJzl+By+?gkLq)?+BTu761 zjmyXF)a;mc^>(B7bo*HQ1NNg1st!zt28YLv>W*y3CdWx9U8f|cqfXDAO`Q48?auQq zHZJR2&bcD49Ip>EY~kKEPV6Wm+eXFV)D)_R=tM0@&p?(!V*Qu1PXHG9o^ zTY0bZ?)4%01p8F`JoeS|<@=<@RE7GY07EYX@lwd>4oW|Yi!o+Su@M`;WuSK z8LKk71XR(_RKHM1xJ5XYX`fk>`6eqY>qNG6HZQwBM=xi4&Sb88?zd}EYguc1@>KIS z<&CX#T35dwS|7K*XM_5Nf(;WJJvJWRMA($P>8E^?{IdL4o5MGE7bq2MEEwP7v8AO@ zqL5!WvekBL-8R%V?zVyL=G&{be=K4bT`e{#t|)$A!YaA?jp;X)-+bB;zhj`(vULAW z%ue3U;av{94wp%n<(7@__S@Z2PA@Mif3+uO&y|X06?J#oSi8M;ejj_^(0<4Lt#wLu#dYrva1Y$6_o(k^&}yhSh&h;f@JVA>W8b%o zZ=0JGnu?n~9O4}sJsfnnx7n(>`H13?(iXTy*fM=I`sj`CT)*pTHEgYKqqP+u1IL8N zo_-(u{qS+0<2@%BCt82d{Gqm;(q7a7b>wu+b|!X?c13m#p7cK1({0<`{-e>4hfb-U zsyQuty7Ua;Ou?B?XLHZaol8GAb3Wnxcu!2v{R_`T4=x`(GvqLI{-*2AOSimk zUAw*F_TX^n@STz9kDQ z$NC=!KfXWC8h`dn#xL(D3Z9UkR7|Q&Hcy#Notk!^zVUSB(}`#4&lYA1f0h2V_PNgU zAAWQEt$#LRcH#y9#i!p(Udq2b^lI6wp1FXzN3T;~FU%Lck$-deE#qz9yYP3D3t8{6 z?<+s(e(3(_^YOu_)K8!O1p}D#{JO;G(*OVf32;bRa{vGf5dZ)S5dnW>Uy%R+02p*d zSaefwW^{L9a%BK;VQFr3E^cLXAT%y8E;js(W8VM(1Hef{K~!i%-I`5n6hRb4gNq>I zLb5PO2;xpeL=i#Uh=?MJD58j>h~oeM3uB#-H{5=?^}2eJAoSuurn>6YJyV(LkD0yA zW^)O8&2TO0`geuQFkXP+gNbD@wkyCG~n9>=s2xM{U;6Pb2%)wu>&98 z7Y!BZ3A`I1kI7QZ!+gx^@|?joCirmPZOln%(Dwsqf34%Bn16PYu#Ex*9yThGp1_BK z+&4=(A##*Lwo!P{$Rd@53;t1X$924uQX%rVoF~}EQ|>e>5e=c?G(3C~@-ZE!^(rB9 zxx8H&PGs!;b0SVa!_T4PvQ$rqT;w~G`%FXQT7z9=sYnF;ynrkgeF%CD#Et+tU*Uzl}aSu zSts$WK>MjsNX-dFM)GdAYn92X!a$XwaLDvTa_H}d+@}(XIaNl5Caa?|TyuqSwJJ;$ zGEPK;|1jwBo>0s&vO46R$Xsp2R@_?^3hSy=B2FSRA{290t3z(`Z)J`f`CqKJDwT+a z{aK*>Mue)>AvgKAGF%DJDf#+8?sQ0RhbZSGAa}@ znGl+jQK6p95i-@bd>$DU2~_B!WsX<3GOPN`tZFw~)p-V1a|Hmax;9(YRr9LuXJ9pV zny{*80IPaB19^2ughDDpWvL$8KO{y(LWZ2T8r%FT60GKlCh!b5cD6a8N@@frF((4# z8+z6ZdL_L?NTo*5aib#SvR@6JX%O63e@;lHvKaKjgO1aBl?b_9-cp8Lg74-lp-L*~ zg%5Om)a;3nXS;i4Y@@(Hu6wf3oD{g=LGJ4$7KwmuOi&7Ec9A(L(4al?7$?De_QUS5jRpK-LeE24%86CzIITy0=DD Date: Wed, 14 Apr 2021 08:20:18 +0300 Subject: [PATCH 155/203] Apply `CursorCentre` to old-style legacy cursor trail --- .../Skinning/Legacy/LegacyCursorTrail.cs | 12 ++++++++- .../UI/Cursor/CursorTrail.cs | 25 ++++++++++++++++--- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorTrail.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorTrail.cs index af9ea99232..0025576325 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorTrail.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursorTrail.cs @@ -26,7 +26,17 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy Texture = skin.GetTexture("cursortrail"); disjointTrail = skin.GetTexture("cursormiddle") == null; - Blending = !disjointTrail ? BlendingParameters.Additive : BlendingParameters.Inherit; + if (disjointTrail) + { + bool centre = skin.GetConfig(OsuSkinConfiguration.CursorCentre)?.Value ?? true; + + TrailOrigin = centre ? Anchor.Centre : Anchor.TopLeft; + Blending = BlendingParameters.Inherit; + } + else + { + Blending = BlendingParameters.Additive; + } if (Texture != null) { diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs index 0b30c28b8d..b55575696e 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs @@ -5,6 +5,7 @@ using System; using System.Diagnostics; using System.Runtime.InteropServices; using osu.Framework.Allocation; +using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Batches; using osu.Framework.Graphics.OpenGL.Vertices; @@ -31,6 +32,8 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor private double timeOffset; private float time; + protected Anchor TrailOrigin = Anchor.Centre; + public CursorTrail() { // as we are currently very dependent on having a running clock, let's make our own clock for the time being. @@ -197,6 +200,8 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor private readonly TrailPart[] parts = new TrailPart[max_sprites]; private Vector2 size; + private Vector2 originPosition; + private readonly QuadBatch vertexBatch = new QuadBatch(max_sprites, 1); public TrailDrawNode(CursorTrail source) @@ -213,6 +218,18 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor size = Source.partSize; time = Source.time; + originPosition = Vector2.Zero; + + if (Source.TrailOrigin.HasFlagFast(Anchor.x1)) + originPosition.X = 0.5f; + else if (Source.TrailOrigin.HasFlagFast(Anchor.x2)) + originPosition.X = 1f; + + if (Source.TrailOrigin.HasFlagFast(Anchor.y1)) + originPosition.Y = 0.5f; + else if (Source.TrailOrigin.HasFlagFast(Anchor.y2)) + originPosition.Y = 1f; + Source.parts.CopyTo(parts, 0); } @@ -237,7 +254,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor vertexBatch.Add(new TexturedTrailVertex { - Position = new Vector2(part.Position.X - size.X / 2, part.Position.Y + size.Y / 2), + Position = new Vector2(part.Position.X - size.X * originPosition.X, part.Position.Y + size.Y * (1 - originPosition.Y)), TexturePosition = textureRect.BottomLeft, TextureRect = new Vector4(0, 0, 1, 1), Colour = DrawColourInfo.Colour.BottomLeft.Linear, @@ -246,7 +263,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor vertexBatch.Add(new TexturedTrailVertex { - Position = new Vector2(part.Position.X + size.X / 2, part.Position.Y + size.Y / 2), + Position = new Vector2(part.Position.X + size.X * (1 - originPosition.X), part.Position.Y + size.Y * (1 - originPosition.Y)), TexturePosition = textureRect.BottomRight, TextureRect = new Vector4(0, 0, 1, 1), Colour = DrawColourInfo.Colour.BottomRight.Linear, @@ -255,7 +272,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor vertexBatch.Add(new TexturedTrailVertex { - Position = new Vector2(part.Position.X + size.X / 2, part.Position.Y - size.Y / 2), + Position = new Vector2(part.Position.X + size.X * (1 - originPosition.X), part.Position.Y - size.Y * originPosition.Y), TexturePosition = textureRect.TopRight, TextureRect = new Vector4(0, 0, 1, 1), Colour = DrawColourInfo.Colour.TopRight.Linear, @@ -264,7 +281,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor vertexBatch.Add(new TexturedTrailVertex { - Position = new Vector2(part.Position.X - size.X / 2, part.Position.Y - size.Y / 2), + Position = new Vector2(part.Position.X - size.X * originPosition.X, part.Position.Y - size.Y * originPosition.Y), TexturePosition = textureRect.TopLeft, TextureRect = new Vector4(0, 0, 1, 1), Colour = DrawColourInfo.Colour.TopLeft.Linear, From 6044083cf71405f9fa0a67b0d105e32ef15861d9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 14 Apr 2021 14:25:16 +0900 Subject: [PATCH 156/203] Speed up the fade of the HUD a touch --- osu.Game/Screens/Play/HUDOverlay.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index 31b49dfbe9..669c920017 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -24,9 +24,9 @@ namespace osu.Game.Screens.Play [Cached] public class HUDOverlay : Container, IKeyBindingHandler { - public const float FADE_DURATION = 400; + public const float FADE_DURATION = 300; - public const Easing FADE_EASING = Easing.Out; + public const Easing FADE_EASING = Easing.OutQuint; /// /// The total height of all the top of screen scoring elements. From b7d2821b5514f0b3e4b76064696354a0f56ae48a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 14 Apr 2021 14:51:52 +0900 Subject: [PATCH 157/203] Display the centre marker above the waveform Gives it a bit more visibility. This is where it was meant to sit, but didn't consider using a proxy drawable to make it work previously. --- .../Edit/Compose/Components/Timeline/Timeline.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs index 86a30b7e2d..aa5cc46e37 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs @@ -74,13 +74,18 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline [BackgroundDependencyLoader] private void load(IBindable beatmap, OsuColour colours, OsuConfigManager config) { + CentreMarker centreMarker; + + // We don't want the centre marker to scroll + AddInternal(centreMarker = new CentreMarker()); + AddRange(new Drawable[] { new Container { RelativeSizeAxes = Axes.Both, Depth = float.MaxValue, - Children = new Drawable[] + Children = new[] { waveform = new WaveformGraph { @@ -90,6 +95,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline MidColour = colours.BlueDark, HighColour = colours.BlueDarker, }, + centreMarker.CreateProxy(), ticks = new TimelineTickDisplay(), controlPoints = new TimelineControlPointDisplay(), new Box @@ -104,9 +110,6 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline }, }); - // We don't want the centre marker to scroll - AddInternal(new CentreMarker { Depth = float.MaxValue }); - waveformOpacity = config.GetBindable(OsuSetting.EditorWaveformOpacity); waveformOpacity.BindValueChanged(_ => updateWaveformOpacity(), true); From e543db9bee68f8d733b62f24a4c4ad8f1c046e8f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 14 Apr 2021 14:56:27 +0900 Subject: [PATCH 158/203] Use additive blending for background box Doesn't make a huge difference but this was intended. --- .../Compose/Components/Timeline/TimelineBlueprintContainer.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs index 7427473a35..7a3781a981 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs @@ -65,6 +65,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline { Colour = Color4.Black, Depth = float.MaxValue, + Blending = BlendingParameters.Additive, }); } From 4538e4b50357ea2b0d4f113e55b6fa3d491c32f2 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 14 Apr 2021 08:58:25 +0300 Subject: [PATCH 159/203] Remove wrong assert --- osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs index ed44bc7309..9a77292aff 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneGameplayCursor.cs @@ -93,7 +93,6 @@ namespace osu.Game.Rulesets.Osu.Tests public void TestTopLeftOrigin() { AddStep("load content", () => loadContent(false, () => new SkinProvidingContainer(new TopLeftCursorSkin()))); - AddAssert("cursor top left", () => lastContainer.ActiveCursor.Origin == Anchor.TopLeft); } private void loadContent(bool automated = true, Func skinProvider = null) From a2094159421cc98d47b4af23a53ae8c4358091fe Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 14 Apr 2021 16:52:29 +0900 Subject: [PATCH 160/203] Add "Barrel Roll" mod --- .../Mods/OsuModBarrelRoll.cs | 30 +++++++++++++++++++ osu.Game.Rulesets.Osu/OsuRuleset.cs | 1 + osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs | 3 ++ 3 files changed, 34 insertions(+) create mode 100644 osu.Game.Rulesets.Osu/Mods/OsuModBarrelRoll.cs diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBarrelRoll.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBarrelRoll.cs new file mode 100644 index 0000000000..1ba6c47f4c --- /dev/null +++ b/osu.Game.Rulesets.Osu/Mods/OsuModBarrelRoll.cs @@ -0,0 +1,30 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Bindables; +using osu.Game.Configuration; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.UI; + +namespace osu.Game.Rulesets.Osu.Mods +{ + public class OsuModBarrelRoll : Mod, IUpdatableByPlayfield + { + [SettingSource("Roll speed", "Speed at which things rotate")] + public BindableNumber SpinSpeed { get; } = new BindableDouble(1) + { + MinValue = 0.1, + MaxValue = 20, + Precision = 0.1, + }; + + public override string Name => "Barrel Roll"; + public override string Acronym => "BR"; + public override double ScoreMultiplier => 1; + + public void Update(Playfield playfield) + { + playfield.Rotation = (float)(playfield.Time.Current / 1000 * SpinSpeed.Value); + } + } +} diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index d6375fa6e3..465d6d7155 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -185,6 +185,7 @@ namespace osu.Game.Rulesets.Osu new MultiMod(new OsuModGrow(), new OsuModDeflate()), new MultiMod(new ModWindUp(), new ModWindDown()), new OsuModTraceable(), + new OsuModBarrelRoll(), }; case ModType.System: diff --git a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs index b1069149f3..ea3eb5eb5c 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs @@ -42,6 +42,9 @@ namespace osu.Game.Rulesets.Osu.UI public OsuPlayfield() { + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + InternalChildren = new Drawable[] { playfieldBorder = new PlayfieldBorder { RelativeSizeAxes = Axes.Both }, From a314f90d3732a0852edcd89fc8c1970012a6071f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 14 Apr 2021 16:00:49 +0900 Subject: [PATCH 161/203] Allow timeline to govern the size of the rest of the editor content --- .../Compose/Components/Timeline/Timeline.cs | 5 + .../Components/Timeline/TimelineArea.cs | 11 +- .../Screens/Edit/EditorScreenWithTimeline.cs | 117 +++++++++++------- 3 files changed, 82 insertions(+), 51 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs index aa5cc46e37..d06f977fc9 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs @@ -56,8 +56,13 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline private Track track; + private const float timeline_height = 90; + public Timeline() { + RelativeSizeAxes = Axes.X; + Height = timeline_height; + ZoomDuration = 200; ZoomEasing = Easing.OutQuint; ScrollbarVisible = false; diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineArea.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineArea.cs index 0ec48e04c6..f144fd3a65 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineArea.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineArea.cs @@ -14,7 +14,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline { public class TimelineArea : Container { - public readonly Timeline Timeline = new Timeline { RelativeSizeAxes = Axes.Both }; + public readonly Timeline Timeline = new Timeline(); protected override Container Content => Timeline; @@ -37,7 +37,8 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline }, new GridContainer { - RelativeSizeAxes = Axes.Both, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, Content = new[] { new Drawable[] @@ -126,11 +127,15 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline Timeline }, }, + RowDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize), + }, ColumnDimensions = new[] { new Dimension(GridSizeMode.AutoSize), new Dimension(GridSizeMode.AutoSize), - new Dimension(GridSizeMode.Distributed), + new Dimension(), } } }; diff --git a/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs b/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs index 2d623a200c..b4b3aafc68 100644 --- a/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs +++ b/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs @@ -19,8 +19,6 @@ namespace osu.Game.Screens.Edit private const float vertical_margins = 10; private const float horizontal_margins = 20; - private const float timeline_height = 110; - private readonly BindableBeatDivisor beatDivisor = new BindableBeatDivisor(); private Container timelineContainer; @@ -40,64 +38,86 @@ namespace osu.Game.Screens.Edit if (beatDivisor != null) this.beatDivisor.BindTo(beatDivisor); - Children = new Drawable[] + Child = new GridContainer { - mainContent = new Container + RelativeSizeAxes = Axes.Both, + RowDimensions = new[] { - Name = "Main content", - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding - { - Horizontal = horizontal_margins, - Top = vertical_margins + timeline_height, - Bottom = vertical_margins - }, - Child = spinner = new LoadingSpinner(true) - { - State = { Value = Visibility.Visible }, - }, + new Dimension(GridSizeMode.AutoSize), + new Dimension(), }, - new Container + Content = new[] { - Name = "Timeline", - RelativeSizeAxes = Axes.X, - Height = timeline_height, - Children = new Drawable[] + new Drawable[] { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black.Opacity(0.5f) - }, new Container { - Name = "Timeline content", - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Horizontal = horizontal_margins, Vertical = vertical_margins }, - Child = new GridContainer + Name = "Timeline", + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new Drawable[] { - RelativeSizeAxes = Axes.Both, - Content = new[] + new Box { - new Drawable[] - { - timelineContainer = new Container - { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Right = 5 }, - }, - new BeatDivisorControl(beatDivisor) { RelativeSizeAxes = Axes.Both } - }, + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black.Opacity(0.5f) }, - ColumnDimensions = new[] + new Container { - new Dimension(), - new Dimension(GridSizeMode.Absolute, 90), + Name = "Timeline content", + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding { Horizontal = horizontal_margins, Vertical = vertical_margins }, + Child = new GridContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Content = new[] + { + new Drawable[] + { + timelineContainer = new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding { Right = 5 }, + }, + new BeatDivisorControl(beatDivisor) { RelativeSizeAxes = Axes.Both } + }, + }, + RowDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize), + }, + ColumnDimensions = new[] + { + new Dimension(), + new Dimension(GridSizeMode.Absolute, 90), + } + }, } + } + }, + }, + new Drawable[] + { + mainContent = new Container + { + Name = "Main content", + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding + { + Horizontal = horizontal_margins, + Top = vertical_margins, + Bottom = vertical_margins }, - } - } - }, + Child = spinner = new LoadingSpinner(true) + { + State = { Value = Visibility.Visible }, + }, + }, + }, + } }; } @@ -114,7 +134,8 @@ namespace osu.Game.Screens.Edit LoadComponentAsync(new TimelineArea { - RelativeSizeAxes = Axes.Both, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, Children = new[] { CreateTimelineContent(), From 26110cd777e3c4880adc2a10c2bb49e1b64c7947 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 14 Apr 2021 18:15:11 +0900 Subject: [PATCH 162/203] Fix timeline not receiving input (being eaten by composer) --- osu.Game/Screens/Edit/EditorScreenWithTimeline.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs b/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs index b4b3aafc68..26083e6a82 100644 --- a/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs +++ b/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs @@ -105,6 +105,7 @@ namespace osu.Game.Screens.Edit { Name = "Main content", RelativeSizeAxes = Axes.Both, + Depth = float.MaxValue, Padding = new MarginPadding { Horizontal = horizontal_margins, From ff2a37b7f492b16f135c026865e410b55c1095c7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 14 Apr 2021 19:38:25 +0900 Subject: [PATCH 163/203] Add new colours for editor designs --- osu.Game/Graphics/OsuColour.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/osu.Game/Graphics/OsuColour.cs b/osu.Game/Graphics/OsuColour.cs index 466d59b08b..c3b9b6006c 100644 --- a/osu.Game/Graphics/OsuColour.cs +++ b/osu.Game/Graphics/OsuColour.cs @@ -186,6 +186,13 @@ namespace osu.Game.Graphics public readonly Color4 GrayE = Color4Extensions.FromHex(@"eee"); public readonly Color4 GrayF = Color4Extensions.FromHex(@"fff"); + // in latest editor design logic, need to figure out where these sit... + public readonly Color4 Lime1 = Color4Extensions.FromHex(@"b2ff66"); + public readonly Color4 Orange1 = Color4Extensions.FromHex(@"ffd966"); + + // Content Background + public readonly Color4 B5 = Color4Extensions.FromHex(@"222a28"); + public readonly Color4 RedLighter = Color4Extensions.FromHex(@"ffeded"); public readonly Color4 RedLight = Color4Extensions.FromHex(@"ed7787"); public readonly Color4 Red = Color4Extensions.FromHex(@"ed1121"); From 1209c9fa32e0c0c98497748bb54f39760007408c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 14 Apr 2021 19:39:12 +0900 Subject: [PATCH 164/203] Allow timeline to expand in height when control points are to be displayed --- .../Compose/Components/Timeline/Timeline.cs | 39 ++++++++++++++++--- .../Timeline/TimelineControlPointDisplay.cs | 6 --- 2 files changed, 34 insertions(+), 11 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs index d06f977fc9..bd7b426044 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs @@ -57,11 +57,11 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline private Track track; private const float timeline_height = 90; + private const float timeline_expanded_height = 180; public Timeline() { RelativeSizeAxes = Axes.X; - Height = timeline_height; ZoomDuration = 200; ZoomEasing = Easing.OutQuint; @@ -86,9 +86,17 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline AddRange(new Drawable[] { + controlPoints = new TimelineControlPointDisplay + { + RelativeSizeAxes = Axes.X, + Height = timeline_expanded_height, + }, new Container { - RelativeSizeAxes = Axes.Both, + RelativeSizeAxes = Axes.X, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Height = timeline_height, Depth = float.MaxValue, Children = new[] { @@ -102,7 +110,6 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline }, centreMarker.CreateProxy(), ticks = new TimelineTickDisplay(), - controlPoints = new TimelineControlPointDisplay(), new Box { Name = "zero marker", @@ -116,13 +123,35 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline }); waveformOpacity = config.GetBindable(OsuSetting.EditorWaveformOpacity); + Beatmap.BindTo(beatmap); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + waveformOpacity.BindValueChanged(_ => updateWaveformOpacity(), true); WaveformVisible.ValueChanged += _ => updateWaveformOpacity(); - ControlPointsVisible.ValueChanged += visible => controlPoints.FadeTo(visible.NewValue ? 1 : 0, 200, Easing.OutQuint); TicksVisible.ValueChanged += visible => ticks.FadeTo(visible.NewValue ? 1 : 0, 200, Easing.OutQuint); + ControlPointsVisible.BindValueChanged(visible => + { + if (visible.NewValue) + { + this.ResizeHeightTo(timeline_expanded_height, 200, Easing.OutQuint); + + // delay the fade in else masking looks weird. + controlPoints.Delay(180).FadeIn(400, Easing.OutQuint); + } + else + { + controlPoints.FadeOut(200, Easing.OutQuint); + + // likewise, delay the resize until the fade is complete. + this.Delay(180).ResizeHeightTo(timeline_height, 200, Easing.OutQuint); + } + }, true); - Beatmap.BindTo(beatmap); Beatmap.BindValueChanged(b => { waveform.Waveform = b.NewValue.Waveform; diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineControlPointDisplay.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineControlPointDisplay.cs index 18600bcdee..8520567fa9 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineControlPointDisplay.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineControlPointDisplay.cs @@ -4,7 +4,6 @@ using System.Collections.Specialized; using System.Linq; using osu.Framework.Bindables; -using osu.Framework.Graphics; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Screens.Edit.Components.Timelines.Summary.Parts; @@ -17,11 +16,6 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline { private readonly IBindableList controlPointGroups = new BindableList(); - public TimelineControlPointDisplay() - { - RelativeSizeAxes = Axes.Both; - } - protected override void LoadBeatmap(EditorBeatmap beatmap) { base.LoadBeatmap(beatmap); From a8df2388eb184c8abb47f4e841a954587d4fef25 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 14 Apr 2021 19:39:27 +0900 Subject: [PATCH 165/203] Update design for TimingControlPoint --- .../ControlPoints/TimingControlPoint.cs | 2 +- .../Timeline/TimelineControlPointGroup.cs | 2 ++ .../Components/Timeline/TimingPointPiece.cs | 20 +++++++++---------- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs index 580642f593..ec20328fab 100644 --- a/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs @@ -20,7 +20,7 @@ namespace osu.Game.Beatmaps.ControlPoints /// private const double default_beat_length = 60000.0 / 60.0; - public override Color4 GetRepresentingColour(OsuColour colours) => colours.YellowDark; + public override Color4 GetRepresentingColour(OsuColour colours) => colours.Orange1; public static readonly TimingControlPoint DEFAULT = new TimingControlPoint { diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineControlPointGroup.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineControlPointGroup.cs index fb69f16792..c4beb40f92 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineControlPointGroup.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineControlPointGroup.cs @@ -27,6 +27,8 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline RelativeSizeAxes = Axes.Y; AutoSizeAxes = Axes.X; + Origin = Anchor.TopCentre; + X = (float)group.Time; } diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimingPointPiece.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimingPointPiece.cs index ba94916458..cd1470aa0a 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimingPointPiece.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimingPointPiece.cs @@ -3,15 +3,12 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; -using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; -using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; -using osuTK.Graphics; namespace osu.Game.Screens.Edit.Compose.Components.Timeline { @@ -31,26 +28,27 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline [BackgroundDependencyLoader] private void load(OsuColour colours) { - Origin = Anchor.CentreLeft; - Anchor = Anchor.CentreLeft; + Margin = new MarginPadding { Vertical = 10 }; + + const float corner_radius = 5; AutoSizeAxes = Axes.Both; - - Color4 colour = point.GetRepresentingColour(colours); + Masking = true; + CornerRadius = corner_radius; InternalChildren = new Drawable[] { new Box { - Alpha = 0.9f, - Colour = ColourInfo.GradientHorizontal(colour, colour.Opacity(0.5f)), + Colour = point.GetRepresentingColour(colours), RelativeSizeAxes = Axes.Both, }, bpmText = new OsuSpriteText { Alpha = 0.9f, - Padding = new MarginPadding(3), - Font = OsuFont.Default.With(size: 40) + Padding = new MarginPadding { Vertical = 3, Horizontal = 6 }, + Font = OsuFont.Default.With(size: 20, weight: FontWeight.SemiBold), + Colour = colours.B5, } }; From f9b1b7fe255370fb502aebaea0fb5c5327e569b1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 14 Apr 2021 20:10:58 +0900 Subject: [PATCH 166/203] Update SamplePointPiece design --- .../Components/Timeline/SamplePointPiece.cs | 61 ++++++++++--------- 1 file changed, 33 insertions(+), 28 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/SamplePointPiece.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/SamplePointPiece.cs index 0f11fb1126..9461f5e885 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/SamplePointPiece.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/SamplePointPiece.cs @@ -3,9 +3,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; -using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; -using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Game.Beatmaps.ControlPoints; @@ -23,7 +21,9 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline private readonly BindableNumber volume; private OsuSpriteText text; - private Box volumeBox; + private Container volumeBox; + + private const int max_volume_height = 22; public SamplePointPiece(SampleControlPoint samplePoint) { @@ -35,8 +35,10 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline [BackgroundDependencyLoader] private void load(OsuColour colours) { - Origin = Anchor.TopLeft; - Anchor = Anchor.TopLeft; + Margin = new MarginPadding { Vertical = 5 }; + + Origin = Anchor.BottomCentre; + Anchor = Anchor.BottomCentre; AutoSizeAxes = Axes.X; RelativeSizeAxes = Axes.Y; @@ -45,40 +47,43 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline InternalChildren = new Drawable[] { + volumeBox = new Circle + { + CornerRadius = 5, + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Y = -20, + Width = 10, + Colour = colour, + }, new Container { - RelativeSizeAxes = Axes.Y, - Width = 20, + AutoSizeAxes = Axes.X, + Height = 16, + Masking = true, + CornerRadius = 8, + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, Children = new Drawable[] { - volumeBox = new Box - { - X = 2, - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Colour = ColourInfo.GradientVertical(colour, Color4.Black), - RelativeSizeAxes = Axes.Both, - }, new Box { - Colour = colour.Lighten(0.2f), - Width = 2, - RelativeSizeAxes = Axes.Y, + Colour = colour, + RelativeSizeAxes = Axes.Both, }, + text = new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Padding = new MarginPadding(5), + Font = OsuFont.Default.With(size: 12, weight: FontWeight.SemiBold), + Colour = colours.B5, + } } }, - text = new OsuSpriteText - { - X = 2, - Y = -5, - Anchor = Anchor.BottomLeft, - Alpha = 0.9f, - Rotation = -90, - Font = OsuFont.Default.With(weight: FontWeight.SemiBold) - } }; - volume.BindValueChanged(volume => volumeBox.Height = volume.NewValue / 100f, true); + volume.BindValueChanged(volume => volumeBox.Height = max_volume_height * volume.NewValue / 100f, true); bank.BindValueChanged(bank => text.Text = bank.NewValue, true); } } From 99f05253fd2f0cc7634e06571666af28b24b3e2a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 14 Apr 2021 20:11:16 +0900 Subject: [PATCH 167/203] Adjust timeline sizing to closer match designs (but not 1:1 yet) --- .../Edit/Compose/Components/Timeline/Timeline.cs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs index bd7b426044..d688ad511f 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs @@ -56,8 +56,8 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline private Track track; - private const float timeline_height = 90; - private const float timeline_expanded_height = 180; + private const float timeline_height = 72; + private const float timeline_expanded_height = 150; public Timeline() { @@ -74,6 +74,8 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline private TimelineControlPointDisplay controlPoints; + private Container mainContent; + private Bindable waveformOpacity; [BackgroundDependencyLoader] @@ -91,11 +93,9 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline RelativeSizeAxes = Axes.X, Height = timeline_expanded_height, }, - new Container + mainContent = new Container { RelativeSizeAxes = Axes.X, - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, Height = timeline_height, Depth = float.MaxValue, Children = new[] @@ -139,6 +139,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline if (visible.NewValue) { this.ResizeHeightTo(timeline_expanded_height, 200, Easing.OutQuint); + mainContent.MoveToY(36, 200, Easing.OutQuint); // delay the fade in else masking looks weird. controlPoints.Delay(180).FadeIn(400, Easing.OutQuint); @@ -149,6 +150,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline // likewise, delay the resize until the fade is complete. this.Delay(180).ResizeHeightTo(timeline_height, 200, Easing.OutQuint); + mainContent.Delay(180).MoveToY(0, 200, Easing.OutQuint); } }, true); From afbb674e526a12148e4c328e5b5a5618720fe081 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 14 Apr 2021 20:11:32 +0900 Subject: [PATCH 168/203] TopLeft align check buttons so they don't move while interacting with them --- .../Screens/Edit/Compose/Components/Timeline/TimelineArea.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineArea.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineArea.cs index f144fd3a65..ee3543354f 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineArea.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineArea.cs @@ -56,11 +56,9 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline }, new FillFlowContainer { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, AutoSizeAxes = Axes.Y, Width = 160, - Padding = new MarginPadding { Horizontal = 10 }, + Padding = new MarginPadding(10), Direction = FillDirection.Vertical, Spacing = new Vector2(0, 4), Children = new[] From be08b9d1eff63dd7ec47c9e26323b90c3e63d208 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 14 Apr 2021 20:54:29 +0900 Subject: [PATCH 169/203] Combine logic of Difficulty and Timing pieces where feasible --- .../ControlPoints/DifficultyControlPoint.cs | 2 +- .../Timeline/DifficultyPointPiece.cs | 58 +++---------------- .../Components/Timeline/TimingPointPiece.cs | 37 +----------- .../Components/Timeline/TopPointPiece.cs | 55 ++++++++++++++++++ 4 files changed, 68 insertions(+), 84 deletions(-) create mode 100644 osu.Game/Screens/Edit/Compose/Components/Timeline/TopPointPiece.cs diff --git a/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs index 73337ab6f5..8a6cfaf688 100644 --- a/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/DifficultyControlPoint.cs @@ -25,7 +25,7 @@ namespace osu.Game.Beatmaps.ControlPoints MaxValue = 10 }; - public override Color4 GetRepresentingColour(OsuColour colours) => colours.GreenDark; + public override Color4 GetRepresentingColour(OsuColour colours) => colours.Lime1; /// /// The speed multiplier at this control point. diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/DifficultyPointPiece.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/DifficultyPointPiece.cs index 510ba8c094..3248936765 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/DifficultyPointPiece.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/DifficultyPointPiece.cs @@ -1,67 +1,27 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework.Allocation; using osu.Framework.Bindables; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; using osu.Game.Beatmaps.ControlPoints; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; -using osuTK.Graphics; namespace osu.Game.Screens.Edit.Compose.Components.Timeline { - public class DifficultyPointPiece : CompositeDrawable + public class DifficultyPointPiece : TopPointPiece { - private readonly DifficultyControlPoint difficultyPoint; - - private OsuSpriteText speedMultiplierText; private readonly BindableNumber speedMultiplier; - public DifficultyPointPiece(DifficultyControlPoint difficultyPoint) + public DifficultyPointPiece(DifficultyControlPoint point) + : base(point) { - this.difficultyPoint = difficultyPoint; - speedMultiplier = difficultyPoint.SpeedMultiplierBindable.GetBoundCopy(); + speedMultiplier = point.SpeedMultiplierBindable.GetBoundCopy(); + + Y = Height; } - [BackgroundDependencyLoader] - private void load(OsuColour colours) + protected override void LoadComplete() { - RelativeSizeAxes = Axes.Y; - AutoSizeAxes = Axes.X; - - Color4 colour = difficultyPoint.GetRepresentingColour(colours); - - InternalChildren = new Drawable[] - { - new Box - { - Colour = colour, - Width = 2, - RelativeSizeAxes = Axes.Y, - }, - new Container - { - AutoSizeAxes = Axes.Both, - Children = new Drawable[] - { - new Box - { - Colour = colour, - RelativeSizeAxes = Axes.Both, - }, - speedMultiplierText = new OsuSpriteText - { - Font = OsuFont.Default.With(weight: FontWeight.Bold), - Colour = Color4.White, - } - } - }, - }; - - speedMultiplier.BindValueChanged(multiplier => speedMultiplierText.Text = $"{multiplier.NewValue:n2}x", true); + base.LoadComplete(); + speedMultiplier.BindValueChanged(multiplier => Label.Text = $"{multiplier.NewValue:n2}x", true); } } } diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimingPointPiece.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimingPointPiece.cs index cd1470aa0a..fa51281c55 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimingPointPiece.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimingPointPiece.cs @@ -3,58 +3,27 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; namespace osu.Game.Screens.Edit.Compose.Components.Timeline { - public class TimingPointPiece : CompositeDrawable + public class TimingPointPiece : TopPointPiece { - private readonly TimingControlPoint point; - private readonly BindableNumber beatLength; - private OsuSpriteText bpmText; public TimingPointPiece(TimingControlPoint point) + : base(point) { - this.point = point; beatLength = point.BeatLengthBindable.GetBoundCopy(); } [BackgroundDependencyLoader] private void load(OsuColour colours) { - Margin = new MarginPadding { Vertical = 10 }; - - const float corner_radius = 5; - - AutoSizeAxes = Axes.Both; - Masking = true; - CornerRadius = corner_radius; - - InternalChildren = new Drawable[] - { - new Box - { - Colour = point.GetRepresentingColour(colours), - RelativeSizeAxes = Axes.Both, - }, - bpmText = new OsuSpriteText - { - Alpha = 0.9f, - Padding = new MarginPadding { Vertical = 3, Horizontal = 6 }, - Font = OsuFont.Default.With(size: 20, weight: FontWeight.SemiBold), - Colour = colours.B5, - } - }; - beatLength.BindValueChanged(beatLength => { - bpmText.Text = $"{60000 / beatLength.NewValue:n1} BPM"; + Label.Text = $"{60000 / beatLength.NewValue:n1} BPM"; }, true); } } diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TopPointPiece.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TopPointPiece.cs new file mode 100644 index 0000000000..60a9e1ed66 --- /dev/null +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TopPointPiece.cs @@ -0,0 +1,55 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; + +namespace osu.Game.Screens.Edit.Compose.Components.Timeline +{ + public class TopPointPiece : CompositeDrawable + { + private readonly ControlPoint point; + + protected OsuSpriteText Label { get; private set; } + + public TopPointPiece(ControlPoint point) + { + this.point = point; + AutoSizeAxes = Axes.X; + Height = 16; + Margin = new MarginPadding(4); + + Masking = true; + CornerRadius = Height / 2; + + Origin = Anchor.TopCentre; + Anchor = Anchor.TopCentre; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + InternalChildren = new Drawable[] + { + new Box + { + Colour = point.GetRepresentingColour(colours), + RelativeSizeAxes = Axes.Both, + }, + Label = new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Padding = new MarginPadding(3), + Font = OsuFont.Default.With(size: 14, weight: FontWeight.SemiBold), + Colour = colours.B5, + } + }; + } + } +} From d1c68cb92b0efe025fcaadd28f9c61ea4f4a17bf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 14 Apr 2021 20:55:12 +0900 Subject: [PATCH 170/203] Simplify content creation of Timeline / TimelineArea --- .../Edit/Compose/Components/Timeline/Timeline.cs | 8 ++++++-- .../Compose/Components/Timeline/TimelineArea.cs | 16 ++++++++++++---- .../Screens/Edit/EditorScreenWithTimeline.cs | 10 +--------- 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs index d688ad511f..55fb557474 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs @@ -23,6 +23,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline [Cached] public class Timeline : ZoomableScrollContainer, IPositionSnapProvider { + private readonly Drawable userContent; public readonly Bindable WaveformVisible = new Bindable(); public readonly Bindable ControlPointsVisible = new Bindable(); @@ -57,10 +58,12 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline private Track track; private const float timeline_height = 72; - private const float timeline_expanded_height = 150; + private const float timeline_expanded_height = 156; - public Timeline() + public Timeline(Drawable userContent) { + this.userContent = userContent; + RelativeSizeAxes = Axes.X; ZoomDuration = 200; @@ -118,6 +121,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline Origin = Anchor.TopCentre, Colour = colours.YellowDarker, }, + userContent, } }, }); diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineArea.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineArea.cs index ee3543354f..1541ceade5 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineArea.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineArea.cs @@ -12,11 +12,19 @@ using osuTK; namespace osu.Game.Screens.Edit.Compose.Components.Timeline { - public class TimelineArea : Container + public class TimelineArea : CompositeDrawable { - public readonly Timeline Timeline = new Timeline(); + public Timeline Timeline; - protected override Container Content => Timeline; + private readonly Drawable userContent; + + public TimelineArea(Drawable content = null) + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + + userContent = content ?? Drawable.Empty(); + } [BackgroundDependencyLoader] private void load() @@ -122,7 +130,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline } } }, - Timeline + Timeline = new Timeline(userContent), }, }, RowDimensions = new[] diff --git a/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs b/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs index 26083e6a82..0d59a7a1a8 100644 --- a/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs +++ b/osu.Game/Screens/Edit/EditorScreenWithTimeline.cs @@ -133,15 +133,7 @@ namespace osu.Game.Screens.Edit mainContent.Add(content); content.FadeInFromZero(300, Easing.OutQuint); - LoadComponentAsync(new TimelineArea - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Children = new[] - { - CreateTimelineContent(), - } - }, t => + LoadComponentAsync(new TimelineArea(CreateTimelineContent()), t => { timelineContainer.Add(t); OnTimelineLoaded(t); From 9dea0ae935ac4d4464bfc31a49ac42a4fe86f817 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 14 Apr 2021 20:55:23 +0900 Subject: [PATCH 171/203] Update test scene to be able to see a bit more --- osu.Game.Tests/Visual/Editing/TimelineTestScene.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TimelineTestScene.cs b/osu.Game.Tests/Visual/Editing/TimelineTestScene.cs index 88b4614791..4aed445d9d 100644 --- a/osu.Game.Tests/Visual/Editing/TimelineTestScene.cs +++ b/osu.Game.Tests/Visual/Editing/TimelineTestScene.cs @@ -53,13 +53,10 @@ namespace osu.Game.Tests.Visual.Editing new AudioVisualiser(), } }, - TimelineArea = new TimelineArea + TimelineArea = new TimelineArea(CreateTestComponent()) { - Child = CreateTestComponent(), Anchor = Anchor.Centre, Origin = Anchor.Centre, - RelativeSizeAxes = Axes.X, - Size = new Vector2(0.8f, 100), } }); } From 505dc15e0389efb9586ac655f682b727e991dd30 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 14 Apr 2021 23:22:38 +0300 Subject: [PATCH 172/203] Add failing test case --- .../Background/TestSceneUserDimBackgrounds.cs | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs index 655b426e43..c0aab1b7ef 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs @@ -65,6 +65,21 @@ namespace osu.Game.Tests.Visual.Background stack.Push(songSelect = new DummySongSelect()); }); + /// + /// User settings should always be ignored on song select screen. + /// + [Test] + public void TestUserSettingsIgnoredOnSongSelect() + { + setupUserSettings(); + AddAssert("Screen is undimmed", () => songSelect.IsBackgroundUndimmed()); + AddAssert("Screen using background blur", () => songSelect.IsBackgroundBlur()); + performFullSetup(); + AddStep("Exit to song select", () => player.Exit()); + AddUntilStep("Screen is undimmed", () => songSelect.IsBackgroundUndimmed()); + AddUntilStep("Screen using background blur", () => songSelect.IsBackgroundBlur()); + } + /// /// Check if properly triggers the visual settings preview when a user hovers over the visual settings panel. /// @@ -227,17 +242,6 @@ namespace osu.Game.Tests.Visual.Background songSelect.IsBackgroundUndimmed() && songSelect.IsBackgroundCurrent() && songSelect.CheckBackgroundBlur(results.ExpectedBackgroundBlur)); } - /// - /// Check if background gets undimmed and unblurred when leaving for - /// - [Test] - public void TestTransitionOut() - { - performFullSetup(); - AddStep("Exit to song select", () => player.Exit()); - AddUntilStep("Screen is undimmed and user blur removed", () => songSelect.IsBackgroundUndimmed() && songSelect.IsBlurCorrect()); - } - /// /// Check if hovering on the visual settings dialogue after resuming from player still previews the background dim. /// @@ -333,7 +337,7 @@ namespace osu.Game.Tests.Visual.Background public bool IsBackgroundVisible() => background.CurrentAlpha == 1; - public bool IsBlurCorrect() => background.CurrentBlur == new Vector2(BACKGROUND_BLUR); + public bool IsBackgroundBlur() => background.CurrentBlur == new Vector2(BACKGROUND_BLUR); public bool CheckBackgroundBlur(Vector2 expected) => background.CurrentBlur == expected; From a5fa14ac4ae010b77a66edd79fe394aeddbad022 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 14 Apr 2021 23:17:06 +0300 Subject: [PATCH 173/203] Ignore user settings on background screen beatmap by default --- osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs b/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs index 10d381b8b7..02166644ab 100644 --- a/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs +++ b/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs @@ -50,6 +50,9 @@ namespace osu.Game.Screens.Backgrounds InternalChild = dimmable = CreateFadeContainer(); + // Beatmap background screens should not apply user settings by default. + IgnoreUserSettings.Value = true; + dimmable.IgnoreUserSettings.BindTo(IgnoreUserSettings); dimmable.IsBreakTime.BindTo(IsBreakTime); dimmable.BlurAmount.BindTo(BlurAmount); From 7a9ff2ab380030871d434a1af6b0ae5c34ad9436 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 15 Apr 2021 00:48:15 +0300 Subject: [PATCH 174/203] Use until steps instead Ah.. --- .../Visual/Background/TestSceneUserDimBackgrounds.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs index c0aab1b7ef..f89988cd1a 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs @@ -72,8 +72,8 @@ namespace osu.Game.Tests.Visual.Background public void TestUserSettingsIgnoredOnSongSelect() { setupUserSettings(); - AddAssert("Screen is undimmed", () => songSelect.IsBackgroundUndimmed()); - AddAssert("Screen using background blur", () => songSelect.IsBackgroundBlur()); + AddUntilStep("Screen is undimmed", () => songSelect.IsBackgroundUndimmed()); + AddUntilStep("Screen using background blur", () => songSelect.IsBackgroundBlur()); performFullSetup(); AddStep("Exit to song select", () => player.Exit()); AddUntilStep("Screen is undimmed", () => songSelect.IsBackgroundUndimmed()); From 362a5a39d0068d3a143fd245ef80a053995c6a8c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Apr 2021 13:06:52 +0900 Subject: [PATCH 175/203] Scale the playfield to avoid off-screen objects --- osu.Game.Rulesets.Osu/Mods/OsuModBarrelRoll.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBarrelRoll.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBarrelRoll.cs index 1ba6c47f4c..d646f41588 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModBarrelRoll.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModBarrelRoll.cs @@ -4,11 +4,14 @@ using osu.Framework.Bindables; using osu.Game.Configuration; using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.UI; using osu.Game.Rulesets.UI; +using osuTK; namespace osu.Game.Rulesets.Osu.Mods { - public class OsuModBarrelRoll : Mod, IUpdatableByPlayfield + public class OsuModBarrelRoll : Mod, IUpdatableByPlayfield, IApplicableToDrawableRuleset { [SettingSource("Roll speed", "Speed at which things rotate")] public BindableNumber SpinSpeed { get; } = new BindableDouble(1) @@ -26,5 +29,11 @@ namespace osu.Game.Rulesets.Osu.Mods { playfield.Rotation = (float)(playfield.Time.Current / 1000 * SpinSpeed.Value); } + + public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) + { + // scale the playfield to allow all hitobjects to stay within the visible region. + drawableRuleset.Playfield.Scale = new Vector2(OsuPlayfield.BASE_SIZE.Y / OsuPlayfield.BASE_SIZE.X); + } } } From 0d32290cd529cceb63e0fed31b3d46aeb4b2bca4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Apr 2021 13:15:52 +0900 Subject: [PATCH 176/203] Show roll speed in rotations-per-minute --- osu.Game.Rulesets.Osu/Mods/OsuModBarrelRoll.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBarrelRoll.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBarrelRoll.cs index d646f41588..7cdfdb3c4a 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModBarrelRoll.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModBarrelRoll.cs @@ -13,12 +13,12 @@ namespace osu.Game.Rulesets.Osu.Mods { public class OsuModBarrelRoll : Mod, IUpdatableByPlayfield, IApplicableToDrawableRuleset { - [SettingSource("Roll speed", "Speed at which things rotate")] - public BindableNumber SpinSpeed { get; } = new BindableDouble(1) + [SettingSource("Roll speed", "Rotations per minute")] + public BindableNumber SpinSpeed { get; } = new BindableDouble(0.5) { - MinValue = 0.1, - MaxValue = 20, - Precision = 0.1, + MinValue = 0.02, + MaxValue = 4, + Precision = 0.01, }; public override string Name => "Barrel Roll"; @@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Osu.Mods public void Update(Playfield playfield) { - playfield.Rotation = (float)(playfield.Time.Current / 1000 * SpinSpeed.Value); + playfield.Rotation = 360 * (float)(playfield.Time.Current / 60000 * SpinSpeed.Value); } public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) From 175b8da2b27002c262d53cfa5c8f44d46034abbf Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 15 Apr 2021 00:04:38 +0300 Subject: [PATCH 177/203] Inverse ignore user settings bindable to "apply user settings" instead --- .../Background/TestSceneUserDimBackgrounds.cs | 16 ++++++++-------- .../Background/TestSceneUserDimContainer.cs | 2 +- osu.Game/Graphics/Containers/UserDimContainer.cs | 8 ++++---- osu.Game/Rulesets/Mods/ModCinema.cs | 4 ++-- .../Backgrounds/BackgroundScreenBeatmap.cs | 15 ++++++--------- osu.Game/Screens/Edit/Editor.cs | 2 +- osu.Game/Screens/Play/DimmableStoryboard.cs | 4 ++-- osu.Game/Screens/Play/Player.cs | 4 ++-- osu.Game/Screens/Play/PlayerLoader.cs | 6 +++--- 9 files changed, 29 insertions(+), 32 deletions(-) diff --git a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs index f89988cd1a..d915442570 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs @@ -157,9 +157,9 @@ namespace osu.Game.Tests.Visual.Background { performFullSetup(); AddUntilStep("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied()); - AddStep("Disable user dim", () => songSelect.IgnoreUserSettings.Value = true); + AddStep("Disable user dim", () => songSelect.ApplyUserSettings.Value = false); AddUntilStep("Screen is undimmed and user blur removed", () => songSelect.IsBackgroundUndimmed() && songSelect.IsUserBlurDisabled()); - AddStep("Enable user dim", () => songSelect.IgnoreUserSettings.Value = false); + AddStep("Enable user dim", () => songSelect.ApplyUserSettings.Value = true); AddUntilStep("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied()); } @@ -176,10 +176,10 @@ namespace osu.Game.Tests.Visual.Background player.ReplacesBackground.Value = true; player.StoryboardEnabled.Value = true; }); - AddStep("Enable user dim", () => player.DimmableStoryboard.IgnoreUserSettings.Value = false); + AddStep("Enable user dim", () => player.DimmableStoryboard.ApplyUserSettings.Value = true); AddStep("Set dim level to 1", () => songSelect.DimLevel.Value = 1f); AddUntilStep("Storyboard is invisible", () => !player.IsStoryboardVisible); - AddStep("Disable user dim", () => player.DimmableStoryboard.IgnoreUserSettings.Value = true); + AddStep("Disable user dim", () => player.DimmableStoryboard.ApplyUserSettings.Value = false); AddUntilStep("Storyboard is visible", () => player.IsStoryboardVisible); } @@ -195,8 +195,8 @@ namespace osu.Game.Tests.Visual.Background AddStep("Ignore user settings", () => { - player.ApplyToBackground(b => b.IgnoreUserSettings.Value = true); - player.DimmableStoryboard.IgnoreUserSettings.Value = true; + player.ApplyToBackground(b => b.ApplyUserSettings.Value = false); + player.DimmableStoryboard.ApplyUserSettings.Value = false; }); AddUntilStep("Storyboard is visible", () => player.IsStoryboardVisible); AddUntilStep("Background is invisible", () => songSelect.IsBackgroundInvisible()); @@ -308,11 +308,11 @@ namespace osu.Game.Tests.Visual.Background protected override BackgroundScreen CreateBackground() { background = new FadeAccessibleBackground(Beatmap.Value); - IgnoreUserSettings.BindTo(background.IgnoreUserSettings); + ApplyUserSettings.BindTo(background.ApplyUserSettings); return background; } - public readonly Bindable IgnoreUserSettings = new Bindable(); + public readonly Bindable ApplyUserSettings = new Bindable(); public readonly Bindable DimLevel = new BindableDouble(); public readonly Bindable BlurLevel = new BindableDouble(); diff --git a/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs b/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs index fede99f450..9c3a044f18 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs @@ -94,7 +94,7 @@ namespace osu.Game.Tests.Visual.Background AddStep("set dim level 0.6", () => userDimContainer.UserDimLevel.Value = test_user_dim); AddUntilStep("dim reached", () => userDimContainer.DimEqual(test_user_dim)); - AddStep("ignore settings", () => userDimContainer.IgnoreUserSettings.Value = true); + AddStep("ignore settings", () => userDimContainer.ApplyUserSettings.Value = false); AddUntilStep("no dim", () => userDimContainer.DimEqual(0)); AddStep("set break", () => isBreakTime.Value = true); AddAssert("no dim", () => userDimContainer.DimEqual(0)); diff --git a/osu.Game/Graphics/Containers/UserDimContainer.cs b/osu.Game/Graphics/Containers/UserDimContainer.cs index 4e555ac1eb..a3d59da961 100644 --- a/osu.Game/Graphics/Containers/UserDimContainer.cs +++ b/osu.Game/Graphics/Containers/UserDimContainer.cs @@ -24,9 +24,9 @@ namespace osu.Game.Graphics.Containers protected const double BACKGROUND_FADE_DURATION = 800; /// - /// Whether or not user-configured settings relating to brightness of elements should be ignored + /// Whether or not user-configured effect settings should be applied to this container. /// - public readonly Bindable IgnoreUserSettings = new Bindable(); + public readonly Bindable ApplyUserSettings = new Bindable(true); /// /// Whether or not the storyboard loaded should completely hide the background behind it. @@ -52,7 +52,7 @@ namespace osu.Game.Graphics.Containers private float breakLightening => LightenDuringBreaks.Value && IsBreakTime.Value ? BREAK_LIGHTEN_AMOUNT : 0; - protected float DimLevel => Math.Max(!IgnoreUserSettings.Value ? (float)UserDimLevel.Value - breakLightening : 0, 0); + protected float DimLevel => Math.Max(ApplyUserSettings.Value ? (float)UserDimLevel.Value - breakLightening : 0, 0); protected override Container Content => dimContent; @@ -78,7 +78,7 @@ namespace osu.Game.Graphics.Containers IsBreakTime.ValueChanged += _ => UpdateVisuals(); ShowStoryboard.ValueChanged += _ => UpdateVisuals(); StoryboardReplacesBackground.ValueChanged += _ => UpdateVisuals(); - IgnoreUserSettings.ValueChanged += _ => UpdateVisuals(); + ApplyUserSettings.ValueChanged += _ => UpdateVisuals(); } protected override void LoadComplete() diff --git a/osu.Game/Rulesets/Mods/ModCinema.cs b/osu.Game/Rulesets/Mods/ModCinema.cs index c78088ba2d..93062218fe 100644 --- a/osu.Game/Rulesets/Mods/ModCinema.cs +++ b/osu.Game/Rulesets/Mods/ModCinema.cs @@ -37,8 +37,8 @@ namespace osu.Game.Rulesets.Mods public void ApplyToPlayer(Player player) { - player.ApplyToBackground(b => b.IgnoreUserSettings.Value = true); - player.DimmableStoryboard.IgnoreUserSettings.Value = true; + player.ApplyToBackground(b => b.ApplyUserSettings.Value = false); + player.DimmableStoryboard.ApplyUserSettings.Value = false; player.BreakOverlay.Hide(); } diff --git a/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs b/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs index 02166644ab..553a8f3689 100644 --- a/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs +++ b/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs @@ -27,9 +27,9 @@ namespace osu.Game.Screens.Backgrounds private WorkingBeatmap beatmap; /// - /// Whether or not user-configured settings relating to brightness of elements should be ignored + /// Whether or not user-configured effect settings should be applied to this background screen. /// - public readonly Bindable IgnoreUserSettings = new Bindable(); + public readonly Bindable ApplyUserSettings = new Bindable(); public readonly Bindable StoryboardReplacesBackground = new Bindable(); @@ -50,10 +50,7 @@ namespace osu.Game.Screens.Backgrounds InternalChild = dimmable = CreateFadeContainer(); - // Beatmap background screens should not apply user settings by default. - IgnoreUserSettings.Value = true; - - dimmable.IgnoreUserSettings.BindTo(IgnoreUserSettings); + dimmable.ApplyUserSettings.BindTo(ApplyUserSettings); dimmable.IsBreakTime.BindTo(IsBreakTime); dimmable.BlurAmount.BindTo(BlurAmount); @@ -151,7 +148,7 @@ namespace osu.Game.Screens.Backgrounds /// /// As an optimisation, we add the two blur portions to be applied rather than actually applying two separate blurs. /// - private Vector2 blurTarget => !IgnoreUserSettings.Value + private Vector2 blurTarget => ApplyUserSettings.Value ? new Vector2(BlurAmount.Value + (float)userBlurLevel.Value * USER_BLUR_FACTOR) : new Vector2(BlurAmount.Value); @@ -170,8 +167,8 @@ namespace osu.Game.Screens.Backgrounds } protected override bool ShowDimContent - // The background needs to be hidden in the case of it being replaced by the storyboard - => (!ShowStoryboard.Value && !IgnoreUserSettings.Value) || !StoryboardReplacesBackground.Value; + // The background needs to be hidden in the case of it being replaced by the storyboard. + => (ApplyUserSettings.Value && !ShowStoryboard.Value) || !StoryboardReplacesBackground.Value; protected override void UpdateVisuals() { diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index fffea65456..99c6cce26f 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -467,7 +467,7 @@ namespace osu.Game.Screens.Edit // todo: temporary. we want to be applying dim using the UserDimContainer eventually. b.FadeColour(Color4.DarkGray, 500); - b.IgnoreUserSettings.Value = true; + b.ApplyUserSettings.Value = false; b.BlurAmount.Value = 0; }); diff --git a/osu.Game/Screens/Play/DimmableStoryboard.cs b/osu.Game/Screens/Play/DimmableStoryboard.cs index 58eb95b7c6..105f672847 100644 --- a/osu.Game/Screens/Play/DimmableStoryboard.cs +++ b/osu.Game/Screens/Play/DimmableStoryboard.cs @@ -38,14 +38,14 @@ namespace osu.Game.Screens.Play base.LoadComplete(); } - protected override bool ShowDimContent => IgnoreUserSettings.Value || (ShowStoryboard.Value && DimLevel < 1); + protected override bool ShowDimContent => !ApplyUserSettings.Value || (ShowStoryboard.Value && DimLevel < 1); private void initializeStoryboard(bool async) { if (drawableStoryboard != null) return; - if (!ShowStoryboard.Value && !IgnoreUserSettings.Value) + if (ApplyUserSettings.Value && !ShowStoryboard.Value) return; drawableStoryboard = storyboard.CreateDrawable(); diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index dd3f58439b..1c71305baf 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -764,7 +764,7 @@ namespace osu.Game.Screens.Play ApplyToBackground(b => { - b.IgnoreUserSettings.Value = false; + b.ApplyUserSettings.Value = true; b.BlurAmount.Value = 0; // bind component bindables. @@ -913,7 +913,7 @@ namespace osu.Game.Screens.Play float fadeOutDuration = instant ? 0 : 250; this.FadeOut(fadeOutDuration); - ApplyToBackground(b => b.IgnoreUserSettings.Value = true); + ApplyToBackground(b => b.ApplyUserSettings.Value = false); storyboardReplacesBackground.Value = false; } diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index cf15104809..8627432c11 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -229,7 +229,7 @@ namespace osu.Game.Screens.Play content.ScaleTo(0.7f, 150, Easing.InQuint); this.FadeOut(150); - ApplyToBackground(b => b.IgnoreUserSettings.Value = true); + ApplyToBackground(b => b.ApplyUserSettings.Value = false); BackgroundBrightnessReduction = false; Beatmap.Value.Track.RemoveAdjustment(AdjustableProperty.Volume, volumeAdjustment); @@ -277,7 +277,7 @@ namespace osu.Game.Screens.Play // Preview user-defined background dim and blur when hovered on the visual settings panel. ApplyToBackground(b => { - b.IgnoreUserSettings.Value = false; + b.ApplyUserSettings.Value = true; b.BlurAmount.Value = 0; }); @@ -288,7 +288,7 @@ namespace osu.Game.Screens.Play ApplyToBackground(b => { // Returns background dim and blur to the values specified by PlayerLoader. - b.IgnoreUserSettings.Value = true; + b.ApplyUserSettings.Value = false; b.BlurAmount.Value = BACKGROUND_BLUR; }); From 92fd34cea99c659155089ef828f54a65e620989b Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 15 Apr 2021 08:02:12 +0300 Subject: [PATCH 178/203] Revert "Inverse ignore user settings bindable to "apply user settings" instead" This reverts commit 175b8da2b27002c262d53cfa5c8f44d46034abbf. --- .../Background/TestSceneUserDimBackgrounds.cs | 16 ++++++++-------- .../Background/TestSceneUserDimContainer.cs | 2 +- osu.Game/Graphics/Containers/UserDimContainer.cs | 8 ++++---- osu.Game/Rulesets/Mods/ModCinema.cs | 4 ++-- .../Backgrounds/BackgroundScreenBeatmap.cs | 15 +++++++++------ osu.Game/Screens/Edit/Editor.cs | 2 +- osu.Game/Screens/Play/DimmableStoryboard.cs | 4 ++-- osu.Game/Screens/Play/Player.cs | 4 ++-- osu.Game/Screens/Play/PlayerLoader.cs | 6 +++--- 9 files changed, 32 insertions(+), 29 deletions(-) diff --git a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs index d915442570..f89988cd1a 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs @@ -157,9 +157,9 @@ namespace osu.Game.Tests.Visual.Background { performFullSetup(); AddUntilStep("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied()); - AddStep("Disable user dim", () => songSelect.ApplyUserSettings.Value = false); + AddStep("Disable user dim", () => songSelect.IgnoreUserSettings.Value = true); AddUntilStep("Screen is undimmed and user blur removed", () => songSelect.IsBackgroundUndimmed() && songSelect.IsUserBlurDisabled()); - AddStep("Enable user dim", () => songSelect.ApplyUserSettings.Value = true); + AddStep("Enable user dim", () => songSelect.IgnoreUserSettings.Value = false); AddUntilStep("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied()); } @@ -176,10 +176,10 @@ namespace osu.Game.Tests.Visual.Background player.ReplacesBackground.Value = true; player.StoryboardEnabled.Value = true; }); - AddStep("Enable user dim", () => player.DimmableStoryboard.ApplyUserSettings.Value = true); + AddStep("Enable user dim", () => player.DimmableStoryboard.IgnoreUserSettings.Value = false); AddStep("Set dim level to 1", () => songSelect.DimLevel.Value = 1f); AddUntilStep("Storyboard is invisible", () => !player.IsStoryboardVisible); - AddStep("Disable user dim", () => player.DimmableStoryboard.ApplyUserSettings.Value = false); + AddStep("Disable user dim", () => player.DimmableStoryboard.IgnoreUserSettings.Value = true); AddUntilStep("Storyboard is visible", () => player.IsStoryboardVisible); } @@ -195,8 +195,8 @@ namespace osu.Game.Tests.Visual.Background AddStep("Ignore user settings", () => { - player.ApplyToBackground(b => b.ApplyUserSettings.Value = false); - player.DimmableStoryboard.ApplyUserSettings.Value = false; + player.ApplyToBackground(b => b.IgnoreUserSettings.Value = true); + player.DimmableStoryboard.IgnoreUserSettings.Value = true; }); AddUntilStep("Storyboard is visible", () => player.IsStoryboardVisible); AddUntilStep("Background is invisible", () => songSelect.IsBackgroundInvisible()); @@ -308,11 +308,11 @@ namespace osu.Game.Tests.Visual.Background protected override BackgroundScreen CreateBackground() { background = new FadeAccessibleBackground(Beatmap.Value); - ApplyUserSettings.BindTo(background.ApplyUserSettings); + IgnoreUserSettings.BindTo(background.IgnoreUserSettings); return background; } - public readonly Bindable ApplyUserSettings = new Bindable(); + public readonly Bindable IgnoreUserSettings = new Bindable(); public readonly Bindable DimLevel = new BindableDouble(); public readonly Bindable BlurLevel = new BindableDouble(); diff --git a/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs b/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs index 9c3a044f18..fede99f450 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneUserDimContainer.cs @@ -94,7 +94,7 @@ namespace osu.Game.Tests.Visual.Background AddStep("set dim level 0.6", () => userDimContainer.UserDimLevel.Value = test_user_dim); AddUntilStep("dim reached", () => userDimContainer.DimEqual(test_user_dim)); - AddStep("ignore settings", () => userDimContainer.ApplyUserSettings.Value = false); + AddStep("ignore settings", () => userDimContainer.IgnoreUserSettings.Value = true); AddUntilStep("no dim", () => userDimContainer.DimEqual(0)); AddStep("set break", () => isBreakTime.Value = true); AddAssert("no dim", () => userDimContainer.DimEqual(0)); diff --git a/osu.Game/Graphics/Containers/UserDimContainer.cs b/osu.Game/Graphics/Containers/UserDimContainer.cs index a3d59da961..4e555ac1eb 100644 --- a/osu.Game/Graphics/Containers/UserDimContainer.cs +++ b/osu.Game/Graphics/Containers/UserDimContainer.cs @@ -24,9 +24,9 @@ namespace osu.Game.Graphics.Containers protected const double BACKGROUND_FADE_DURATION = 800; /// - /// Whether or not user-configured effect settings should be applied to this container. + /// Whether or not user-configured settings relating to brightness of elements should be ignored /// - public readonly Bindable ApplyUserSettings = new Bindable(true); + public readonly Bindable IgnoreUserSettings = new Bindable(); /// /// Whether or not the storyboard loaded should completely hide the background behind it. @@ -52,7 +52,7 @@ namespace osu.Game.Graphics.Containers private float breakLightening => LightenDuringBreaks.Value && IsBreakTime.Value ? BREAK_LIGHTEN_AMOUNT : 0; - protected float DimLevel => Math.Max(ApplyUserSettings.Value ? (float)UserDimLevel.Value - breakLightening : 0, 0); + protected float DimLevel => Math.Max(!IgnoreUserSettings.Value ? (float)UserDimLevel.Value - breakLightening : 0, 0); protected override Container Content => dimContent; @@ -78,7 +78,7 @@ namespace osu.Game.Graphics.Containers IsBreakTime.ValueChanged += _ => UpdateVisuals(); ShowStoryboard.ValueChanged += _ => UpdateVisuals(); StoryboardReplacesBackground.ValueChanged += _ => UpdateVisuals(); - ApplyUserSettings.ValueChanged += _ => UpdateVisuals(); + IgnoreUserSettings.ValueChanged += _ => UpdateVisuals(); } protected override void LoadComplete() diff --git a/osu.Game/Rulesets/Mods/ModCinema.cs b/osu.Game/Rulesets/Mods/ModCinema.cs index 93062218fe..c78088ba2d 100644 --- a/osu.Game/Rulesets/Mods/ModCinema.cs +++ b/osu.Game/Rulesets/Mods/ModCinema.cs @@ -37,8 +37,8 @@ namespace osu.Game.Rulesets.Mods public void ApplyToPlayer(Player player) { - player.ApplyToBackground(b => b.ApplyUserSettings.Value = false); - player.DimmableStoryboard.ApplyUserSettings.Value = false; + player.ApplyToBackground(b => b.IgnoreUserSettings.Value = true); + player.DimmableStoryboard.IgnoreUserSettings.Value = true; player.BreakOverlay.Hide(); } diff --git a/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs b/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs index 553a8f3689..02166644ab 100644 --- a/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs +++ b/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs @@ -27,9 +27,9 @@ namespace osu.Game.Screens.Backgrounds private WorkingBeatmap beatmap; /// - /// Whether or not user-configured effect settings should be applied to this background screen. + /// Whether or not user-configured settings relating to brightness of elements should be ignored /// - public readonly Bindable ApplyUserSettings = new Bindable(); + public readonly Bindable IgnoreUserSettings = new Bindable(); public readonly Bindable StoryboardReplacesBackground = new Bindable(); @@ -50,7 +50,10 @@ namespace osu.Game.Screens.Backgrounds InternalChild = dimmable = CreateFadeContainer(); - dimmable.ApplyUserSettings.BindTo(ApplyUserSettings); + // Beatmap background screens should not apply user settings by default. + IgnoreUserSettings.Value = true; + + dimmable.IgnoreUserSettings.BindTo(IgnoreUserSettings); dimmable.IsBreakTime.BindTo(IsBreakTime); dimmable.BlurAmount.BindTo(BlurAmount); @@ -148,7 +151,7 @@ namespace osu.Game.Screens.Backgrounds /// /// As an optimisation, we add the two blur portions to be applied rather than actually applying two separate blurs. /// - private Vector2 blurTarget => ApplyUserSettings.Value + private Vector2 blurTarget => !IgnoreUserSettings.Value ? new Vector2(BlurAmount.Value + (float)userBlurLevel.Value * USER_BLUR_FACTOR) : new Vector2(BlurAmount.Value); @@ -167,8 +170,8 @@ namespace osu.Game.Screens.Backgrounds } protected override bool ShowDimContent - // The background needs to be hidden in the case of it being replaced by the storyboard. - => (ApplyUserSettings.Value && !ShowStoryboard.Value) || !StoryboardReplacesBackground.Value; + // The background needs to be hidden in the case of it being replaced by the storyboard + => (!ShowStoryboard.Value && !IgnoreUserSettings.Value) || !StoryboardReplacesBackground.Value; protected override void UpdateVisuals() { diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 99c6cce26f..fffea65456 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -467,7 +467,7 @@ namespace osu.Game.Screens.Edit // todo: temporary. we want to be applying dim using the UserDimContainer eventually. b.FadeColour(Color4.DarkGray, 500); - b.ApplyUserSettings.Value = false; + b.IgnoreUserSettings.Value = true; b.BlurAmount.Value = 0; }); diff --git a/osu.Game/Screens/Play/DimmableStoryboard.cs b/osu.Game/Screens/Play/DimmableStoryboard.cs index 105f672847..58eb95b7c6 100644 --- a/osu.Game/Screens/Play/DimmableStoryboard.cs +++ b/osu.Game/Screens/Play/DimmableStoryboard.cs @@ -38,14 +38,14 @@ namespace osu.Game.Screens.Play base.LoadComplete(); } - protected override bool ShowDimContent => !ApplyUserSettings.Value || (ShowStoryboard.Value && DimLevel < 1); + protected override bool ShowDimContent => IgnoreUserSettings.Value || (ShowStoryboard.Value && DimLevel < 1); private void initializeStoryboard(bool async) { if (drawableStoryboard != null) return; - if (ApplyUserSettings.Value && !ShowStoryboard.Value) + if (!ShowStoryboard.Value && !IgnoreUserSettings.Value) return; drawableStoryboard = storyboard.CreateDrawable(); diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 1c71305baf..dd3f58439b 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -764,7 +764,7 @@ namespace osu.Game.Screens.Play ApplyToBackground(b => { - b.ApplyUserSettings.Value = true; + b.IgnoreUserSettings.Value = false; b.BlurAmount.Value = 0; // bind component bindables. @@ -913,7 +913,7 @@ namespace osu.Game.Screens.Play float fadeOutDuration = instant ? 0 : 250; this.FadeOut(fadeOutDuration); - ApplyToBackground(b => b.ApplyUserSettings.Value = false); + ApplyToBackground(b => b.IgnoreUserSettings.Value = true); storyboardReplacesBackground.Value = false; } diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 8627432c11..cf15104809 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -229,7 +229,7 @@ namespace osu.Game.Screens.Play content.ScaleTo(0.7f, 150, Easing.InQuint); this.FadeOut(150); - ApplyToBackground(b => b.ApplyUserSettings.Value = false); + ApplyToBackground(b => b.IgnoreUserSettings.Value = true); BackgroundBrightnessReduction = false; Beatmap.Value.Track.RemoveAdjustment(AdjustableProperty.Volume, volumeAdjustment); @@ -277,7 +277,7 @@ namespace osu.Game.Screens.Play // Preview user-defined background dim and blur when hovered on the visual settings panel. ApplyToBackground(b => { - b.ApplyUserSettings.Value = true; + b.IgnoreUserSettings.Value = false; b.BlurAmount.Value = 0; }); @@ -288,7 +288,7 @@ namespace osu.Game.Screens.Play ApplyToBackground(b => { // Returns background dim and blur to the values specified by PlayerLoader. - b.ApplyUserSettings.Value = false; + b.IgnoreUserSettings.Value = true; b.BlurAmount.Value = BACKGROUND_BLUR; }); From 6c5234f8daa605ffb936b492edd0e247ee71351a Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 15 Apr 2021 08:04:03 +0300 Subject: [PATCH 179/203] Move default `IgnoreUserSettings` value to construction --- .../Screens/Backgrounds/BackgroundScreenBeatmap.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs b/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs index 02166644ab..65bc9cfaea 100644 --- a/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs +++ b/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs @@ -27,9 +27,12 @@ namespace osu.Game.Screens.Backgrounds private WorkingBeatmap beatmap; /// - /// Whether or not user-configured settings relating to brightness of elements should be ignored + /// Whether or not user-configured settings relating to brightness of elements should be ignored. /// - public readonly Bindable IgnoreUserSettings = new Bindable(); + /// + /// Beatmap background screens should not apply user settings by default. + /// + public readonly Bindable IgnoreUserSettings = new Bindable(true); public readonly Bindable StoryboardReplacesBackground = new Bindable(); @@ -50,9 +53,6 @@ namespace osu.Game.Screens.Backgrounds InternalChild = dimmable = CreateFadeContainer(); - // Beatmap background screens should not apply user settings by default. - IgnoreUserSettings.Value = true; - dimmable.IgnoreUserSettings.BindTo(IgnoreUserSettings); dimmable.IsBreakTime.BindTo(IsBreakTime); dimmable.BlurAmount.BindTo(BlurAmount); From 5eaf3ea5765e21a0137b983fa586651d0228ffc5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Apr 2021 14:19:06 +0900 Subject: [PATCH 180/203] Reorganise and reword comments to make time override behaviour a bit clearer --- osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs b/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs index a7f11b1e6f..279087ead9 100644 --- a/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs +++ b/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs @@ -128,13 +128,13 @@ namespace osu.Game.Rulesets.Replays double frameStart = getFrameTime(currentFrameIndex); double frameEnd = getFrameTime(currentFrameIndex + 1); - // If the proposed time is after the current frame end time, we progress forwards. - // If the proposed time is before the current frame start time, and we are at the frame boundary, we progress backwards. + // If the proposed time is after the current frame end time, we progress forwards to precisely the new frame's time (regardless of incoming time). if (frameEnd <= time) { time = frameEnd; currentFrameIndex++; } + // If the proposed time is before the current frame start time, and we are at the frame boundary, we progress backwards. else if (time < frameStart && CurrentTime == frameStart) currentFrameIndex--; From ba325de5959bb83828fc222b359fcb9f9c846236 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Apr 2021 14:19:59 +0900 Subject: [PATCH 181/203] Merge conditionals for readability --- osu.Game/Rulesets/UI/RulesetInputManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/UI/RulesetInputManager.cs b/osu.Game/Rulesets/UI/RulesetInputManager.cs index 1c0d820a3d..5ab09f9516 100644 --- a/osu.Game/Rulesets/UI/RulesetInputManager.cs +++ b/osu.Game/Rulesets/UI/RulesetInputManager.cs @@ -107,7 +107,7 @@ namespace osu.Game.Rulesets.UI protected override List GetPendingInputs() { - if (replayInputHandler != null && !replayInputHandler.IsActive) + if (replayInputHandler?.IsActive == false) return emptyInputList; return base.GetPendingInputs(); From 346e36d32a09ebdafb1c902f4eb0b03e45fddf42 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Apr 2021 14:32:01 +0900 Subject: [PATCH 182/203] Make `Mod.Description` abstract and add missing descriptions --- osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs | 1 + osu.Game.Rulesets.Osu/Mods/OsuModTouchDevice.cs | 1 + .../NonVisual/DifficultyAdjustmentModCombinationsTest.cs | 5 +++++ osu.Game.Tests/Online/TestAPIModJsonSerialization.cs | 2 ++ osu.Game.Tests/Online/TestAPIModMessagePackSerialization.cs | 3 +++ osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs | 1 + osu.Game.Tests/Visual/UserInterface/TestSceneModButton.cs | 2 ++ osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs | 2 ++ osu.Game/Rulesets/Mods/Mod.cs | 2 +- osu.Game/Rulesets/Mods/ModNoMod.cs | 1 + 10 files changed, 19 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs index 485595cea9..fa1eb10f5e 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs @@ -15,6 +15,7 @@ namespace osu.Game.Rulesets.Mania.Mods public override string Name => "Mirror"; public override string Acronym => "MR"; public override ModType Type => ModType.Conversion; + public override string Description => "Notes are flipped horizontally"; public override double ScoreMultiplier => 1; public override bool Ranked => true; diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTouchDevice.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTouchDevice.cs index f0db548e74..3b16e9d2b7 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTouchDevice.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTouchDevice.cs @@ -9,6 +9,7 @@ namespace osu.Game.Rulesets.Osu.Mods { public override string Name => "Touch Device"; public override string Acronym => "TD"; + public override string Description => "Automatically applied to plays on devices with a touchscreen."; public override double ScoreMultiplier => 1; public override ModType Type => ModType.System; diff --git a/osu.Game.Tests/NonVisual/DifficultyAdjustmentModCombinationsTest.cs b/osu.Game.Tests/NonVisual/DifficultyAdjustmentModCombinationsTest.cs index 1c0bfd56dd..16c1004f37 100644 --- a/osu.Game.Tests/NonVisual/DifficultyAdjustmentModCombinationsTest.cs +++ b/osu.Game.Tests/NonVisual/DifficultyAdjustmentModCombinationsTest.cs @@ -144,6 +144,7 @@ namespace osu.Game.Tests.NonVisual { public override string Name => nameof(ModA); public override string Acronym => nameof(ModA); + public override string Description => string.Empty; public override double ScoreMultiplier => 1; public override Type[] IncompatibleMods => new[] { typeof(ModIncompatibleWithA), typeof(ModIncompatibleWithAAndB) }; @@ -152,6 +153,7 @@ namespace osu.Game.Tests.NonVisual private class ModB : Mod { public override string Name => nameof(ModB); + public override string Description => string.Empty; public override string Acronym => nameof(ModB); public override double ScoreMultiplier => 1; @@ -162,6 +164,7 @@ namespace osu.Game.Tests.NonVisual { public override string Name => nameof(ModC); public override string Acronym => nameof(ModC); + public override string Description => string.Empty; public override double ScoreMultiplier => 1; } @@ -169,6 +172,7 @@ namespace osu.Game.Tests.NonVisual { public override string Name => $"Incompatible With {nameof(ModA)}"; public override string Acronym => $"Incompatible With {nameof(ModA)}"; + public override string Description => string.Empty; public override double ScoreMultiplier => 1; public override Type[] IncompatibleMods => new[] { typeof(ModA) }; @@ -187,6 +191,7 @@ namespace osu.Game.Tests.NonVisual { public override string Name => $"Incompatible With {nameof(ModA)} and {nameof(ModB)}"; public override string Acronym => $"Incompatible With {nameof(ModA)} and {nameof(ModB)}"; + public override string Description => string.Empty; public override double ScoreMultiplier => 1; public override Type[] IncompatibleMods => new[] { typeof(ModA), typeof(ModB) }; diff --git a/osu.Game.Tests/Online/TestAPIModJsonSerialization.cs b/osu.Game.Tests/Online/TestAPIModJsonSerialization.cs index 3afb7481b1..ad2007f202 100644 --- a/osu.Game.Tests/Online/TestAPIModJsonSerialization.cs +++ b/osu.Game.Tests/Online/TestAPIModJsonSerialization.cs @@ -140,6 +140,7 @@ namespace osu.Game.Tests.Online { public override string Name => "Test Mod"; public override string Acronym => "TM"; + public override string Description => "This is a test mod."; public override double ScoreMultiplier => 1; [SettingSource("Test")] @@ -156,6 +157,7 @@ namespace osu.Game.Tests.Online { public override string Name => "Test Mod"; public override string Acronym => "TMTR"; + public override string Description => "This is a test mod."; public override double ScoreMultiplier => 1; [SettingSource("Initial rate", "The starting speed of the track")] diff --git a/osu.Game.Tests/Online/TestAPIModMessagePackSerialization.cs b/osu.Game.Tests/Online/TestAPIModMessagePackSerialization.cs index 74db477cfc..0462e9feb5 100644 --- a/osu.Game.Tests/Online/TestAPIModMessagePackSerialization.cs +++ b/osu.Game.Tests/Online/TestAPIModMessagePackSerialization.cs @@ -100,6 +100,7 @@ namespace osu.Game.Tests.Online { public override string Name => "Test Mod"; public override string Acronym => "TM"; + public override string Description => "This is a test mod."; public override double ScoreMultiplier => 1; [SettingSource("Test")] @@ -116,6 +117,7 @@ namespace osu.Game.Tests.Online { public override string Name => "Test Mod"; public override string Acronym => "TMTR"; + public override string Description => "This is a test mod."; public override double ScoreMultiplier => 1; [SettingSource("Initial rate", "The starting speed of the track")] @@ -150,6 +152,7 @@ namespace osu.Game.Tests.Online { public override string Name => "Test Mod"; public override string Acronym => "TM"; + public override string Description => "This is a test mod."; public override double ScoreMultiplier => 1; [SettingSource("Test")] diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs index 88fbf09ef4..280c182259 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs @@ -321,6 +321,7 @@ namespace osu.Game.Tests.Visual.Gameplay public override string Name => string.Empty; public override string Acronym => string.Empty; public override double ScoreMultiplier => 1; + public override string Description => string.Empty; public bool Applied { get; private set; } diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModButton.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModButton.cs index 443cf59003..fdc21d80ff 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModButton.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModButton.cs @@ -57,6 +57,8 @@ namespace osu.Game.Tests.Visual.UserInterface private abstract class TestMod : Mod, IApplicableMod { public override double ScoreMultiplier => 1.0; + + public override string Description => "This is a test mod."; } } } diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs index 89f9b7381b..2158cf77e5 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs @@ -226,6 +226,8 @@ namespace osu.Game.Tests.Visual.UserInterface { public override double ScoreMultiplier => 1.0; + public override string Description => "This is a customisable test mod."; + public override ModType Type => ModType.Conversion; [SettingSource("Sample float", "Change something for a mod")] diff --git a/osu.Game/Rulesets/Mods/Mod.cs b/osu.Game/Rulesets/Mods/Mod.cs index 832a14ee1e..4879590e24 100644 --- a/osu.Game/Rulesets/Mods/Mod.cs +++ b/osu.Game/Rulesets/Mods/Mod.cs @@ -49,7 +49,7 @@ namespace osu.Game.Rulesets.Mods /// The user readable description of this mod. /// [JsonIgnore] - public virtual string Description => string.Empty; + public abstract string Description { get; } /// /// The tooltip to display for this mod when used in a . diff --git a/osu.Game/Rulesets/Mods/ModNoMod.cs b/osu.Game/Rulesets/Mods/ModNoMod.cs index 379a2122f2..1009c5bc42 100644 --- a/osu.Game/Rulesets/Mods/ModNoMod.cs +++ b/osu.Game/Rulesets/Mods/ModNoMod.cs @@ -12,6 +12,7 @@ namespace osu.Game.Rulesets.Mods { public override string Name => "No Mod"; public override string Acronym => "NM"; + public override string Description => "No mods applied."; public override double ScoreMultiplier => 1; public override IconUsage? Icon => FontAwesome.Solid.Ban; public override ModType Type => ModType.System; From 23eb1c655ce3b2dd5f6bea7e5f68bdc4d111fe8e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Apr 2021 14:37:47 +0900 Subject: [PATCH 183/203] Add missing description --- osu.Game.Rulesets.Osu/Mods/OsuModBarrelRoll.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBarrelRoll.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBarrelRoll.cs index 7cdfdb3c4a..3a61968075 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModBarrelRoll.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModBarrelRoll.cs @@ -23,6 +23,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override string Name => "Barrel Roll"; public override string Acronym => "BR"; + public override string Description => "The whole playfield is on a wheel!"; public override double ScoreMultiplier => 1; public void Update(Playfield playfield) From 698a9d3feddddbb80fc9a483f02160446b985993 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Apr 2021 14:40:03 +0900 Subject: [PATCH 184/203] Add rotation direction setting --- osu.Game.Rulesets.Osu/Mods/OsuModBarrelRoll.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBarrelRoll.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBarrelRoll.cs index 3a61968075..b6cfa514a1 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModBarrelRoll.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModBarrelRoll.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Bindables; +using osu.Framework.Graphics; using osu.Game.Configuration; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Objects; @@ -21,6 +22,9 @@ namespace osu.Game.Rulesets.Osu.Mods Precision = 0.01, }; + [SettingSource("Direction", "The direction of rotation")] + public Bindable Direction { get; } = new Bindable(RotationDirection.Clockwise); + public override string Name => "Barrel Roll"; public override string Acronym => "BR"; public override string Description => "The whole playfield is on a wheel!"; @@ -28,7 +32,7 @@ namespace osu.Game.Rulesets.Osu.Mods public void Update(Playfield playfield) { - playfield.Rotation = 360 * (float)(playfield.Time.Current / 60000 * SpinSpeed.Value); + playfield.Rotation = (Direction.Value == RotationDirection.CounterClockwise ? -1 : 1) * 360 * (float)(playfield.Time.Current / 60000 * SpinSpeed.Value); } public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) From 55421b0065a8e22b66e260ae594463a04d1bdbf5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Apr 2021 14:41:10 +0900 Subject: [PATCH 185/203] Add missing full stop Co-authored-by: Salman Ahmed --- osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs index fa1eb10f5e..12f379bddb 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs @@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Mania.Mods public override string Name => "Mirror"; public override string Acronym => "MR"; public override ModType Type => ModType.Conversion; - public override string Description => "Notes are flipped horizontally"; + public override string Description => "Notes are flipped horizontally."; public override double ScoreMultiplier => 1; public override bool Ranked => true; From bc3b2af39d5d391e3bf0c95a5514193e6853faf2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Apr 2021 15:29:22 +0900 Subject: [PATCH 186/203] Add rounded corners to timeline ticks display --- .../Timelines/Summary/Visualisations/PointVisualisation.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Components/Timelines/Summary/Visualisations/PointVisualisation.cs b/osu.Game/Screens/Edit/Components/Timelines/Summary/Visualisations/PointVisualisation.cs index 53a1f94731..d647c6bfe8 100644 --- a/osu.Game/Screens/Edit/Components/Timelines/Summary/Visualisations/PointVisualisation.cs +++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/Visualisations/PointVisualisation.cs @@ -9,7 +9,7 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Visualisations /// /// Represents a singular point on a timeline part. /// - public class PointVisualisation : Box + public class PointVisualisation : Circle { public const float MAX_WIDTH = 4; From 66bb5766b9486bd06b6059007cde79680e4fcc2c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Apr 2021 14:32:01 +0900 Subject: [PATCH 187/203] Make `Mod.Description` abstract and add missing descriptions --- osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs | 1 + osu.Game.Rulesets.Osu/Mods/OsuModTouchDevice.cs | 1 + .../NonVisual/DifficultyAdjustmentModCombinationsTest.cs | 5 +++++ osu.Game.Tests/Online/TestAPIModJsonSerialization.cs | 2 ++ osu.Game.Tests/Online/TestAPIModMessagePackSerialization.cs | 3 +++ osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs | 1 + osu.Game.Tests/Visual/UserInterface/TestSceneModButton.cs | 2 ++ osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs | 2 ++ osu.Game/Rulesets/Mods/Mod.cs | 2 +- osu.Game/Rulesets/Mods/ModNoMod.cs | 1 + 10 files changed, 19 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs index 485595cea9..fa1eb10f5e 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs @@ -15,6 +15,7 @@ namespace osu.Game.Rulesets.Mania.Mods public override string Name => "Mirror"; public override string Acronym => "MR"; public override ModType Type => ModType.Conversion; + public override string Description => "Notes are flipped horizontally"; public override double ScoreMultiplier => 1; public override bool Ranked => true; diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTouchDevice.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTouchDevice.cs index f0db548e74..3b16e9d2b7 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTouchDevice.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTouchDevice.cs @@ -9,6 +9,7 @@ namespace osu.Game.Rulesets.Osu.Mods { public override string Name => "Touch Device"; public override string Acronym => "TD"; + public override string Description => "Automatically applied to plays on devices with a touchscreen."; public override double ScoreMultiplier => 1; public override ModType Type => ModType.System; diff --git a/osu.Game.Tests/NonVisual/DifficultyAdjustmentModCombinationsTest.cs b/osu.Game.Tests/NonVisual/DifficultyAdjustmentModCombinationsTest.cs index 1c0bfd56dd..16c1004f37 100644 --- a/osu.Game.Tests/NonVisual/DifficultyAdjustmentModCombinationsTest.cs +++ b/osu.Game.Tests/NonVisual/DifficultyAdjustmentModCombinationsTest.cs @@ -144,6 +144,7 @@ namespace osu.Game.Tests.NonVisual { public override string Name => nameof(ModA); public override string Acronym => nameof(ModA); + public override string Description => string.Empty; public override double ScoreMultiplier => 1; public override Type[] IncompatibleMods => new[] { typeof(ModIncompatibleWithA), typeof(ModIncompatibleWithAAndB) }; @@ -152,6 +153,7 @@ namespace osu.Game.Tests.NonVisual private class ModB : Mod { public override string Name => nameof(ModB); + public override string Description => string.Empty; public override string Acronym => nameof(ModB); public override double ScoreMultiplier => 1; @@ -162,6 +164,7 @@ namespace osu.Game.Tests.NonVisual { public override string Name => nameof(ModC); public override string Acronym => nameof(ModC); + public override string Description => string.Empty; public override double ScoreMultiplier => 1; } @@ -169,6 +172,7 @@ namespace osu.Game.Tests.NonVisual { public override string Name => $"Incompatible With {nameof(ModA)}"; public override string Acronym => $"Incompatible With {nameof(ModA)}"; + public override string Description => string.Empty; public override double ScoreMultiplier => 1; public override Type[] IncompatibleMods => new[] { typeof(ModA) }; @@ -187,6 +191,7 @@ namespace osu.Game.Tests.NonVisual { public override string Name => $"Incompatible With {nameof(ModA)} and {nameof(ModB)}"; public override string Acronym => $"Incompatible With {nameof(ModA)} and {nameof(ModB)}"; + public override string Description => string.Empty; public override double ScoreMultiplier => 1; public override Type[] IncompatibleMods => new[] { typeof(ModA), typeof(ModB) }; diff --git a/osu.Game.Tests/Online/TestAPIModJsonSerialization.cs b/osu.Game.Tests/Online/TestAPIModJsonSerialization.cs index 3afb7481b1..ad2007f202 100644 --- a/osu.Game.Tests/Online/TestAPIModJsonSerialization.cs +++ b/osu.Game.Tests/Online/TestAPIModJsonSerialization.cs @@ -140,6 +140,7 @@ namespace osu.Game.Tests.Online { public override string Name => "Test Mod"; public override string Acronym => "TM"; + public override string Description => "This is a test mod."; public override double ScoreMultiplier => 1; [SettingSource("Test")] @@ -156,6 +157,7 @@ namespace osu.Game.Tests.Online { public override string Name => "Test Mod"; public override string Acronym => "TMTR"; + public override string Description => "This is a test mod."; public override double ScoreMultiplier => 1; [SettingSource("Initial rate", "The starting speed of the track")] diff --git a/osu.Game.Tests/Online/TestAPIModMessagePackSerialization.cs b/osu.Game.Tests/Online/TestAPIModMessagePackSerialization.cs index 74db477cfc..0462e9feb5 100644 --- a/osu.Game.Tests/Online/TestAPIModMessagePackSerialization.cs +++ b/osu.Game.Tests/Online/TestAPIModMessagePackSerialization.cs @@ -100,6 +100,7 @@ namespace osu.Game.Tests.Online { public override string Name => "Test Mod"; public override string Acronym => "TM"; + public override string Description => "This is a test mod."; public override double ScoreMultiplier => 1; [SettingSource("Test")] @@ -116,6 +117,7 @@ namespace osu.Game.Tests.Online { public override string Name => "Test Mod"; public override string Acronym => "TMTR"; + public override string Description => "This is a test mod."; public override double ScoreMultiplier => 1; [SettingSource("Initial rate", "The starting speed of the track")] @@ -150,6 +152,7 @@ namespace osu.Game.Tests.Online { public override string Name => "Test Mod"; public override string Acronym => "TM"; + public override string Description => "This is a test mod."; public override double ScoreMultiplier => 1; [SettingSource("Test")] diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs index 88fbf09ef4..280c182259 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs @@ -321,6 +321,7 @@ namespace osu.Game.Tests.Visual.Gameplay public override string Name => string.Empty; public override string Acronym => string.Empty; public override double ScoreMultiplier => 1; + public override string Description => string.Empty; public bool Applied { get; private set; } diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModButton.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModButton.cs index 443cf59003..fdc21d80ff 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModButton.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModButton.cs @@ -57,6 +57,8 @@ namespace osu.Game.Tests.Visual.UserInterface private abstract class TestMod : Mod, IApplicableMod { public override double ScoreMultiplier => 1.0; + + public override string Description => "This is a test mod."; } } } diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs index 89f9b7381b..2158cf77e5 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs @@ -226,6 +226,8 @@ namespace osu.Game.Tests.Visual.UserInterface { public override double ScoreMultiplier => 1.0; + public override string Description => "This is a customisable test mod."; + public override ModType Type => ModType.Conversion; [SettingSource("Sample float", "Change something for a mod")] diff --git a/osu.Game/Rulesets/Mods/Mod.cs b/osu.Game/Rulesets/Mods/Mod.cs index 832a14ee1e..4879590e24 100644 --- a/osu.Game/Rulesets/Mods/Mod.cs +++ b/osu.Game/Rulesets/Mods/Mod.cs @@ -49,7 +49,7 @@ namespace osu.Game.Rulesets.Mods /// The user readable description of this mod. /// [JsonIgnore] - public virtual string Description => string.Empty; + public abstract string Description { get; } /// /// The tooltip to display for this mod when used in a . diff --git a/osu.Game/Rulesets/Mods/ModNoMod.cs b/osu.Game/Rulesets/Mods/ModNoMod.cs index 379a2122f2..1009c5bc42 100644 --- a/osu.Game/Rulesets/Mods/ModNoMod.cs +++ b/osu.Game/Rulesets/Mods/ModNoMod.cs @@ -12,6 +12,7 @@ namespace osu.Game.Rulesets.Mods { public override string Name => "No Mod"; public override string Acronym => "NM"; + public override string Description => "No mods applied."; public override double ScoreMultiplier => 1; public override IconUsage? Icon => FontAwesome.Solid.Ban; public override ModType Type => ModType.System; From ed14e014015042a2527210fab3af73c060677f88 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Apr 2021 14:41:10 +0900 Subject: [PATCH 188/203] Add missing full stop Co-authored-by: Salman Ahmed --- osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs index fa1eb10f5e..12f379bddb 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs @@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Mania.Mods public override string Name => "Mirror"; public override string Acronym => "MR"; public override ModType Type => ModType.Conversion; - public override string Description => "Notes are flipped horizontally"; + public override string Description => "Notes are flipped horizontally."; public override double ScoreMultiplier => 1; public override bool Ranked => true; From dd9a142e899030c6a53d0fd0275af6a57efd34f6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Apr 2021 16:30:02 +0900 Subject: [PATCH 189/203] Fix `TestSceneEditorSummaryTimeline` not displaying actual beatmap content --- .../Editing/TestSceneEditorSummaryTimeline.cs | 27 +++++++++++++------ 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneEditorSummaryTimeline.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorSummaryTimeline.cs index 94a9fd7b35..ba57eeacbe 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneEditorSummaryTimeline.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneEditorSummaryTimeline.cs @@ -4,6 +4,7 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Game.Beatmaps; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Beatmaps; using osu.Game.Screens.Edit; @@ -16,18 +17,28 @@ namespace osu.Game.Tests.Visual.Editing public class TestSceneEditorSummaryTimeline : EditorClockTestScene { [Cached(typeof(EditorBeatmap))] - private readonly EditorBeatmap editorBeatmap = new EditorBeatmap(new OsuBeatmap()); + private readonly EditorBeatmap editorBeatmap; - [BackgroundDependencyLoader] - private void load() + public TestSceneEditorSummaryTimeline() { - Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo); + editorBeatmap = new EditorBeatmap(CreateBeatmap(new OsuRuleset().RulesetInfo)); + } - Add(new SummaryTimeline + protected override void LoadComplete() + { + base.LoadComplete(); + + AddStep("create timeline", () => { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Size = new Vector2(500, 50) + // required for track + Beatmap.Value = CreateWorkingBeatmap(editorBeatmap.PlayableBeatmap); + + Add(new SummaryTimeline + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(500, 50) + }); }); } } From 73821beb1d8d127be59f77c9fa980259b3081a5d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Apr 2021 16:30:20 +0900 Subject: [PATCH 190/203] Fix break display looking bad on very long beatmaps due to fixed corner radius --- .../Edit/Components/Timelines/Summary/SummaryTimeline.cs | 2 +- .../Summary/Visualisations/DurationVisualisation.cs | 8 ++------ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/osu.Game/Screens/Edit/Components/Timelines/Summary/SummaryTimeline.cs b/osu.Game/Screens/Edit/Components/Timelines/Summary/SummaryTimeline.cs index 02cd4bccb4..e1a1eff0cb 100644 --- a/osu.Game/Screens/Edit/Components/Timelines/Summary/SummaryTimeline.cs +++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/SummaryTimeline.cs @@ -69,7 +69,7 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary Anchor = Anchor.Centre, Origin = Anchor.Centre, RelativeSizeAxes = Axes.Both, - Height = 0.25f + Height = 0.10f } }; } diff --git a/osu.Game/Screens/Edit/Components/Timelines/Summary/Visualisations/DurationVisualisation.cs b/osu.Game/Screens/Edit/Components/Timelines/Summary/Visualisations/DurationVisualisation.cs index de63df5463..86e6446555 100644 --- a/osu.Game/Screens/Edit/Components/Timelines/Summary/Visualisations/DurationVisualisation.cs +++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/Visualisations/DurationVisualisation.cs @@ -10,19 +10,15 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Visualisations /// /// Represents a spanning point on a timeline part. /// - public class DurationVisualisation : Container + public class DurationVisualisation : Circle { protected DurationVisualisation(double startTime, double endTime) { - Masking = true; - CornerRadius = 5; - RelativePositionAxes = Axes.X; RelativeSizeAxes = Axes.Both; + X = (float)startTime; Width = (float)(endTime - startTime); - - AddInternal(new Box { RelativeSizeAxes = Axes.Both }); } } } From da6f9060fa2eb98c982367ec2a0ef00fcf1edf26 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Apr 2021 16:30:56 +0900 Subject: [PATCH 191/203] Centre end circles to avoid visual gaps --- .../Edit/Components/Timelines/Summary/SummaryTimeline.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Edit/Components/Timelines/Summary/SummaryTimeline.cs b/osu.Game/Screens/Edit/Components/Timelines/Summary/SummaryTimeline.cs index e1a1eff0cb..ada7810599 100644 --- a/osu.Game/Screens/Edit/Components/Timelines/Summary/SummaryTimeline.cs +++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/SummaryTimeline.cs @@ -45,7 +45,7 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary new Circle { Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreRight, + Origin = Anchor.Centre, Size = new Vector2(5) }, new Box @@ -59,7 +59,7 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary new Circle { Anchor = Anchor.CentreRight, - Origin = Anchor.CentreLeft, + Origin = Anchor.Centre, Size = new Vector2(5) }, } From 757475e6d4c3f300684d46f5f3c860f73ecd66e5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Apr 2021 16:33:29 +0900 Subject: [PATCH 192/203] Use correct representation colours --- .../Components/Timelines/Summary/Parts/GroupVisualisation.cs | 2 +- .../Edit/Components/Timelines/Summary/SummaryTimeline.cs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/GroupVisualisation.cs b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/GroupVisualisation.cs index 93fe6f9989..8bc8618479 100644 --- a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/GroupVisualisation.cs +++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/GroupVisualisation.cs @@ -39,7 +39,7 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts return; } - Colour = controlPoints.Any(c => c is TimingControlPoint) ? colours.YellowDark : colours.Green; + Colour = Group.ControlPoints.First().GetRepresentingColour(colours); }, true); } } diff --git a/osu.Game/Screens/Edit/Components/Timelines/Summary/SummaryTimeline.cs b/osu.Game/Screens/Edit/Components/Timelines/Summary/SummaryTimeline.cs index ada7810599..ae60cd4dd3 100644 --- a/osu.Game/Screens/Edit/Components/Timelines/Summary/SummaryTimeline.cs +++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/SummaryTimeline.cs @@ -38,6 +38,7 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary }, new Container { + Name = "centre line", RelativeSizeAxes = Axes.Both, Colour = colours.Gray5, Children = new Drawable[] From 18e8682f391ea78fa40487af2d813fe6b6fe77b2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Apr 2021 17:01:25 +0900 Subject: [PATCH 193/203] Remove unused using statements --- osu.Game.Tests/Visual/Editing/TestSceneEditorSummaryTimeline.cs | 2 -- .../Timelines/Summary/Visualisations/DurationVisualisation.cs | 1 - 2 files changed, 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneEditorSummaryTimeline.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorSummaryTimeline.cs index ba57eeacbe..da0c83bb11 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneEditorSummaryTimeline.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneEditorSummaryTimeline.cs @@ -4,9 +4,7 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; -using osu.Game.Beatmaps; using osu.Game.Rulesets.Osu; -using osu.Game.Rulesets.Osu.Beatmaps; using osu.Game.Screens.Edit; using osu.Game.Screens.Edit.Components.Timelines.Summary; using osuTK; diff --git a/osu.Game/Screens/Edit/Components/Timelines/Summary/Visualisations/DurationVisualisation.cs b/osu.Game/Screens/Edit/Components/Timelines/Summary/Visualisations/DurationVisualisation.cs index 86e6446555..ec68bf9c00 100644 --- a/osu.Game/Screens/Edit/Components/Timelines/Summary/Visualisations/DurationVisualisation.cs +++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/Visualisations/DurationVisualisation.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Visualisations From bf5af3310aa1d643e08a0135531fff7b9aff9416 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Apr 2021 17:04:11 +0900 Subject: [PATCH 194/203] Update break colour to not look like kiai time --- .../Edit/Components/Timelines/Summary/Parts/BreakPart.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/BreakPart.cs b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/BreakPart.cs index e8a4b5c8c7..3d535ec915 100644 --- a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/BreakPart.cs +++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/BreakPart.cs @@ -28,7 +28,7 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts } [BackgroundDependencyLoader] - private void load(OsuColour colours) => Colour = colours.Yellow; + private void load(OsuColour colours) => Colour = colours.GreyCarmineLight; } } } From 50fad47ebc45d744dcd2f0005a9a66aa80639e63 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Thu, 15 Apr 2021 18:06:45 +0900 Subject: [PATCH 195/203] Remove usage of Lazy> for NestedHitObjects --- .../Objects/Drawables/DrawableHitObject.cs | 23 ++++++++----------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index d95b246c96..669e4cecbe 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -56,8 +56,8 @@ namespace osu.Game.Rulesets.Objects.Drawables public virtual IEnumerable GetSamples() => HitObject.Samples; - private readonly Lazy> nestedHitObjects = new Lazy>(); - public IReadOnlyList NestedHitObjects => nestedHitObjects.IsValueCreated ? nestedHitObjects.Value : (IReadOnlyList)Array.Empty(); + private readonly List nestedHitObjects = new List(); + public IReadOnlyList NestedHitObjects => nestedHitObjects; /// /// Whether this object should handle any user input events. @@ -249,7 +249,7 @@ namespace osu.Game.Rulesets.Objects.Drawables // Must be done before the nested DHO is added to occur before the nested Apply()! drawableNested.ParentHitObject = this; - nestedHitObjects.Value.Add(drawableNested); + nestedHitObjects.Add(drawableNested); AddNestedHitObject(drawableNested); } @@ -305,19 +305,16 @@ namespace osu.Game.Rulesets.Objects.Drawables if (Samples != null) Samples.Samples = null; - if (nestedHitObjects.IsValueCreated) + foreach (var obj in nestedHitObjects) { - foreach (var obj in nestedHitObjects.Value) - { - obj.OnNewResult -= onNewResult; - obj.OnRevertResult -= onRevertResult; - obj.ApplyCustomUpdateState -= onApplyCustomUpdateState; - } - - nestedHitObjects.Value.Clear(); - ClearNestedHitObjects(); + obj.OnNewResult -= onNewResult; + obj.OnRevertResult -= onRevertResult; + obj.ApplyCustomUpdateState -= onApplyCustomUpdateState; } + nestedHitObjects.Clear(); + ClearNestedHitObjects(); + HitObject.DefaultsApplied -= onDefaultsApplied; OnFree(); From d8aa436e81a1f70160983c882cf2b94d83063677 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Thu, 15 Apr 2021 18:11:47 +0900 Subject: [PATCH 196/203] Remove usage of Lazy> in NestedPlayfields --- osu.Game/Rulesets/UI/Playfield.cs | 35 ++++++++----------------------- 1 file changed, 9 insertions(+), 26 deletions(-) diff --git a/osu.Game/Rulesets/UI/Playfield.cs b/osu.Game/Rulesets/UI/Playfield.cs index c40ab4bd94..d55005363c 100644 --- a/osu.Game/Rulesets/UI/Playfield.cs +++ b/osu.Game/Rulesets/UI/Playfield.cs @@ -66,7 +66,7 @@ namespace osu.Game.Rulesets.UI var enumerable = HitObjectContainer.Objects; - if (nestedPlayfields.IsValueCreated) + if (nestedPlayfields.Count != 0) enumerable = enumerable.Concat(NestedPlayfields.SelectMany(p => p.AllHitObjects)); return enumerable; @@ -76,9 +76,9 @@ namespace osu.Game.Rulesets.UI /// /// All s nested inside this . /// - public IEnumerable NestedPlayfields => nestedPlayfields.IsValueCreated ? nestedPlayfields.Value : Enumerable.Empty(); + public IEnumerable NestedPlayfields => nestedPlayfields; - private readonly Lazy> nestedPlayfields = new Lazy>(); + private readonly List nestedPlayfields = new List(); /// /// Whether judgements should be displayed by this and and all nested s. @@ -217,7 +217,7 @@ namespace osu.Game.Rulesets.UI otherPlayfield.HitObjectUsageBegan += h => HitObjectUsageBegan?.Invoke(h); otherPlayfield.HitObjectUsageFinished += h => HitObjectUsageFinished?.Invoke(h); - nestedPlayfields.Value.Add(otherPlayfield); + nestedPlayfields.Add(otherPlayfield); } protected override void LoadComplete() @@ -279,12 +279,7 @@ namespace osu.Game.Rulesets.UI return true; } - bool removedFromNested = false; - - if (nestedPlayfields.IsValueCreated) - removedFromNested = nestedPlayfields.Value.Any(p => p.Remove(hitObject)); - - return removedFromNested; + return nestedPlayfields.Any(p => p.Remove(hitObject)); } /// @@ -429,10 +424,7 @@ namespace osu.Game.Rulesets.UI return; } - if (!nestedPlayfields.IsValueCreated) - return; - - foreach (var p in nestedPlayfields.Value) + foreach (var p in nestedPlayfields) p.SetKeepAlive(hitObject, keepAlive); } @@ -444,10 +436,7 @@ namespace osu.Game.Rulesets.UI foreach (var (_, entry) in lifetimeEntryMap) entry.KeepAlive = true; - if (!nestedPlayfields.IsValueCreated) - return; - - foreach (var p in nestedPlayfields.Value) + foreach (var p in nestedPlayfields) p.KeepAllAlive(); } @@ -461,10 +450,7 @@ namespace osu.Game.Rulesets.UI { HitObjectContainer.PastLifetimeExtension = value; - if (!nestedPlayfields.IsValueCreated) - return; - - foreach (var nested in nestedPlayfields.Value) + foreach (var nested in nestedPlayfields) nested.PastLifetimeExtension = value; } } @@ -479,10 +465,7 @@ namespace osu.Game.Rulesets.UI { HitObjectContainer.FutureLifetimeExtension = value; - if (!nestedPlayfields.IsValueCreated) - return; - - foreach (var nested in nestedPlayfields.Value) + foreach (var nested in nestedPlayfields) nested.FutureLifetimeExtension = value; } } From 153ee2551048cbbd25bf1c83856adb3adaa154f1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Apr 2021 18:42:07 +0900 Subject: [PATCH 197/203] Update base specifications to a more sane default --- .../Timelines/Summary/Visualisations/PointVisualisation.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game/Screens/Edit/Components/Timelines/Summary/Visualisations/PointVisualisation.cs b/osu.Game/Screens/Edit/Components/Timelines/Summary/Visualisations/PointVisualisation.cs index d647c6bfe8..a4b6b0c392 100644 --- a/osu.Game/Screens/Edit/Components/Timelines/Summary/Visualisations/PointVisualisation.cs +++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/Visualisations/PointVisualisation.cs @@ -21,9 +21,7 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Visualisations public PointVisualisation() { - Origin = Anchor.TopCentre; - - RelativePositionAxes = Axes.X; + RelativePositionAxes = Axes.Both; RelativeSizeAxes = Axes.Y; Anchor = Anchor.CentreLeft; From 0dc1577f6804a0b7c3863abfe3db39411ab2b980 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Apr 2021 18:42:30 +0900 Subject: [PATCH 198/203] Split out control point visualisation logic and add special kiai duration handling --- .../Parts/ControlPointVisualisation.cs | 30 ++++++++ .../Summary/Parts/EffectPointVisualisation.cs | 72 +++++++++++++++++++ .../Summary/Parts/GroupVisualisation.cs | 51 +++++++++---- .../Timelines/Summary/SummaryTimeline.cs | 1 + 4 files changed, 140 insertions(+), 14 deletions(-) create mode 100644 osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/ControlPointVisualisation.cs create mode 100644 osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/EffectPointVisualisation.cs diff --git a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/ControlPointVisualisation.cs b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/ControlPointVisualisation.cs new file mode 100644 index 0000000000..a8e41d220a --- /dev/null +++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/ControlPointVisualisation.cs @@ -0,0 +1,30 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Graphics; +using osu.Game.Screens.Edit.Components.Timelines.Summary.Visualisations; + +namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts +{ + public class ControlPointVisualisation : PointVisualisation + { + protected readonly ControlPoint Point; + + public ControlPointVisualisation(ControlPoint point) + { + Point = point; + + Height = 0.25f; + Origin = Anchor.TopCentre; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + Colour = Point.GetRepresentingColour(colours); + } + } +} diff --git a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/EffectPointVisualisation.cs b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/EffectPointVisualisation.cs new file mode 100644 index 0000000000..801372305b --- /dev/null +++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/EffectPointVisualisation.cs @@ -0,0 +1,72 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Graphics; +using osu.Game.Screens.Edit.Components.Timelines.Summary.Visualisations; + +namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts +{ + public class EffectPointVisualisation : CompositeDrawable + { + private readonly EffectControlPoint effect; + private Bindable kiai; + + [Resolved] + private EditorBeatmap beatmap { get; set; } + + [Resolved] + private OsuColour colours { get; set; } + + public EffectPointVisualisation(EffectControlPoint point) + { + RelativePositionAxes = Axes.Both; + RelativeSizeAxes = Axes.Y; + + effect = point; + } + + [BackgroundDependencyLoader] + private void load() + { + kiai = effect.KiaiModeBindable.GetBoundCopy(); + kiai.BindValueChanged(_ => + { + ClearInternal(); + + AddInternal(new ControlPointVisualisation(effect)); + + if (!kiai.Value) + return; + + var endControlPoint = beatmap.ControlPointInfo.EffectPoints.FirstOrDefault(c => c.Time > effect.Time && !c.KiaiMode); + + // handle kiai duration + // eventually this will be simpler when we have control points with durations. + if (endControlPoint != null) + { + RelativeSizeAxes = Axes.Both; + Origin = Anchor.TopLeft; + + Width = (float)(endControlPoint.Time - effect.Time); + + AddInternal(new PointVisualisation + { + RelativeSizeAxes = Axes.Both, + Origin = Anchor.TopLeft, + Width = 1, + Height = 0.25f, + Depth = float.MaxValue, + Colour = effect.GetRepresentingColour(colours).Darken(0.5f), + }); + } + }, true); + } + } +} diff --git a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/GroupVisualisation.cs b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/GroupVisualisation.cs index 8bc8618479..4629f9b540 100644 --- a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/GroupVisualisation.cs +++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/GroupVisualisation.cs @@ -1,29 +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.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Graphics; -using osu.Game.Screens.Edit.Components.Timelines.Summary.Visualisations; -using osuTK.Graphics; namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts { - public class GroupVisualisation : PointVisualisation + public class GroupVisualisation : CompositeDrawable { + [Resolved] + private OsuColour colours { get; set; } + public readonly ControlPointGroup Group; private readonly IBindableList controlPoints = new BindableList(); - [Resolved] - private OsuColour colours { get; set; } - public GroupVisualisation(ControlPointGroup group) - : base(group.Time) { + RelativePositionAxes = Axes.X; + + RelativeSizeAxes = Axes.Both; + Origin = Anchor.TopLeft; + Group = group; + X = (float)group.Time; } protected override void LoadComplete() @@ -33,13 +37,32 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts controlPoints.BindTo(Group.ControlPoints); controlPoints.BindCollectionChanged((_, __) => { - if (controlPoints.Count == 0) - { - Colour = Color4.Transparent; - return; - } + ClearInternal(); - Colour = Group.ControlPoints.First().GetRepresentingColour(colours); + if (controlPoints.Count == 0) + return; + + foreach (var point in Group.ControlPoints) + { + switch (point) + { + case TimingControlPoint _: + AddInternal(new ControlPointVisualisation(point) { Y = 0, }); + break; + + case DifficultyControlPoint _: + AddInternal(new ControlPointVisualisation(point) { Y = 0.25f, }); + break; + + case SampleControlPoint _: + AddInternal(new ControlPointVisualisation(point) { Y = 0.5f, }); + break; + + case EffectControlPoint effect: + AddInternal(new EffectPointVisualisation(effect) { Y = 0.75f }); + break; + } + } }, true); } } diff --git a/osu.Game/Screens/Edit/Components/Timelines/Summary/SummaryTimeline.cs b/osu.Game/Screens/Edit/Components/Timelines/Summary/SummaryTimeline.cs index ae60cd4dd3..e90ae411de 100644 --- a/osu.Game/Screens/Edit/Components/Timelines/Summary/SummaryTimeline.cs +++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/SummaryTimeline.cs @@ -27,6 +27,7 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary Anchor = Anchor.Centre, Origin = Anchor.BottomCentre, RelativeSizeAxes = Axes.Both, + Y = -10, Height = 0.35f }, new BookmarkPart From 17e021c549f424e9151eefe2351395925e08d365 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Apr 2021 18:45:52 +0900 Subject: [PATCH 199/203] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index b5315c3616..32e236ccd5 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,6 +52,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 45b3d5c161..954cf511b6 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -29,7 +29,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 105a6e59c2..09f6033bfe 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -93,7 +93,7 @@ - + From 1a987dfbc0712b5ee54ba35de6a88a54fb0aa20e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 15 Apr 2021 21:16:38 +0900 Subject: [PATCH 200/203] Fix gameplay cursor showing offscreen --- osu.Game.Rulesets.Osu/UI/OsuResumeOverlay.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/UI/OsuResumeOverlay.cs b/osu.Game.Rulesets.Osu/UI/OsuResumeOverlay.cs index ec7751d2b4..44ca5e850f 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuResumeOverlay.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuResumeOverlay.cs @@ -33,7 +33,6 @@ namespace osu.Game.Rulesets.Osu.UI { Add(cursorScaleContainer = new Container { - RelativePositionAxes = Axes.Both, Child = clickToResumeCursor = new OsuClickToResumeCursor { ResumeRequested = Resume } }); } From 71b06d7e61024503a7e983860ecfd07412d8585f Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 15 Apr 2021 15:53:21 +0300 Subject: [PATCH 201/203] Simplify ExtendableCircle even more --- .../Components/Timeline/TimelineHitObjectBlueprint.cs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs index 1c0d6e5ab6..23069f6079 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs @@ -345,7 +345,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline /// public class ExtendableCircle : CompositeDrawable { - private readonly CircularContainer content; + private readonly Circle content; public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => content.ReceivePositionalInputAt(screenSpacePos); @@ -354,19 +354,14 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline public ExtendableCircle() { Padding = new MarginPadding { Horizontal = -circle_size / 2f }; - InternalChild = content = new CircularContainer + InternalChild = content = new Circle { RelativeSizeAxes = Axes.Both, - Masking = true, EdgeEffect = new EdgeEffectParameters { Type = EdgeEffectType.Shadow, Radius = 5, Colour = Color4.Black.Opacity(0.4f) - }, - Child = new Box - { - RelativeSizeAxes = Axes.Both } }; } From 6f3158e8c88b0d82541b9990965756ddc74cdaf6 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 15 Apr 2021 23:24:13 +0900 Subject: [PATCH 202/203] Increase multiplier to 1.8 --- osu.Game/Overlays/Volume/VolumeMeter.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Volume/VolumeMeter.cs b/osu.Game/Overlays/Volume/VolumeMeter.cs index 57485946c1..202eac93ea 100644 --- a/osu.Game/Overlays/Volume/VolumeMeter.cs +++ b/osu.Game/Overlays/Volume/VolumeMeter.cs @@ -237,6 +237,7 @@ namespace osu.Game.Overlays.Volume private double accelerationModifier = 1; private const double max_acceleration = 5; + private const double acceleration_multiplier = 1.8; private ScheduledDelegate accelerationDebounce; @@ -250,7 +251,7 @@ namespace osu.Game.Overlays.Volume accelerationDebounce = Scheduler.AddDelayed(resetAcceleration, 150); delta *= accelerationModifier; - accelerationModifier = Math.Min(max_acceleration, accelerationModifier * 1.2f); + accelerationModifier = Math.Min(max_acceleration, accelerationModifier * acceleration_multiplier); var precision = Bindable.Precision; From 34859a476012ca6895a4e0216ff06f85f48a074f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 15 Apr 2021 23:37:05 +0900 Subject: [PATCH 203/203] Invalidate drawnode on change --- osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs index b55575696e..7f86e9daf7 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs @@ -32,7 +32,17 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor private double timeOffset; private float time; - protected Anchor TrailOrigin = Anchor.Centre; + private Anchor trailOrigin = Anchor.Centre; + + protected Anchor TrailOrigin + { + get => trailOrigin; + set + { + trailOrigin = value; + Invalidate(Invalidation.DrawNode); + } + } public CursorTrail() {