From ce864011f20366179cee5281fbf46f0f2b29c234 Mon Sep 17 00:00:00 2001 From: Joehu Date: Sun, 10 Oct 2021 12:19:56 -0700 Subject: [PATCH 001/260] Add ability to change ui scaling via keyboard --- osu.Game/OsuGame.cs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 8a018f17d9..e604e91471 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -986,6 +986,31 @@ namespace osu.Game return false; } + public override bool OnPressed(KeyBindingPressEvent e) + { + switch (e.Action) + { + case PlatformAction.ZoomIn: + adjustUIScaling(1); + return true; + + case PlatformAction.ZoomOut: + adjustUIScaling(-1); + return true; + + case PlatformAction.ZoomDefault: + LocalConfig.GetBindable(OsuSetting.UIScale).SetDefault(); + return true; + } + + void adjustUIScaling(int amount) + { + LocalConfig.SetValue(OsuSetting.UIScale, LocalConfig.Get(OsuSetting.UIScale) + (0.05f * amount)); + } + + return base.OnPressed(e); + } + #region Inactive audio dimming private readonly BindableDouble inactiveVolumeFade = new BindableDouble(); From 25f172fae8ba1c06771948afa0a9d0f73807a8a0 Mon Sep 17 00:00:00 2001 From: Susko3 <16479013+Susko3@users.noreply.github.com> Date: Sun, 17 Oct 2021 12:17:38 +0200 Subject: [PATCH 002/260] Update usages of OpenInNativeExplorer to use ShowFileInNativeExplorer Also updates WrappedStorage to override ShowFileInNativeExplorer. --- osu.Game/Database/ArchiveModelManager.cs | 6 ++++-- osu.Game/Graphics/ScreenshotManager.cs | 2 +- osu.Game/IO/WrappedStorage.cs | 2 ++ osu.Game/OsuGame.cs | 4 +++- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 84e33e3f36..0c6b229413 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -462,10 +462,12 @@ namespace osu.Game.Database if (retrievedItem == null) throw new ArgumentException(@"Specified model could not be found", nameof(item)); - using (var outputStream = exportStorage.GetStream($"{getValidFilename(item.ToString())}{HandledExtensions.First()}", FileAccess.Write, FileMode.Create)) + var filename = $"{getValidFilename(item.ToString())}{HandledExtensions.First()}"; + + using (var outputStream = exportStorage.GetStream(filename, FileAccess.Write, FileMode.Create)) ExportModelTo(retrievedItem, outputStream); - exportStorage.OpenInNativeExplorer(); + exportStorage.ShowFileInNativeExplorer(filename); } /// diff --git a/osu.Game/Graphics/ScreenshotManager.cs b/osu.Game/Graphics/ScreenshotManager.cs index 9cd403f409..27e6e2bb1c 100644 --- a/osu.Game/Graphics/ScreenshotManager.cs +++ b/osu.Game/Graphics/ScreenshotManager.cs @@ -135,7 +135,7 @@ namespace osu.Game.Graphics Text = $"{fileName} saved!", Activated = () => { - storage.OpenInNativeExplorer(); + storage.ShowFileInNativeExplorer(fileName); return true; } }); diff --git a/osu.Game/IO/WrappedStorage.cs b/osu.Game/IO/WrappedStorage.cs index b9ccc907d9..1e2d53d6fb 100644 --- a/osu.Game/IO/WrappedStorage.cs +++ b/osu.Game/IO/WrappedStorage.cs @@ -72,6 +72,8 @@ namespace osu.Game.IO public override void OpenPathInNativeExplorer(string path) => UnderlyingStorage.OpenPathInNativeExplorer(MutatePath(path)); + public override void ShowFileInNativeExplorer(string filename) => UnderlyingStorage.ShowFileInNativeExplorer(MutatePath(filename)); + public override Storage GetStorageForDirectory(string path) { if (string.IsNullOrEmpty(path)) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 820597488b..a36c1e18e0 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -913,13 +913,15 @@ namespace osu.Game } else if (recentLogCount == short_term_display_limit) { + var logFile = $@"{entry.Target.ToString().ToLowerInvariant()}.log"; + Schedule(() => Notifications.Post(new SimpleNotification { Icon = FontAwesome.Solid.EllipsisH, Text = "Subsequent messages have been logged. Click to view log files.", Activated = () => { - Storage.GetStorageForDirectory("logs").OpenInNativeExplorer(); + Storage.GetStorageForDirectory(@"logs").ShowFileInNativeExplorer(logFile); return true; } })); From f7ec135b9b0a1bf357a3b508fea8b746a52a8c97 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 19 Oct 2021 14:39:21 +0900 Subject: [PATCH 003/260] Fix `TestSceneLoungeRoomsContainer` crashing on selecting a room with a password --- .../Multiplayer/TestSceneLoungeRoomsContainer.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs index 99b530c2a2..1bdf3c2750 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs @@ -5,6 +5,7 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; using osu.Framework.Testing; using osu.Game.Online.Rooms; using osu.Game.Rulesets.Catch; @@ -25,12 +26,18 @@ namespace osu.Game.Tests.Visual.Multiplayer [SetUp] public new void Setup() => Schedule(() => { - Child = container = new RoomsContainer + Child = new PopoverContainer { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, Anchor = Anchor.Centre, Origin = Anchor.Centre, Width = 0.5f, - SelectedRoom = { BindTarget = SelectedRoom } + + Child = container = new RoomsContainer + { + SelectedRoom = { BindTarget = SelectedRoom } + } }; }); From d35fe5493a6638f14795ef033f09dbef7afd1234 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 19 Oct 2021 14:50:19 +0900 Subject: [PATCH 004/260] Change default value of `DrawableLoungeRoom.matchingFilter` so they display by default --- osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs index fe7c7cc364..e787191d5d 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs @@ -103,7 +103,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge public IEnumerable FilterTerms => new[] { Room.Name.Value }; - private bool matchingFilter; + private bool matchingFilter = true; public bool MatchingFilter { From 473459d1910b1a164b2784f74531b7beb09d264f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 19 Oct 2021 15:04:14 +0900 Subject: [PATCH 005/260] Add layout duration to `PasswordEntryPopover` to make error text look a bit smoother --- osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs index e787191d5d..79515bf352 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs @@ -200,6 +200,8 @@ namespace osu.Game.Screens.OnlinePlay.Lounge Spacing = new Vector2(5), AutoSizeAxes = Axes.Both, Direction = FillDirection.Vertical, + LayoutDuration = 500, + LayoutEasing = Easing.OutQuint, Children = new Drawable[] { new FillFlowContainer From 8944b1dd78cd5daa4b48eb706ae7f1ca2cadd0e3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 19 Oct 2021 15:04:32 +0900 Subject: [PATCH 006/260] Add basic test coverage of `DrawableLoungeRoom` --- .../TestSceneDrawableLoungeRoom.cs | 72 +++++++++++++++++++ .../OnlinePlay/Lounge/LoungeSubScreen.cs | 2 +- 2 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableLoungeRoom.cs diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableLoungeRoom.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableLoungeRoom.cs new file mode 100644 index 0000000000..91c65d14a7 --- /dev/null +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableLoungeRoom.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; +using System.Threading; +using System.Threading.Tasks; +using Moq; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Cursor; +using osu.Framework.Testing; +using osu.Game.Online.Rooms; +using osu.Game.Overlays; +using osu.Game.Screens.OnlinePlay.Lounge; + +namespace osu.Game.Tests.Visual.Multiplayer +{ + public class TestSceneDrawableLoungeRoom : OsuTestScene + { + private readonly Room room = new Room + { + HasPassword = { Value = true } + }; + + [Cached] + protected readonly OverlayColourProvider ColourProvider = new OverlayColourProvider(OverlayColourScheme.Pink); + + [BackgroundDependencyLoader] + private void load() + { + var mockLounge = new Mock(); + mockLounge + .Setup(l => l.Join(It.IsAny(), It.IsAny(), It.IsAny>(), It.IsAny>())) + .Callback, Action>((a, b, c, d) => + { + Task.Run(() => + { + Thread.Sleep(500); + Schedule(() => d?.Invoke("Incorrect password")); + }); + }); + + Dependencies.CacheAs(mockLounge.Object); + } + + [SetUpSteps] + public void SetUpSteps() + { + AddStep("create drawable", () => + { + Child = new PopoverContainer + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + new DrawableLoungeRoom(room) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + } + } + }; + }); + } + + [Test] + public void TestFocus() + { + } + } +} diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs index 08bdd0487a..f9183ec74a 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs @@ -290,7 +290,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge popoverContainer.HidePopover(); } - public void Join(Room room, string password, Action onSuccess = null, Action onFailure = null) => Schedule(() => + public virtual void Join(Room room, string password, Action onSuccess = null, Action onFailure = null) => Schedule(() => { if (joiningRoomOperation != null) return; From 32cbf6e54be7f8cdccc0b78eec7a4f3a175e1b61 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 19 Oct 2021 15:20:30 +0900 Subject: [PATCH 007/260] Add test coverage ensuring password popover keeps focus --- .../TestSceneDrawableLoungeRoom.cs | 103 +++++++++++++++++- 1 file changed, 99 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableLoungeRoom.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableLoungeRoom.cs index 91c65d14a7..6d969b6a83 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableLoungeRoom.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableLoungeRoom.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Linq; using System.Threading; using System.Threading.Tasks; using Moq; @@ -10,13 +11,15 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Cursor; using osu.Framework.Testing; +using osu.Game.Graphics.UserInterface; using osu.Game.Online.Rooms; using osu.Game.Overlays; using osu.Game.Screens.OnlinePlay.Lounge; +using osuTK.Input; namespace osu.Game.Tests.Visual.Multiplayer { - public class TestSceneDrawableLoungeRoom : OsuTestScene + public class TestSceneDrawableLoungeRoom : OsuManualInputManagerTestScene { private readonly Room room = new Room { @@ -26,6 +29,11 @@ namespace osu.Game.Tests.Visual.Multiplayer [Cached] protected readonly OverlayColourProvider ColourProvider = new OverlayColourProvider(OverlayColourScheme.Pink); + private DrawableLoungeRoom drawableRoom; + private SearchTextBox searchTextBox; + + private readonly ManualResetEventSlim allowResponseCallback = new ManualResetEventSlim(); + [BackgroundDependencyLoader] private void load() { @@ -36,7 +44,8 @@ namespace osu.Game.Tests.Visual.Multiplayer { Task.Run(() => { - Thread.Sleep(500); + allowResponseCallback.Wait(); + allowResponseCallback.Reset(); Schedule(() => d?.Invoke("Incorrect password")); }); }); @@ -54,7 +63,16 @@ namespace osu.Game.Tests.Visual.Multiplayer RelativeSizeAxes = Axes.Both, Children = new Drawable[] { - new DrawableLoungeRoom(room) + searchTextBox = new SearchTextBox() + { + HoldFocus = true, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Margin = new MarginPadding(50), + Width = 500, + Depth = float.MaxValue + }, + drawableRoom = new DrawableLoungeRoom(room) { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -65,8 +83,85 @@ namespace osu.Game.Tests.Visual.Multiplayer } [Test] - public void TestFocus() + public void TestFocusViaKeyboardCommit() { + DrawableLoungeRoom.PasswordEntryPopover popover = null; + + AddAssert("search textbox has focus", () => checkFocus(searchTextBox)); + AddStep("click room twice", () => + { + InputManager.MoveMouseTo(drawableRoom); + InputManager.Click(MouseButton.Left); + InputManager.Click(MouseButton.Left); + }); + AddUntilStep("wait for popover", () => (popover = InputManager.ChildrenOfType().SingleOrDefault()) != null); + + AddAssert("textbox has focus", () => checkFocus(popover.ChildrenOfType().Single())); + + AddStep("enter password", () => popover.ChildrenOfType().Single().Text = "password"); + AddStep("commit via enter", () => InputManager.Key(Key.Enter)); + + AddAssert("popover has focus", () => checkFocus(popover)); + + AddStep("attempt another enter", () => InputManager.Key(Key.Enter)); + + AddAssert("popover still has focus", () => checkFocus(popover)); + + AddStep("unblock response", () => allowResponseCallback.Set()); + + AddUntilStep("wait for textbox refocus", () => checkFocus(popover.ChildrenOfType().Single())); + + AddStep("press escape", () => InputManager.Key(Key.Escape)); + AddStep("press escape", () => InputManager.Key(Key.Escape)); + + AddUntilStep("search textbox has focus", () => checkFocus(searchTextBox)); } + + [Test] + public void TestFocusViaMouseCommit() + { + DrawableLoungeRoom.PasswordEntryPopover popover = null; + + AddAssert("search textbox has focus", () => checkFocus(searchTextBox)); + AddStep("click room twice", () => + { + InputManager.MoveMouseTo(drawableRoom); + InputManager.Click(MouseButton.Left); + InputManager.Click(MouseButton.Left); + }); + AddUntilStep("wait for popover", () => (popover = InputManager.ChildrenOfType().SingleOrDefault()) != null); + + AddAssert("textbox has focus", () => checkFocus(popover.ChildrenOfType().Single())); + + AddStep("enter password", () => popover.ChildrenOfType().Single().Text = "password"); + + AddStep("commit via click button", () => + { + var button = popover.ChildrenOfType().Single(); + InputManager.MoveMouseTo(button); + InputManager.Click(MouseButton.Left); + }); + + AddAssert("popover has focus", () => checkFocus(popover)); + + AddStep("attempt another click", () => InputManager.Click(MouseButton.Left)); + + AddAssert("popover still has focus", () => checkFocus(popover)); + + AddStep("unblock response", () => allowResponseCallback.Set()); + + AddUntilStep("wait for textbox refocus", () => checkFocus(popover.ChildrenOfType().Single())); + + AddStep("click away", () => + { + InputManager.MoveMouseTo(searchTextBox); + InputManager.Click(MouseButton.Left); + }); + + AddUntilStep("search textbox has focus", () => checkFocus(searchTextBox)); + } + + private bool checkFocus(Drawable expected) => + InputManager.FocusedDrawable == expected; } } From a589964dc7f50743536b020efe5006900d7f396d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 19 Oct 2021 15:27:51 +0900 Subject: [PATCH 008/260] Centralise join logic --- .../OnlinePlay/Lounge/DrawableLoungeRoom.cs | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs index 79515bf352..64bd61b960 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs @@ -232,7 +232,21 @@ namespace osu.Game.Screens.OnlinePlay.Lounge sampleJoinFail = audio.Samples.Get(@"UI/password-fail"); - joinButton.Action = () => lounge?.Join(room, passwordTextbox.Text, null, joinFailed); + joinButton.Action = performJoin; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + Schedule(() => GetContainingInputManager().ChangeFocus(passwordTextbox)); + passwordTextbox.OnCommit += (_, __) => performJoin(); + } + + private void performJoin() + { + lounge?.Join(room, passwordTextbox.Text, null, joinFailed); + GetContainingInputManager().TriggerFocusContention(passwordTextbox); } private void joinFailed(string error) @@ -252,14 +266,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge sampleJoinFail?.Play(); } - - protected override void LoadComplete() - { - base.LoadComplete(); - - Schedule(() => GetContainingInputManager().ChangeFocus(passwordTextbox)); - passwordTextbox.OnCommit += (_, __) => lounge?.Join(room, passwordTextbox.Text, null, joinFailed); - } } } } From da524261c076ca844d1f28e611de97d7ffd16c03 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 19 Oct 2021 15:36:15 +0900 Subject: [PATCH 009/260] Fix focus potentially being transferred too far up when password is being verified --- osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs index 64bd61b960..2a68b1d9b3 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs @@ -181,6 +181,10 @@ namespace osu.Game.Screens.OnlinePlay.Lounge [Resolved(canBeNull: true)] private LoungeSubScreen lounge { get; set; } + public override bool HandleNonPositionalInput => true; + + protected override bool BlockNonPositionalInput => true; + public PasswordEntryPopover(Room room) { this.room = room; From 480dc571025924d4b4bf5fadb776d8654eb3abbc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 19 Oct 2021 15:54:10 +0900 Subject: [PATCH 010/260] Schedule `joinFailed` callback for added safety --- osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs index 2a68b1d9b3..72574b729a 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs @@ -253,7 +253,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge GetContainingInputManager().TriggerFocusContention(passwordTextbox); } - private void joinFailed(string error) + private void joinFailed(string error) => Schedule(() => { passwordTextbox.Text = string.Empty; @@ -269,7 +269,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge Body.Shake(); sampleJoinFail?.Play(); - } + }); } } } From 789c715f136254e68cdccce5d986bbfd9a05e1d6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 Oct 2021 15:22:47 +0900 Subject: [PATCH 011/260] Add `skin.ini` write support to allow for more correct hashing --- osu.Game.Tests/Skins/IO/ImportSkinTest.cs | 33 +++++- osu.Game/Database/ArchiveModelManager.cs | 43 +++++-- osu.Game/Skinning/SkinManager.cs | 132 +++++++++++++++++----- 3 files changed, 162 insertions(+), 46 deletions(-) diff --git a/osu.Game.Tests/Skins/IO/ImportSkinTest.cs b/osu.Game.Tests/Skins/IO/ImportSkinTest.cs index b2600bb887..67c94413ca 100644 --- a/osu.Game.Tests/Skins/IO/ImportSkinTest.cs +++ b/osu.Game.Tests/Skins/IO/ImportSkinTest.cs @@ -38,7 +38,32 @@ namespace osu.Game.Tests.Skins.IO } [Test] - public async Task TestImportTwiceWithSameMetadata() + public async Task TestImportTwiceWithSameMetadataAndFilename() + { + using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(ImportSkinTest))) + { + try + { + var osu = LoadOsuIntoHost(host); + + var imported = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk("test skin", "skinner"), "skin.osk")); + var imported2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk("test skin", "skinner"), "skin.osk")); + + Assert.That(imported2.ID, Is.EqualTo(imported.ID)); + Assert.That(osu.Dependencies.Get().GetAllUserSkins(true).Count, Is.EqualTo(1)); + + // the first should be overwritten by the second import. + Assert.That(imported.Files.First().FileInfoID, Is.EqualTo(imported2.Files.First().FileInfoID)); + } + finally + { + host.Exit(); + } + } + } + + [Test] + public async Task TestImportTwiceWithSameMetadataButDifferentFilename() { using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(ImportSkinTest))) { @@ -50,10 +75,10 @@ namespace osu.Game.Tests.Skins.IO var imported2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk("test skin", "skinner"), "skin2.osk")); Assert.That(imported2.ID, Is.Not.EqualTo(imported.ID)); - Assert.That(osu.Dependencies.Get().GetAllUserSkins(true).Count, Is.EqualTo(1)); + Assert.That(osu.Dependencies.Get().GetAllUserSkins(true).Count, Is.EqualTo(2)); - // the first should be overwritten by the second import. - Assert.That(osu.Dependencies.Get().GetAllUserSkins(true).First().Files.First().FileInfoID, Is.EqualTo(imported2.Files.First().FileInfoID)); + // skin.ini will be rewritten and therefore not match. + Assert.That(imported.Files.First().FileInfoID, Is.Not.EqualTo(imported2.Files.First().FileInfoID)); } finally { diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 9c777d324b..e52e5bdfe7 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -507,18 +507,26 @@ namespace osu.Game.Database /// The existing file to be deleted. public void DeleteFile(TModel model, TFileModel file) { - using (var usage = ContextFactory.GetForWrite()) + if (model.ID > 0) { - // Dereference the existing file info, since the file model will be removed. - if (file.FileInfo != null) + using (var usage = ContextFactory.GetForWrite()) { - Files.Dereference(file.FileInfo); + // Dereference the existing file info, since the file model will be removed. + if (file.FileInfo != null) + { + Files.Dereference(file.FileInfo); - // This shouldn't be required, but here for safety in case the provided TModel is not being change tracked - // Definitely can be removed once we rework the database backend. - usage.Context.Set().Remove(file); + // This shouldn't be required, but here for safety in case the provided TModel is not being change tracked + // Definitely can be removed once we rework the database backend. + usage.Context.Set().Remove(file); + } + + model.Files.Remove(file); } - + } + else + { + Files.Dereference(file.FileInfo); model.Files.Remove(file); } } @@ -531,15 +539,28 @@ namespace osu.Game.Database /// The filename for the new file. public void AddFile(TModel model, Stream contents, string filename) { - using (ContextFactory.GetForWrite()) + if (model.ID > 0) { + using (ContextFactory.GetForWrite()) + { + model.Files.Add(new TFileModel + { + Filename = filename, + FileInfo = Files.Add(contents) + }); + + Update(model); + } + } + else + { + // This function may be called during the import process. + // Should not exist like this once we have switched to realm. model.Files.Add(new TFileModel { Filename = filename, FileInfo = Files.Add(contents) }); - - Update(model); } } diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index 3842acab74..724ca3bc62 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -14,7 +14,6 @@ using Newtonsoft.Json; using osu.Framework.Audio; using osu.Framework.Audio.Sample; using osu.Framework.Bindables; -using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.OpenGL.Textures; using osu.Framework.Graphics.Textures; @@ -138,52 +137,123 @@ namespace osu.Game.Skinning { var instance = GetSkin(item); - // in the case the skin has a skin.ini file, we are going to create a hash based on that. - // we don't want to do this in the case we don't have a skin.ini, as it would match only on the filename portion, - // causing potentially unique skin imports to be considered as a duplicate. - if (!string.IsNullOrEmpty(instance.Configuration.SkinInfo.Name)) - { - // we need to populate early to create a hash based off skin.ini contents - populateMetadata(item, instance, reader?.Name); + // This function can be run on fresh import or save. The logic here ensures a skin.ini file is in a good state for both operations. - return item.ToString().ComputeSHA2Hash(); + // LegacySkin will parse the skin.ini and populate `Skin.Configuration` during construction above. + bool hasSkinIni = string.IsNullOrEmpty(instance.Configuration.SkinInfo.Name); + + if (hasSkinIni) + { + item.Name = instance.Configuration.SkinInfo.Name; + item.Creator = instance.Configuration.SkinInfo.Creator; } + item.Creator ??= unknown_creator_string; + + bool isImport = reader != null; + + if (isImport) + { + // For imports, we want to use the archive or folder name as part of the metadata, in addition to any existing skin.ini metadata. + // In an ideal world, skin.ini would be the only source of metadata, but a lot of skin creators and users don't update it when making modifications. + // In both of these cases, the expectation from the user is that the filename or folder name is displayed somewhere to identify the skin. + + string archiveName = item.Name.Replace(".osk", "", StringComparison.OrdinalIgnoreCase); + + if (archiveName != item.Name) + item.Name = $"{item.Name} [{archiveName}]"; + } + + // By this point, the metadata in SkinInfo will be correct. + // Regardless of whether this is an import or not, let's write the skin.ini if non-existing or non-matching. + // This is (weirdly) done inside ComputeHash to avoid adding a new method to handle this case. After switching to realm it can be moved into another place. + if (instance.Configuration.SkinInfo.Name != item.Name) + updateSkinIniMetadata(item, hasSkinIni); + return base.ComputeHash(item, reader); } + private void updateSkinIniMetadata(SkinInfo item, bool hasSkinIni) + { + string nameLine = $"Name: {item.Name}"; + string authorLine = $"Author: {item.Name}"; + + if (hasSkinIni) + { + List outputLines = new List(); + + bool addedName = false; + bool addedAuthor = false; + + var existingFile = item.Files.First(f => f.Filename == "skin.ini"); + + using (var stream = Files.Storage.GetStream(existingFile.FileInfo.StoragePath)) + using (var sr = new StreamReader(stream)) + { + string line; + + while ((line = sr.ReadLine()) != null) + { + if (line.StartsWith("Name:", StringComparison.Ordinal)) + { + outputLines.Add(nameLine); + addedName = true; + } + else if (line.StartsWith("Author:", StringComparison.Ordinal)) + { + outputLines.Add(authorLine); + addedAuthor = true; + } + else + outputLines.Add(line); + } + } + + if (!addedName || !addedAuthor) + { + outputLines.AddRange(new[] + { + "[General]", + nameLine, + authorLine, + }); + } + + using (Stream stream = new MemoryStream()) + using (var sw = new StreamWriter(stream, Encoding.UTF8, 1024, true)) + { + foreach (string line in outputLines) + sw.WriteLine(line); + + ReplaceFile(item, existingFile, stream); + } + } + else + { + using (Stream stream = new MemoryStream()) + using (var sw = new StreamWriter(stream, Encoding.UTF8, 1024, true)) + { + sw.WriteLine("[General]"); + sw.WriteLine(nameLine); + sw.WriteLine(authorLine); + + AddFile(item, stream, "skin.ini"); + } + } + } + protected override Task Populate(SkinInfo model, ArchiveReader archive, CancellationToken cancellationToken = default) { var instance = GetSkin(model); model.InstantiationInfo ??= instance.GetType().GetInvariantInstantiationInfo(); - populateMetadata(model, instance, archive?.Name); + model.Name = instance.Configuration.SkinInfo.Name; + model.Creator = instance.Configuration.SkinInfo.Creator; return Task.CompletedTask; } - private void populateMetadata(SkinInfo item, Skin instance, string archiveName) - { - if (!string.IsNullOrEmpty(instance.Configuration.SkinInfo.Name)) - { - item.Name = instance.Configuration.SkinInfo.Name; - item.Creator = instance.Configuration.SkinInfo.Creator; - } - else - { - item.Name = item.Name.Replace(".osk", "", StringComparison.OrdinalIgnoreCase); - item.Creator ??= unknown_creator_string; - } - - // generally when importing from a folder, the ".osk" extension will not be present. - // if we ever need a more reliable method of determining this, the type of `ArchiveReader` can be checked. - bool isArchiveImport = archiveName?.Contains(".osk", StringComparison.OrdinalIgnoreCase) == true; - - if (archiveName != null && !isArchiveImport && archiveName != item.Name) - item.Name = $"{item.Name} [{archiveName}]"; - } - /// /// Retrieve a instance for the provided /// From fa542f254778747c2dfb8e8c0d17fe5f2e995de9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 Oct 2021 14:05:32 +0900 Subject: [PATCH 012/260] Include `json` files in skin hashing This covers the new layout storage we are doing. --- osu.Game/Skinning/SkinManager.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index 724ca3bc62..9c7ea28cc4 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -50,7 +50,7 @@ namespace osu.Game.Skinning public override IEnumerable HandledExtensions => new[] { ".osk" }; - protected override string[] HashableFileTypes => new[] { ".ini" }; + protected override string[] HashableFileTypes => new[] { ".ini", ".json" }; protected override string ImportFromStablePath => "Skins"; @@ -140,7 +140,7 @@ namespace osu.Game.Skinning // This function can be run on fresh import or save. The logic here ensures a skin.ini file is in a good state for both operations. // LegacySkin will parse the skin.ini and populate `Skin.Configuration` during construction above. - bool hasSkinIni = string.IsNullOrEmpty(instance.Configuration.SkinInfo.Name); + bool hasSkinIni = !string.IsNullOrEmpty(instance.Configuration.SkinInfo.Name); if (hasSkinIni) { From 439e90fce3d3c804fceb80e928af0ed2f87836bb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 Oct 2021 15:51:14 +0900 Subject: [PATCH 013/260] Disallow archive imports with no hashable files --- osu.Game/Database/ArchiveModelManager.cs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index e52e5bdfe7..5b07bfe9a5 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -317,14 +317,18 @@ namespace osu.Game.Database /// protected virtual string ComputeHash(TModel item, ArchiveReader reader = null) { - if (reader != null) - // fast hashing for cases where the item's files may not be populated. - return computeHashFast(reader); + var hashableFiles = item.Files + .Where(f => HashableFileTypes.Any(ext => f.Filename.EndsWith(ext, StringComparison.OrdinalIgnoreCase))) + .OrderBy(f => f.Filename) + .ToArray(); + + if (hashableFiles.Length == 0) + throw new InvalidOperationException("Attempted to hash an archive with no files"); // for now, concatenate all hashable files in the set to create a unique hash. MemoryStream hashable = new MemoryStream(); - foreach (TFileModel file in item.Files.Where(f => HashableFileTypes.Any(ext => f.Filename.EndsWith(ext, StringComparison.OrdinalIgnoreCase))).OrderBy(f => f.Filename)) + foreach (TFileModel file in hashableFiles) { using (Stream s = Files.Store.GetStream(file.FileInfo.StoragePath)) s.CopyTo(hashable); @@ -700,10 +704,10 @@ namespace osu.Game.Database s.CopyTo(hashable); } - if (hashable.Length > 0) - return hashable.ComputeSHA2Hash(); + if (hashable.Length == 0) + throw new InvalidOperationException("Attempted to hash an archive with no files"); - return reader.Name.ComputeSHA2Hash(); + return hashable.ComputeSHA2Hash(); } /// From 19f30177eacb10e733216d3534ec997b1dc9a5a8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 Oct 2021 16:48:32 +0900 Subject: [PATCH 014/260] Rewrite tests completely --- osu.Game.Tests/Skins/IO/ImportSkinTest.cs | 249 ++++++++++------------ 1 file changed, 108 insertions(+), 141 deletions(-) diff --git a/osu.Game.Tests/Skins/IO/ImportSkinTest.cs b/osu.Game.Tests/Skins/IO/ImportSkinTest.cs index 67c94413ca..8449b0bbd3 100644 --- a/osu.Game.Tests/Skins/IO/ImportSkinTest.cs +++ b/osu.Game.Tests/Skins/IO/ImportSkinTest.cs @@ -4,6 +4,7 @@ using System; using System.IO; using System.Linq; +using System.Runtime.CompilerServices; using System.Threading.Tasks; using NUnit.Framework; using osu.Framework.Allocation; @@ -16,180 +17,130 @@ namespace osu.Game.Tests.Skins.IO { public class ImportSkinTest : ImportTest { - [Test] - public async Task TestBasicImport() - { - using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(ImportSkinTest))) - { - try - { - var osu = LoadOsuIntoHost(host); - - var imported = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk("test skin", "skinner"), "skin.osk")); - - Assert.That(imported.Name, Is.EqualTo("test skin")); - Assert.That(imported.Creator, Is.EqualTo("skinner")); - } - finally - { - host.Exit(); - } - } - } + #region Testing filename metadata inclusion [Test] - public async Task TestImportTwiceWithSameMetadataAndFilename() + public Task TestSingleImportDifferentFilename() => runSkinTest(async osu => { - using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(ImportSkinTest))) - { - try - { - var osu = LoadOsuIntoHost(host); + var imported = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk("test skin", "skinner"), "skin.osk")); - var imported = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk("test skin", "skinner"), "skin.osk")); - var imported2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk("test skin", "skinner"), "skin.osk")); - - Assert.That(imported2.ID, Is.EqualTo(imported.ID)); - Assert.That(osu.Dependencies.Get().GetAllUserSkins(true).Count, Is.EqualTo(1)); - - // the first should be overwritten by the second import. - Assert.That(imported.Files.First().FileInfoID, Is.EqualTo(imported2.Files.First().FileInfoID)); - } - finally - { - host.Exit(); - } - } - } + // When the import filename doesn't match, it should be appended (and update the skin.ini). + Assert.That(imported.Name, Is.EqualTo("test skin [skin]")); + Assert.That(imported.Creator, Is.EqualTo("skinner")); + }); [Test] - public async Task TestImportTwiceWithSameMetadataButDifferentFilename() + public Task TestSingleImportMatchingFilename() => runSkinTest(async osu => { - using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(ImportSkinTest))) - { - try - { - var osu = LoadOsuIntoHost(host); + var imported = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk("test skin", "skinner"), "test skin.osk")); - var imported = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk("test skin", "skinner"), "skin.osk")); - var imported2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk("test skin", "skinner"), "skin2.osk")); + // When the import filename matches it shouldn't be appended. + Assert.That(imported.Name, Is.EqualTo("test skin")); + Assert.That(imported.Creator, Is.EqualTo("skinner")); + }); - Assert.That(imported2.ID, Is.Not.EqualTo(imported.ID)); - Assert.That(osu.Dependencies.Get().GetAllUserSkins(true).Count, Is.EqualTo(2)); + #endregion - // skin.ini will be rewritten and therefore not match. - Assert.That(imported.Files.First().FileInfoID, Is.Not.EqualTo(imported2.Files.First().FileInfoID)); - } - finally - { - host.Exit(); - } - } - } + #region Cases where imports should match existing [Test] - public async Task TestImportTwiceWithNoMetadata() + public Task TestImportTwiceWithSameMetadataAndFilename() => runSkinTest(async osu => { - using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(ImportSkinTest))) - { - try - { - var osu = LoadOsuIntoHost(host); + var imported = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk("test skin", "skinner"), "skin.osk")); + var imported2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk("test skin", "skinner"), "skin.osk")); - // if a user downloads two skins that do have skin.ini files but don't have any creator metadata in the skin.ini, they should both import separately just for safety. - var imported = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk(string.Empty, string.Empty), "download.osk")); - var imported2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk(string.Empty, string.Empty), "download.osk")); - - Assert.That(imported2.ID, Is.Not.EqualTo(imported.ID)); - Assert.That(osu.Dependencies.Get().GetAllUserSkins(true).Count, Is.EqualTo(2)); - - Assert.That(osu.Dependencies.Get().GetAllUserSkins(true).First().Files.First().FileInfoID, Is.EqualTo(imported.Files.First().FileInfoID)); - Assert.That(osu.Dependencies.Get().GetAllUserSkins(true).Last().Files.First().FileInfoID, Is.EqualTo(imported2.Files.First().FileInfoID)); - } - finally - { - host.Exit(); - } - } - } + assertImportedOnce(imported, imported2); + }); [Test] - public async Task TestImportTwiceWithDifferentMetadata() + public Task TestImportTwiceWithNoMetadataSameDownloadFilename() => runSkinTest(async osu => { - using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(ImportSkinTest))) - { - try - { - var osu = LoadOsuIntoHost(host); + // if a user downloads two skins that do have skin.ini files but don't have any creator metadata in the skin.ini, they should both import separately just for safety. + var imported = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk(string.Empty, string.Empty), "download.osk")); + var imported2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk(string.Empty, string.Empty), "download.osk")); - var imported = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk("test skin v2", "skinner"), "skin.osk")); - var imported2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk("test skin v2.1", "skinner"), "skin2.osk")); - - Assert.That(imported2.ID, Is.Not.EqualTo(imported.ID)); - Assert.That(osu.Dependencies.Get().GetAllUserSkins(true).Count, Is.EqualTo(2)); - - Assert.That(osu.Dependencies.Get().GetAllUserSkins(true).First().Files.First().FileInfoID, Is.EqualTo(imported.Files.First().FileInfoID)); - Assert.That(osu.Dependencies.Get().GetAllUserSkins(true).Last().Files.First().FileInfoID, Is.EqualTo(imported2.Files.First().FileInfoID)); - } - finally - { - host.Exit(); - } - } - } + assertImportedOnce(imported, imported2); + }); [Test] - public async Task TestImportUpperCasedOskArchive() + public Task TestImportUpperCasedOskArchive() => runSkinTest(async osu => { - using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(ImportSkinTest))) - { - try - { - var osu = LoadOsuIntoHost(host); + var imported = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk("name 1", "author 1"), "name 1.OsK")); + assertCorrectMetadata(imported, "name 1", "author 1"); - var imported = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk("name 1", "author 1"), "skin1.OsK")); + var imported2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk("name 1", "author 1"), "name 1.oSK")); - Assert.That(imported.Name, Is.EqualTo("name 1")); - Assert.That(imported.Creator, Is.EqualTo("author 1")); + assertImportedOnce(imported, imported2); + }); - var imported2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk("name 1", "author 1"), "skin1.oSK")); + #endregion - Assert.That(imported2.Hash, Is.EqualTo(imported.Hash)); - } - finally - { - host.Exit(); - } - } - } + #region Cases where imports should be uniquely imported [Test] - public async Task TestSameMetadataNameDifferentFolderName() + public Task TestImportTwiceWithSameMetadataButDifferentFilename() => runSkinTest(async osu => { - using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(ImportSkinTest))) - { - try - { - var osu = LoadOsuIntoHost(host); + var imported = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk("test skin", "skinner"), "skin.osk")); + var imported2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk("test skin", "skinner"), "skin2.osk")); - var imported = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk("name 1", "author 1", false), "my custom skin 1")); - Assert.That(imported.Name, Is.EqualTo("name 1 [my custom skin 1]")); - Assert.That(imported.Creator, Is.EqualTo("author 1")); + assertImportedBoth(imported, imported2); + }); - var imported2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk("name 1", "author 1", false), "my custom skin 2")); - Assert.That(imported2.Name, Is.EqualTo("name 1 [my custom skin 2]")); - Assert.That(imported2.Creator, Is.EqualTo("author 1")); + [Test] + public Task TestImportTwiceWithNoMetadataDifferentDownloadFilename() => runSkinTest(async osu => + { + // if a user downloads two skins that do have skin.ini files but don't have any creator metadata in the skin.ini, they should both import separately just for safety. + var imported = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk(string.Empty, string.Empty), "download.osk")); + var imported2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk(string.Empty, string.Empty), "download2.osk")); - Assert.That(imported2.Hash, Is.Not.EqualTo(imported.Hash)); - } - finally - { - host.Exit(); - } - } + assertImportedBoth(imported, imported2); + }); + + [Test] + public Task TestImportTwiceWithSameFilenameDifferentMetadata() => runSkinTest(async osu => + { + var imported = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk("test skin v2", "skinner"), "skin.osk")); + var imported2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk("test skin v2.1", "skinner"), "skin.osk")); + + assertImportedBoth(imported, imported2); + assertCorrectMetadata(imported, "test skin v2 [skin]", "skinner"); + assertCorrectMetadata(imported2, "test skin v2.1 [skin]", "skinner"); + }); + + [Test] + public Task TestSameMetadataNameDifferentFolderName() => runSkinTest(async osu => + { + var imported = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk("name 1", "author 1"), "my custom skin 1")); + var imported2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk("name 1", "author 1"), "my custom skin 2")); + + assertImportedBoth(imported, imported2); + assertCorrectMetadata(imported, "name 1 [my custom skin 2]", "author 1"); + assertCorrectMetadata(imported, "name 1 [my custom skin 2]", "author 1"); + }); + + #endregion + + private void assertCorrectMetadata(SkinInfo imported, string name, string creator) + { + Assert.That(imported.Name, Is.EqualTo(name)); + Assert.That(imported.Creator, Is.EqualTo(creator)); } - private MemoryStream createOsk(string name, string author, bool makeUnique = true) + private void assertImportedBoth(SkinInfo imported, SkinInfo imported2) + { + Assert.That(imported2.ID, Is.Not.EqualTo(imported.ID)); + Assert.That(imported2.Hash, Is.Not.EqualTo(imported.Hash)); + Assert.That(imported2.Files.Select(f => f.FileInfoID), Is.Not.EquivalentTo(imported.Files.Select(f => f.FileInfoID))); + } + + private void assertImportedOnce(SkinInfo imported, SkinInfo imported2) + { + Assert.That(imported2.ID, Is.EqualTo(imported.ID)); + Assert.That(imported2.Hash, Is.EqualTo(imported.Hash)); + Assert.That(imported2.Files.Select(f => f.FileInfoID), Is.EquivalentTo(imported.Files.Select(f => f.FileInfoID))); + } + + private MemoryStream createOsk(string name, string author, bool makeUnique = false) { var zipStream = new MemoryStream(); using var zip = ZipArchive.Create(); @@ -218,6 +169,22 @@ namespace osu.Game.Tests.Skins.IO return stream; } + private async Task runSkinTest(Func action, [CallerMemberName] string callingMethodName = @"") + { + using (HeadlessGameHost host = new CleanRunHeadlessGameHost(callingMethodName)) + { + try + { + var osu = LoadOsuIntoHost(host); + await action(osu); + } + finally + { + host.Exit(); + } + } + } + private async Task loadSkinIntoOsu(OsuGameBase osu, ArchiveReader archive = null) { var skinManager = osu.Dependencies.Get(); From a95c754fd3d0e867d7910fe3f2a746a10261329b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 Oct 2021 16:49:58 +0900 Subject: [PATCH 015/260] Fix multiple issues and make metadata fallback process more logical --- osu.Game/Skinning/SkinManager.cs | 49 +++++++++++++++----------------- 1 file changed, 23 insertions(+), 26 deletions(-) diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index 9c7ea28cc4..452c081a41 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -140,26 +140,19 @@ namespace osu.Game.Skinning // This function can be run on fresh import or save. The logic here ensures a skin.ini file is in a good state for both operations. // LegacySkin will parse the skin.ini and populate `Skin.Configuration` during construction above. - bool hasSkinIni = !string.IsNullOrEmpty(instance.Configuration.SkinInfo.Name); - - if (hasSkinIni) - { - item.Name = instance.Configuration.SkinInfo.Name; - item.Creator = instance.Configuration.SkinInfo.Creator; - } - - item.Creator ??= unknown_creator_string; + string skinIniSourcedName = instance.Configuration.SkinInfo.Name; + string archiveName = item.Name.Replace(".osk", "", StringComparison.OrdinalIgnoreCase); bool isImport = reader != null; if (isImport) { + item.Name = !string.IsNullOrEmpty(skinIniSourcedName) ? skinIniSourcedName : archiveName; + item.Creator = instance.Configuration.SkinInfo.Creator ?? unknown_creator_string; + // For imports, we want to use the archive or folder name as part of the metadata, in addition to any existing skin.ini metadata. // In an ideal world, skin.ini would be the only source of metadata, but a lot of skin creators and users don't update it when making modifications. // In both of these cases, the expectation from the user is that the filename or folder name is displayed somewhere to identify the skin. - - string archiveName = item.Name.Replace(".osk", "", StringComparison.OrdinalIgnoreCase); - if (archiveName != item.Name) item.Name = $"{item.Name} [{archiveName}]"; } @@ -167,26 +160,26 @@ namespace osu.Game.Skinning // By this point, the metadata in SkinInfo will be correct. // Regardless of whether this is an import or not, let's write the skin.ini if non-existing or non-matching. // This is (weirdly) done inside ComputeHash to avoid adding a new method to handle this case. After switching to realm it can be moved into another place. - if (instance.Configuration.SkinInfo.Name != item.Name) - updateSkinIniMetadata(item, hasSkinIni); + if (skinIniSourcedName != item.Name) + updateSkinIniMetadata(item); return base.ComputeHash(item, reader); } - private void updateSkinIniMetadata(SkinInfo item, bool hasSkinIni) + private void updateSkinIniMetadata(SkinInfo item) { string nameLine = $"Name: {item.Name}"; - string authorLine = $"Author: {item.Name}"; + string authorLine = $"Author: {item.Creator}"; - if (hasSkinIni) + var existingFile = item.Files.SingleOrDefault(f => f.Filename == "skin.ini"); + + if (existingFile != null) { List outputLines = new List(); bool addedName = false; bool addedAuthor = false; - var existingFile = item.Files.First(f => f.Filename == "skin.ini"); - using (var stream = Files.Storage.GetStream(existingFile.FileInfo.StoragePath)) using (var sr = new StreamReader(stream)) { @@ -220,10 +213,12 @@ namespace osu.Game.Skinning } using (Stream stream = new MemoryStream()) - using (var sw = new StreamWriter(stream, Encoding.UTF8, 1024, true)) { - foreach (string line in outputLines) - sw.WriteLine(line); + using (var sw = new StreamWriter(stream, Encoding.UTF8, 1024, true)) + { + foreach (string line in outputLines) + sw.WriteLine(line); + } ReplaceFile(item, existingFile, stream); } @@ -231,11 +226,13 @@ namespace osu.Game.Skinning else { using (Stream stream = new MemoryStream()) - using (var sw = new StreamWriter(stream, Encoding.UTF8, 1024, true)) { - sw.WriteLine("[General]"); - sw.WriteLine(nameLine); - sw.WriteLine(authorLine); + using (var sw = new StreamWriter(stream, Encoding.UTF8, 1024, true)) + { + sw.WriteLine("[General]"); + sw.WriteLine(nameLine); + sw.WriteLine(authorLine); + } AddFile(item, stream, "skin.ini"); } From 602303e9470225f1a57f647fb648331a1150a10e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 Oct 2021 17:03:30 +0900 Subject: [PATCH 016/260] Add test coverage for `skin.ini` contents --- osu.Game.Tests/Skins/IO/ImportSkinTest.cs | 103 ++++++++++++++++------ osu.Game/Skinning/SkinManager.cs | 4 + 2 files changed, 80 insertions(+), 27 deletions(-) diff --git a/osu.Game.Tests/Skins/IO/ImportSkinTest.cs b/osu.Game.Tests/Skins/IO/ImportSkinTest.cs index 8449b0bbd3..67c5be0c2a 100644 --- a/osu.Game.Tests/Skins/IO/ImportSkinTest.cs +++ b/osu.Game.Tests/Skins/IO/ImportSkinTest.cs @@ -9,6 +9,7 @@ using System.Threading.Tasks; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Platform; +using osu.Game.IO; using osu.Game.IO.Archives; using osu.Game.Skinning; using SharpCompress.Archives.Zip; @@ -22,21 +23,36 @@ namespace osu.Game.Tests.Skins.IO [Test] public Task TestSingleImportDifferentFilename() => runSkinTest(async osu => { - var imported = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk("test skin", "skinner"), "skin.osk")); + var imported = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("test skin", "skinner"), "skin.osk")); // When the import filename doesn't match, it should be appended (and update the skin.ini). - Assert.That(imported.Name, Is.EqualTo("test skin [skin]")); - Assert.That(imported.Creator, Is.EqualTo("skinner")); + assertCorrectMetadata(imported, "test skin [skin]", "skinner", osu); }); [Test] public Task TestSingleImportMatchingFilename() => runSkinTest(async osu => { - var imported = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk("test skin", "skinner"), "test skin.osk")); + var imported = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("test skin", "skinner"), "test skin.osk")); // When the import filename matches it shouldn't be appended. - Assert.That(imported.Name, Is.EqualTo("test skin")); - Assert.That(imported.Creator, Is.EqualTo("skinner")); + assertCorrectMetadata(imported, "test skin", "skinner", osu); + }); + + [Test] + public Task TestSingleImportNoIniFile() => runSkinTest(async osu => + { + var imported = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithNonIniFile(), "test skin.osk")); + + // When the import filename matches it shouldn't be appended. + assertCorrectMetadata(imported, "test skin", "Unknown", osu); + }); + + [Test] + public Task TestEmptyImportFails() => runSkinTest(osu => + { + Assert.ThrowsAsync(() => loadSkinIntoOsu(osu, new ZipArchiveReader(createEmptyOsk(), "test skin.osk"))); + + return Task.CompletedTask; }); #endregion @@ -46,8 +62,8 @@ namespace osu.Game.Tests.Skins.IO [Test] public Task TestImportTwiceWithSameMetadataAndFilename() => runSkinTest(async osu => { - var imported = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk("test skin", "skinner"), "skin.osk")); - var imported2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk("test skin", "skinner"), "skin.osk")); + var imported = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("test skin", "skinner"), "skin.osk")); + var imported2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("test skin", "skinner"), "skin.osk")); assertImportedOnce(imported, imported2); }); @@ -56,8 +72,8 @@ namespace osu.Game.Tests.Skins.IO public Task TestImportTwiceWithNoMetadataSameDownloadFilename() => runSkinTest(async osu => { // if a user downloads two skins that do have skin.ini files but don't have any creator metadata in the skin.ini, they should both import separately just for safety. - var imported = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk(string.Empty, string.Empty), "download.osk")); - var imported2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk(string.Empty, string.Empty), "download.osk")); + var imported = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni(string.Empty, string.Empty), "download.osk")); + var imported2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni(string.Empty, string.Empty), "download.osk")); assertImportedOnce(imported, imported2); }); @@ -65,14 +81,24 @@ namespace osu.Game.Tests.Skins.IO [Test] public Task TestImportUpperCasedOskArchive() => runSkinTest(async osu => { - var imported = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk("name 1", "author 1"), "name 1.OsK")); - assertCorrectMetadata(imported, "name 1", "author 1"); + var imported = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("name 1", "author 1"), "name 1.OsK")); + assertCorrectMetadata(imported, "name 1", "author 1", osu); - var imported2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk("name 1", "author 1"), "name 1.oSK")); + var imported2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("name 1", "author 1"), "name 1.oSK")); assertImportedOnce(imported, imported2); }); + [Test] + public Task TestSameMetadataNameSameFolderName() => runSkinTest(async osu => + { + var imported = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("name 1", "author 1"), "my custom skin 1")); + var imported2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("name 1", "author 1"), "my custom skin 1")); + + assertImportedOnce(imported, imported2); + assertCorrectMetadata(imported, "name 1 [my custom skin 1]", "author 1", osu); + }); + #endregion #region Cases where imports should be uniquely imported @@ -80,8 +106,8 @@ namespace osu.Game.Tests.Skins.IO [Test] public Task TestImportTwiceWithSameMetadataButDifferentFilename() => runSkinTest(async osu => { - var imported = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk("test skin", "skinner"), "skin.osk")); - var imported2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk("test skin", "skinner"), "skin2.osk")); + var imported = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("test skin", "skinner"), "skin.osk")); + var imported2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("test skin", "skinner"), "skin2.osk")); assertImportedBoth(imported, imported2); }); @@ -90,8 +116,8 @@ namespace osu.Game.Tests.Skins.IO public Task TestImportTwiceWithNoMetadataDifferentDownloadFilename() => runSkinTest(async osu => { // if a user downloads two skins that do have skin.ini files but don't have any creator metadata in the skin.ini, they should both import separately just for safety. - var imported = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk(string.Empty, string.Empty), "download.osk")); - var imported2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk(string.Empty, string.Empty), "download2.osk")); + var imported = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni(string.Empty, string.Empty), "download.osk")); + var imported2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni(string.Empty, string.Empty), "download2.osk")); assertImportedBoth(imported, imported2); }); @@ -99,31 +125,37 @@ namespace osu.Game.Tests.Skins.IO [Test] public Task TestImportTwiceWithSameFilenameDifferentMetadata() => runSkinTest(async osu => { - var imported = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk("test skin v2", "skinner"), "skin.osk")); - var imported2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk("test skin v2.1", "skinner"), "skin.osk")); + var imported = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("test skin v2", "skinner"), "skin.osk")); + var imported2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("test skin v2.1", "skinner"), "skin.osk")); assertImportedBoth(imported, imported2); - assertCorrectMetadata(imported, "test skin v2 [skin]", "skinner"); - assertCorrectMetadata(imported2, "test skin v2.1 [skin]", "skinner"); + assertCorrectMetadata(imported, "test skin v2 [skin]", "skinner", osu); + assertCorrectMetadata(imported2, "test skin v2.1 [skin]", "skinner", osu); }); [Test] public Task TestSameMetadataNameDifferentFolderName() => runSkinTest(async osu => { - var imported = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk("name 1", "author 1"), "my custom skin 1")); - var imported2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk("name 1", "author 1"), "my custom skin 2")); + var imported = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("name 1", "author 1"), "my custom skin 1")); + var imported2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("name 1", "author 1"), "my custom skin 2")); assertImportedBoth(imported, imported2); - assertCorrectMetadata(imported, "name 1 [my custom skin 2]", "author 1"); - assertCorrectMetadata(imported, "name 1 [my custom skin 2]", "author 1"); + assertCorrectMetadata(imported, "name 1 [my custom skin 1]", "author 1", osu); + assertCorrectMetadata(imported2, "name 1 [my custom skin 2]", "author 1", osu); }); #endregion - private void assertCorrectMetadata(SkinInfo imported, string name, string creator) + private void assertCorrectMetadata(SkinInfo imported, string name, string creator, OsuGameBase osu) { Assert.That(imported.Name, Is.EqualTo(name)); Assert.That(imported.Creator, Is.EqualTo(creator)); + + // for extra safety let's reconstruct the skin, reading from the skin.ini. + var instance = imported.CreateInstance((IStorageResourceProvider)osu.Dependencies.Get(typeof(SkinManager))); + + Assert.That(instance.Configuration.SkinInfo.Name, Is.EqualTo(name)); + Assert.That(instance.Configuration.SkinInfo.Creator, Is.EqualTo(creator)); } private void assertImportedBoth(SkinInfo imported, SkinInfo imported2) @@ -140,7 +172,24 @@ namespace osu.Game.Tests.Skins.IO Assert.That(imported2.Files.Select(f => f.FileInfoID), Is.EquivalentTo(imported.Files.Select(f => f.FileInfoID))); } - private MemoryStream createOsk(string name, string author, bool makeUnique = false) + private MemoryStream createEmptyOsk() + { + var zipStream = new MemoryStream(); + using var zip = ZipArchive.Create(); + zip.SaveTo(zipStream); + return zipStream; + } + + private MemoryStream createOskWithNonIniFile() + { + var zipStream = new MemoryStream(); + using var zip = ZipArchive.Create(); + zip.AddEntry("hitcircle.png", new MemoryStream(new byte[] { 0, 1, 2, 3 })); + zip.SaveTo(zipStream); + return zipStream; + } + + private MemoryStream createOskWithIni(string name, string author, bool makeUnique = false) { var zipStream = new MemoryStream(); using var zip = ZipArchive.Create(); diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index 452c081a41..213c92c103 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -135,6 +135,10 @@ namespace osu.Game.Skinning protected override string ComputeHash(SkinInfo item, ArchiveReader reader = null) { + // we will be adding a hashable file below, but this is only useful if there are any other files in the skin. + if (item.Files.Count == 0) + throw new InvalidOperationException("Attempted to hash an archive with no files"); + var instance = GetSkin(item); // This function can be run on fresh import or save. The logic here ensures a skin.ini file is in a good state for both operations. From 5f53dd80213c50eccb324c668109429584f94ec6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 Oct 2021 17:12:44 +0900 Subject: [PATCH 017/260] Rename test variable for legibility --- osu.Game.Tests/Skins/IO/ImportSkinTest.cs | 98 +++++++++++------------ 1 file changed, 49 insertions(+), 49 deletions(-) diff --git a/osu.Game.Tests/Skins/IO/ImportSkinTest.cs b/osu.Game.Tests/Skins/IO/ImportSkinTest.cs index 67c5be0c2a..21e4948972 100644 --- a/osu.Game.Tests/Skins/IO/ImportSkinTest.cs +++ b/osu.Game.Tests/Skins/IO/ImportSkinTest.cs @@ -23,28 +23,28 @@ namespace osu.Game.Tests.Skins.IO [Test] public Task TestSingleImportDifferentFilename() => runSkinTest(async osu => { - var imported = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("test skin", "skinner"), "skin.osk")); + var import1 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("test skin", "skinner"), "skin.osk")); // When the import filename doesn't match, it should be appended (and update the skin.ini). - assertCorrectMetadata(imported, "test skin [skin]", "skinner", osu); + assertCorrectMetadata(import1, "test skin [skin]", "skinner", osu); }); [Test] public Task TestSingleImportMatchingFilename() => runSkinTest(async osu => { - var imported = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("test skin", "skinner"), "test skin.osk")); + var import1 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("test skin", "skinner"), "test skin.osk")); // When the import filename matches it shouldn't be appended. - assertCorrectMetadata(imported, "test skin", "skinner", osu); + assertCorrectMetadata(import1, "test skin", "skinner", osu); }); [Test] public Task TestSingleImportNoIniFile() => runSkinTest(async osu => { - var imported = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithNonIniFile(), "test skin.osk")); + var import1 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithNonIniFile(), "test skin.osk")); // When the import filename matches it shouldn't be appended. - assertCorrectMetadata(imported, "test skin", "Unknown", osu); + assertCorrectMetadata(import1, "test skin", "Unknown", osu); }); [Test] @@ -62,114 +62,114 @@ namespace osu.Game.Tests.Skins.IO [Test] public Task TestImportTwiceWithSameMetadataAndFilename() => runSkinTest(async osu => { - var imported = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("test skin", "skinner"), "skin.osk")); - var imported2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("test skin", "skinner"), "skin.osk")); + var import1 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("test skin", "skinner"), "skin.osk")); + var import2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("test skin", "skinner"), "skin.osk")); - assertImportedOnce(imported, imported2); + assertImportedOnce(import1, import2); }); [Test] public Task TestImportTwiceWithNoMetadataSameDownloadFilename() => runSkinTest(async osu => { // if a user downloads two skins that do have skin.ini files but don't have any creator metadata in the skin.ini, they should both import separately just for safety. - var imported = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni(string.Empty, string.Empty), "download.osk")); - var imported2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni(string.Empty, string.Empty), "download.osk")); + var import1 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni(string.Empty, string.Empty), "download.osk")); + var import2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni(string.Empty, string.Empty), "download.osk")); - assertImportedOnce(imported, imported2); + assertImportedOnce(import1, import2); }); [Test] public Task TestImportUpperCasedOskArchive() => runSkinTest(async osu => { - var imported = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("name 1", "author 1"), "name 1.OsK")); - assertCorrectMetadata(imported, "name 1", "author 1", osu); + var import1 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("name 1", "author 1"), "name 1.OsK")); + assertCorrectMetadata(import1, "name 1", "author 1", osu); - var imported2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("name 1", "author 1"), "name 1.oSK")); + var import2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("name 1", "author 1"), "name 1.oSK")); - assertImportedOnce(imported, imported2); + assertImportedOnce(import1, import2); }); [Test] public Task TestSameMetadataNameSameFolderName() => runSkinTest(async osu => { - var imported = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("name 1", "author 1"), "my custom skin 1")); - var imported2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("name 1", "author 1"), "my custom skin 1")); + var import1 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("name 1", "author 1"), "my custom skin 1")); + var import2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("name 1", "author 1"), "my custom skin 1")); - assertImportedOnce(imported, imported2); - assertCorrectMetadata(imported, "name 1 [my custom skin 1]", "author 1", osu); + assertImportedOnce(import1, import2); + assertCorrectMetadata(import1, "name 1 [my custom skin 1]", "author 1", osu); }); #endregion - #region Cases where imports should be uniquely imported + #region Cases where imports should be uniquely import1 [Test] public Task TestImportTwiceWithSameMetadataButDifferentFilename() => runSkinTest(async osu => { - var imported = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("test skin", "skinner"), "skin.osk")); - var imported2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("test skin", "skinner"), "skin2.osk")); + var import1 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("test skin", "skinner"), "skin.osk")); + var import2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("test skin", "skinner"), "skin2.osk")); - assertImportedBoth(imported, imported2); + assertImportedBoth(import1, import2); }); [Test] public Task TestImportTwiceWithNoMetadataDifferentDownloadFilename() => runSkinTest(async osu => { // if a user downloads two skins that do have skin.ini files but don't have any creator metadata in the skin.ini, they should both import separately just for safety. - var imported = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni(string.Empty, string.Empty), "download.osk")); - var imported2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni(string.Empty, string.Empty), "download2.osk")); + var import1 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni(string.Empty, string.Empty), "download.osk")); + var import2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni(string.Empty, string.Empty), "download2.osk")); - assertImportedBoth(imported, imported2); + assertImportedBoth(import1, import2); }); [Test] public Task TestImportTwiceWithSameFilenameDifferentMetadata() => runSkinTest(async osu => { - var imported = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("test skin v2", "skinner"), "skin.osk")); - var imported2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("test skin v2.1", "skinner"), "skin.osk")); + var import1 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("test skin v2", "skinner"), "skin.osk")); + var import2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("test skin v2.1", "skinner"), "skin.osk")); - assertImportedBoth(imported, imported2); - assertCorrectMetadata(imported, "test skin v2 [skin]", "skinner", osu); - assertCorrectMetadata(imported2, "test skin v2.1 [skin]", "skinner", osu); + assertImportedBoth(import1, import2); + assertCorrectMetadata(import1, "test skin v2 [skin]", "skinner", osu); + assertCorrectMetadata(import2, "test skin v2.1 [skin]", "skinner", osu); }); [Test] public Task TestSameMetadataNameDifferentFolderName() => runSkinTest(async osu => { - var imported = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("name 1", "author 1"), "my custom skin 1")); - var imported2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("name 1", "author 1"), "my custom skin 2")); + var import1 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("name 1", "author 1"), "my custom skin 1")); + var import2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("name 1", "author 1"), "my custom skin 2")); - assertImportedBoth(imported, imported2); - assertCorrectMetadata(imported, "name 1 [my custom skin 1]", "author 1", osu); - assertCorrectMetadata(imported2, "name 1 [my custom skin 2]", "author 1", osu); + assertImportedBoth(import1, import2); + assertCorrectMetadata(import1, "name 1 [my custom skin 1]", "author 1", osu); + assertCorrectMetadata(import2, "name 1 [my custom skin 2]", "author 1", osu); }); #endregion - private void assertCorrectMetadata(SkinInfo imported, string name, string creator, OsuGameBase osu) + private void assertCorrectMetadata(SkinInfo import1, string name, string creator, OsuGameBase osu) { - Assert.That(imported.Name, Is.EqualTo(name)); - Assert.That(imported.Creator, Is.EqualTo(creator)); + Assert.That(import1.Name, Is.EqualTo(name)); + Assert.That(import1.Creator, Is.EqualTo(creator)); // for extra safety let's reconstruct the skin, reading from the skin.ini. - var instance = imported.CreateInstance((IStorageResourceProvider)osu.Dependencies.Get(typeof(SkinManager))); + var instance = import1.CreateInstance((IStorageResourceProvider)osu.Dependencies.Get(typeof(SkinManager))); Assert.That(instance.Configuration.SkinInfo.Name, Is.EqualTo(name)); Assert.That(instance.Configuration.SkinInfo.Creator, Is.EqualTo(creator)); } - private void assertImportedBoth(SkinInfo imported, SkinInfo imported2) + private void assertImportedBoth(SkinInfo import1, SkinInfo import2) { - Assert.That(imported2.ID, Is.Not.EqualTo(imported.ID)); - Assert.That(imported2.Hash, Is.Not.EqualTo(imported.Hash)); - Assert.That(imported2.Files.Select(f => f.FileInfoID), Is.Not.EquivalentTo(imported.Files.Select(f => f.FileInfoID))); + Assert.That(import2.ID, Is.Not.EqualTo(import1.ID)); + Assert.That(import2.Hash, Is.Not.EqualTo(import1.Hash)); + Assert.That(import2.Files.Select(f => f.FileInfoID), Is.Not.EquivalentTo(import1.Files.Select(f => f.FileInfoID))); } - private void assertImportedOnce(SkinInfo imported, SkinInfo imported2) + private void assertImportedOnce(SkinInfo import1, SkinInfo import2) { - Assert.That(imported2.ID, Is.EqualTo(imported.ID)); - Assert.That(imported2.Hash, Is.EqualTo(imported.Hash)); - Assert.That(imported2.Files.Select(f => f.FileInfoID), Is.EquivalentTo(imported.Files.Select(f => f.FileInfoID))); + Assert.That(import2.ID, Is.EqualTo(import1.ID)); + Assert.That(import2.Hash, Is.EqualTo(import1.Hash)); + Assert.That(import2.Files.Select(f => f.FileInfoID), Is.EquivalentTo(import1.Files.Select(f => f.FileInfoID))); } private MemoryStream createEmptyOsk() From 9e6e41d7c06c09e671c3274ef7f56a75cfd09f1e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 Oct 2021 17:33:36 +0900 Subject: [PATCH 018/260] Add migration to reset and repopulate existing skin hashes --- osu.Game/Database/ArchiveModelManager.cs | 11 +++++---- .../20211020081609_ResetSkinHashes.cs | 23 +++++++++++++++++++ osu.Game/Skinning/SkinManager.cs | 22 ++++++++++++++++++ 3 files changed, 52 insertions(+), 4 deletions(-) create mode 100644 osu.Game/Migrations/20211020081609_ResetSkinHashes.cs diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 5b07bfe9a5..63a5da79a7 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -32,7 +32,7 @@ namespace osu.Game.Database /// The associated file join type. public abstract class ArchiveModelManager : IModelManager, IModelFileManager where TModel : class, IHasFiles, IHasPrimaryKey, ISoftDelete - where TFileModel : class, INamedFileInfo, new() + where TFileModel : class, INamedFileInfo, IHasPrimaryKey, new() { private const int import_queue_request_concurrency = 1; @@ -520,9 +520,12 @@ namespace osu.Game.Database { Files.Dereference(file.FileInfo); - // This shouldn't be required, but here for safety in case the provided TModel is not being change tracked - // Definitely can be removed once we rework the database backend. - usage.Context.Set().Remove(file); + if (file.ID > 0) + { + // This shouldn't be required, but here for safety in case the provided TModel is not being change tracked + // Definitely can be removed once we rework the database backend. + usage.Context.Set().Remove(file); + } } model.Files.Remove(file); diff --git a/osu.Game/Migrations/20211020081609_ResetSkinHashes.cs b/osu.Game/Migrations/20211020081609_ResetSkinHashes.cs new file mode 100644 index 0000000000..6d53c019ec --- /dev/null +++ b/osu.Game/Migrations/20211020081609_ResetSkinHashes.cs @@ -0,0 +1,23 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using osu.Game.Database; + +namespace osu.Game.Migrations +{ + [DbContext(typeof(OsuDbContext))] + [Migration("20211020081609_ResetSkinHashes")] + public partial class ResetSkinHashes : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.Sql($"UPDATE SkinInfo SET Hash = null"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + } + } +} diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index 213c92c103..0b611bfe8f 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -18,6 +18,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.OpenGL.Textures; using osu.Framework.Graphics.Textures; using osu.Framework.IO.Stores; +using osu.Framework.Logging; using osu.Framework.Platform; using osu.Framework.Testing; using osu.Framework.Utils; @@ -84,6 +85,27 @@ namespace osu.Game.Skinning SourceChanged?.Invoke(); }; + + // can be removed 20220420. + populateMissingHashes(); + } + + private void populateMissingHashes() + { + var skinsWithoutHashes = ModelStore.ConsumableItems.Where(i => i.Hash == null).ToArray(); + + foreach (SkinInfo skin in skinsWithoutHashes) + { + try + { + Update(skin); + } + catch (Exception e) + { + Delete(skin); + Logger.Error(e, $"Existing skin {skin} has been deleted during hash recomputation due to being invalid"); + } + } } protected override bool ShouldDeleteArchive(string path) => Path.GetExtension(path)?.ToLowerInvariant() == ".osk"; From 68c01fc20442312cd09a27c29194c2fb3774be35 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 Oct 2021 17:34:09 +0900 Subject: [PATCH 019/260] Fix infinite loop on default skin (it can't have a `skin.ini`) --- osu.Game/Skinning/SkinManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index 0b611bfe8f..afa411a15b 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -186,7 +186,7 @@ namespace osu.Game.Skinning // By this point, the metadata in SkinInfo will be correct. // Regardless of whether this is an import or not, let's write the skin.ini if non-existing or non-matching. // This is (weirdly) done inside ComputeHash to avoid adding a new method to handle this case. After switching to realm it can be moved into another place. - if (skinIniSourcedName != item.Name) + if (instance is LegacySkin && skinIniSourcedName != item.Name) updateSkinIniMetadata(item); return base.ComputeHash(item, reader); From fd01a226db28023a06b578d37b1f3bdd7962b23a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 Oct 2021 18:11:33 +0900 Subject: [PATCH 020/260] Remove redundant parenthesis MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- .../Visual/Multiplayer/TestSceneDrawableLoungeRoom.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableLoungeRoom.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableLoungeRoom.cs index 6d969b6a83..ea895a23d2 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableLoungeRoom.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableLoungeRoom.cs @@ -63,7 +63,7 @@ namespace osu.Game.Tests.Visual.Multiplayer RelativeSizeAxes = Axes.Both, Children = new Drawable[] { - searchTextBox = new SearchTextBox() + searchTextBox = new SearchTextBox { HoldFocus = true, Anchor = Anchor.TopCentre, From 59b7210efaaf976301deefc9fdbd3c2d756f675f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 21 Oct 2021 13:35:26 +0900 Subject: [PATCH 021/260] Revert disallowing imports with no files While it is logical that we want this, from a testing perspective this is a bit of a nightmare to fix. Let's revisit at a later point in time. --- osu.Game.Tests/Skins/IO/ImportSkinTest.cs | 7 ++-- osu.Game/Database/ArchiveModelManager.cs | 40 ++++++++++++++--------- osu.Game/Skinning/SkinManager.cs | 4 --- 3 files changed, 28 insertions(+), 23 deletions(-) diff --git a/osu.Game.Tests/Skins/IO/ImportSkinTest.cs b/osu.Game.Tests/Skins/IO/ImportSkinTest.cs index 21e4948972..20bf42345f 100644 --- a/osu.Game.Tests/Skins/IO/ImportSkinTest.cs +++ b/osu.Game.Tests/Skins/IO/ImportSkinTest.cs @@ -48,11 +48,12 @@ namespace osu.Game.Tests.Skins.IO }); [Test] - public Task TestEmptyImportFails() => runSkinTest(osu => + public Task TestEmptyImportImportsWithFilename() => runSkinTest(async osu => { - Assert.ThrowsAsync(() => loadSkinIntoOsu(osu, new ZipArchiveReader(createEmptyOsk(), "test skin.osk"))); + var import1 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createEmptyOsk(), "test skin.osk")); - return Task.CompletedTask; + // When the import filename matches it shouldn't be appended. + assertCorrectMetadata(import1, "test skin", "Unknown", osu); }); #endregion diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 63a5da79a7..0bc4c40459 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -322,22 +322,22 @@ namespace osu.Game.Database .OrderBy(f => f.Filename) .ToArray(); - if (hashableFiles.Length == 0) - throw new InvalidOperationException("Attempted to hash an archive with no files"); - - // for now, concatenate all hashable files in the set to create a unique hash. - MemoryStream hashable = new MemoryStream(); - - foreach (TFileModel file in hashableFiles) + if (hashableFiles.Length > 0) { - using (Stream s = Files.Store.GetStream(file.FileInfo.StoragePath)) - s.CopyTo(hashable); + // for now, concatenate all hashable files in the set to create a unique hash. + MemoryStream hashable = new MemoryStream(); + + foreach (TFileModel file in hashableFiles) + { + using (Stream s = Files.Store.GetStream(file.FileInfo.StoragePath)) + s.CopyTo(hashable); + } + + if (hashable.Length > 0) + return hashable.ComputeSHA2Hash(); } - if (hashable.Length > 0) - return hashable.ComputeSHA2Hash(); - - return item.Hash; + return generateFallbackHash(); } /// @@ -707,10 +707,10 @@ namespace osu.Game.Database s.CopyTo(hashable); } - if (hashable.Length == 0) - throw new InvalidOperationException("Attempted to hash an archive with no files"); + if (hashable.Length > 0) + return hashable.ComputeSHA2Hash(); - return hashable.ComputeSHA2Hash(); + return generateFallbackHash(); } /// @@ -923,6 +923,14 @@ namespace osu.Game.Database #endregion + private static string generateFallbackHash() + { + // if a hash could no be generated from file content, presume a unique / new import. + // therefore, let's use a guaranteed unique hash. + // this doesn't follow the SHA2 hashing schema intentionally, so such entries on the data store can be identified. + return Guid.NewGuid().ToString(); + } + private string getValidFilename(string filename) { foreach (char c in Path.GetInvalidFileNameChars()) diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index afa411a15b..304e48e854 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -157,10 +157,6 @@ namespace osu.Game.Skinning protected override string ComputeHash(SkinInfo item, ArchiveReader reader = null) { - // we will be adding a hashable file below, but this is only useful if there are any other files in the skin. - if (item.Files.Count == 0) - throw new InvalidOperationException("Attempted to hash an archive with no files"); - var instance = GetSkin(item); // This function can be run on fresh import or save. The logic here ensures a skin.ini file is in a good state for both operations. From a5088cac27c2d6955165efd45a86b68ed7952128 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 21 Oct 2021 13:36:04 +0900 Subject: [PATCH 022/260] Fix default metadata propagation when no files are present --- osu.Game/Skinning/SkinInfo.cs | 6 +++--- osu.Game/Skinning/SkinManager.cs | 5 +++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/osu.Game/Skinning/SkinInfo.cs b/osu.Game/Skinning/SkinInfo.cs index 2bf8668ec6..3b34e23d57 100644 --- a/osu.Game/Skinning/SkinInfo.cs +++ b/osu.Game/Skinning/SkinInfo.cs @@ -18,12 +18,12 @@ namespace osu.Game.Skinning public int ID { get; set; } - public string Name { get; set; } + public string Name { get; set; } = string.Empty; + + public string Creator { get; set; } = string.Empty; public string Hash { get; set; } - public string Creator { get; set; } - public string InstantiationInfo { get; set; } public virtual Skin CreateInstance(IStorageResourceProvider resources) diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index 304e48e854..fffdf19976 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -149,7 +149,7 @@ namespace osu.Game.Skinning CurrentSkinInfo.Value = ModelStore.ConsumableItems.Single(i => i.ID == chosen.ID); } - protected override SkinInfo CreateModel(ArchiveReader archive) => new SkinInfo { Name = archive.Name }; + protected override SkinInfo CreateModel(ArchiveReader archive) => new SkinInfo { Name = archive.Name ?? "No name" }; private const string unknown_creator_string = "Unknown"; @@ -163,6 +163,7 @@ namespace osu.Game.Skinning // LegacySkin will parse the skin.ini and populate `Skin.Configuration` during construction above. string skinIniSourcedName = instance.Configuration.SkinInfo.Name; + string skinIniSourcedCreator = instance.Configuration.SkinInfo.Creator; string archiveName = item.Name.Replace(".osk", "", StringComparison.OrdinalIgnoreCase); bool isImport = reader != null; @@ -170,7 +171,7 @@ namespace osu.Game.Skinning if (isImport) { item.Name = !string.IsNullOrEmpty(skinIniSourcedName) ? skinIniSourcedName : archiveName; - item.Creator = instance.Configuration.SkinInfo.Creator ?? unknown_creator_string; + item.Creator = !string.IsNullOrEmpty(skinIniSourcedCreator) ? skinIniSourcedCreator : unknown_creator_string; // For imports, we want to use the archive or folder name as part of the metadata, in addition to any existing skin.ini metadata. // In an ideal world, skin.ini would be the only source of metadata, but a lot of skin creators and users don't update it when making modifications. From 6dd54a417fbb12ee0ae1fa376e1abdeca39e8103 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 21 Oct 2021 21:32:07 +0900 Subject: [PATCH 023/260] Remove `BeatmapMetadata` base class from `APIBeatmapSet` --- .../API/Requests/Responses/APIBeatmapSet.cs | 62 +++++++++++++++++-- 1 file changed, 57 insertions(+), 5 deletions(-) diff --git a/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs b/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs index 47f880cf54..24d0e09649 100644 --- a/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs +++ b/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs @@ -8,12 +8,13 @@ using Newtonsoft.Json; using osu.Game.Beatmaps; using osu.Game.Database; using osu.Game.Rulesets; +using osu.Game.Users; #nullable enable namespace osu.Game.Online.API.Requests.Responses { - public class APIBeatmapSet : BeatmapMetadata, IBeatmapSetOnlineInfo, IBeatmapSetInfo + public class APIBeatmapSet : IBeatmapSetOnlineInfo, IBeatmapSetInfo { [JsonProperty(@"covers")] public BeatmapSetOnlineCovers Covers { get; set; } @@ -63,10 +64,44 @@ namespace osu.Game.Online.API.Requests.Responses [JsonProperty(@"track_id")] public int? TrackId { get; set; } + public string Title { get; set; } = string.Empty; + + [JsonProperty("title_unicode")] + public string TitleUnicode { get; set; } = string.Empty; + + public string Artist { get; set; } = string.Empty; + + [JsonProperty("artist_unicode")] + public string ArtistUnicode { get; set; } = string.Empty; + + public User? Author = new User(); + + /// + /// Helper property to deserialize a username to . + /// [JsonProperty(@"user_id")] - private int creatorId + public int AuthorID { - set => Author.Id = value; + get => Author?.Id ?? 1; + set + { + Author ??= new User(); + Author.Id = value; + } + } + + /// + /// Helper property to deserialize a username to . + /// + [JsonProperty(@"creator")] + public string AuthorString + { + get => Author?.Username ?? string.Empty; + set + { + Author ??= new User(); + Author.Username = value; + } } [JsonProperty(@"availability")] @@ -78,6 +113,11 @@ namespace osu.Game.Online.API.Requests.Responses [JsonProperty(@"language")] public BeatmapSetOnlineLanguage Language { get; set; } + public string Source { get; set; } = string.Empty; + + [JsonProperty(@"tags")] + public string Tags { get; set; } = string.Empty; + [JsonProperty(@"beatmaps")] private IEnumerable beatmaps { get; set; } = Array.Empty(); @@ -86,7 +126,7 @@ namespace osu.Game.Online.API.Requests.Responses var beatmapSet = new BeatmapSetInfo { OnlineBeatmapSetID = OnlineID, - Metadata = this, + Metadata = metadata, Status = Status, Metrics = new BeatmapSetMetrics { Ratings = ratings }, OnlineInfo = this @@ -103,11 +143,23 @@ namespace osu.Game.Online.API.Requests.Responses return beatmapSet; } + private BeatmapMetadata metadata => new BeatmapMetadata + { + Title = Title, + TitleUnicode = TitleUnicode, + Artist = Artist, + ArtistUnicode = ArtistUnicode, + AuthorID = AuthorID, + Author = Author, + Source = Source, + Tags = Tags, + }; + #region Implementation of IBeatmapSetInfo IEnumerable IBeatmapSetInfo.Beatmaps => beatmaps; - IBeatmapMetadataInfo IBeatmapSetInfo.Metadata => this; + IBeatmapMetadataInfo IBeatmapSetInfo.Metadata => metadata; DateTimeOffset IBeatmapSetInfo.DateAdded => throw new NotImplementedException(); IEnumerable IBeatmapSetInfo.Files => throw new NotImplementedException(); From fb7cf3548119221d6e9af4e1778f9b2a96f195e6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 21 Oct 2021 21:48:14 +0900 Subject: [PATCH 024/260] Remove `BeatmapMetadata` base class from `APIBeatmap` --- osu.Game/Online/API/Requests/Responses/APIBeatmap.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs b/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs index 42e519223b..49124a404d 100644 --- a/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs +++ b/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs @@ -10,7 +10,7 @@ using osu.Game.Rulesets; namespace osu.Game.Online.API.Requests.Responses { - public class APIBeatmap : BeatmapMetadata, IBeatmapInfo + public class APIBeatmap : IBeatmapInfo { [JsonProperty(@"id")] public int OnlineID { get; set; } @@ -24,6 +24,9 @@ namespace osu.Game.Online.API.Requests.Responses [JsonProperty("checksum")] public string Checksum { get; set; } = string.Empty; + [JsonProperty(@"user_id")] + public int AuthorID { get; set; } + [JsonProperty(@"beatmapset")] public APIBeatmapSet? BeatmapSet { get; set; } @@ -75,7 +78,7 @@ namespace osu.Game.Online.API.Requests.Responses return new BeatmapInfo { - Metadata = set?.Metadata ?? this, + Metadata = set?.Metadata ?? new BeatmapMetadata(), Ruleset = rulesets.GetRuleset(RulesetID), StarDifficulty = StarRating, OnlineBeatmapID = OnlineID, @@ -106,7 +109,7 @@ namespace osu.Game.Online.API.Requests.Responses #region Implementation of IBeatmapInfo - public IBeatmapMetadataInfo Metadata => this; + public IBeatmapMetadataInfo Metadata => new BeatmapMetadata(); public IBeatmapDifficultyInfo Difficulty => new BeatmapDifficulty { From 08971ff8f27e1489354f8ee596f89ce186bd6565 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 22 Oct 2021 11:03:28 +0900 Subject: [PATCH 025/260] Fix typo in region spedc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- osu.Game.Tests/Skins/IO/ImportSkinTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Skins/IO/ImportSkinTest.cs b/osu.Game.Tests/Skins/IO/ImportSkinTest.cs index 20bf42345f..b62fb8bd87 100644 --- a/osu.Game.Tests/Skins/IO/ImportSkinTest.cs +++ b/osu.Game.Tests/Skins/IO/ImportSkinTest.cs @@ -102,7 +102,7 @@ namespace osu.Game.Tests.Skins.IO #endregion - #region Cases where imports should be uniquely import1 + #region Cases where imports should be uniquely imported [Test] public Task TestImportTwiceWithSameMetadataButDifferentFilename() => runSkinTest(async osu => From e5b73f25cd40cadf90884fb8f2dc160d6a764ad8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 22 Oct 2021 12:56:05 +0900 Subject: [PATCH 026/260] Ensure newly created `skin.ini` files are set to latest version --- osu.Game/Skinning/SkinManager.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index fffdf19976..16ed25eeb1 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -255,6 +255,7 @@ namespace osu.Game.Skinning sw.WriteLine("[General]"); sw.WriteLine(nameLine); sw.WriteLine(authorLine); + sw.WriteLine("Version: latest"); } AddFile(item, stream, "skin.ini"); From 9cdc1ba59219ecd31df04c235def14567f4fbee4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 22 Oct 2021 13:43:45 +0900 Subject: [PATCH 027/260] Fix default legacy skin not being able to read from stored `skin.ini` --- osu.Game/Skinning/DefaultLegacySkin.cs | 9 +++++- osu.Game/Skinning/LegacySkin.cs | 39 ++++++++++++++++---------- 2 files changed, 32 insertions(+), 16 deletions(-) diff --git a/osu.Game/Skinning/DefaultLegacySkin.cs b/osu.Game/Skinning/DefaultLegacySkin.cs index 16ac17546d..cd6dbd9ddd 100644 --- a/osu.Game/Skinning/DefaultLegacySkin.cs +++ b/osu.Game/Skinning/DefaultLegacySkin.cs @@ -19,7 +19,14 @@ namespace osu.Game.Skinning [UsedImplicitly(ImplicitUseKindFlags.InstantiatedWithFixedConstructorSignature)] public DefaultLegacySkin(SkinInfo skin, IStorageResourceProvider resources) - : base(skin, new NamespacedResourceStore(resources.Resources, "Skins/Legacy"), resources, string.Empty) + : base( + skin, + new NamespacedResourceStore(resources.Resources, "Skins/Legacy"), + resources, + // A default legacy skin may still have a skin.ini if it is modified by the user. + // We must specify the stream directly as we are redirecting storage to the osu-resources location for other files. + new LegacySkinResourceStore(skin, resources.Files).GetStream("skin.ini") + ) { Configuration.CustomColours["SliderBall"] = new Color4(2, 170, 255, 255); Configuration.CustomComboColours = new List diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index b09620411b..5ab3ef84db 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -69,28 +69,37 @@ namespace osu.Game.Skinning /// Access to raw game resources. /// The user-facing filename of the configuration file to be parsed. Can accept an .osu or skin.ini file. protected LegacySkin(SkinInfo skin, [CanBeNull] IResourceStore storage, [CanBeNull] IStorageResourceProvider resources, string configurationFilename) + : this(skin, storage, resources, storage?.GetStream(configurationFilename)) + { + } + + /// + /// Construct a new legacy skin instance. + /// + /// The model for this skin. + /// A storage for looking up files within this skin using user-facing filenames. + /// Access to raw game resources. + /// An optional stream containing the contents of a skin.ini file. + protected LegacySkin(SkinInfo skin, [CanBeNull] IResourceStore storage, [CanBeNull] IStorageResourceProvider resources, [CanBeNull] Stream configurationStream) : base(skin, resources) { - using (var stream = storage?.GetStream(configurationFilename)) + if (configurationStream != null) { - if (stream != null) + using (LineBufferedReader reader = new LineBufferedReader(configurationStream, true)) + Configuration = new LegacySkinDecoder().Decode(reader); + + configurationStream.Seek(0, SeekOrigin.Begin); + + using (LineBufferedReader reader = new LineBufferedReader(configurationStream)) { - using (LineBufferedReader reader = new LineBufferedReader(stream, true)) - Configuration = new LegacySkinDecoder().Decode(reader); + var maniaList = new LegacyManiaSkinDecoder().Decode(reader); - stream.Seek(0, SeekOrigin.Begin); - - using (LineBufferedReader reader = new LineBufferedReader(stream)) - { - var maniaList = new LegacyManiaSkinDecoder().Decode(reader); - - foreach (var config in maniaList) - maniaConfigurations[config.Keys] = config; - } + foreach (var config in maniaList) + maniaConfigurations[config.Keys] = config; } - else - Configuration = new LegacySkinConfiguration(); } + else + Configuration = new LegacySkinConfiguration(); if (storage != null) { From 82d0a6515fbe39536dd86122aede7caec25badd8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 22 Oct 2021 14:08:12 +0900 Subject: [PATCH 028/260] Fix incorrect conditional for checking whether import (would fail for default skin) --- osu.Game/Skinning/SkinManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index 16ed25eeb1..707afade09 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -166,7 +166,7 @@ namespace osu.Game.Skinning string skinIniSourcedCreator = instance.Configuration.SkinInfo.Creator; string archiveName = item.Name.Replace(".osk", "", StringComparison.OrdinalIgnoreCase); - bool isImport = reader != null; + bool isImport = item.ID == 0; if (isImport) { From 93482414d6d69fff3e02a0083afb1fb55244db23 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 22 Oct 2021 14:41:59 +0900 Subject: [PATCH 029/260] Remove `SkinConfiguration` subclasses and allow configuration parsing for all skin types --- .../Legacy/CatchLegacySkinTransformer.cs | 2 +- .../Skinning/Legacy/LegacyStageBackground.cs | 2 +- .../Legacy/ManiaLegacySkinTransformer.cs | 2 +- .../Skinning/Legacy/LegacyMainCirclePiece.cs | 3 +- .../Skinning/Legacy/LegacyInputDrum.cs | 2 +- .../Gameplay/TestSceneHitObjectSamples.cs | 2 +- osu.Game.Tests/Skins/LegacySkinDecoderTest.cs | 2 +- .../Skins/TestSceneSkinConfigurationLookup.cs | 8 +-- .../Objects/Legacy/ConvertHitObjectParser.cs | 2 +- osu.Game/Skinning/DefaultSkin.cs | 1 - osu.Game/Skinning/DefaultSkinConfiguration.cs | 12 ---- osu.Game/Skinning/LegacyBeatmapSkin.cs | 2 +- osu.Game/Skinning/LegacySkin.cs | 55 ++++++++----------- osu.Game/Skinning/LegacySkinConfiguration.cs | 28 ---------- osu.Game/Skinning/LegacySkinDecoder.cs | 8 +-- osu.Game/Skinning/LegacySkinExtensions.cs | 2 +- osu.Game/Skinning/LegacySkinTransformer.cs | 2 +- osu.Game/Skinning/Skin.cs | 31 ++++++++++- osu.Game/Skinning/SkinConfiguration.cs | 20 +++++++ osu.Game/Skinning/SkinManager.cs | 2 +- 20 files changed, 92 insertions(+), 96 deletions(-) delete mode 100644 osu.Game/Skinning/DefaultSkinConfiguration.cs delete mode 100644 osu.Game/Skinning/LegacySkinConfiguration.cs diff --git a/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs b/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs index 10fc4e78b2..335d78388b 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs @@ -66,7 +66,7 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy return null; case CatchSkinComponents.Catcher: - var version = GetConfig(LegacySkinConfiguration.LegacySetting.Version)?.Value ?? 1; + var version = GetConfig(SkinConfiguration.LegacySetting.Version)?.Value ?? 1; if (version < 2.3m) { diff --git a/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyStageBackground.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyStageBackground.cs index fec3e9493e..952fc7ddd6 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyStageBackground.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyStageBackground.cs @@ -98,7 +98,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy float rightLineWidth = skin.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.RightLineWidth, columnIndex)?.Value ?? 1; bool hasLeftLine = leftLineWidth > 0; - bool hasRightLine = rightLineWidth > 0 && skin.GetConfig(LegacySkinConfiguration.LegacySetting.Version)?.Value >= 2.4m + bool hasRightLine = rightLineWidth > 0 && skin.GetConfig(SkinConfiguration.LegacySetting.Version)?.Value >= 2.4m || isLastColumn; Color4 lineColour = skin.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.ColumnLineColour, columnIndex)?.Value ?? Color4.White; diff --git a/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs index 814a737034..0de9a098bc 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs @@ -63,7 +63,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy { this.beatmap = (ManiaBeatmap)beatmap; - isLegacySkin = new Lazy(() => GetConfig(LegacySkinConfiguration.LegacySetting.Version) != null); + isLegacySkin = new Lazy(() => GetConfig(SkinConfiguration.LegacySetting.Version) != null); hasKeyTexture = new Lazy(() => { var keyImage = this.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.KeyImage, 0)?.Value ?? "mania-key1"; diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs index d1c9b1bf92..0e0f4f4291 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs @@ -14,7 +14,6 @@ using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Skinning; using osuTK; using osuTK.Graphics; -using static osu.Game.Skinning.LegacySkinConfiguration; namespace osu.Game.Rulesets.Osu.Skinning.Legacy { @@ -158,7 +157,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy if (hasNumber) { - var legacyVersion = skin.GetConfig(LegacySetting.Version)?.Value; + var legacyVersion = skin.GetConfig(SkinConfiguration.LegacySetting.Version)?.Value; if (legacyVersion >= 2.0m) // legacy skins of version 2.0 and newer only apply very short fade out to the number piece. diff --git a/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyInputDrum.cs b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyInputDrum.cs index 86be40dea8..43c5c07f80 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyInputDrum.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyInputDrum.cs @@ -69,7 +69,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy // because the right half is flipped, we need to position using width - position to get the true "topleft" origin position float negativeScaleAdjust = content.Width / ratio; - if (skin.GetConfig(LegacySkinConfiguration.LegacySetting.Version)?.Value >= 2.1m) + if (skin.GetConfig(SkinConfiguration.LegacySetting.Version)?.Value >= 2.1m) { left.Centre.Position = new Vector2(0, taiko_bar_y) * ratio; right.Centre.Position = new Vector2(negativeScaleAdjust - 56, taiko_bar_y) * ratio; diff --git a/osu.Game.Tests/Gameplay/TestSceneHitObjectSamples.cs b/osu.Game.Tests/Gameplay/TestSceneHitObjectSamples.cs index fc420e22a1..153d5b8e36 100644 --- a/osu.Game.Tests/Gameplay/TestSceneHitObjectSamples.cs +++ b/osu.Game.Tests/Gameplay/TestSceneHitObjectSamples.cs @@ -7,7 +7,7 @@ using osu.Game.Rulesets; using osu.Game.Rulesets.Osu; using osu.Game.Tests.Beatmaps; using osu.Game.Tests.Resources; -using static osu.Game.Skinning.LegacySkinConfiguration; +using static osu.Game.Skinning.SkinConfiguration; namespace osu.Game.Tests.Gameplay { diff --git a/osu.Game.Tests/Skins/LegacySkinDecoderTest.cs b/osu.Game.Tests/Skins/LegacySkinDecoderTest.cs index dcb866c99f..cfc140ce39 100644 --- a/osu.Game.Tests/Skins/LegacySkinDecoderTest.cs +++ b/osu.Game.Tests/Skins/LegacySkinDecoderTest.cs @@ -106,7 +106,7 @@ namespace osu.Game.Tests.Skins var decoder = new LegacySkinDecoder(); using (var resStream = TestResources.OpenResource("skin-latest.ini")) using (var stream = new LineBufferedReader(resStream)) - Assert.AreEqual(LegacySkinConfiguration.LATEST_VERSION, decoder.Decode(stream).LegacyVersion); + Assert.AreEqual(SkinConfiguration.LATEST_VERSION, decoder.Decode(stream).LegacyVersion); } [Test] diff --git a/osu.Game.Tests/Skins/TestSceneSkinConfigurationLookup.cs b/osu.Game.Tests/Skins/TestSceneSkinConfigurationLookup.cs index aadabec100..870d6d8f57 100644 --- a/osu.Game.Tests/Skins/TestSceneSkinConfigurationLookup.cs +++ b/osu.Game.Tests/Skins/TestSceneSkinConfigurationLookup.cs @@ -151,7 +151,7 @@ namespace osu.Game.Tests.Skins { AddStep("Set user skin version 2.3", () => userSource.Configuration.LegacyVersion = 2.3m); AddStep("Set beatmap skin version null", () => beatmapSource.Configuration.LegacyVersion = null); - AddAssert("Check legacy version lookup", () => requester.GetConfig(LegacySkinConfiguration.LegacySetting.Version)?.Value == 2.3m); + AddAssert("Check legacy version lookup", () => requester.GetConfig(SkinConfiguration.LegacySetting.Version)?.Value == 2.3m); } [Test] @@ -160,7 +160,7 @@ namespace osu.Game.Tests.Skins // completely ignoring beatmap versions for simplicity. AddStep("Set user skin version 2.3", () => userSource.Configuration.LegacyVersion = 2.3m); AddStep("Set beatmap skin version null", () => beatmapSource.Configuration.LegacyVersion = 1.7m); - AddAssert("Check legacy version lookup", () => requester.GetConfig(LegacySkinConfiguration.LegacySetting.Version)?.Value == 2.3m); + AddAssert("Check legacy version lookup", () => requester.GetConfig(SkinConfiguration.LegacySetting.Version)?.Value == 2.3m); } [Test] @@ -169,14 +169,14 @@ namespace osu.Game.Tests.Skins AddStep("Set user skin version 2.3", () => userSource.Configuration.LegacyVersion = null); AddStep("Set beatmap skin version null", () => beatmapSource.Configuration.LegacyVersion = null); AddAssert("Check legacy version lookup", - () => requester.GetConfig(LegacySkinConfiguration.LegacySetting.Version)?.Value == LegacySkinConfiguration.LATEST_VERSION); + () => requester.GetConfig(SkinConfiguration.LegacySetting.Version)?.Value == SkinConfiguration.LATEST_VERSION); } [Test] public void TestIniWithNoVersionFallsBackTo1() { AddStep("Parse skin with no version", () => userSource.Configuration = new LegacySkinDecoder().Decode(new LineBufferedReader(new MemoryStream()))); - AddAssert("Check legacy version lookup", () => requester.GetConfig(LegacySkinConfiguration.LegacySetting.Version)?.Value == 1.0m); + AddAssert("Check legacy version lookup", () => requester.GetConfig(SkinConfiguration.LegacySetting.Version)?.Value == 1.0m); } public enum LookupType diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs index 0942a7264d..d902918e83 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs @@ -481,7 +481,7 @@ namespace osu.Game.Rulesets.Objects.Legacy /// /// /// Layered hit samples are automatically added in all modes (except osu!mania), but can be disabled - /// using the skin config option. + /// using the skin config option. /// public readonly bool IsLayered; diff --git a/osu.Game/Skinning/DefaultSkin.cs b/osu.Game/Skinning/DefaultSkin.cs index 8e03bddb4d..d972ce3af6 100644 --- a/osu.Game/Skinning/DefaultSkin.cs +++ b/osu.Game/Skinning/DefaultSkin.cs @@ -35,7 +35,6 @@ namespace osu.Game.Skinning : base(skin, resources) { this.resources = resources; - Configuration = new DefaultSkinConfiguration(); } public override Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => null; diff --git a/osu.Game/Skinning/DefaultSkinConfiguration.cs b/osu.Game/Skinning/DefaultSkinConfiguration.cs deleted file mode 100644 index 5842ee82ee..0000000000 --- a/osu.Game/Skinning/DefaultSkinConfiguration.cs +++ /dev/null @@ -1,12 +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.Skinning -{ - /// - /// A skin configuration pre-populated with sane defaults. - /// - public class DefaultSkinConfiguration : SkinConfiguration - { - } -} diff --git a/osu.Game/Skinning/LegacyBeatmapSkin.cs b/osu.Game/Skinning/LegacyBeatmapSkin.cs index 2093182dcc..8720a55076 100644 --- a/osu.Game/Skinning/LegacyBeatmapSkin.cs +++ b/osu.Game/Skinning/LegacyBeatmapSkin.cs @@ -50,7 +50,7 @@ namespace osu.Game.Skinning { switch (lookup) { - case LegacySkinConfiguration.LegacySetting s when s == LegacySkinConfiguration.LegacySetting.Version: + case SkinConfiguration.LegacySetting s when s == SkinConfiguration.LegacySetting.Version: // For lookup simplicity, ignore beatmap-level versioning completely. // If it is decided that we need this due to beatmaps somehow using it, the default (1.0 specified in LegacySkinDecoder.CreateTemplateObject) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 5ab3ef84db..6facb6fafd 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -47,12 +47,6 @@ namespace osu.Game.Skinning /// protected virtual bool UseCustomSampleBanks => false; - public new LegacySkinConfiguration Configuration - { - get => base.Configuration as LegacySkinConfiguration; - set => base.Configuration = value; - } - private readonly Dictionary maniaConfigurations = new Dictionary(); [UsedImplicitly(ImplicitUseKindFlags.InstantiatedWithFixedConstructorSignature)] @@ -81,26 +75,8 @@ namespace osu.Game.Skinning /// Access to raw game resources. /// An optional stream containing the contents of a skin.ini file. protected LegacySkin(SkinInfo skin, [CanBeNull] IResourceStore storage, [CanBeNull] IStorageResourceProvider resources, [CanBeNull] Stream configurationStream) - : base(skin, resources) + : base(skin, resources, configurationStream) { - if (configurationStream != null) - { - using (LineBufferedReader reader = new LineBufferedReader(configurationStream, true)) - Configuration = new LegacySkinDecoder().Decode(reader); - - configurationStream.Seek(0, SeekOrigin.Begin); - - using (LineBufferedReader reader = new LineBufferedReader(configurationStream)) - { - var maniaList = new LegacyManiaSkinDecoder().Decode(reader); - - foreach (var config in maniaList) - maniaConfigurations[config.Keys] = config; - } - } - else - Configuration = new LegacySkinConfiguration(); - if (storage != null) { var samples = resources?.AudioManager?.GetSampleStore(storage); @@ -119,6 +95,21 @@ namespace osu.Game.Skinning true) != null); } + protected override void ParseConfigurationStream(Stream stream) + { + base.ParseConfigurationStream(stream); + + stream.Seek(0, SeekOrigin.Begin); + + using (LineBufferedReader reader = new LineBufferedReader(stream)) + { + var maniaList = new LegacyManiaSkinDecoder().Decode(reader); + + foreach (var config in maniaList) + maniaConfigurations[config.Keys] = config; + } + } + public override IBindable GetConfig(TLookup lookup) { switch (lookup) @@ -155,7 +146,7 @@ namespace osu.Game.Skinning break; - case LegacySkinConfiguration.LegacySetting legacy: + case SkinConfiguration.LegacySetting legacy: return legacySettingLookup(legacy); default: @@ -198,7 +189,7 @@ namespace osu.Game.Skinning case LegacyManiaSkinConfigurationLookups.ExplosionScale: Debug.Assert(maniaLookup.TargetColumn != null); - if (GetConfig(LegacySkinConfiguration.LegacySetting.Version)?.Value < 2.5m) + if (GetConfig(SkinConfiguration.LegacySetting.Version)?.Value < 2.5m) return SkinUtils.As(new Bindable(1)); if (existing.ExplosionWidth[maniaLookup.TargetColumn.Value] != 0) @@ -245,7 +236,7 @@ namespace osu.Game.Skinning case LegacyManiaSkinConfigurationLookups.HoldNoteLightScale: Debug.Assert(maniaLookup.TargetColumn != null); - if (GetConfig(LegacySkinConfiguration.LegacySetting.Version)?.Value < 2.5m) + if (GetConfig(SkinConfiguration.LegacySetting.Version)?.Value < 2.5m) return SkinUtils.As(new Bindable(1)); if (existing.HoldNoteLightWidth[maniaLookup.TargetColumn.Value] != 0) @@ -318,15 +309,15 @@ namespace osu.Game.Skinning => source.ImageLookups.TryGetValue(lookup, out var image) ? new Bindable(image) : null; [CanBeNull] - private IBindable legacySettingLookup(LegacySkinConfiguration.LegacySetting legacySetting) + private IBindable legacySettingLookup(SkinConfiguration.LegacySetting legacySetting) { switch (legacySetting) { - case LegacySkinConfiguration.LegacySetting.Version: - return SkinUtils.As(new Bindable(Configuration.LegacyVersion ?? LegacySkinConfiguration.LATEST_VERSION)); + case SkinConfiguration.LegacySetting.Version: + return SkinUtils.As(new Bindable(Configuration.LegacyVersion ?? SkinConfiguration.LATEST_VERSION)); default: - return genericLookup(legacySetting); + return genericLookup(legacySetting); } } diff --git a/osu.Game/Skinning/LegacySkinConfiguration.cs b/osu.Game/Skinning/LegacySkinConfiguration.cs deleted file mode 100644 index 20d1da8aaa..0000000000 --- a/osu.Game/Skinning/LegacySkinConfiguration.cs +++ /dev/null @@ -1,28 +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.Skinning -{ - public class LegacySkinConfiguration : SkinConfiguration - { - public const decimal LATEST_VERSION = 2.7m; - - /// - /// Legacy version of this skin. - /// - public decimal? LegacyVersion { get; internal set; } - - public enum LegacySetting - { - Version, - ComboPrefix, - ComboOverlap, - ScorePrefix, - ScoreOverlap, - HitCirclePrefix, - HitCircleOverlap, - AnimationFramerate, - LayeredHitSounds - } - } -} diff --git a/osu.Game/Skinning/LegacySkinDecoder.cs b/osu.Game/Skinning/LegacySkinDecoder.cs index 2700f84815..dd8a9dedb2 100644 --- a/osu.Game/Skinning/LegacySkinDecoder.cs +++ b/osu.Game/Skinning/LegacySkinDecoder.cs @@ -6,14 +6,14 @@ using osu.Game.Beatmaps.Formats; namespace osu.Game.Skinning { - public class LegacySkinDecoder : LegacyDecoder + public class LegacySkinDecoder : LegacyDecoder { public LegacySkinDecoder() : base(1) { } - protected override void ParseLine(LegacySkinConfiguration skin, Section section, string line) + protected override void ParseLine(SkinConfiguration skin, Section section, string line) { if (section != Section.Colours) { @@ -34,7 +34,7 @@ namespace osu.Game.Skinning case @"Version": if (pair.Value == "latest") - skin.LegacyVersion = LegacySkinConfiguration.LATEST_VERSION; + skin.LegacyVersion = SkinConfiguration.LATEST_VERSION; else if (decimal.TryParse(pair.Value, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out var version)) skin.LegacyVersion = version; @@ -57,7 +57,7 @@ namespace osu.Game.Skinning base.ParseLine(skin, section, line); } - protected override LegacySkinConfiguration CreateTemplateObject() + protected override SkinConfiguration CreateTemplateObject() { var config = base.CreateTemplateObject(); config.LegacyVersion = 1.0m; diff --git a/osu.Game/Skinning/LegacySkinExtensions.cs b/osu.Game/Skinning/LegacySkinExtensions.cs index fd1f905868..479afabb00 100644 --- a/osu.Game/Skinning/LegacySkinExtensions.cs +++ b/osu.Game/Skinning/LegacySkinExtensions.cs @@ -12,7 +12,7 @@ using osu.Framework.Graphics.Animations; using osu.Framework.Graphics.OpenGL.Textures; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; -using static osu.Game.Skinning.LegacySkinConfiguration; +using static osu.Game.Skinning.SkinConfiguration; namespace osu.Game.Skinning { diff --git a/osu.Game/Skinning/LegacySkinTransformer.cs b/osu.Game/Skinning/LegacySkinTransformer.cs index 92b7a04dee..97084f34e0 100644 --- a/osu.Game/Skinning/LegacySkinTransformer.cs +++ b/osu.Game/Skinning/LegacySkinTransformer.cs @@ -10,7 +10,7 @@ using osu.Framework.Graphics.OpenGL.Textures; using osu.Framework.Graphics.Textures; using osu.Game.Audio; using osu.Game.Rulesets.Objects.Legacy; -using static osu.Game.Skinning.LegacySkinConfiguration; +using static osu.Game.Skinning.SkinConfiguration; namespace osu.Game.Skinning { diff --git a/osu.Game/Skinning/Skin.cs b/osu.Game/Skinning/Skin.cs index 92441f40da..d5f41f01a4 100644 --- a/osu.Game/Skinning/Skin.cs +++ b/osu.Game/Skinning/Skin.cs @@ -3,8 +3,10 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Text; +using JetBrains.Annotations; using Newtonsoft.Json; using osu.Framework.Audio.Sample; using osu.Framework.Bindables; @@ -21,8 +23,9 @@ namespace osu.Game.Skinning public abstract class Skin : IDisposable, ISkin { public readonly SkinInfo SkinInfo; + private readonly IStorageResourceProvider resources; - public SkinConfiguration Configuration { get; protected set; } + public SkinConfiguration Configuration { get; set; } public IDictionary DrawableComponentInfo => drawableComponentInfo; @@ -36,9 +39,17 @@ namespace osu.Game.Skinning public abstract IBindable GetConfig(TLookup lookup); - protected Skin(SkinInfo skin, IStorageResourceProvider resources) + protected Skin(SkinInfo skin, IStorageResourceProvider resources, [CanBeNull] Stream configurationStream = null) { SkinInfo = skin; + this.resources = resources; + + configurationStream ??= getConfigurationStream(); + + if (configurationStream != null) + ParseConfigurationStream(configurationStream); + else + Configuration = new SkinConfiguration(); // we may want to move this to some kind of async operation in the future. foreach (SkinnableTarget skinnableTarget in Enum.GetValues(typeof(SkinnableTarget))) @@ -73,6 +84,22 @@ namespace osu.Game.Skinning } } + protected virtual void ParseConfigurationStream(Stream stream) + { + using (LineBufferedReader reader = new LineBufferedReader(stream, true)) + Configuration = new LegacySkinDecoder().Decode(reader); + } + + private Stream getConfigurationStream() + { + var path = SkinInfo.Files.SingleOrDefault(f => f.Filename == "skin.ini")?.FileInfo.StoragePath; + + if (string.IsNullOrEmpty(path)) + return null; + + return resources?.Files.GetStream(path); + } + /// /// Remove all stored customisations for the provided target. /// diff --git a/osu.Game/Skinning/SkinConfiguration.cs b/osu.Game/Skinning/SkinConfiguration.cs index a18144246f..f71f6811e8 100644 --- a/osu.Game/Skinning/SkinConfiguration.cs +++ b/osu.Game/Skinning/SkinConfiguration.cs @@ -14,11 +14,31 @@ namespace osu.Game.Skinning { public readonly SkinInfo SkinInfo = new SkinInfo(); + public const decimal LATEST_VERSION = 2.7m; + /// /// Whether to allow as a fallback list for when no combo colours are provided. /// internal bool AllowDefaultComboColoursFallback = true; + /// + /// Legacy version of this skin. + /// + public decimal? LegacyVersion { get; internal set; } + + public enum LegacySetting + { + Version, + ComboPrefix, + ComboOverlap, + ScorePrefix, + ScoreOverlap, + HitCirclePrefix, + HitCircleOverlap, + AnimationFramerate, + LayeredHitSounds + } + public static List DefaultComboColours { get; } = new List { new Color4(255, 192, 0, 255), diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index 707afade09..177dbaf28f 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -183,7 +183,7 @@ namespace osu.Game.Skinning // By this point, the metadata in SkinInfo will be correct. // Regardless of whether this is an import or not, let's write the skin.ini if non-existing or non-matching. // This is (weirdly) done inside ComputeHash to avoid adding a new method to handle this case. After switching to realm it can be moved into another place. - if (instance is LegacySkin && skinIniSourcedName != item.Name) + if (skinIniSourcedName != item.Name) updateSkinIniMetadata(item); return base.ComputeHash(item, reader); From eef9949a0a928f3d10df97375119ccc1fbdbbf90 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 22 Oct 2021 14:48:20 +0900 Subject: [PATCH 030/260] Remove unnecessary branching around EF logic --- osu.Game/Database/ArchiveModelManager.cs | 48 +++++++----------------- 1 file changed, 14 insertions(+), 34 deletions(-) diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 0bc4c40459..ad5bc6d420 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -511,29 +511,21 @@ namespace osu.Game.Database /// The existing file to be deleted. public void DeleteFile(TModel model, TFileModel file) { - if (model.ID > 0) + using (var usage = ContextFactory.GetForWrite()) { - using (var usage = ContextFactory.GetForWrite()) + // Dereference the existing file info, since the file model will be removed. + if (file.FileInfo != null) { - // Dereference the existing file info, since the file model will be removed. - if (file.FileInfo != null) + Files.Dereference(file.FileInfo); + + if (file.ID > 0) { - Files.Dereference(file.FileInfo); - - if (file.ID > 0) - { - // This shouldn't be required, but here for safety in case the provided TModel is not being change tracked - // Definitely can be removed once we rework the database backend. - usage.Context.Set().Remove(file); - } + // This shouldn't be required, but here for safety in case the provided TModel is not being change tracked + // Definitely can be removed once we rework the database backend. + usage.Context.Set().Remove(file); } - - model.Files.Remove(file); } - } - else - { - Files.Dereference(file.FileInfo); + model.Files.Remove(file); } } @@ -546,29 +538,17 @@ namespace osu.Game.Database /// The filename for the new file. public void AddFile(TModel model, Stream contents, string filename) { - if (model.ID > 0) + using (ContextFactory.GetForWrite()) { - using (ContextFactory.GetForWrite()) - { - model.Files.Add(new TFileModel - { - Filename = filename, - FileInfo = Files.Add(contents) - }); - - Update(model); - } - } - else - { - // This function may be called during the import process. - // Should not exist like this once we have switched to realm. model.Files.Add(new TFileModel { Filename = filename, FileInfo = Files.Add(contents) }); } + + if (model.ID > 0) + Update(model); } /// From de33b2f45a67d3fd6e503c5886375d7086ba838c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 22 Oct 2021 15:00:31 +0900 Subject: [PATCH 031/260] Return set metadata if available before falling back to empty object --- osu.Game/Online/API/Requests/Responses/APIBeatmap.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs b/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs index 49124a404d..7335a1c18d 100644 --- a/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs +++ b/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs @@ -109,7 +109,7 @@ namespace osu.Game.Online.API.Requests.Responses #region Implementation of IBeatmapInfo - public IBeatmapMetadataInfo Metadata => new BeatmapMetadata(); + public IBeatmapMetadataInfo Metadata => (BeatmapSet as IBeatmapSetInfo)?.Metadata ?? new BeatmapMetadata(); public IBeatmapDifficultyInfo Difficulty => new BeatmapDifficulty { From ea473428e84f7adf43f1727eb15281df25b01901 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 22 Oct 2021 15:54:40 +0900 Subject: [PATCH 032/260] Fix incorrect length mapping in `APIBeatmap` --- osu.Game/Online/API/Requests/Responses/APIBeatmap.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs b/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs index 42e519223b..6ab1630604 100644 --- a/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs +++ b/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs @@ -51,8 +51,10 @@ namespace osu.Game.Online.API.Requests.Responses [JsonProperty(@"accuracy")] private float overallDifficulty { get; set; } + public double Length => lengthInSeconds * 1000; + [JsonProperty(@"total_length")] - public double Length { get; set; } + private double lengthInSeconds { get; set; } [JsonProperty(@"count_circles")] private int circleCount { get; set; } From 91cc77407d3ee9e2c03b108217fc90bc5aa2ae26 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 22 Oct 2021 17:02:02 +0900 Subject: [PATCH 033/260] Fix low-pass filter continuing to take effect after fail animation has already ended --- osu.Game/Screens/Play/FailAnimation.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Screens/Play/FailAnimation.cs b/osu.Game/Screens/Play/FailAnimation.cs index 242d997dd7..edfb8186bb 100644 --- a/osu.Game/Screens/Play/FailAnimation.cs +++ b/osu.Game/Screens/Play/FailAnimation.cs @@ -106,6 +106,7 @@ namespace osu.Game.Screens.Play this.TransformBindableTo(trackFreq, 0, duration).OnComplete(_ => { + RemoveFilters(); OnComplete?.Invoke(); }); @@ -137,6 +138,9 @@ namespace osu.Game.Screens.Play public void RemoveFilters() { + if (filters.Parent == null) + return; + RemoveInternal(filters); filters.Dispose(); From cc1d759290552e87cd3452f82290c68718aa5141 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 22 Oct 2021 17:48:09 +0900 Subject: [PATCH 034/260] Remove unnecessary setters from `IBeatmapSetOnlineInfo` interface --- osu.Game/Beatmaps/BeatmapSetInfo.cs | 96 ++++------------------ osu.Game/Beatmaps/IBeatmapSetOnlineInfo.cs | 34 ++++---- 2 files changed, 33 insertions(+), 97 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapSetInfo.cs b/osu.Game/Beatmaps/BeatmapSetInfo.cs index c3e2399d53..79cd8253f7 100644 --- a/osu.Game/Beatmaps/BeatmapSetInfo.cs +++ b/osu.Game/Beatmaps/BeatmapSetInfo.cs @@ -107,27 +107,15 @@ namespace osu.Game.Beatmaps [NotMapped] [JsonIgnore] - public DateTimeOffset Submitted - { - get => OnlineInfo.Submitted; - set => OnlineInfo.Submitted = value; - } + public DateTimeOffset Submitted => OnlineInfo.Submitted; [NotMapped] [JsonIgnore] - public DateTimeOffset? Ranked - { - get => OnlineInfo.Ranked; - set => OnlineInfo.Ranked = value; - } + public DateTimeOffset? Ranked => OnlineInfo.Ranked; [NotMapped] [JsonIgnore] - public DateTimeOffset? LastUpdated - { - get => OnlineInfo.LastUpdated; - set => OnlineInfo.LastUpdated = value; - } + public DateTimeOffset? LastUpdated => OnlineInfo.LastUpdated; [NotMapped] [JsonIgnore] @@ -135,107 +123,55 @@ namespace osu.Game.Beatmaps [NotMapped] [JsonIgnore] - public bool HasExplicitContent - { - get => OnlineInfo.HasExplicitContent; - set => OnlineInfo.HasExplicitContent = value; - } + public bool HasExplicitContent => OnlineInfo.HasExplicitContent; [NotMapped] [JsonIgnore] - public bool HasVideo - { - get => OnlineInfo.HasVideo; - set => OnlineInfo.HasVideo = value; - } + public bool HasVideo => OnlineInfo.HasVideo; [NotMapped] [JsonIgnore] - public bool HasStoryboard - { - get => OnlineInfo.HasStoryboard; - set => OnlineInfo.HasStoryboard = value; - } + public bool HasStoryboard => OnlineInfo.HasStoryboard; [NotMapped] [JsonIgnore] - public BeatmapSetOnlineCovers Covers - { - get => OnlineInfo.Covers; - set => OnlineInfo.Covers = value; - } + public BeatmapSetOnlineCovers Covers => OnlineInfo.Covers; [NotMapped] [JsonIgnore] - public string Preview - { - get => OnlineInfo.Preview; - set => OnlineInfo.Preview = value; - } + public string Preview => OnlineInfo.Preview; [NotMapped] [JsonIgnore] - public double BPM - { - get => OnlineInfo.BPM; - set => OnlineInfo.BPM = value; - } + public double BPM => OnlineInfo.BPM; [NotMapped] [JsonIgnore] - public int PlayCount - { - get => OnlineInfo.PlayCount; - set => OnlineInfo.PlayCount = value; - } + public int PlayCount => OnlineInfo.PlayCount; [NotMapped] [JsonIgnore] - public int FavouriteCount - { - get => OnlineInfo.FavouriteCount; - set => OnlineInfo.FavouriteCount = value; - } + public int FavouriteCount => OnlineInfo.FavouriteCount; [NotMapped] [JsonIgnore] - public bool HasFavourited - { - get => OnlineInfo.HasFavourited; - set => OnlineInfo.HasFavourited = value; - } + public bool HasFavourited => OnlineInfo.HasFavourited; [NotMapped] [JsonIgnore] - public BeatmapSetOnlineAvailability Availability - { - get => OnlineInfo.Availability; - set => OnlineInfo.Availability = value; - } + public BeatmapSetOnlineAvailability Availability => OnlineInfo.Availability; [NotMapped] [JsonIgnore] - public BeatmapSetOnlineGenre Genre - { - get => OnlineInfo.Genre; - set => OnlineInfo.Genre = value; - } + public BeatmapSetOnlineGenre Genre => OnlineInfo.Genre; [NotMapped] [JsonIgnore] - public BeatmapSetOnlineLanguage Language - { - get => OnlineInfo.Language; - set => OnlineInfo.Language = value; - } + public BeatmapSetOnlineLanguage Language => OnlineInfo.Language; [NotMapped] [JsonIgnore] - public int? TrackId - { - get => OnlineInfo.TrackId; - set => OnlineInfo.TrackId = value; - } + public int? TrackId => OnlineInfo?.TrackId; #endregion } diff --git a/osu.Game/Beatmaps/IBeatmapSetOnlineInfo.cs b/osu.Game/Beatmaps/IBeatmapSetOnlineInfo.cs index b9800bc2e6..1d2bb46bde 100644 --- a/osu.Game/Beatmaps/IBeatmapSetOnlineInfo.cs +++ b/osu.Game/Beatmaps/IBeatmapSetOnlineInfo.cs @@ -15,87 +15,87 @@ namespace osu.Game.Beatmaps /// /// The date this beatmap set was submitted to the online listing. /// - DateTimeOffset Submitted { get; set; } + DateTimeOffset Submitted { get; } /// /// The date this beatmap set was ranked. /// - DateTimeOffset? Ranked { get; set; } + DateTimeOffset? Ranked { get; } /// /// The date this beatmap set was last updated. /// - DateTimeOffset? LastUpdated { get; set; } + DateTimeOffset? LastUpdated { get; } /// /// The status of this beatmap set. /// - BeatmapSetOnlineStatus Status { get; set; } + BeatmapSetOnlineStatus Status { get; } /// /// Whether or not this beatmap set has explicit content. /// - bool HasExplicitContent { get; set; } + bool HasExplicitContent { get; } /// /// Whether or not this beatmap set has a background video. /// - bool HasVideo { get; set; } + bool HasVideo { get; } /// /// Whether or not this beatmap set has a storyboard. /// - bool HasStoryboard { get; set; } + bool HasStoryboard { get; } /// /// The different sizes of cover art for this beatmap set. /// - BeatmapSetOnlineCovers Covers { get; set; } + BeatmapSetOnlineCovers Covers { get; } /// /// A small sample clip of this beatmap set's song. /// - string Preview { get; set; } + string Preview { get; } /// /// The beats per minute of this beatmap set's song. /// - double BPM { get; set; } + double BPM { get; } /// /// The amount of plays this beatmap set has. /// - int PlayCount { get; set; } + int PlayCount { get; } /// /// The amount of people who have favourited this beatmap set. /// - int FavouriteCount { get; set; } + int FavouriteCount { get; } /// /// Whether this beatmap set has been favourited by the current user. /// - bool HasFavourited { get; set; } + bool HasFavourited { get; } /// /// The availability of this beatmap set. /// - BeatmapSetOnlineAvailability Availability { get; set; } + BeatmapSetOnlineAvailability Availability { get; } /// /// The song genre of this beatmap set. /// - BeatmapSetOnlineGenre Genre { get; set; } + BeatmapSetOnlineGenre Genre { get; } /// /// The song language of this beatmap set. /// - BeatmapSetOnlineLanguage Language { get; set; } + BeatmapSetOnlineLanguage Language { get; } /// /// The track ID of this beatmap set. /// Non-null only if the track is linked to a featured artist track entry. /// - int? TrackId { get; set; } + int? TrackId { get; } } } From 90477e3788fdeccc88e5181ce7f2851cbebca609 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 22 Oct 2021 19:12:37 +0900 Subject: [PATCH 035/260] Remove unused `BeatmapTypeInfo` class Helps reduce the scope of upcoming changes. --- .../OnlinePlay/Components/BeatmapTypeInfo.cs | 72 ------------------- 1 file changed, 72 deletions(-) delete mode 100644 osu.Game/Screens/OnlinePlay/Components/BeatmapTypeInfo.cs diff --git a/osu.Game/Screens/OnlinePlay/Components/BeatmapTypeInfo.cs b/osu.Game/Screens/OnlinePlay/Components/BeatmapTypeInfo.cs deleted file mode 100644 index 3aa13458a4..0000000000 --- a/osu.Game/Screens/OnlinePlay/Components/BeatmapTypeInfo.cs +++ /dev/null @@ -1,72 +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.Linq; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Graphics; -using osu.Game.Graphics.Containers; -using osuTK; - -namespace osu.Game.Screens.OnlinePlay.Components -{ - public class BeatmapTypeInfo : OnlinePlayComposite - { - private LinkFlowContainer beatmapAuthor; - - public BeatmapTypeInfo() - { - AutoSizeAxes = Axes.Both; - } - - [BackgroundDependencyLoader] - private void load() - { - InternalChild = new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - LayoutDuration = 100, - Spacing = new Vector2(5, 0), - Children = new Drawable[] - { - new ModeTypeInfo(), - new Container - { - AutoSizeAxes = Axes.X, - Height = 30, - Margin = new MarginPadding { Left = 5 }, - Children = new Drawable[] - { - new BeatmapTitle(), - beatmapAuthor = new LinkFlowContainer(s => s.Font = s.Font.With(size: 14)) - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - AutoSizeAxes = Axes.Both - }, - }, - }, - } - }; - - Playlist.CollectionChanged += (_, __) => updateInfo(); - - updateInfo(); - } - - private void updateInfo() - { - beatmapAuthor.Clear(); - - var beatmap = Playlist.FirstOrDefault()?.Beatmap; - - if (beatmap != null) - { - beatmapAuthor.AddText("mapped by ", s => s.Colour = OsuColour.Gray(0.8f)); - beatmapAuthor.AddUserLink(beatmap.Value.Metadata.Author); - } - } - } -} From c701579c693f345d6ea3046d79264949c9f2d57a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 22 Oct 2021 21:25:13 +0900 Subject: [PATCH 036/260] Refactor `BeatmapMetadataContainer` and usages to use interface types --- .../Responses/APIUserMostPlayedBeatmap.cs | 22 +++---- .../Sections/BeatmapMetadataContainer.cs | 11 ++-- .../Historical/DrawableMostPlayedBeatmap.cs | 61 ++++++++++++------- .../PaginatedMostPlayedBeatmapContainer.cs | 4 +- .../Sections/Ranks/DrawableProfileScore.cs | 49 +++++++++------ 5 files changed, 86 insertions(+), 61 deletions(-) diff --git a/osu.Game/Online/API/Requests/Responses/APIUserMostPlayedBeatmap.cs b/osu.Game/Online/API/Requests/Responses/APIUserMostPlayedBeatmap.cs index 10f7ca6fe2..19c581bf95 100644 --- a/osu.Game/Online/API/Requests/Responses/APIUserMostPlayedBeatmap.cs +++ b/osu.Game/Online/API/Requests/Responses/APIUserMostPlayedBeatmap.cs @@ -2,8 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using Newtonsoft.Json; -using osu.Game.Beatmaps; -using osu.Game.Rulesets; namespace osu.Game.Online.API.Requests.Responses { @@ -16,17 +14,19 @@ namespace osu.Game.Online.API.Requests.Responses public int PlayCount { get; set; } [JsonProperty("beatmap")] - private BeatmapInfo beatmapInfo { get; set; } + private APIBeatmap beatmap { get; set; } - [JsonProperty] - private APIBeatmapSet beatmapSet { get; set; } - - public BeatmapInfo GetBeatmapInfo(RulesetStore rulesets) + public APIBeatmap BeatmapInfo { - BeatmapSetInfo setInfo = beatmapSet.ToBeatmapSet(rulesets); - beatmapInfo.BeatmapSet = setInfo; - beatmapInfo.Metadata = setInfo.Metadata; - return beatmapInfo; + get + { + // old osu-web code doesn't nest set. + beatmap.BeatmapSet = BeatmapSet; + return beatmap; + } } + + [JsonProperty("beatmapset")] + public APIBeatmapSet BeatmapSet { get; set; } } } diff --git a/osu.Game/Overlays/Profile/Sections/BeatmapMetadataContainer.cs b/osu.Game/Overlays/Profile/Sections/BeatmapMetadataContainer.cs index 7812a81f30..94ef5e5d86 100644 --- a/osu.Game/Overlays/Profile/Sections/BeatmapMetadataContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/BeatmapMetadataContainer.cs @@ -15,9 +15,9 @@ namespace osu.Game.Overlays.Profile.Sections /// public abstract class BeatmapMetadataContainer : OsuHoverContainer { - private readonly BeatmapInfo beatmapInfo; + private readonly IBeatmapInfo beatmapInfo; - protected BeatmapMetadataContainer(BeatmapInfo beatmapInfo) + protected BeatmapMetadataContainer(IBeatmapInfo beatmapInfo) : base(HoverSampleSet.Submit) { this.beatmapInfo = beatmapInfo; @@ -30,10 +30,7 @@ namespace osu.Game.Overlays.Profile.Sections { Action = () => { - if (beatmapInfo.OnlineBeatmapID != null) - beatmapSetOverlay?.FetchAndShowBeatmap(beatmapInfo.OnlineBeatmapID.Value); - else if (beatmapInfo.BeatmapSet?.OnlineBeatmapSetID != null) - beatmapSetOverlay?.FetchAndShowBeatmapSet(beatmapInfo.BeatmapSet.OnlineBeatmapSetID.Value); + beatmapSetOverlay?.FetchAndShowBeatmap(beatmapInfo.OnlineID); }; Child = new FillFlowContainer @@ -43,6 +40,6 @@ namespace osu.Game.Overlays.Profile.Sections }; } - protected abstract Drawable[] CreateText(BeatmapInfo beatmapInfo); + protected abstract Drawable[] CreateText(IBeatmapInfo beatmapInfo); } } diff --git a/osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedBeatmap.cs b/osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedBeatmap.cs index 32201e36a9..c4c8bfb84f 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedBeatmap.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedBeatmap.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.Diagnostics; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -13,6 +14,7 @@ using osu.Game.Graphics.Sprites; using osuTK; using osu.Framework.Graphics.Cursor; using osu.Framework.Localisation; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Resources.Localisation.Web; namespace osu.Game.Overlays.Profile.Sections.Historical @@ -22,13 +24,11 @@ namespace osu.Game.Overlays.Profile.Sections.Historical private const int cover_width = 100; private const int corner_radius = 6; - private readonly BeatmapInfo beatmapInfo; - private readonly int playCount; + private readonly APIUserMostPlayedBeatmap mostPlayed; - public DrawableMostPlayedBeatmap(BeatmapInfo beatmapInfo, int playCount) + public DrawableMostPlayedBeatmap(APIUserMostPlayedBeatmap mostPlayed) { - this.beatmapInfo = beatmapInfo; - this.playCount = playCount; + this.mostPlayed = mostPlayed; RelativeSizeAxes = Axes.X; Height = 50; @@ -46,7 +46,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical { RelativeSizeAxes = Axes.Y, Width = cover_width, - BeatmapSet = beatmapInfo.BeatmapSet, + BeatmapSet = mostPlayed.BeatmapSet, }, new Container { @@ -77,7 +77,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical Direction = FillDirection.Vertical, Children = new Drawable[] { - new MostPlayedBeatmapMetadataContainer(beatmapInfo), + new MostPlayedBeatmapMetadataContainer(mostPlayed.BeatmapInfo), new LinkFlowContainer(t => { t.Font = OsuFont.GetFont(size: 12, weight: FontWeight.Regular); @@ -89,11 +89,11 @@ namespace osu.Game.Overlays.Profile.Sections.Historical }.With(d => { d.AddText("mapped by "); - d.AddUserLink(beatmapInfo.Metadata.Author); + d.AddUserLink(mostPlayed.BeatmapSet.Author); }), } }, - new PlayCountText(playCount) + new PlayCountText(mostPlayed.PlayCount) { Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight @@ -120,26 +120,41 @@ namespace osu.Game.Overlays.Profile.Sections.Historical private class MostPlayedBeatmapMetadataContainer : BeatmapMetadataContainer { - public MostPlayedBeatmapMetadataContainer(BeatmapInfo beatmapInfo) + public MostPlayedBeatmapMetadataContainer(IBeatmapInfo beatmapInfo) : base(beatmapInfo) { } - protected override Drawable[] CreateText(BeatmapInfo beatmapInfo) => new Drawable[] + protected override Drawable[] CreateText(IBeatmapInfo beatmapInfo) { - new OsuSpriteText + var metadata = beatmapInfo.Metadata; + + Debug.Assert(metadata != null); + + return new Drawable[] { - Text = new RomanisableString( - $"{beatmapInfo.Metadata.TitleUnicode ?? beatmapInfo.Metadata.Title} [{beatmapInfo.Version}] ", - $"{beatmapInfo.Metadata.Title ?? beatmapInfo.Metadata.TitleUnicode} [{beatmapInfo.Version}] "), - Font = OsuFont.GetFont(weight: FontWeight.Bold) - }, - new OsuSpriteText - { - Text = "by " + new RomanisableString(beatmapInfo.Metadata.ArtistUnicode, beatmapInfo.Metadata.Artist), - Font = OsuFont.GetFont(weight: FontWeight.Regular) - }, - }; + new OsuSpriteText + { + Text = new RomanisableString(metadata.TitleUnicode, metadata.Title), + Font = OsuFont.GetFont(weight: FontWeight.Bold) + }, + new OsuSpriteText + { + Text = $" [{beatmapInfo.DifficultyName}]", + Font = OsuFont.GetFont(weight: FontWeight.Bold) + }, + new OsuSpriteText + { + Text = " by ", + Font = OsuFont.GetFont(weight: FontWeight.Regular) + }, + new OsuSpriteText + { + Text = new RomanisableString(metadata.ArtistUnicode, metadata.Artist), + Font = OsuFont.GetFont(weight: FontWeight.Regular) + }, + }; + } } private class PlayCountText : CompositeDrawable, IHasTooltip diff --git a/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs b/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs index d0979526da..428d04f985 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs @@ -33,7 +33,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical protected override APIRequest> CreateRequest() => new GetUserMostPlayedBeatmapsRequest(User.Value.Id, VisiblePages++, ItemsPerPage); - protected override Drawable CreateDrawableItem(APIUserMostPlayedBeatmap model) => - new DrawableMostPlayedBeatmap(model.GetBeatmapInfo(Rulesets), model.PlayCount); + protected override Drawable CreateDrawableItem(APIUserMostPlayedBeatmap mostPlayed) => + new DrawableMostPlayedBeatmap(mostPlayed); } } diff --git a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs index 3561e9700e..7bfa2ee51e 100644 --- a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs +++ b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.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.Diagnostics; using System.Linq; using JetBrains.Annotations; using osu.Framework.Allocation; @@ -245,30 +246,42 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks private class ScoreBeatmapMetadataContainer : BeatmapMetadataContainer { - public ScoreBeatmapMetadataContainer(BeatmapInfo beatmapInfo) + public ScoreBeatmapMetadataContainer(IBeatmapInfo beatmapInfo) : base(beatmapInfo) { } - protected override Drawable[] CreateText(BeatmapInfo beatmapInfo) => new Drawable[] + protected override Drawable[] CreateText(IBeatmapInfo beatmapInfo) { - new OsuSpriteText + var metadata = beatmapInfo.Metadata; + + Debug.Assert(metadata != null); + + return new Drawable[] { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Text = new RomanisableString( - $"{beatmapInfo.Metadata.TitleUnicode ?? beatmapInfo.Metadata.Title} ", - $"{beatmapInfo.Metadata.Title ?? beatmapInfo.Metadata.TitleUnicode} "), - Font = OsuFont.GetFont(size: 14, weight: FontWeight.SemiBold, italics: true) - }, - new OsuSpriteText - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Text = "by " + new RomanisableString(beatmapInfo.Metadata.ArtistUnicode, beatmapInfo.Metadata.Artist), - Font = OsuFont.GetFont(size: 12, italics: true) - }, - }; + new OsuSpriteText + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Text = new RomanisableString(metadata.TitleUnicode, metadata.Title), + Font = OsuFont.GetFont(size: 14, weight: FontWeight.SemiBold, italics: true) + }, + new OsuSpriteText + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Text = " by ", + Font = OsuFont.GetFont(size: 12, italics: true) + }, + new OsuSpriteText + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Text = new RomanisableString(metadata.ArtistUnicode, metadata.Artist), + Font = OsuFont.GetFont(size: 12, italics: true) + }, + }; + } } } } From 9fcb3d9dd1ca32f87fd2d4404fa5bdacf3d1e4aa Mon Sep 17 00:00:00 2001 From: Jason Won Date: Fri, 22 Oct 2021 17:10:53 -0400 Subject: [PATCH 037/260] NoScopeMod: show cursor during breaks + spinners --- osu.Game.Rulesets.Osu/Mods/OsuModNoScope.cs | 31 +++++++++++++++++---- 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModNoScope.cs b/osu.Game.Rulesets.Osu/Mods/OsuModNoScope.cs index c48cbd9992..026bb6c1cb 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModNoScope.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModNoScope.cs @@ -2,22 +2,27 @@ // See the LICENCE file in the repository root for full licence text. using System; -using osu.Game.Rulesets.UI; -using osu.Game.Rulesets.Mods; +using System.Linq; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; -using osu.Framework.Bindables; using osu.Framework.Localisation; using osu.Framework.Utils; -using osu.Game.Graphics.UserInterface; +using osu.Game.Beatmaps; using osu.Game.Configuration; +using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.Settings; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.UI; using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; +using osu.Game.Screens.Play; +using osu.Game.Utils; namespace osu.Game.Rulesets.Osu.Mods { - public class OsuModNoScope : Mod, IUpdatableByPlayfield, IApplicableToScoreProcessor + public class OsuModNoScope : Mod, IUpdatableByPlayfield, IApplicableToScoreProcessor, IApplicableToPlayer, IApplicableToBeatmap { /// /// Slightly higher than the cutoff for . @@ -34,6 +39,8 @@ namespace osu.Game.Rulesets.Osu.Mods public override double ScoreMultiplier => 1; private BindableNumber currentCombo; + private IBindable isBreakTime; + private PeriodTracker spinnerPeriods; private float targetAlpha; @@ -52,6 +59,16 @@ namespace osu.Game.Rulesets.Osu.Mods public ScoreRank AdjustRank(ScoreRank rank, double accuracy) => rank; + public void ApplyToPlayer(Player player) + { + isBreakTime = player.IsBreakTime.GetBoundCopy(); + } + + public void ApplyToBeatmap(IBeatmap beatmap) + { + spinnerPeriods = new PeriodTracker(beatmap.HitObjects.OfType().Select(b => new Period(b.StartTime - transition_duration, b.EndTime))); + } + public void ApplyToScoreProcessor(ScoreProcessor scoreProcessor) { if (HiddenComboCount.Value == 0) return; @@ -65,7 +82,9 @@ namespace osu.Game.Rulesets.Osu.Mods public virtual void Update(Playfield playfield) { - playfield.Cursor.Alpha = (float)Interpolation.Lerp(playfield.Cursor.Alpha, targetAlpha, Math.Clamp(playfield.Time.Elapsed / transition_duration, 0, 1)); + var overrideAlpha = isBreakTime.Value || spinnerPeriods.IsInAny(playfield.Clock.CurrentTime); + var targetOverriddenAlpha = overrideAlpha ? 1 : targetAlpha; + playfield.Cursor.Alpha = (float)Interpolation.Lerp(playfield.Cursor.Alpha, targetOverriddenAlpha, Math.Clamp(playfield.Time.Elapsed / transition_duration, 0, 1)); } } From 1ae6621c85bd8cb24aed197b114bffab272cf0e0 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Fri, 22 Oct 2021 15:45:48 -0700 Subject: [PATCH 038/260] Fix a missed toast shortcut string not localising correctly --- osu.Game/Configuration/OsuConfigManager.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index acb4a9ca02..6d37f68473 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -218,7 +218,9 @@ namespace osu.Game.Configuration rawValue: skinName, name: SkinSettingsStrings.SkinSectionHeader, value: skinName, - shortcut: $"{GlobalActionKeyBindingStrings.RandomSkin}: {LookupKeyBindings(GlobalAction.RandomSkin)}" + shortcut: new TranslatableString(@"_", @"{0}: {1}", + GlobalActionKeyBindingStrings.RandomSkin, + LookupKeyBindings(GlobalAction.RandomSkin)) ); }), new TrackedSetting(OsuSetting.UIScale, scale => new SettingDescription( From 4440b9ca11b6c5e8add43c24a4546d0b8256bd01 Mon Sep 17 00:00:00 2001 From: goodtrailer Date: Sat, 23 Oct 2021 01:59:07 -0700 Subject: [PATCH 039/260] Change IHasRepeats.NodeSamples to IList from List --- osu.Game.Rulesets.Catch/Objects/JuiceStream.cs | 2 +- .../ManiaBeatmapSampleConversionTest.cs | 2 +- .../Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs | 2 +- osu.Game.Rulesets.Mania/Objects/HoldNote.cs | 2 +- osu.Game.Rulesets.Osu/Objects/Slider.cs | 2 +- osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs | 2 +- .../Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs | 2 +- osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs | 2 +- osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs | 2 +- .../Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs | 3 ++- osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs | 2 +- .../Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs | 2 +- osu.Game/Rulesets/Objects/Types/IHasRepeats.cs | 2 +- 13 files changed, 14 insertions(+), 13 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs index 6d5a960f06..282afb6343 100644 --- a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs +++ b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs @@ -145,7 +145,7 @@ namespace osu.Game.Rulesets.Catch.Objects public double Distance => Path.Distance; - public List> NodeSamples { get; set; } = new List>(); + public IList> NodeSamples { get; set; } = new List>(); public double? LegacyLastTickOffset { get; set; } } diff --git a/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapSampleConversionTest.cs b/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapSampleConversionTest.cs index c8feb4ae24..9c690f360a 100644 --- a/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapSampleConversionTest.cs +++ b/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapSampleConversionTest.cs @@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Mania.Tests private IList getSampleNames(IList hitSampleInfo) => hitSampleInfo.Select(sample => sample.LookupNames.First()).ToList(); - private IList> getNodeSampleNames(List> hitSampleInfo) + private IList> getNodeSampleNames(IList> hitSampleInfo) => hitSampleInfo?.Select(getSampleNames) .ToList(); diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs index 1ed045f7e0..8f18521063 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs @@ -488,7 +488,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy /// Retrieves the list of node samples that occur at time greater than or equal to . /// /// The time to retrieve node samples at. - private List> nodeSamplesAt(int time) + private IList> nodeSamplesAt(int time) { if (!(HitObject is IHasPathWithRepeats curveData)) return null; diff --git a/osu.Game.Rulesets.Mania/Objects/HoldNote.cs b/osu.Game.Rulesets.Mania/Objects/HoldNote.cs index c1937af7e4..db0d3e2c5a 100644 --- a/osu.Game.Rulesets.Mania/Objects/HoldNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/HoldNote.cs @@ -67,7 +67,7 @@ namespace osu.Game.Rulesets.Mania.Objects } } - public List> NodeSamples { get; set; } + public IList> NodeSamples { get; set; } /// /// The head note of the hold. diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs index 07d03ee1eb..a0c503a0b0 100644 --- a/osu.Game.Rulesets.Osu/Objects/Slider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs @@ -79,7 +79,7 @@ namespace osu.Game.Rulesets.Osu.Objects /// internal float LazyTravelDistance; - public List> NodeSamples { get; set; } = new List>(); + public IList> NodeSamples { get; set; } = new List>(); [JsonIgnore] public IList TailSamples { get; private set; } diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs index 32aad6c36a..0c5719f29c 100644 --- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs +++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs @@ -81,7 +81,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps { if (shouldConvertSliderToHits(obj, beatmap, distanceData, out var taikoDuration, out var tickSpacing)) { - List> allSamples = obj is IHasPathWithRepeats curveData ? curveData.NodeSamples : new List>(new[] { samples }); + IList> allSamples = obj is IHasPathWithRepeats curveData ? curveData.NodeSamples : new List>(new[] { samples }); int i = 0; diff --git a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs index c29179f749..2beb6bdbf2 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs @@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch } protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, PathControlPoint[] controlPoints, double? length, int repeatCount, - List> nodeSamples) + IList> nodeSamples) { newCombo |= forceNewCombo; comboOffset += extraComboOffset; diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs index 0942a7264d..24022b8588 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs @@ -408,7 +408,7 @@ namespace osu.Game.Rulesets.Objects.Legacy /// The samples to be played when the slider nodes are hit. This includes the head and tail of the slider. /// The hit object. protected abstract HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, PathControlPoint[] controlPoints, double? length, int repeatCount, - List> nodeSamples); + IList> nodeSamples); /// /// Creates a legacy Spinner-type hit object. diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs index ad191f7ff5..9ff92fcc75 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs @@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Objects.Legacy public double Distance => Path.Distance; - public List> NodeSamples { get; set; } + public IList> NodeSamples { get; set; } public int RepeatCount { get; set; } [JsonIgnore] diff --git a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs index bc64518f40..0eb6caf8ee 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs @@ -4,6 +4,7 @@ using osuTK; using osu.Game.Audio; using System.Collections.Generic; +using osu.Framework.Bindables; namespace osu.Game.Rulesets.Objects.Legacy.Mania { @@ -26,7 +27,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Mania } protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, PathControlPoint[] controlPoints, double? length, int repeatCount, - List> nodeSamples) + IList> nodeSamples) { return new ConvertSlider { diff --git a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs index 75ecab0b8f..cb98721be5 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs @@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu } protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, PathControlPoint[] controlPoints, double? length, int repeatCount, - List> nodeSamples) + IList> nodeSamples) { newCombo |= forceNewCombo; comboOffset += extraComboOffset; diff --git a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs index 13e3e84c6a..1eafc4e68b 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs @@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Taiko } protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, PathControlPoint[] controlPoints, double? length, int repeatCount, - List> nodeSamples) + IList> nodeSamples) { return new ConvertSlider { diff --git a/osu.Game/Rulesets/Objects/Types/IHasRepeats.cs b/osu.Game/Rulesets/Objects/Types/IHasRepeats.cs index 674e2aee88..2a4215b960 100644 --- a/osu.Game/Rulesets/Objects/Types/IHasRepeats.cs +++ b/osu.Game/Rulesets/Objects/Types/IHasRepeats.cs @@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Objects.Types /// n-1: The last repeat.
/// n: The last node. ///
- List> NodeSamples { get; } + IList> NodeSamples { get; } } public static class HasRepeatsExtensions From 0affe7b79d338e808f001476af5b2ae5cee89293 Mon Sep 17 00:00:00 2001 From: goodtrailer Date: Sat, 23 Oct 2021 02:25:20 -0700 Subject: [PATCH 040/260] Remove unnecessary using --- osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs index 0eb6caf8ee..386eb8d3ee 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs @@ -4,7 +4,6 @@ using osuTK; using osu.Game.Audio; using System.Collections.Generic; -using osu.Framework.Bindables; namespace osu.Game.Rulesets.Objects.Legacy.Mania { From 051cc2cd92972c20027e4ef4dd4973f23cc7ac69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 24 Oct 2021 12:47:17 +0200 Subject: [PATCH 041/260] Update reference to configuration population process in comment --- osu.Game/Skinning/SkinManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index 177dbaf28f..860d09e011 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -161,7 +161,7 @@ namespace osu.Game.Skinning // This function can be run on fresh import or save. The logic here ensures a skin.ini file is in a good state for both operations. - // LegacySkin will parse the skin.ini and populate `Skin.Configuration` during construction above. + // `Skin` will parse the skin.ini and populate `Skin.Configuration` during construction above. string skinIniSourcedName = instance.Configuration.SkinInfo.Name; string skinIniSourcedCreator = instance.Configuration.SkinInfo.Creator; string archiveName = item.Name.Replace(".osk", "", StringComparison.OrdinalIgnoreCase); From 51c5d0aec7e1fc33606f6b74c3d4c524f8d3fd33 Mon Sep 17 00:00:00 2001 From: Susko3 <16479013+Susko3@users.noreply.github.com> Date: Sun, 24 Oct 2021 12:59:19 +0200 Subject: [PATCH 042/260] Update to be in line with framework changes --- osu.Game/Database/ArchiveModelManager.cs | 2 +- osu.Game/Graphics/ScreenshotManager.cs | 2 +- osu.Game/IO/WrappedStorage.cs | 4 ++-- osu.Game/OsuGame.cs | 2 +- osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index cc25f84ecb..a0ca23eaec 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -467,7 +467,7 @@ namespace osu.Game.Database using (var outputStream = exportStorage.GetStream(filename, FileAccess.Write, FileMode.Create)) ExportModelTo(retrievedItem, outputStream); - exportStorage.ShowFileInNativeExplorer(filename); + exportStorage.PresentFileExternally(filename); } /// diff --git a/osu.Game/Graphics/ScreenshotManager.cs b/osu.Game/Graphics/ScreenshotManager.cs index 27e6e2bb1c..c21986f6c4 100644 --- a/osu.Game/Graphics/ScreenshotManager.cs +++ b/osu.Game/Graphics/ScreenshotManager.cs @@ -135,7 +135,7 @@ namespace osu.Game.Graphics Text = $"{fileName} saved!", Activated = () => { - storage.ShowFileInNativeExplorer(fileName); + storage.PresentFileExternally(fileName); return true; } }); diff --git a/osu.Game/IO/WrappedStorage.cs b/osu.Game/IO/WrappedStorage.cs index 1e2d53d6fb..aadc4e760b 100644 --- a/osu.Game/IO/WrappedStorage.cs +++ b/osu.Game/IO/WrappedStorage.cs @@ -70,9 +70,9 @@ namespace osu.Game.IO public override Stream GetStream(string path, FileAccess access = FileAccess.Read, FileMode mode = FileMode.OpenOrCreate) => UnderlyingStorage.GetStream(MutatePath(path), access, mode); - public override void OpenPathInNativeExplorer(string path) => UnderlyingStorage.OpenPathInNativeExplorer(MutatePath(path)); + public override void OpenFileExternally(string filename) => UnderlyingStorage.OpenFileExternally(MutatePath(filename)); - public override void ShowFileInNativeExplorer(string filename) => UnderlyingStorage.ShowFileInNativeExplorer(MutatePath(filename)); + public override void PresentFileExternally(string filename) => UnderlyingStorage.PresentFileExternally(MutatePath(filename)); public override Storage GetStorageForDirectory(string path) { diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 4b001f03ad..985451fd6f 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -921,7 +921,7 @@ namespace osu.Game Text = "Subsequent messages have been logged. Click to view log files.", Activated = () => { - Storage.GetStorageForDirectory(@"logs").ShowFileInNativeExplorer(logFile); + Storage.GetStorageForDirectory(@"logs").PresentFileExternally(logFile); return true; } })); diff --git a/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs b/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs index aa37748653..6bcb5ef715 100644 --- a/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs @@ -67,7 +67,7 @@ namespace osu.Game.Overlays.Settings.Sections.General Add(new SettingsButton { Text = GeneralSettingsStrings.OpenOsuFolder, - Action = storage.OpenInNativeExplorer, + Action = storage.PresentExternally, }); Add(new SettingsButton From 5303cae0444064e8696a8db979273ca6730155ee Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 24 Oct 2021 23:43:37 +0900 Subject: [PATCH 043/260] Add mention of stream close logic --- osu.Game/Skinning/Skin.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Skinning/Skin.cs b/osu.Game/Skinning/Skin.cs index d5f41f01a4..823a5d5133 100644 --- a/osu.Game/Skinning/Skin.cs +++ b/osu.Game/Skinning/Skin.cs @@ -47,6 +47,7 @@ namespace osu.Game.Skinning configurationStream ??= getConfigurationStream(); if (configurationStream != null) + // stream will be closed after use by LineBufferedReader. ParseConfigurationStream(configurationStream); else Configuration = new SkinConfiguration(); From 26cf5370c35bf977388eaf651503d783746bb34b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 24 Oct 2021 23:48:46 +0900 Subject: [PATCH 044/260] Remove unused `reader` parameter --- osu.Game/Database/ArchiveModelManager.cs | 4 ++-- osu.Game/Skinning/SkinManager.cs | 4 ++-- osu.Game/Tests/Visual/EditorTestScene.cs | 3 +-- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index ad5bc6d420..00e1f03b92 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -315,7 +315,7 @@ namespace osu.Game.Database /// /// In the case of no matching files, a hash will be generated from the passed archive's . /// - protected virtual string ComputeHash(TModel item, ArchiveReader reader = null) + protected virtual string ComputeHash(TModel item) { var hashableFiles = item.Files .Where(f => HashableFileTypes.Any(ext => f.Filename.EndsWith(ext, StringComparison.OrdinalIgnoreCase))) @@ -397,7 +397,7 @@ namespace osu.Game.Database LogForModel(item, @"Beginning import..."); item.Files = archive != null ? createFileInfos(archive, Files) : new List(); - item.Hash = ComputeHash(item, archive); + item.Hash = ComputeHash(item); await Populate(item, archive, cancellationToken).ConfigureAwait(false); diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index 860d09e011..2187d2d875 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -155,7 +155,7 @@ namespace osu.Game.Skinning protected override bool HasCustomHashFunction => true; - protected override string ComputeHash(SkinInfo item, ArchiveReader reader = null) + protected override string ComputeHash(SkinInfo item) { var instance = GetSkin(item); @@ -186,7 +186,7 @@ namespace osu.Game.Skinning if (skinIniSourcedName != item.Name) updateSkinIniMetadata(item); - return base.ComputeHash(item, reader); + return base.ComputeHash(item); } private void updateSkinIniMetadata(SkinInfo item) diff --git a/osu.Game/Tests/Visual/EditorTestScene.cs b/osu.Game/Tests/Visual/EditorTestScene.cs index 798b0d01ee..4b02306d33 100644 --- a/osu.Game/Tests/Visual/EditorTestScene.cs +++ b/osu.Game/Tests/Visual/EditorTestScene.cs @@ -10,7 +10,6 @@ using osu.Framework.Platform; using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Database; -using osu.Game.IO.Archives; using osu.Game.Online.API; using osu.Game.Rulesets; using osu.Game.Rulesets.Edit; @@ -154,7 +153,7 @@ namespace osu.Game.Tests.Visual { } - protected override string ComputeHash(BeatmapSetInfo item, ArchiveReader reader = null) + protected override string ComputeHash(BeatmapSetInfo item) => string.Empty; } From 99d01f2162bf35fd0c523e2760f257a8fd24a33b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 24 Oct 2021 23:51:49 +0900 Subject: [PATCH 045/260] Fix a couple of new layout inspections introduces in Rider 2021.3 EAP5 --- osu.Game.Rulesets.Osu/Objects/Slider.cs | 5 +++-- osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs | 3 +-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs index 07d03ee1eb..ba817d2e40 100644 --- a/osu.Game.Rulesets.Osu/Objects/Slider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs @@ -151,8 +151,9 @@ namespace osu.Game.Rulesets.Osu.Objects { base.CreateNestedHitObjects(cancellationToken); - foreach (var e in - SliderEventGenerator.Generate(StartTime, SpanDuration, Velocity, TickDistance, Path.Distance, this.SpanCount(), LegacyLastTickOffset, cancellationToken)) + var sliderEvents = SliderEventGenerator.Generate(StartTime, SpanDuration, Velocity, TickDistance, Path.Distance, this.SpanCount(), LegacyLastTickOffset, cancellationToken); + + foreach (var e in sliderEvents) { switch (e.Type) { diff --git a/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs b/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs index 022b2c1a59..2c1e39c2cf 100644 --- a/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs @@ -77,8 +77,7 @@ namespace osu.Game.Tests.Beatmaps.IO { var reader = new ZipArchiveReader(osz); - using (var stream = new StreamReader( - reader.GetStream("Soleily - Renatus (Deif) [Platter].osu"))) + using (var stream = new StreamReader(reader.GetStream("Soleily - Renatus (Deif) [Platter].osu"))) { Assert.AreEqual("osu file format v13", stream.ReadLine()?.Trim()); } From 5f2c5770d47a9a62d951e311896e490d26210beb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 24 Oct 2021 17:25:53 +0200 Subject: [PATCH 046/260] Add build-only CI jobs for mobile platforms --- .github/workflows/ci.yml | 42 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0da1f9636b..2a3b2fd978 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -50,6 +50,48 @@ jobs: name: osu-test-results-${{matrix.os.prettyname}}-${{matrix.threadingMode}} path: ${{github.workspace}}/TestResults/TestResults-${{matrix.os.prettyname}}-${{matrix.threadingMode}}.trx + build-only-android: + name: Build only (Android) + runs-on: windows-latest + timeout-minutes: 60 + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Install .NET 5.0.x + uses: actions/setup-dotnet@v1 + with: + dotnet-version: "5.0.x" + + - name: Setup MSBuild + uses: microsoft/setup-msbuild@v1 + + - name: Build + run: msbuild osu.Android.slnf /restore /p:Configuration=Debug + + build-only-ios: + # While this workflow technically *can* run, it fails as iOS builds are blocked by multiple issues. + # See https://github.com/ppy/osu-framework/issues/4677 for the details. + # The job can be unblocked once those issues are resolved and game deployments can happen again. + if: false + name: Build only (iOS) + runs-on: macos-latest + timeout-minutes: 60 + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Install .NET 5.0.x + uses: actions/setup-dotnet@v1 + with: + dotnet-version: "5.0.x" + + # Contrary to seemingly any other msbuild, msbuild running on macOS/Mono + # cannot accept .sln(f) files as arguments. + # Build just the main game for now. + - name: Build + run: msbuild osu.iOS/osu.iOS.csproj /restore /p:Configuration=Debug + inspect-code: name: Code Quality runs-on: ubuntu-latest From 00732822c34a9dcd4ae7716f9b8141a90e1307a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 24 Oct 2021 17:41:55 +0200 Subject: [PATCH 047/260] Replace insufficiently specific exception type Resolves compilation failures for the newly-added Android build-only CI job caused by inspection CA2201 ("Exception type System.Exception is not sufficiently specific"). --- osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs index 9562b41363..ab4e1b4457 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Net; using JetBrains.Annotations; using NUnit.Framework; using osu.Framework.Allocation; @@ -105,7 +106,7 @@ namespace osu.Game.Tests.Visual.Online } else { - getUser.TriggerFailure(new Exception()); + getUser.TriggerFailure(new WebException()); } return true; From 207ccc4756474cea1a57b056f5404593433089d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 17 Oct 2021 14:15:36 +0200 Subject: [PATCH 048/260] Add test scene for beatmapset online status pill display --- .../TestSceneBeatmapSetOnlineStatusPill.cs | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapSetOnlineStatusPill.cs diff --git a/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapSetOnlineStatusPill.cs b/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapSetOnlineStatusPill.cs new file mode 100644 index 0000000000..7bbf3c629b --- /dev/null +++ b/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapSetOnlineStatusPill.cs @@ -0,0 +1,32 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Linq; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.Drawables; +using osu.Game.Tests.Visual.UserInterface; +using osuTK; + +namespace osu.Game.Tests.Visual.Beatmaps +{ + public class TestSceneBeatmapSetOnlineStatusPill : ThemeComparisonTestScene + { + protected override Drawable CreateContent() => new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 10), + ChildrenEnumerable = Enum.GetValues(typeof(BeatmapSetOnlineStatus)).Cast().Select(status => new BeatmapSetOnlineStatusPill + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Status = status + }) + }; + } +} From 5ab3337a105a104074e1b019ea93bae329021402 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 17 Oct 2021 14:42:04 +0200 Subject: [PATCH 049/260] Update beatmap set status pill appearance --- .../Drawables/BeatmapSetOnlineStatusPill.cs | 46 ++++++++++++++----- osu.Game/Graphics/OsuColour.cs | 36 +++++++++++++++ .../BeatmapSet/BeatmapSetHeaderContent.cs | 1 - 3 files changed, 71 insertions(+), 12 deletions(-) diff --git a/osu.Game/Beatmaps/Drawables/BeatmapSetOnlineStatusPill.cs b/osu.Game/Beatmaps/Drawables/BeatmapSetOnlineStatusPill.cs index ffc010b3a3..bc8af90069 100644 --- a/osu.Game/Beatmaps/Drawables/BeatmapSetOnlineStatusPill.cs +++ b/osu.Game/Beatmaps/Drawables/BeatmapSetOnlineStatusPill.cs @@ -1,6 +1,9 @@ // 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 osu.Framework.Allocation; using osu.Framework.Extensions; using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; @@ -8,15 +11,13 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; +using osu.Game.Overlays; using osuTK.Graphics; namespace osu.Game.Beatmaps.Drawables { public class BeatmapSetOnlineStatusPill : CircularContainer { - private readonly OsuSpriteText statusText; - private readonly Box background; - private BeatmapSetOnlineStatus status; public BeatmapSetOnlineStatus Status @@ -29,8 +30,8 @@ namespace osu.Game.Beatmaps.Drawables status = value; - Alpha = value == BeatmapSetOnlineStatus.None ? 0 : 1; - statusText.Text = value.GetLocalisableDescription().ToUpper(); + if (IsLoaded) + updateState(); } } @@ -46,11 +47,14 @@ namespace osu.Game.Beatmaps.Drawables set => statusText.Padding = value; } - public Color4 BackgroundColour - { - get => background.Colour; - set => background.Colour = value; - } + private readonly OsuSpriteText statusText; + private readonly Box background; + + [Resolved] + private OsuColour colours { get; set; } = null!; + + [Resolved(CanBeNull = true)] + private OverlayColourProvider? colourProvider { get; set; } public BeatmapSetOnlineStatusPill() { @@ -63,7 +67,6 @@ namespace osu.Game.Beatmaps.Drawables { RelativeSizeAxes = Axes.Both, Colour = Color4.Black, - Alpha = 0.5f, }, statusText = new OsuSpriteText { @@ -74,6 +77,27 @@ namespace osu.Game.Beatmaps.Drawables }; Status = BeatmapSetOnlineStatus.None; + TextPadding = new MarginPadding { Horizontal = 5, Bottom = 1 }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + updateState(); + } + + private void updateState() + { + Alpha = Status == BeatmapSetOnlineStatus.None ? 0 : 1; + + statusText.Text = Status.GetLocalisableDescription().ToUpper(); + + if (colourProvider != null) + statusText.Colour = status == BeatmapSetOnlineStatus.Graveyard ? colourProvider.Background1 : colourProvider.Background3; + else + statusText.Colour = status == BeatmapSetOnlineStatus.Graveyard ? colours.GreySeafoamLight : Color4.Black; + + background.Colour = OsuColour.ForBeatmapSetOnlineStatus(Status) ?? colourProvider?.Light1 ?? colours.GreySeafoamLighter; } } } diff --git a/osu.Game/Graphics/OsuColour.cs b/osu.Game/Graphics/OsuColour.cs index 40d163635a..3aa4dbf1d8 100644 --- a/osu.Game/Graphics/OsuColour.cs +++ b/osu.Game/Graphics/OsuColour.cs @@ -118,6 +118,42 @@ namespace osu.Game.Graphics } } + /// + /// Retrieves a colour for the given . + /// A value indicates that a "background" shade from the local + /// (or another fallback colour) should be used. + /// + /// + /// Sourced from web: https://github.com/ppy/osu-web/blob/007eebb1916ed5cb6a7866d82d8011b1060a945e/resources/assets/less/layout.less#L36-L50 + /// + public static Color4? ForBeatmapSetOnlineStatus(BeatmapSetOnlineStatus status) + { + switch (status) + { + case BeatmapSetOnlineStatus.Ranked: + case BeatmapSetOnlineStatus.Approved: + return Color4Extensions.FromHex(@"b3ff66"); + + case BeatmapSetOnlineStatus.Loved: + return Color4Extensions.FromHex(@"ff66ab"); + + case BeatmapSetOnlineStatus.Qualified: + return Color4Extensions.FromHex(@"66ccff"); + + case BeatmapSetOnlineStatus.Pending: + return Color4Extensions.FromHex(@"ffd966"); + + case BeatmapSetOnlineStatus.WIP: + return Color4Extensions.FromHex(@"ff9966"); + + case BeatmapSetOnlineStatus.Graveyard: + return Color4.Black; + + default: + return null; + } + } + /// /// Returns a foreground text colour that is supposed to contrast well with /// the supplied . diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs b/osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs index c1029923f7..df88b06879 100644 --- a/osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs +++ b/osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs @@ -220,7 +220,6 @@ namespace osu.Game.Overlays.BeatmapSet private void load(OverlayColourProvider colourProvider) { coverGradient.Colour = ColourInfo.GradientVertical(colourProvider.Background6.Opacity(0.3f), colourProvider.Background6.Opacity(0.8f)); - onlineStatusPill.BackgroundColour = colourProvider.Background6; State.BindValueChanged(_ => updateDownloadButtons()); From feedd53a53d86945fe0460b73abd135f16474e5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 17 Oct 2021 14:55:52 +0200 Subject: [PATCH 050/260] Add support for setting fixed size of status pill --- .../TestSceneBeatmapSetOnlineStatusPill.cs | 16 ++++++++++++ .../Drawables/BeatmapSetOnlineStatusPill.cs | 25 +++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapSetOnlineStatusPill.cs b/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapSetOnlineStatusPill.cs index 7bbf3c629b..2d61385269 100644 --- a/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapSetOnlineStatusPill.cs +++ b/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapSetOnlineStatusPill.cs @@ -2,11 +2,16 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; using System.Linq; +using NUnit.Framework; +using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; +using osu.Game.Overlays; using osu.Game.Tests.Visual.UserInterface; using osuTK; @@ -28,5 +33,16 @@ namespace osu.Game.Tests.Visual.Beatmaps Status = status }) }; + + private IEnumerable statusPills => this.ChildrenOfType(); + + [Test] + public void TestFixedWidth() + { + AddStep("create themed content", () => CreateThemedContent(OverlayColourScheme.Red)); + + AddStep("set fixed width", () => statusPills.ForEach(pill => pill.FixedWidth = 90)); + AddStep("unset fixed width", () => statusPills.ForEach(pill => pill.FixedWidth = null)); + } } } diff --git a/osu.Game/Beatmaps/Drawables/BeatmapSetOnlineStatusPill.cs b/osu.Game/Beatmaps/Drawables/BeatmapSetOnlineStatusPill.cs index bc8af90069..848bab6394 100644 --- a/osu.Game/Beatmaps/Drawables/BeatmapSetOnlineStatusPill.cs +++ b/osu.Game/Beatmaps/Drawables/BeatmapSetOnlineStatusPill.cs @@ -47,6 +47,31 @@ namespace osu.Game.Beatmaps.Drawables set => statusText.Padding = value; } + private float? fixedWidth; + + /// + /// When set to a non- value, the pill will be forcibly sized to the given width. + /// When set to a value, the pill will autosize to its contents. + /// + public float? FixedWidth + { + get => fixedWidth; + set + { + fixedWidth = value; + + if (fixedWidth == null) + { + AutoSizeAxes = Axes.Both; + } + else + { + AutoSizeAxes = Axes.Y; + Width = fixedWidth.Value; + } + } + } + private readonly OsuSpriteText statusText; private readonly Box background; From b8308b971545ba7589b66aa74fed01acc80febc9 Mon Sep 17 00:00:00 2001 From: Roxie is Flux3on <28934062+Flux3on@users.noreply.github.com> Date: Sun, 24 Oct 2021 15:28:38 -0700 Subject: [PATCH 051/260] Corrected spinner judgement positions. For some reason, spinner judgements would stack from the top left in an awkward fashion. This patch ensures that the judgement location will always be in the centre of the screen. --- .../Objects/Drawables/DrawableOsuJudgement.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs index e4df41a4fe..4762285b37 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs @@ -39,7 +39,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables if (JudgedObject?.HitObject is OsuHitObject osuObject) { - Position = osuObject.StackedEndPosition; + if (JudgedObject?.HitObject is Spinner) + { + Position = osuObject.Position; + } else + { + Position = osuObject.StackedEndPosition; + } Scale = new Vector2(osuObject.Scale); } } From 386910ca2ed4998c4a2c3b56b0e9078bce636b44 Mon Sep 17 00:00:00 2001 From: Roxie is Flux3on <28934062+Flux3on@users.noreply.github.com> Date: Sun, 24 Oct 2021 15:29:47 -0700 Subject: [PATCH 052/260] Cut amount of code down for the spinner judgement fix --- .../Objects/Drawables/DrawableOsuJudgement.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs index 4762285b37..79b8320136 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs @@ -40,12 +40,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables if (JudgedObject?.HitObject is OsuHitObject osuObject) { if (JudgedObject?.HitObject is Spinner) - { Position = osuObject.Position; - } else - { + else Position = osuObject.StackedEndPosition; - } Scale = new Vector2(osuObject.Scale); } } From 9f62d1d98c71d763d68ef3837c5e0052a4a68a41 Mon Sep 17 00:00:00 2001 From: Jason Won Date: Sun, 24 Oct 2021 11:41:35 -0400 Subject: [PATCH 053/260] add tests for NoScope --- .../Mods/TestSceneOsuModNoScope.cs | 150 ++++++++++++++++++ 1 file changed, 150 insertions(+) create mode 100644 osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModNoScope.cs diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModNoScope.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModNoScope.cs new file mode 100644 index 0000000000..0d0fefe0ff --- /dev/null +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModNoScope.cs @@ -0,0 +1,150 @@ +// 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.Framework.Utils; +using osu.Framework.Testing; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.Timing; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Osu.Mods; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.Objects.Drawables; +using osuTK; + +namespace osu.Game.Rulesets.Osu.Tests.Mods +{ + public class TestSceneOsuModNoScope : OsuModTestScene + { + [Test] + public void TestVisibleDuringBreak() + { + CreateModTest(new ModTestData + { + Mod = new OsuModNoScope + { + HiddenComboCount = { Value = 0 }, + }, + Autoplay = true, + PassCondition = () => true, + Beatmap = new Beatmap + { + HitObjects = new List + { + new HitCircle + { + Position = new Vector2(300, 192), + StartTime = 1000, + }, + new HitCircle + { + Position = new Vector2(300, 192), + StartTime = 5000, + } + }, + Breaks = new List + { + new BreakPeriod(2000, 4000), + } + } + }); + + AddUntilStep("wait for cursor to hide", () => cursorAlphaAlmostEquals(0)); + AddUntilStep("wait for start of break", isBreak); + AddUntilStep("wait for cursor to show", () => cursorAlphaAlmostEquals(1)); + AddUntilStep("wait for end of break", () => !isBreak()); + AddUntilStep("wait for cursor to hide", () => cursorAlphaAlmostEquals(0)); + } + + [Test] + public void TestVisibleDuringSpinner() + { + CreateModTest(new ModTestData + { + Mod = new OsuModNoScope + { + HiddenComboCount = { Value = 0 }, + }, + Autoplay = true, + PassCondition = () => true, + Beatmap = new Beatmap + { + HitObjects = new List + { + new HitCircle + { + Position = new Vector2(300, 192), + StartTime = 1000, + }, + new Spinner + { + Position = new Vector2(256, 192), + StartTime = 2000, + Duration = 2000, + }, + new HitCircle + { + Position = new Vector2(300, 192), + StartTime = 5000, + } + } + } + }); + + AddUntilStep("wait for cursor to hide", () => cursorAlphaAlmostEquals(0)); + AddUntilStep("wait for start of spinner", isSpinning); + AddUntilStep("wait for cursor to show", () => cursorAlphaAlmostEquals(1)); + AddUntilStep("wait for end of spinner", () => !isSpinning()); + AddUntilStep("wait for cursor to hide", () => cursorAlphaAlmostEquals(0)); + } + + [Test] + public void TestVisibleAfterComboBreak() + { + CreateModTest(new ModTestData + { + Mod = new OsuModNoScope + { + HiddenComboCount = { Value = 2 }, + }, + Autoplay = true, + PassCondition = () => true, + Beatmap = new Beatmap + { + HitObjects = new List + { + new HitCircle + { + Position = new Vector2(100, 192), + StartTime = 1000, + }, + new HitCircle + { + Position = new Vector2(150, 192), + StartTime = 3000, + }, + new HitCircle + { + Position = new Vector2(200, 192), + StartTime = 5000, + }, + } + } + }); + + AddAssert("cursor must start visible", () => cursorAlphaAlmostEquals(1)); + AddUntilStep("wait for combo", () => Player.ScoreProcessor.Combo.Value >= 2); + AddAssert("cursor must dim after combo", () => !cursorAlphaAlmostEquals(1)); + AddStep("break combo", () => Player.ScoreProcessor.Combo.Set(0)); + AddUntilStep("wait for cursor to show", () => cursorAlphaAlmostEquals(1)); + } + + private bool isSpinning() => Player.ChildrenOfType().SingleOrDefault()?.Progress > 0; + + private bool isBreak() => Player.IsBreakTime.Value; + + private bool cursorAlphaAlmostEquals(float alpha) => Precision.AlmostEquals(Player.DrawableRuleset.Cursor.Alpha, alpha); + } +} From 861cd4852a41ad12de76604860e2c88ee2e06089 Mon Sep 17 00:00:00 2001 From: Roxie is Flux3on <28934062+Flux3on@users.noreply.github.com> Date: Sun, 24 Oct 2021 18:14:37 -0700 Subject: [PATCH 054/260] Made StackedEndPosition virtual Done to allow overriding in the other object classes. --- osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs index 7c45b2bc07..a9b4027ea7 100644 --- a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs @@ -49,7 +49,7 @@ namespace osu.Game.Rulesets.Osu.Objects public virtual Vector2 EndPosition => Position; - public Vector2 StackedEndPosition => EndPosition + StackOffset; + public virtual Vector2 StackedEndPosition => EndPosition + StackOffset; public readonly Bindable StackHeightBindable = new Bindable(); From a8e3d0baccdbda1da28a4821dc89c620565ba8e9 Mon Sep 17 00:00:00 2001 From: Roxie is Flux3on <28934062+Flux3on@users.noreply.github.com> Date: Sun, 24 Oct 2021 18:15:40 -0700 Subject: [PATCH 055/260] Added an override for StackedEndPosition This ensures the spinner's hit judgement will always be centre instead of in the top left corner. --- osu.Game.Rulesets.Osu/Objects/Spinner.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Objects/Spinner.cs b/osu.Game.Rulesets.Osu/Objects/Spinner.cs index f85dc0d391..efaea88674 100644 --- a/osu.Game.Rulesets.Osu/Objects/Spinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Spinner.cs @@ -8,6 +8,7 @@ using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu.Judgements; using osu.Game.Rulesets.Scoring; +using osuTK; namespace osu.Game.Rulesets.Osu.Objects { @@ -31,6 +32,8 @@ namespace osu.Game.Rulesets.Osu.Objects /// public int MaximumBonusSpins { get; protected set; } = 1; + public override Vector2 StackedEndPosition => EndPosition; + protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, IBeatmapDifficultyInfo difficulty) { base.ApplyDefaultsToSelf(controlPointInfo, difficulty); From 0c57453f4d5adb7d374775879e2b38a47c61306c Mon Sep 17 00:00:00 2001 From: Roxie is Flux3on <28934062+Flux3on@users.noreply.github.com> Date: Sun, 24 Oct 2021 18:16:14 -0700 Subject: [PATCH 056/260] Reverted special case code changes As requested by @frenzibyte --- .../Objects/Drawables/DrawableOsuJudgement.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs index 79b8320136..e4df41a4fe 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs @@ -39,10 +39,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables if (JudgedObject?.HitObject is OsuHitObject osuObject) { - if (JudgedObject?.HitObject is Spinner) - Position = osuObject.Position; - else - Position = osuObject.StackedEndPosition; + Position = osuObject.StackedEndPosition; Scale = new Vector2(osuObject.Scale); } } From 27d4201dae129e8ad27ed8522b29b500e40d5ccd Mon Sep 17 00:00:00 2001 From: Roxie Wattz Date: Sun, 24 Oct 2021 19:42:37 -0700 Subject: [PATCH 057/260] Added a spinner spam test Shoutout to Echo for making Wizards in Winter x3 --- .../TestSceneSpinnerSpam.cs | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerSpam.cs diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerSpam.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerSpam.cs new file mode 100644 index 0000000000..26729979f9 --- /dev/null +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerSpam.cs @@ -0,0 +1,35 @@ +// 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 NUnit.Framework; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Osu.Objects; +using osuTK; + +namespace osu.Game.Rulesets.Osu.Tests +{ + [TestFixture] + public class TestSceneSpinnerSpam : TestSceneOsuPlayer + { + protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) + { + var beatmap = new Beatmap + { + BeatmapInfo = new BeatmapInfo + { + BaseDifficulty = new BeatmapDifficulty { OverallDifficulty = 10 }, + Ruleset = ruleset + } + }; + + for (int i = 0; i < 512; i++) + { + if (i % 32 < 20) + beatmap.HitObjects.Add(new Spinner { Position = new Vector2(256, 192), StartTime = i * 200, EndTime = (i * 200) + 100 }); + } + + return beatmap; + } + } +} From 78edc7554e65898cefa200c744abf2b45b631825 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 25 Oct 2021 12:05:34 +0900 Subject: [PATCH 058/260] Fix `BeatmapSetInfo`'s `Status` value being marked as non-databased --- osu.Game/Beatmaps/BeatmapSetInfo.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Beatmaps/BeatmapSetInfo.cs b/osu.Game/Beatmaps/BeatmapSetInfo.cs index 79cd8253f7..0c032e1482 100644 --- a/osu.Game/Beatmaps/BeatmapSetInfo.cs +++ b/osu.Game/Beatmaps/BeatmapSetInfo.cs @@ -117,7 +117,6 @@ namespace osu.Game.Beatmaps [JsonIgnore] public DateTimeOffset? LastUpdated => OnlineInfo.LastUpdated; - [NotMapped] [JsonIgnore] public BeatmapSetOnlineStatus Status { get; set; } = BeatmapSetOnlineStatus.None; From 6b9e8dbd2e4ab90c57c1bb16bd20d22d6c56e81a Mon Sep 17 00:00:00 2001 From: Roxie Wattz Date: Sun, 24 Oct 2021 21:34:40 -0700 Subject: [PATCH 059/260] Added the ouendan 2 style hidden Added as an option for nostalgia nerds like me --- osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs | 24 ++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs index 9c7784a00a..59d6769e2c 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs @@ -5,6 +5,8 @@ using System; using System.Diagnostics; using System.Linq; using osu.Framework.Graphics; +using osu.Framework.Bindables; +using osu.Game.Configuration; using osu.Game.Beatmaps; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; @@ -13,10 +15,14 @@ using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Skinning; + namespace osu.Game.Rulesets.Osu.Mods { public class OsuModHidden : ModHidden, IHidesApproachCircles { + [SettingSource("Don't fade out hit circles", "This makes the hidden mod only remove approach circles.")] + public Bindable NoHitCircles { get; } = new BindableBool(false); + public override string Description => @"Play with no approach circles and fading circles/sliders."; public override double ScoreMultiplier => 1.06; @@ -65,14 +71,16 @@ namespace osu.Game.Rulesets.Osu.Mods { case DrawableSliderTail _: using (drawableObject.BeginAbsoluteSequence(fadeStartTime)) - drawableObject.FadeOut(fadeDuration); + if (!NoHitCircles.Value) + drawableObject.FadeOut(fadeDuration); break; case DrawableSliderRepeat sliderRepeat: using (drawableObject.BeginAbsoluteSequence(fadeStartTime)) // only apply to circle piece – reverse arrow is not affected by hidden. - sliderRepeat.CirclePiece.FadeOut(fadeDuration); + if (!NoHitCircles.Value) + sliderRepeat.CirclePiece.FadeOut(fadeDuration); break; @@ -82,7 +90,8 @@ namespace osu.Game.Rulesets.Osu.Mods if (increaseVisibility) { // only fade the circle piece (not the approach circle) for the increased visibility object. - fadeTarget = circle.CirclePiece; + if (!NoHitCircles.Value) + fadeTarget = circle.CirclePiece; } else { @@ -92,18 +101,21 @@ namespace osu.Game.Rulesets.Osu.Mods } using (drawableObject.BeginAbsoluteSequence(fadeStartTime)) - fadeTarget.FadeOut(fadeDuration); + if (!NoHitCircles.Value) + fadeTarget.FadeOut(fadeDuration); break; case DrawableSlider slider: using (slider.BeginAbsoluteSequence(fadeStartTime)) - slider.Body.FadeOut(fadeDuration, Easing.Out); + if (!NoHitCircles.Value) + slider.Body.FadeOut(fadeDuration, Easing.Out); break; case DrawableSliderTick sliderTick: using (sliderTick.BeginAbsoluteSequence(fadeStartTime)) - sliderTick.FadeOut(fadeDuration); + if (!NoHitCircles.Value) + sliderTick.FadeOut(fadeDuration); break; From d1b720af167141c44566ceb1ce24b5eed04677fb Mon Sep 17 00:00:00 2001 From: Roxie Wattz Date: Sun, 24 Oct 2021 21:51:12 -0700 Subject: [PATCH 060/260] Add Ouendan 2 reference --- osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs index 59d6769e2c..a57b22451d 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs @@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Osu.Mods { public class OsuModHidden : ModHidden, IHidesApproachCircles { - [SettingSource("Don't fade out hit circles", "This makes the hidden mod only remove approach circles.")] + [SettingSource("Don't fade out hit circles", "This makes the hidden mod only remove approach circles, similar to Ouendan 2's Easy difficulties.")] public Bindable NoHitCircles { get; } = new BindableBool(false); public override string Description => @"Play with no approach circles and fading circles/sliders."; From 03896fd2ebf219489deb289942c6f0c99e568304 Mon Sep 17 00:00:00 2001 From: Roxie Wattz Date: Sun, 24 Oct 2021 22:17:55 -0700 Subject: [PATCH 061/260] Added test for Ouendan 2 style Hidden mod --- .../Mods/TestSceneOsuModHidden.cs | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModHidden.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModHidden.cs index ed9da36b05..f25df90737 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModHidden.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModHidden.cs @@ -117,6 +117,42 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods PassCondition = checkSomeHit }); + [Test] + public void OuendanTwoHidden() => CreateModTest(new ModTestData + { + Mod = new OsuModHidden { NoHitCircles = { Value = true } }, + Autoplay = true, + Beatmap = new Beatmap + { + HitObjects = new List + { + new HitCircle + { + StartTime = 1000, + Position = new Vector2(206,142) + }, + new HitCircle + { + StartTime = 2000, + Position = new Vector2(306,142) + }, + new Slider + { + StartTime = 3000, + Position = new Vector2(156,242), + Path = new SliderPath(PathType.Linear, new[] { Vector2.Zero, new Vector2(200,0), }) + }, + new Spinner + { + Position = new Vector2(256,192), + StartTime = 7000, + EndTime = 9000 + } + } + }, + PassCondition = checkSomeHit + }); + private bool checkSomeHit() => Player.ScoreProcessor.JudgedHits >= 4; private bool objectWithIncreasedVisibilityHasIndex(int index) From 19312435d0b53de9e2619aedc75beef9005b5b8e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 25 Oct 2021 13:47:12 +0900 Subject: [PATCH 062/260] Add ability to make test `APIBeatmaps` from test scenes Allow tests to create a sample `APIBeatmapSet` One step towards removing calls to `ToBeatmap` / `ToBeatmapSet`. --- .../Online/TestSceneBeatmapListingOverlay.cs | 26 +++------- .../API/Requests/Responses/APIBeatmapSet.cs | 6 +-- osu.Game/Tests/Visual/OsuTestScene.cs | 51 +++++++++++++++++++ 3 files changed, 60 insertions(+), 23 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs index 963809ebe1..7042f1e4fe 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs @@ -7,14 +7,12 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Graphics.Containers; using osu.Framework.Testing; -using osu.Game.Beatmaps; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays; using osu.Game.Overlays.BeatmapListing; -using osu.Game.Rulesets; using osu.Game.Scoring; using osu.Game.Users; using osuTK.Input; @@ -92,7 +90,7 @@ namespace osu.Game.Tests.Visual.Online { AddAssert("is visible", () => overlay.State.Value == Visibility.Visible); - AddStep("show many results", () => fetchFor(Enumerable.Repeat(CreateBeatmap(Ruleset.Value).BeatmapInfo.BeatmapSet, 100).ToArray())); + AddStep("show many results", () => fetchFor(Enumerable.Repeat(CreateAPIBeatmapSet(Ruleset.Value), 10).ToArray())); AddUntilStep("placeholder hidden", () => !overlay.ChildrenOfType().Any(d => d.IsPresent)); @@ -114,7 +112,7 @@ namespace osu.Game.Tests.Visual.Online AddStep("fetch for 0 beatmaps", () => fetchFor()); AddUntilStep("placeholder shown", () => overlay.ChildrenOfType().SingleOrDefault()?.IsPresent == true); - AddStep("fetch for 1 beatmap", () => fetchFor(CreateBeatmap(Ruleset.Value).BeatmapInfo.BeatmapSet)); + AddStep("fetch for 1 beatmap", () => fetchFor(CreateAPIBeatmapSet(Ruleset.Value))); AddUntilStep("placeholder hidden", () => !overlay.ChildrenOfType().Any(d => d.IsPresent)); AddStep("fetch for 0 beatmaps", () => fetchFor()); @@ -188,7 +186,7 @@ namespace osu.Game.Tests.Visual.Online [Test] public void TestUserWithoutSupporterUsesSupporterOnlyFiltersWithResults() { - AddStep("fetch for 1 beatmap", () => fetchFor(CreateBeatmap(Ruleset.Value).BeatmapInfo.BeatmapSet)); + AddStep("fetch for 1 beatmap", () => fetchFor(CreateAPIBeatmapSet(Ruleset.Value))); AddStep("set dummy as non-supporter", () => ((DummyAPIAccess)API).LocalUser.Value.IsSupporter = false); // only Rank Achieved filter @@ -218,7 +216,7 @@ namespace osu.Game.Tests.Visual.Online [Test] public void TestUserWithSupporterUsesSupporterOnlyFiltersWithResults() { - AddStep("fetch for 1 beatmap", () => fetchFor(CreateBeatmap(Ruleset.Value).BeatmapInfo.BeatmapSet)); + AddStep("fetch for 1 beatmap", () => fetchFor(CreateAPIBeatmapSet(Ruleset.Value))); AddStep("set dummy as supporter", () => ((DummyAPIAccess)API).LocalUser.Value.IsSupporter = true); // only Rank Achieved filter @@ -247,10 +245,10 @@ namespace osu.Game.Tests.Visual.Online private static int searchCount; - private void fetchFor(params BeatmapSetInfo[] beatmaps) + private void fetchFor(params APIBeatmapSet[] beatmaps) { setsForResponse.Clear(); - setsForResponse.AddRange(beatmaps.Select(b => new TestAPIBeatmapSet(b))); + setsForResponse.AddRange(beatmaps); // trigger arbitrary change for fetching. searchControl.Query.Value = $"search {searchCount++}"; @@ -286,17 +284,5 @@ namespace osu.Game.Tests.Visual.Online !overlay.ChildrenOfType().Any(d => d.IsPresent) && !overlay.ChildrenOfType().Any(d => d.IsPresent)); } - - private class TestAPIBeatmapSet : APIBeatmapSet - { - private readonly BeatmapSetInfo beatmapSet; - - public TestAPIBeatmapSet(BeatmapSetInfo beatmapSet) - { - this.beatmapSet = beatmapSet; - } - - public override BeatmapSetInfo ToBeatmapSet(RulesetStore rulesets) => beatmapSet; - } } } diff --git a/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs b/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs index 24d0e09649..7fbdd47394 100644 --- a/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs +++ b/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs @@ -119,7 +119,7 @@ namespace osu.Game.Online.API.Requests.Responses public string Tags { get; set; } = string.Empty; [JsonProperty(@"beatmaps")] - private IEnumerable beatmaps { get; set; } = Array.Empty(); + public IEnumerable Beatmaps { get; set; } = Array.Empty(); public virtual BeatmapSetInfo ToBeatmapSet(RulesetStore rulesets) { @@ -132,7 +132,7 @@ namespace osu.Game.Online.API.Requests.Responses OnlineInfo = this }; - beatmapSet.Beatmaps = beatmaps.Select(b => + beatmapSet.Beatmaps = Beatmaps.Select(b => { var beatmap = b.ToBeatmapInfo(rulesets); beatmap.BeatmapSet = beatmapSet; @@ -157,7 +157,7 @@ namespace osu.Game.Online.API.Requests.Responses #region Implementation of IBeatmapSetInfo - IEnumerable IBeatmapSetInfo.Beatmaps => beatmaps; + IEnumerable IBeatmapSetInfo.Beatmaps => Beatmaps; IBeatmapMetadataInfo IBeatmapSetInfo.Metadata => metadata; diff --git a/osu.Game/Tests/Visual/OsuTestScene.cs b/osu.Game/Tests/Visual/OsuTestScene.cs index 03434961ea..90e85f7716 100644 --- a/osu.Game/Tests/Visual/OsuTestScene.cs +++ b/osu.Game/Tests/Visual/OsuTestScene.cs @@ -20,6 +20,7 @@ using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Database; using osu.Game.Online.API; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; @@ -174,6 +175,56 @@ namespace osu.Game.Tests.Visual protected virtual IBeatmap CreateBeatmap(RulesetInfo ruleset) => new TestBeatmap(ruleset); + protected APIBeatmapSet CreateAPIBeatmapSet(RulesetInfo ruleset) + { + var beatmap = CreateBeatmap(ruleset).BeatmapInfo; + + return new APIBeatmapSet + { + Covers = beatmap.BeatmapSet.Covers, + OnlineID = beatmap.BeatmapSet.OnlineID, + Status = beatmap.BeatmapSet.Status, + Preview = beatmap.BeatmapSet.Preview, + HasFavourited = beatmap.BeatmapSet.HasFavourited, + PlayCount = beatmap.BeatmapSet.PlayCount, + FavouriteCount = beatmap.BeatmapSet.FavouriteCount, + BPM = beatmap.BeatmapSet.BPM, + HasExplicitContent = beatmap.BeatmapSet.HasExplicitContent, + HasVideo = beatmap.BeatmapSet.HasVideo, + HasStoryboard = beatmap.BeatmapSet.HasStoryboard, + Submitted = beatmap.BeatmapSet.Submitted, + Ranked = beatmap.BeatmapSet.Ranked, + LastUpdated = beatmap.BeatmapSet.LastUpdated, + TrackId = beatmap.BeatmapSet.TrackId, + Title = beatmap.BeatmapSet.Metadata.Title, + TitleUnicode = beatmap.BeatmapSet.Metadata.TitleUnicode, + Artist = beatmap.BeatmapSet.Metadata.Artist, + ArtistUnicode = beatmap.BeatmapSet.Metadata.ArtistUnicode, + Author = beatmap.BeatmapSet.Metadata.Author, + AuthorID = beatmap.BeatmapSet.Metadata.AuthorID, + AuthorString = beatmap.BeatmapSet.Metadata.AuthorString, + Availability = beatmap.BeatmapSet.Availability, + Genre = beatmap.BeatmapSet.Genre, + Language = beatmap.BeatmapSet.Language, + Source = beatmap.BeatmapSet.Metadata.Source, + Tags = beatmap.BeatmapSet.Metadata.Tags, + Beatmaps = new[] + { + new APIBeatmap + { + OnlineID = beatmap.OnlineID, + OnlineBeatmapSetID = beatmap.BeatmapSet.OnlineID, + Status = beatmap.Status, + Checksum = beatmap.MD5Hash, + AuthorID = beatmap.Metadata.AuthorID, + RulesetID = beatmap.RulesetID, + StarRating = beatmap.StarDifficulty, + DifficultyName = beatmap.Version, + } + } + }; + } + protected WorkingBeatmap CreateWorkingBeatmap(RulesetInfo ruleset) => CreateWorkingBeatmap(CreateBeatmap(ruleset)); From 94175bfb4a5b675b840ccce8fa1fcf3928e0baf9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 25 Oct 2021 14:25:01 +0900 Subject: [PATCH 063/260] Update `PreviewTrackManager` to use `IBeatmapSetInfo` --- .../Components/TestScenePreviewTrackManager.cs | 4 ++-- osu.Game/Audio/PreviewTrackManager.cs | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/osu.Game.Tests/Visual/Components/TestScenePreviewTrackManager.cs b/osu.Game.Tests/Visual/Components/TestScenePreviewTrackManager.cs index 9a999a4931..89e20043fb 100644 --- a/osu.Game.Tests/Visual/Components/TestScenePreviewTrackManager.cs +++ b/osu.Game.Tests/Visual/Components/TestScenePreviewTrackManager.cs @@ -224,7 +224,7 @@ namespace osu.Game.Tests.Visual.Components public new PreviewTrack CurrentTrack => base.CurrentTrack; - protected override TrackManagerPreviewTrack CreatePreviewTrack(BeatmapSetInfo beatmapSetInfo, ITrackStore trackStore) => new TestPreviewTrack(beatmapSetInfo, trackStore); + protected override TrackManagerPreviewTrack CreatePreviewTrack(IBeatmapSetInfo beatmapSetInfo, ITrackStore trackStore) => new TestPreviewTrack(beatmapSetInfo, trackStore); public override bool UpdateSubTree() { @@ -240,7 +240,7 @@ namespace osu.Game.Tests.Visual.Components public new Track Track => base.Track; - public TestPreviewTrack(BeatmapSetInfo beatmapSetInfo, ITrackStore trackManager) + public TestPreviewTrack(IBeatmapSetInfo beatmapSetInfo, ITrackStore trackManager) : base(beatmapSetInfo, trackManager) { this.trackManager = trackManager; diff --git a/osu.Game/Audio/PreviewTrackManager.cs b/osu.Game/Audio/PreviewTrackManager.cs index 1de9e1561f..26787c362b 100644 --- a/osu.Game/Audio/PreviewTrackManager.cs +++ b/osu.Game/Audio/PreviewTrackManager.cs @@ -43,11 +43,11 @@ namespace osu.Game.Audio } /// - /// Retrieves a for a . + /// Retrieves a for a . /// - /// The to retrieve the preview track for. + /// The to retrieve the preview track for. /// The playable . - public PreviewTrack Get(BeatmapSetInfo beatmapSetInfo) + public PreviewTrack Get(IBeatmapSetInfo beatmapSetInfo) { var track = CreatePreviewTrack(beatmapSetInfo, trackStore); @@ -91,7 +91,7 @@ namespace osu.Game.Audio /// /// Creates the . /// - protected virtual TrackManagerPreviewTrack CreatePreviewTrack(BeatmapSetInfo beatmapSetInfo, ITrackStore trackStore) => + protected virtual TrackManagerPreviewTrack CreatePreviewTrack(IBeatmapSetInfo beatmapSetInfo, ITrackStore trackStore) => new TrackManagerPreviewTrack(beatmapSetInfo, trackStore); public class TrackManagerPreviewTrack : PreviewTrack @@ -99,10 +99,10 @@ namespace osu.Game.Audio [Resolved(canBeNull: true)] public IPreviewTrackOwner Owner { get; private set; } - private readonly BeatmapSetInfo beatmapSetInfo; + private readonly IBeatmapSetInfo beatmapSetInfo; private readonly ITrackStore trackManager; - public TrackManagerPreviewTrack(BeatmapSetInfo beatmapSetInfo, ITrackStore trackManager) + public TrackManagerPreviewTrack(IBeatmapSetInfo beatmapSetInfo, ITrackStore trackManager) { this.beatmapSetInfo = beatmapSetInfo; this.trackManager = trackManager; @@ -114,7 +114,7 @@ namespace osu.Game.Audio Logger.Log($"A {nameof(PreviewTrack)} was created without a containing {nameof(IPreviewTrackOwner)}. An owner should be added for correct behaviour."); } - protected override Track GetTrack() => trackManager.Get($"https://b.ppy.sh/preview/{beatmapSetInfo?.OnlineBeatmapSetID}.mp3"); + protected override Track GetTrack() => trackManager.Get($"https://b.ppy.sh/preview/{beatmapSetInfo?.OnlineID}.mp3"); } private class PreviewTrackStore : AudioCollectionManager, ITrackStore From cb36a23cdc639f57568cc2fca8dcaad47efa8b20 Mon Sep 17 00:00:00 2001 From: Roxie Wattz Date: Sun, 24 Oct 2021 22:52:10 -0700 Subject: [PATCH 064/260] Update osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs Suggested by peppy. Changes name and description of setting Co-authored-by: Dean Herbert --- osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs index a57b22451d..59ac6746a0 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs @@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Osu.Mods { public class OsuModHidden : ModHidden, IHidesApproachCircles { - [SettingSource("Don't fade out hit circles", "This makes the hidden mod only remove approach circles, similar to Ouendan 2's Easy difficulties.")] + [SettingSource("Only fade approach circles", "The main object body will not fade when enabled.")] public Bindable NoHitCircles { get; } = new BindableBool(false); public override string Description => @"Play with no approach circles and fading circles/sliders."; From 045dd94a6effae7aa1e0c13aaf30b42333d7d62b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 25 Oct 2021 14:44:49 +0900 Subject: [PATCH 065/260] Move online metrics out of `BeatmapSetInfo` model --- .../Online/TestSceneBeatmapSetOverlay.cs | 8 ++-- .../TestSceneBeatmapSetOverlayDetails.cs | 6 +-- .../SongSelect/TestSceneBeatmapDetails.cs | 16 ++++++-- osu.Game/Beatmaps/BeatmapInfo.cs | 2 +- osu.Game/Beatmaps/BeatmapSetInfo.cs | 7 ++-- osu.Game/Beatmaps/BeatmapSetMetrics.cs | 17 -------- osu.Game/Beatmaps/IBeatmapOnlineInfo.cs | 17 ++++++++ osu.Game/Beatmaps/IBeatmapSetOnlineInfo.cs | 5 +++ .../API/Requests/Responses/APIBeatmap.cs | 10 ++--- .../API/Requests/Responses/APIBeatmapSet.cs | 5 +-- osu.Game/Overlays/BeatmapSet/Details.cs | 2 +- osu.Game/Screens/Select/BeatmapDetails.cs | 39 +++++++++---------- .../Screens/Select/Details/UserRatings.cs | 25 ++++++------ 13 files changed, 86 insertions(+), 73 deletions(-) delete mode 100644 osu.Game/Beatmaps/BeatmapSetMetrics.cs create mode 100644 osu.Game/Beatmaps/IBeatmapOnlineInfo.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs index ef89a86e79..da033e324c 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs @@ -73,10 +73,10 @@ namespace osu.Game.Tests.Visual.Online Ranked = DateTime.Now, BPM = 111, HasVideo = true, + Ratings = Enumerable.Range(0, 11).ToArray(), HasStoryboard = true, Covers = new BeatmapSetOnlineCovers(), }, - Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() }, Beatmaps = new List { new BeatmapInfo @@ -153,8 +153,8 @@ namespace osu.Game.Tests.Visual.Online Covers = new BeatmapSetOnlineCovers(), Language = new BeatmapSetOnlineLanguage { Id = 3, Name = "English" }, Genre = new BeatmapSetOnlineGenre { Id = 4, Name = "Rock" }, + Ratings = Enumerable.Range(0, 11).ToArray(), }, - Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() }, Beatmaps = new List { new BeatmapInfo @@ -228,8 +228,8 @@ namespace osu.Game.Tests.Visual.Online OnlineInfo = new APIBeatmapSet { Covers = new BeatmapSetOnlineCovers(), + Ratings = Enumerable.Range(0, 11).ToArray(), }, - Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() }, Beatmaps = beatmaps }); }); @@ -316,8 +316,8 @@ namespace osu.Game.Tests.Visual.Online HasVideo = true, HasStoryboard = true, Covers = new BeatmapSetOnlineCovers(), + Ratings = Enumerable.Range(0, 11).ToArray(), }, - Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() }, Beatmaps = beatmaps, }; } diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlayDetails.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlayDetails.cs index c15c9f44e4..f16d5aaa23 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlayDetails.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlayDetails.cs @@ -39,14 +39,13 @@ namespace osu.Game.Tests.Visual.Online var secondSet = createSet(); AddStep("set first set", () => details.BeatmapSet = firstSet); - AddAssert("ratings set", () => details.Ratings.Metrics == firstSet.Metrics); + AddAssert("ratings set", () => details.Ratings.Ratings == firstSet.Ratings); AddStep("set second set", () => details.BeatmapSet = secondSet); - AddAssert("ratings set", () => details.Ratings.Metrics == secondSet.Metrics); + AddAssert("ratings set", () => details.Ratings.Ratings == secondSet.Ratings); static BeatmapSetInfo createSet() => new BeatmapSetInfo { - Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).Select(_ => RNG.Next(10)).ToArray() }, Beatmaps = new List { new BeatmapInfo @@ -60,6 +59,7 @@ namespace osu.Game.Tests.Visual.Online }, OnlineInfo = new APIBeatmapSet { + Ratings = Enumerable.Range(0, 11).Select(_ => RNG.Next(10)).ToArray(), Status = BeatmapSetOnlineStatus.Ranked } }; diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetails.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetails.cs index d5b4fb9a80..6b508878bf 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetails.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetails.cs @@ -6,6 +6,7 @@ using NUnit.Framework; using osu.Framework.Graphics; using osu.Game.Beatmaps; using osu.Game.Online.API; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Screens.Select; namespace osu.Game.Tests.Visual.SongSelect @@ -34,7 +35,10 @@ namespace osu.Game.Tests.Visual.SongSelect { BeatmapSet = new BeatmapSetInfo { - Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() } + OnlineInfo = new APIBeatmapSet + { + Ratings = Enumerable.Range(0, 11).ToArray(), + } }, Version = "All Metrics", Metadata = new BeatmapMetadata @@ -65,7 +69,10 @@ namespace osu.Game.Tests.Visual.SongSelect { BeatmapSet = new BeatmapSetInfo { - Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() } + OnlineInfo = new APIBeatmapSet + { + Ratings = Enumerable.Range(0, 11).ToArray(), + } }, Version = "All Metrics", Metadata = new BeatmapMetadata @@ -95,7 +102,10 @@ namespace osu.Game.Tests.Visual.SongSelect { BeatmapSet = new BeatmapSetInfo { - Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() } + OnlineInfo = new APIBeatmapSet + { + Ratings = Enumerable.Range(0, 11).ToArray(), + } }, Version = "Only Ratings", Metadata = new BeatmapMetadata diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs index 3bcc00f5de..6da50657a6 100644 --- a/osu.Game/Beatmaps/BeatmapInfo.cs +++ b/osu.Game/Beatmaps/BeatmapInfo.cs @@ -16,7 +16,7 @@ namespace osu.Game.Beatmaps { [ExcludeFromDynamicCompile] [Serializable] - public class BeatmapInfo : IEquatable, IHasPrimaryKey, IBeatmapInfo + public class BeatmapInfo : IEquatable, IHasPrimaryKey, IBeatmapInfo, IBeatmapOnlineInfo { public int ID { get; set; } diff --git a/osu.Game/Beatmaps/BeatmapSetInfo.cs b/osu.Game/Beatmaps/BeatmapSetInfo.cs index 0c032e1482..ae32ad000e 100644 --- a/osu.Game/Beatmaps/BeatmapSetInfo.cs +++ b/osu.Game/Beatmaps/BeatmapSetInfo.cs @@ -38,9 +38,6 @@ namespace osu.Game.Beatmaps [NotMapped] public APIBeatmapSet OnlineInfo { get; set; } - [NotMapped] - public BeatmapSetMetrics Metrics { get; set; } - /// /// The maximum star difficulty of all beatmaps in this set. /// @@ -172,6 +169,10 @@ namespace osu.Game.Beatmaps [JsonIgnore] public int? TrackId => OnlineInfo?.TrackId; + [NotMapped] + [JsonIgnore] + public int[] Ratings => OnlineInfo?.Ratings; + #endregion } } diff --git a/osu.Game/Beatmaps/BeatmapSetMetrics.cs b/osu.Game/Beatmaps/BeatmapSetMetrics.cs deleted file mode 100644 index 51c5de19a6..0000000000 --- a/osu.Game/Beatmaps/BeatmapSetMetrics.cs +++ /dev/null @@ -1,17 +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 Newtonsoft.Json; - -namespace osu.Game.Beatmaps -{ - public class BeatmapSetMetrics - { - /// - /// Total vote counts of user ratings on a scale of 0..10 where 0 is unused (probably will be fixed at API?). - /// - [JsonProperty("ratings")] - public int[] Ratings { get; set; } = Array.Empty(); - } -} diff --git a/osu.Game/Beatmaps/IBeatmapOnlineInfo.cs b/osu.Game/Beatmaps/IBeatmapOnlineInfo.cs new file mode 100644 index 0000000000..4898e45c56 --- /dev/null +++ b/osu.Game/Beatmaps/IBeatmapOnlineInfo.cs @@ -0,0 +1,17 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +#nullable enable + +namespace osu.Game.Beatmaps +{ + /// + /// Beatmap info retrieved for previewing locally. + /// + public interface IBeatmapOnlineInfo + { + int? MaxCombo { get; } + + BeatmapMetrics? Metrics { get; } + } +} diff --git a/osu.Game/Beatmaps/IBeatmapSetOnlineInfo.cs b/osu.Game/Beatmaps/IBeatmapSetOnlineInfo.cs index 1d2bb46bde..6def6ec21d 100644 --- a/osu.Game/Beatmaps/IBeatmapSetOnlineInfo.cs +++ b/osu.Game/Beatmaps/IBeatmapSetOnlineInfo.cs @@ -97,5 +97,10 @@ namespace osu.Game.Beatmaps /// Non-null only if the track is linked to a featured artist track entry. /// int? TrackId { get; } + + /// + /// Total vote counts of user ratings on a scale of 0..10 where 0 is unused (probably will be fixed at API?). + /// + int[]? Ratings { get; } } } diff --git a/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs b/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs index 6e43ac5504..6a05811bba 100644 --- a/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs +++ b/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs @@ -10,7 +10,7 @@ using osu.Game.Rulesets; namespace osu.Game.Online.API.Requests.Responses { - public class APIBeatmap : IBeatmapInfo + public class APIBeatmap : IBeatmapInfo, IBeatmapOnlineInfo { [JsonProperty(@"id")] public int OnlineID { get; set; } @@ -69,10 +69,10 @@ namespace osu.Game.Online.API.Requests.Responses public string DifficultyName { get; set; } = string.Empty; [JsonProperty(@"failtimes")] - private BeatmapMetrics? metrics { get; set; } + public BeatmapMetrics? Metrics { get; set; } [JsonProperty(@"max_combo")] - private int? maxCombo { get; set; } + public int? MaxCombo { get; set; } public virtual BeatmapInfo ToBeatmapInfo(RulesetStore rulesets) { @@ -90,8 +90,8 @@ namespace osu.Game.Online.API.Requests.Responses Status = Status, MD5Hash = Checksum, BeatmapSet = set, - Metrics = metrics, - MaxCombo = maxCombo, + Metrics = Metrics, + MaxCombo = MaxCombo, BaseDifficulty = new BeatmapDifficulty { DrainRate = drainRate, diff --git a/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs b/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs index 24d0e09649..17b1e9d3fe 100644 --- a/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs +++ b/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs @@ -58,8 +58,8 @@ namespace osu.Game.Online.API.Requests.Responses [JsonProperty(@"last_updated")] public DateTimeOffset? LastUpdated { get; set; } - [JsonProperty(@"ratings")] - private int[] ratings { get; set; } = Array.Empty(); + [JsonProperty("ratings")] + public int[] Ratings { get; set; } = Array.Empty(); [JsonProperty(@"track_id")] public int? TrackId { get; set; } @@ -128,7 +128,6 @@ namespace osu.Game.Online.API.Requests.Responses OnlineBeatmapSetID = OnlineID, Metadata = metadata, Status = Status, - Metrics = new BeatmapSetMetrics { Ratings = ratings }, OnlineInfo = this }; diff --git a/osu.Game/Overlays/BeatmapSet/Details.cs b/osu.Game/Overlays/BeatmapSet/Details.cs index 92361ae4f8..d6720e5f35 100644 --- a/osu.Game/Overlays/BeatmapSet/Details.cs +++ b/osu.Game/Overlays/BeatmapSet/Details.cs @@ -52,7 +52,7 @@ namespace osu.Game.Overlays.BeatmapSet private void updateDisplay() { - Ratings.Metrics = BeatmapSet?.Metrics; + Ratings.Ratings = BeatmapSet?.Ratings; ratingBox.Alpha = BeatmapSet?.OnlineInfo?.Status > 0 ? 1 : 0; } diff --git a/osu.Game/Screens/Select/BeatmapDetails.cs b/osu.Game/Screens/Select/BeatmapDetails.cs index 6ace92370c..8dbe438ab5 100644 --- a/osu.Game/Screens/Select/BeatmapDetails.cs +++ b/osu.Game/Screens/Select/BeatmapDetails.cs @@ -29,7 +29,7 @@ namespace osu.Game.Screens.Select private const float transition_duration = 250; private readonly AdvancedStats advanced; - private readonly UserRatings ratings; + private readonly UserRatings ratingsDisplay; private readonly MetadataSection description, source, tags; private readonly Container failRetryContainer; private readonly FailRetryGraph failRetryGraph; @@ -43,6 +43,9 @@ namespace osu.Game.Screens.Select private BeatmapInfo beatmapInfo; + private BeatmapMetrics metrics; + private int[] ratings; + public BeatmapInfo BeatmapInfo { get => beatmapInfo; @@ -52,6 +55,9 @@ namespace osu.Game.Screens.Select beatmapInfo = value; + metrics = beatmapInfo?.Metrics; + ratings = beatmapInfo?.BeatmapSet.Ratings; + Scheduler.AddOnce(updateStatistics); } } @@ -110,7 +116,7 @@ namespace osu.Game.Screens.Select RelativeSizeAxes = Axes.X, Height = 134, Padding = new MarginPadding { Horizontal = spacing, Top = spacing }, - Child = ratings = new UserRatings + Child = ratingsDisplay = new UserRatings { RelativeSizeAxes = Axes.Both, }, @@ -176,7 +182,7 @@ namespace osu.Game.Screens.Select tags.Text = BeatmapInfo?.Metadata?.Tags; // metrics may have been previously fetched - if (BeatmapInfo?.BeatmapSet?.Metrics != null && BeatmapInfo?.Metrics != null) + if (ratings != null && metrics != null) { updateMetrics(); return; @@ -201,14 +207,8 @@ namespace osu.Game.Screens.Select // the beatmap has been changed since we started the lookup. return; - var b = res.ToBeatmapInfo(rulesets); - - if (requestedBeatmap.BeatmapSet == null) - requestedBeatmap.BeatmapSet = b.BeatmapSet; - else - requestedBeatmap.BeatmapSet.Metrics = b.BeatmapSet.Metrics; - - requestedBeatmap.Metrics = b.Metrics; + ratings = res.BeatmapSet?.Ratings; + metrics = res.Metrics; updateMetrics(); }); @@ -232,24 +232,23 @@ namespace osu.Game.Screens.Select private void updateMetrics() { - var hasRatings = beatmapInfo?.BeatmapSet?.Metrics?.Ratings?.Any() ?? false; - var hasRetriesFails = (beatmapInfo?.Metrics?.Retries?.Any() ?? false) || (beatmapInfo?.Metrics?.Fails?.Any() ?? false); + var hasMetrics = (metrics?.Retries?.Any() ?? false) || (metrics?.Fails?.Any() ?? false); - if (hasRatings) + if (ratings?.Any() ?? false) { - ratings.Metrics = beatmapInfo.BeatmapSet.Metrics; - ratings.FadeIn(transition_duration); + ratingsDisplay.Ratings = ratings; + ratingsDisplay.FadeIn(transition_duration); } else { // loading or just has no data server-side. - ratings.Metrics = new BeatmapSetMetrics { Ratings = new int[10] }; - ratings.FadeTo(0.25f, transition_duration); + ratingsDisplay.Ratings = new int[10]; + ratingsDisplay.FadeTo(0.25f, transition_duration); } - if (hasRetriesFails) + if (hasMetrics) { - failRetryGraph.Metrics = beatmapInfo.Metrics; + failRetryGraph.Metrics = metrics; failRetryContainer.FadeIn(transition_duration); } else diff --git a/osu.Game/Screens/Select/Details/UserRatings.cs b/osu.Game/Screens/Select/Details/UserRatings.cs index eabc476db9..aa316d6e40 100644 --- a/osu.Game/Screens/Select/Details/UserRatings.cs +++ b/osu.Game/Screens/Select/Details/UserRatings.cs @@ -1,15 +1,14 @@ // 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.Extensions.LocalisationExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; -using System.Linq; -using osu.Framework.Extensions.LocalisationExtensions; -using osu.Game.Beatmaps; using osu.Game.Resources.Localisation.Web; namespace osu.Game.Screens.Select.Details @@ -22,20 +21,20 @@ namespace osu.Game.Screens.Select.Details private readonly Container graphContainer; private readonly BarGraph graph; - private BeatmapSetMetrics metrics; + private int[] ratings; - public BeatmapSetMetrics Metrics + public int[] Ratings { - get => metrics; + get => ratings; set { - if (value == metrics) return; + if (value == ratings) return; - metrics = value; + ratings = value; const int rating_range = 10; - if (metrics == null) + if (ratings == null) { negativeRatings.Text = 0.ToLocalisableString(@"N0"); positiveRatings.Text = 0.ToLocalisableString(@"N0"); @@ -44,15 +43,15 @@ namespace osu.Game.Screens.Select.Details } else { - var ratings = Metrics.Ratings.Skip(1).Take(rating_range); // adjust for API returning weird empty data at 0. + var usableRange = Ratings.Skip(1).Take(rating_range); // adjust for API returning weird empty data at 0. - var negativeCount = ratings.Take(rating_range / 2).Sum(); - var totalCount = ratings.Sum(); + var negativeCount = usableRange.Take(rating_range / 2).Sum(); + var totalCount = usableRange.Sum(); negativeRatings.Text = negativeCount.ToLocalisableString(@"N0"); positiveRatings.Text = (totalCount - negativeCount).ToLocalisableString(@"N0"); ratingsBar.Length = totalCount == 0 ? 0 : (float)negativeCount / totalCount; - graph.Values = ratings.Take(rating_range).Select(r => (float)r); + graph.Values = usableRange.Take(rating_range).Select(r => (float)r); } } } From 60df0151c024bdc47bf1b4b111d521bda184bf5c Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 7 Jul 2021 20:24:05 +0900 Subject: [PATCH 066/260] Add very basic distance snap grid for catch editor --- .../Editor/TestSceneCatchDistanceSnapGrid.cs | 91 +++++++++++++++ .../Edit/CatchDistanceSnapGrid.cs | 107 ++++++++++++++++++ 2 files changed, 198 insertions(+) create mode 100644 osu.Game.Rulesets.Catch.Tests/Editor/TestSceneCatchDistanceSnapGrid.cs create mode 100644 osu.Game.Rulesets.Catch/Edit/CatchDistanceSnapGrid.cs diff --git a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneCatchDistanceSnapGrid.cs b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneCatchDistanceSnapGrid.cs new file mode 100644 index 0000000000..2be0b7e9b2 --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneCatchDistanceSnapGrid.cs @@ -0,0 +1,91 @@ +// 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.Input.Events; +using osu.Framework.Timing; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Catch.Edit; +using osu.Game.Rulesets.Catch.Edit.Blueprints.Components; +using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Catch.UI; +using osu.Game.Rulesets.UI; +using osu.Game.Rulesets.UI.Scrolling; +using osu.Game.Tests.Visual; +using osuTK; + +namespace osu.Game.Rulesets.Catch.Tests.Editor +{ + public class TestSceneCatchDistanceSnapGrid : OsuManualInputManagerTestScene + { + private readonly ManualClock manualClock = new ManualClock(); + + [Cached(typeof(Playfield))] + private readonly CatchPlayfield playfield; + + private ScrollingHitObjectContainer hitObjectContainer => playfield.HitObjectContainer; + + private readonly CatchDistanceSnapGrid distanceGrid; + + private readonly FruitOutline fruitOutline; + + private readonly Fruit fruit = new Fruit(); + + public TestSceneCatchDistanceSnapGrid() + { + Child = new Container + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Y, + Width = 500, + + Children = new Drawable[] + { + new ScrollingTestContainer(ScrollingDirection.Down) + { + RelativeSizeAxes = Axes.Both, + Child = playfield = new CatchPlayfield(new BeatmapDifficulty()) + { + RelativeSizeAxes = Axes.Both, + Clock = new FramedClock(manualClock) + } + }, + distanceGrid = new CatchDistanceSnapGrid(new double[] { 0, -1, 1 }), + fruitOutline = new FruitOutline() + }, + }; + } + + protected override void Update() + { + base.Update(); + + distanceGrid.StartTime = 100; + distanceGrid.StartX = 250; + + Vector2 screenSpacePosition = InputManager.CurrentState.Mouse.Position; + + var result = distanceGrid.GetSnappedPosition(screenSpacePosition); + + if (result != null) + { + fruit.OriginalX = hitObjectContainer.ToLocalSpace(result.ScreenSpacePosition).X; + + if (result.Time != null) + fruit.StartTime = result.Time.Value; + } + + fruitOutline.Position = CatchHitObjectUtils.GetStartPosition(hitObjectContainer, fruit); + fruitOutline.UpdateFrom(fruit); + } + + protected override bool OnScroll(ScrollEvent e) + { + manualClock.CurrentTime -= e.ScrollDelta.Y * 50; + return true; + } + } +} diff --git a/osu.Game.Rulesets.Catch/Edit/CatchDistanceSnapGrid.cs b/osu.Game.Rulesets.Catch/Edit/CatchDistanceSnapGrid.cs new file mode 100644 index 0000000000..5808b332b0 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Edit/CatchDistanceSnapGrid.cs @@ -0,0 +1,107 @@ +// 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 JetBrains.Annotations; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Lines; +using osu.Framework.Graphics.Primitives; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.UI; +using osu.Game.Rulesets.UI.Scrolling; +using osuTK; + +namespace osu.Game.Rulesets.Catch.Edit +{ + public class CatchDistanceSnapGrid : CompositeDrawable + { + public double StartTime { get; set; } + + public float StartX { get; set; } + + private readonly double[] velocities; + + private readonly List verticalPaths = new List(); + + private readonly List> verticalLineVertices = new List>(); + + [Resolved] + private Playfield playfield { get; set; } + + private ScrollingHitObjectContainer hitObjectContainer => (ScrollingHitObjectContainer)playfield.HitObjectContainer; + + public CatchDistanceSnapGrid(double[] velocities) + { + RelativeSizeAxes = Axes.Both; + Anchor = Anchor.BottomLeft; + + this.velocities = velocities; + + for (int i = 0; i < velocities.Length; i++) + { + verticalPaths.Add(new SmoothPath + { + PathRadius = 2, + Alpha = 0.5f, + }); + + verticalLineVertices.Add(new List { Vector2.Zero, Vector2.Zero }); + } + + AddRangeInternal(verticalPaths); + } + + protected override void Update() + { + base.Update(); + + float startY = hitObjectContainer.PositionAtTime(StartTime); + + for (int i = 0; i < velocities.Length; i++) + { + double velocity = velocities[i]; + + // The line ends at the top of the screen. + double endTime = hitObjectContainer.TimeAtPosition(-hitObjectContainer.DrawHeight, hitObjectContainer.Time.Current); + + float x = (float)((endTime - StartTime) * velocity); + float y = hitObjectContainer.PositionAtTime(endTime, StartTime); + + List lineVertices = verticalLineVertices[i]; + lineVertices[0] = new Vector2(StartX, startY); + lineVertices[1] = lineVertices[0] + new Vector2(x, y); + + var verticalPath = verticalPaths[i]; + verticalPath.Vertices = verticalLineVertices[i]; + verticalPath.OriginPosition = verticalPath.PositionInBoundingBox(Vector2.Zero); + } + } + + [CanBeNull] + public SnapResult GetSnappedPosition(Vector2 screenSpacePosition) + { + double time = hitObjectContainer.TimeAtScreenSpacePosition(screenSpacePosition); + + return enumerateSnappingCandidates(time) + .OrderBy(pos => Vector2.DistanceSquared(screenSpacePosition, pos.ScreenSpacePosition)) + .FirstOrDefault(); + } + + private IEnumerable enumerateSnappingCandidates(double time) + { + float y = hitObjectContainer.PositionAtTime(time); + + foreach (double velocity in velocities) + { + float x = (float)(StartX + (time - StartTime) * velocity); + Vector2 screenSpacePosition = hitObjectContainer.ToScreenSpace(new Vector2(x, y + hitObjectContainer.DrawHeight)); + yield return new SnapResult(screenSpacePosition, time); + } + } + + protected override bool ComputeIsMaskedAway(RectangleF maskingBounds) => false; + } +} From 20baae90945da9ad276e2277d5497172e7276baf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 25 Oct 2021 15:12:11 +0900 Subject: [PATCH 067/260] Move online metrics out of `BeatmapInfo` model --- .../Online/TestSceneBeatmapSetOverlay.cs | 48 ++++++++++--------- .../TestSceneBeatmapSetOverlayDetails.cs | 11 +++-- .../TestSceneBeatmapSetOverlaySuccessRate.cs | 29 +++++++---- .../SongSelect/TestSceneBeatmapDetails.cs | 33 ++++++++----- osu.Game/Beatmaps/BeatmapInfo.cs | 24 ++++++++-- osu.Game/Beatmaps/BeatmapOnlineInfo.cs | 31 ------------ osu.Game/Beatmaps/IBeatmapOnlineInfo.cs | 23 +++++++++ .../API/Requests/Responses/APIBeatmap.cs | 17 ++----- osu.Game/Screens/Select/BeatmapDetails.cs | 4 +- osu.Game/Tests/Beatmaps/TestBeatmap.cs | 2 +- 10 files changed, 124 insertions(+), 98 deletions(-) delete mode 100644 osu.Game/Beatmaps/BeatmapOnlineInfo.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs index da033e324c..3b0c2f5a7a 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs @@ -92,17 +92,17 @@ namespace osu.Game.Tests.Visual.Online OverallDifficulty = 4.5f, ApproachRate = 6, }, - OnlineInfo = new BeatmapOnlineInfo + OnlineInfo = new APIBeatmap { CircleCount = 111, SliderCount = 12, PlayCount = 222, PassCount = 21, - }, - Metrics = new BeatmapMetrics - { - Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(), - Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(), + Metrics = new BeatmapMetrics + { + Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(), + Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(), + }, }, }, }, @@ -170,17 +170,17 @@ namespace osu.Game.Tests.Visual.Online OverallDifficulty = 7, ApproachRate = 6, }, - OnlineInfo = new BeatmapOnlineInfo + OnlineInfo = new APIBeatmap { CircleCount = 123, SliderCount = 45, PlayCount = 567, PassCount = 89, - }, - Metrics = new BeatmapMetrics - { - Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(), - Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(), + Metrics = new BeatmapMetrics + { + Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(), + Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(), + }, }, }, }, @@ -204,12 +204,14 @@ namespace osu.Game.Tests.Visual.Online Version = ruleset.Name, Ruleset = ruleset, BaseDifficulty = new BeatmapDifficulty(), - OnlineInfo = new BeatmapOnlineInfo(), - Metrics = new BeatmapMetrics + OnlineInfo = new APIBeatmap { - Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(), - Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(), - }, + Metrics = new BeatmapMetrics + { + Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(), + Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(), + }, + } }); } @@ -288,12 +290,14 @@ namespace osu.Game.Tests.Visual.Online { OverallDifficulty = 3.5f, }, - OnlineInfo = new BeatmapOnlineInfo(), - Metrics = new BeatmapMetrics + OnlineInfo = new APIBeatmap { - Fails = Enumerable.Range(1, 100).Select(j => j % 12 - 6).ToArray(), - Retries = Enumerable.Range(-2, 100).Select(j => j % 12 - 6).ToArray(), - }, + Metrics = new BeatmapMetrics + { + Fails = Enumerable.Range(1, 100).Select(j => j % 12 - 6).ToArray(), + Retries = Enumerable.Range(-2, 100).Select(j => j % 12 - 6).ToArray(), + }, + } }); } diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlayDetails.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlayDetails.cs index f16d5aaa23..078e50e0d9 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlayDetails.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlayDetails.cs @@ -50,11 +50,14 @@ namespace osu.Game.Tests.Visual.Online { new BeatmapInfo { - Metrics = new BeatmapMetrics + OnlineInfo = new APIBeatmap { - Fails = Enumerable.Range(1, 100).Select(_ => RNG.Next(10)).ToArray(), - Retries = Enumerable.Range(-2, 100).Select(_ => RNG.Next(10)).ToArray(), - }, + Metrics = new BeatmapMetrics + { + Fails = Enumerable.Range(1, 100).Select(_ => RNG.Next(10)).ToArray(), + Retries = Enumerable.Range(-2, 100).Select(_ => RNG.Next(10)).ToArray(), + }, + } } }, OnlineInfo = new APIBeatmapSet diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlaySuccessRate.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlaySuccessRate.cs index fe8e33f783..84d0b9f868 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlaySuccessRate.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlaySuccessRate.cs @@ -11,6 +11,7 @@ using osu.Framework.Testing; using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Graphics.UserInterface; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays; using osu.Game.Overlays.BeatmapSet; using osu.Game.Screens.Select.Details; @@ -66,10 +67,13 @@ namespace osu.Game.Tests.Visual.Online static BeatmapInfo createBeatmap() => new BeatmapInfo { - Metrics = new BeatmapMetrics + OnlineInfo = new APIBeatmap { - Fails = Enumerable.Range(1, 100).Select(_ => RNG.Next(10)).ToArray(), - Retries = Enumerable.Range(-2, 100).Select(_ => RNG.Next(10)).ToArray(), + Metrics = new BeatmapMetrics + { + Fails = Enumerable.Range(1, 100).Select(_ => RNG.Next(10)).ToArray(), + Retries = Enumerable.Range(-2, 100).Select(_ => RNG.Next(10)).ToArray(), + } } }; } @@ -79,13 +83,16 @@ namespace osu.Game.Tests.Visual.Online { AddStep("set beatmap", () => successRate.BeatmapInfo = new BeatmapInfo { - Metrics = new BeatmapMetrics + OnlineInfo = new APIBeatmap { - Fails = Enumerable.Range(1, 100).ToArray(), + Metrics = new BeatmapMetrics + { + Fails = Enumerable.Range(1, 100).ToArray(), + } } }); - AddAssert("graph max values correct", - () => successRate.ChildrenOfType().All(graph => graph.MaxValue == 100)); + + AddAssert("graph max values correct", () => successRate.ChildrenOfType().All(graph => graph.MaxValue == 100)); } [Test] @@ -93,11 +100,13 @@ namespace osu.Game.Tests.Visual.Online { AddStep("set beatmap", () => successRate.BeatmapInfo = new BeatmapInfo { - Metrics = new BeatmapMetrics() + OnlineInfo = new APIBeatmap + { + Metrics = new BeatmapMetrics(), + } }); - AddAssert("graph max values correct", - () => successRate.ChildrenOfType().All(graph => graph.MaxValue == 0)); + AddAssert("graph max values correct", () => successRate.ChildrenOfType().All(graph => graph.MaxValue == 0)); } private class GraphExposingSuccessRate : SuccessRate diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetails.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetails.cs index 6b508878bf..4bdb8539ba 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetails.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetails.cs @@ -54,11 +54,14 @@ namespace osu.Game.Tests.Visual.SongSelect ApproachRate = 3.5f, }, StarDifficulty = 5.3f, - Metrics = new BeatmapMetrics + OnlineInfo = new APIBeatmap { - Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(), - Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(), - }, + Metrics = new BeatmapMetrics + { + Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(), + Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(), + }, + } }); } @@ -87,11 +90,14 @@ namespace osu.Game.Tests.Visual.SongSelect ApproachRate = 3.5f, }, StarDifficulty = 5.3f, - Metrics = new BeatmapMetrics + OnlineInfo = new APIBeatmap { - Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(), - Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(), - }, + Metrics = new BeatmapMetrics + { + Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(), + Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(), + }, + } }); } @@ -143,11 +149,14 @@ namespace osu.Game.Tests.Visual.SongSelect ApproachRate = 7, }, StarDifficulty = 2.91f, - Metrics = new BeatmapMetrics + OnlineInfo = new APIBeatmap { - Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(), - Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(), - }, + Metrics = new BeatmapMetrics + { + Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(), + Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(), + }, + } }); } diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs index 6da50657a6..fb599853c7 100644 --- a/osu.Game/Beatmaps/BeatmapInfo.cs +++ b/osu.Game/Beatmaps/BeatmapInfo.cs @@ -47,10 +47,7 @@ namespace osu.Game.Beatmaps public BeatmapDifficulty BaseDifficulty { get; set; } [NotMapped] - public BeatmapMetrics Metrics { get; set; } - - [NotMapped] - public BeatmapOnlineInfo OnlineInfo { get; set; } + public IBeatmapOnlineInfo OnlineInfo { get; set; } [NotMapped] public int? MaxCombo { get; set; } @@ -192,5 +189,24 @@ namespace osu.Game.Beatmaps double IBeatmapInfo.StarRating => StarDifficulty; #endregion + + #region Implementation of IBeatmapOnlineInfo + + [JsonIgnore] + public int CircleCount => OnlineInfo.CircleCount; + + [JsonIgnore] + public int SliderCount => OnlineInfo.SliderCount; + + [JsonIgnore] + public int PlayCount => OnlineInfo.PlayCount; + + [JsonIgnore] + public int PassCount => OnlineInfo.PassCount; + + [JsonIgnore] + public BeatmapMetrics Metrics => OnlineInfo.Metrics; + + #endregion } } diff --git a/osu.Game/Beatmaps/BeatmapOnlineInfo.cs b/osu.Game/Beatmaps/BeatmapOnlineInfo.cs deleted file mode 100644 index bfeacd9bfc..0000000000 --- a/osu.Game/Beatmaps/BeatmapOnlineInfo.cs +++ /dev/null @@ -1,31 +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.Beatmaps -{ - /// - /// Beatmap info retrieved for previewing locally without having the beatmap downloaded. - /// - public class BeatmapOnlineInfo - { - /// - /// The amount of circles in this beatmap. - /// - public int CircleCount { get; set; } - - /// - /// The amount of sliders in this beatmap. - /// - public int SliderCount { get; set; } - - /// - /// The amount of plays this beatmap has. - /// - public int PlayCount { get; set; } - - /// - /// The amount of passes this beatmap has. - /// - public int PassCount { get; set; } - } -} diff --git a/osu.Game/Beatmaps/IBeatmapOnlineInfo.cs b/osu.Game/Beatmaps/IBeatmapOnlineInfo.cs index 4898e45c56..9f8e739759 100644 --- a/osu.Game/Beatmaps/IBeatmapOnlineInfo.cs +++ b/osu.Game/Beatmaps/IBeatmapOnlineInfo.cs @@ -10,8 +10,31 @@ namespace osu.Game.Beatmaps ///
public interface IBeatmapOnlineInfo { + /// + /// The max combo of this beatmap. + /// int? MaxCombo { get; } + /// + /// The amount of circles in this beatmap. + /// + public int CircleCount { get; } + + /// + /// The amount of sliders in this beatmap. + /// + public int SliderCount { get; } + + /// + /// The amount of plays this beatmap has. + /// + public int PlayCount { get; } + + /// + /// The amount of passes this beatmap has. + /// + public int PassCount { get; } + BeatmapMetrics? Metrics { get; } } } diff --git a/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs b/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs index 6a05811bba..f0f97cc11c 100644 --- a/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs +++ b/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs @@ -31,10 +31,10 @@ namespace osu.Game.Online.API.Requests.Responses public APIBeatmapSet? BeatmapSet { get; set; } [JsonProperty(@"playcount")] - private int playCount { get; set; } + public int PlayCount { get; set; } [JsonProperty(@"passcount")] - private int passCount { get; set; } + public int PassCount { get; set; } [JsonProperty(@"mode_int")] public int RulesetID { get; set; } @@ -60,10 +60,10 @@ namespace osu.Game.Online.API.Requests.Responses private double lengthInSeconds { get; set; } [JsonProperty(@"count_circles")] - private int circleCount { get; set; } + public int CircleCount { get; set; } [JsonProperty(@"count_sliders")] - private int sliderCount { get; set; } + public int SliderCount { get; set; } [JsonProperty(@"version")] public string DifficultyName { get; set; } = string.Empty; @@ -90,7 +90,6 @@ namespace osu.Game.Online.API.Requests.Responses Status = Status, MD5Hash = Checksum, BeatmapSet = set, - Metrics = Metrics, MaxCombo = MaxCombo, BaseDifficulty = new BeatmapDifficulty { @@ -99,13 +98,7 @@ namespace osu.Game.Online.API.Requests.Responses ApproachRate = approachRate, OverallDifficulty = overallDifficulty, }, - OnlineInfo = new BeatmapOnlineInfo - { - PlayCount = playCount, - PassCount = passCount, - CircleCount = circleCount, - SliderCount = sliderCount, - }, + OnlineInfo = this, }; } diff --git a/osu.Game/Screens/Select/BeatmapDetails.cs b/osu.Game/Screens/Select/BeatmapDetails.cs index 8dbe438ab5..9fab36229f 100644 --- a/osu.Game/Screens/Select/BeatmapDetails.cs +++ b/osu.Game/Screens/Select/BeatmapDetails.cs @@ -55,8 +55,8 @@ namespace osu.Game.Screens.Select beatmapInfo = value; - metrics = beatmapInfo?.Metrics; - ratings = beatmapInfo?.BeatmapSet.Ratings; + metrics = beatmapInfo?.OnlineInfo?.Metrics; + ratings = beatmapInfo?.BeatmapSet?.Ratings; Scheduler.AddOnce(updateStatistics); } diff --git a/osu.Game/Tests/Beatmaps/TestBeatmap.cs b/osu.Game/Tests/Beatmaps/TestBeatmap.cs index d8e72d31a7..15b72ce6e3 100644 --- a/osu.Game/Tests/Beatmaps/TestBeatmap.cs +++ b/osu.Game/Tests/Beatmaps/TestBeatmap.cs @@ -32,7 +32,7 @@ namespace osu.Game.Tests.Beatmaps BeatmapInfo.BeatmapSet.Metadata = BeatmapInfo.Metadata; BeatmapInfo.BeatmapSet.Beatmaps = new List { BeatmapInfo }; BeatmapInfo.Length = 75000; - BeatmapInfo.OnlineInfo = new BeatmapOnlineInfo(); + BeatmapInfo.OnlineInfo = new APIBeatmap(); BeatmapInfo.BeatmapSet.OnlineInfo = new APIBeatmapSet { Status = BeatmapSetOnlineStatus.Ranked, From 4969ab0599a9f60158a40fe51798cf9420e4b9da Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 25 Oct 2021 15:12:21 +0900 Subject: [PATCH 068/260] Add forgotten `JsonIgnore` rules on interface types Just to avoid them getting serialised as part of the "new" .osu serialisation format. --- osu.Game/Beatmaps/BeatmapInfo.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs index fb599853c7..7be58e6472 100644 --- a/osu.Game/Beatmaps/BeatmapInfo.cs +++ b/osu.Game/Beatmaps/BeatmapInfo.cs @@ -181,11 +181,22 @@ namespace osu.Game.Beatmaps #region Implementation of IBeatmapInfo + [JsonIgnore] string IBeatmapInfo.DifficultyName => Version; + + [JsonIgnore] IBeatmapMetadataInfo IBeatmapInfo.Metadata => Metadata; + + [JsonIgnore] IBeatmapDifficultyInfo IBeatmapInfo.Difficulty => BaseDifficulty; + + [JsonIgnore] IBeatmapSetInfo IBeatmapInfo.BeatmapSet => BeatmapSet; + + [JsonIgnore] IRulesetInfo IBeatmapInfo.Ruleset => Ruleset; + + [JsonIgnore] double IBeatmapInfo.StarRating => StarDifficulty; #endregion From 2c308f3008c9c08e5ec94029965a4a601a6701f6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 25 Oct 2021 15:34:41 +0900 Subject: [PATCH 069/260] Rename `BeatmapMetrics` to `APIFailTimes` --- .../Visual/Online/TestSceneBeatmapSetOverlay.cs | 8 ++++---- .../Online/TestSceneBeatmapSetOverlayDetails.cs | 2 +- .../TestSceneBeatmapSetOverlaySuccessRate.cs | 10 +++++----- .../Visual/SongSelect/TestSceneBeatmapDetails.cs | 6 +++--- .../{BeatmapMetrics.cs => APIFailTimes.cs} | 2 +- osu.Game/Beatmaps/BeatmapInfo.cs | 2 +- osu.Game/Beatmaps/IBeatmapOnlineInfo.cs | 2 +- .../Online/API/Requests/Responses/APIBeatmap.cs | 2 +- osu.Game/Overlays/BeatmapSet/SuccessRate.cs | 2 +- osu.Game/Screens/Select/BeatmapDetails.cs | 15 ++++++++------- osu.Game/Screens/Select/Details/FailRetryGraph.cs | 14 +++++++------- 11 files changed, 33 insertions(+), 32 deletions(-) rename osu.Game/Beatmaps/{BeatmapMetrics.cs => APIFailTimes.cs} (96%) diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs index 3b0c2f5a7a..7f9b56e873 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs @@ -98,7 +98,7 @@ namespace osu.Game.Tests.Visual.Online SliderCount = 12, PlayCount = 222, PassCount = 21, - Metrics = new BeatmapMetrics + FailTimes = new APIFailTimes { Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(), Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(), @@ -176,7 +176,7 @@ namespace osu.Game.Tests.Visual.Online SliderCount = 45, PlayCount = 567, PassCount = 89, - Metrics = new BeatmapMetrics + FailTimes = new APIFailTimes { Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(), Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(), @@ -206,7 +206,7 @@ namespace osu.Game.Tests.Visual.Online BaseDifficulty = new BeatmapDifficulty(), OnlineInfo = new APIBeatmap { - Metrics = new BeatmapMetrics + FailTimes = new APIFailTimes { Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(), Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(), @@ -292,7 +292,7 @@ namespace osu.Game.Tests.Visual.Online }, OnlineInfo = new APIBeatmap { - Metrics = new BeatmapMetrics + FailTimes = new APIFailTimes { Fails = Enumerable.Range(1, 100).Select(j => j % 12 - 6).ToArray(), Retries = Enumerable.Range(-2, 100).Select(j => j % 12 - 6).ToArray(), diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlayDetails.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlayDetails.cs index 078e50e0d9..d14f9f47d1 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlayDetails.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlayDetails.cs @@ -52,7 +52,7 @@ namespace osu.Game.Tests.Visual.Online { OnlineInfo = new APIBeatmap { - Metrics = new BeatmapMetrics + FailTimes = new APIFailTimes { Fails = Enumerable.Range(1, 100).Select(_ => RNG.Next(10)).ToArray(), Retries = Enumerable.Range(-2, 100).Select(_ => RNG.Next(10)).ToArray(), diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlaySuccessRate.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlaySuccessRate.cs index 84d0b9f868..b3b67fcbca 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlaySuccessRate.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlaySuccessRate.cs @@ -60,16 +60,16 @@ namespace osu.Game.Tests.Visual.Online var secondBeatmap = createBeatmap(); AddStep("set first set", () => successRate.BeatmapInfo = firstBeatmap); - AddAssert("ratings set", () => successRate.Graph.Metrics == firstBeatmap.Metrics); + AddAssert("ratings set", () => successRate.Graph.FailTimes == firstBeatmap.FailTimes); AddStep("set second set", () => successRate.BeatmapInfo = secondBeatmap); - AddAssert("ratings set", () => successRate.Graph.Metrics == secondBeatmap.Metrics); + AddAssert("ratings set", () => successRate.Graph.FailTimes == secondBeatmap.FailTimes); static BeatmapInfo createBeatmap() => new BeatmapInfo { OnlineInfo = new APIBeatmap { - Metrics = new BeatmapMetrics + FailTimes = new APIFailTimes { Fails = Enumerable.Range(1, 100).Select(_ => RNG.Next(10)).ToArray(), Retries = Enumerable.Range(-2, 100).Select(_ => RNG.Next(10)).ToArray(), @@ -85,7 +85,7 @@ namespace osu.Game.Tests.Visual.Online { OnlineInfo = new APIBeatmap { - Metrics = new BeatmapMetrics + FailTimes = new APIFailTimes { Fails = Enumerable.Range(1, 100).ToArray(), } @@ -102,7 +102,7 @@ namespace osu.Game.Tests.Visual.Online { OnlineInfo = new APIBeatmap { - Metrics = new BeatmapMetrics(), + FailTimes = new APIFailTimes(), } }); diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetails.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetails.cs index 4bdb8539ba..1125e16d91 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetails.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetails.cs @@ -56,7 +56,7 @@ namespace osu.Game.Tests.Visual.SongSelect StarDifficulty = 5.3f, OnlineInfo = new APIBeatmap { - Metrics = new BeatmapMetrics + FailTimes = new APIFailTimes { Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(), Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(), @@ -92,7 +92,7 @@ namespace osu.Game.Tests.Visual.SongSelect StarDifficulty = 5.3f, OnlineInfo = new APIBeatmap { - Metrics = new BeatmapMetrics + FailTimes = new APIFailTimes { Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(), Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(), @@ -151,7 +151,7 @@ namespace osu.Game.Tests.Visual.SongSelect StarDifficulty = 2.91f, OnlineInfo = new APIBeatmap { - Metrics = new BeatmapMetrics + FailTimes = new APIFailTimes { Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(), Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(), diff --git a/osu.Game/Beatmaps/BeatmapMetrics.cs b/osu.Game/Beatmaps/APIFailTimes.cs similarity index 96% rename from osu.Game/Beatmaps/BeatmapMetrics.cs rename to osu.Game/Beatmaps/APIFailTimes.cs index b164aa6b30..7218906b38 100644 --- a/osu.Game/Beatmaps/BeatmapMetrics.cs +++ b/osu.Game/Beatmaps/APIFailTimes.cs @@ -9,7 +9,7 @@ namespace osu.Game.Beatmaps /// /// Beatmap metrics based on accumulated online data from community plays. /// - public class BeatmapMetrics + public class APIFailTimes { /// /// Points of failure on a relative time scale (usually 0..100). diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs index 7be58e6472..571809db36 100644 --- a/osu.Game/Beatmaps/BeatmapInfo.cs +++ b/osu.Game/Beatmaps/BeatmapInfo.cs @@ -216,7 +216,7 @@ namespace osu.Game.Beatmaps public int PassCount => OnlineInfo.PassCount; [JsonIgnore] - public BeatmapMetrics Metrics => OnlineInfo.Metrics; + public APIFailTimes FailTimes => OnlineInfo.FailTimes; #endregion } diff --git a/osu.Game/Beatmaps/IBeatmapOnlineInfo.cs b/osu.Game/Beatmaps/IBeatmapOnlineInfo.cs index 9f8e739759..385646eeaa 100644 --- a/osu.Game/Beatmaps/IBeatmapOnlineInfo.cs +++ b/osu.Game/Beatmaps/IBeatmapOnlineInfo.cs @@ -35,6 +35,6 @@ namespace osu.Game.Beatmaps /// public int PassCount { get; } - BeatmapMetrics? Metrics { get; } + APIFailTimes? FailTimes { get; } } } diff --git a/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs b/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs index f0f97cc11c..de86f0f875 100644 --- a/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs +++ b/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs @@ -69,7 +69,7 @@ namespace osu.Game.Online.API.Requests.Responses public string DifficultyName { get; set; } = string.Empty; [JsonProperty(@"failtimes")] - public BeatmapMetrics? Metrics { get; set; } + public APIFailTimes? FailTimes { get; set; } [JsonProperty(@"max_combo")] public int? MaxCombo { get; set; } diff --git a/osu.Game/Overlays/BeatmapSet/SuccessRate.cs b/osu.Game/Overlays/BeatmapSet/SuccessRate.cs index 4a9b8244a5..604c4e1949 100644 --- a/osu.Game/Overlays/BeatmapSet/SuccessRate.cs +++ b/osu.Game/Overlays/BeatmapSet/SuccessRate.cs @@ -48,7 +48,7 @@ namespace osu.Game.Overlays.BeatmapSet successRate.Length = rate; percentContainer.ResizeWidthTo(successRate.Length, 250, Easing.InOutCubic); - Graph.Metrics = beatmapInfo?.Metrics; + Graph.FailTimes = beatmapInfo?.FailTimes; } public SuccessRate() diff --git a/osu.Game/Screens/Select/BeatmapDetails.cs b/osu.Game/Screens/Select/BeatmapDetails.cs index 9fab36229f..16455940bf 100644 --- a/osu.Game/Screens/Select/BeatmapDetails.cs +++ b/osu.Game/Screens/Select/BeatmapDetails.cs @@ -43,7 +43,8 @@ namespace osu.Game.Screens.Select private BeatmapInfo beatmapInfo; - private BeatmapMetrics metrics; + private APIFailTimes failTimes; + private int[] ratings; public BeatmapInfo BeatmapInfo @@ -55,7 +56,7 @@ namespace osu.Game.Screens.Select beatmapInfo = value; - metrics = beatmapInfo?.OnlineInfo?.Metrics; + failTimes = beatmapInfo?.OnlineInfo?.FailTimes; ratings = beatmapInfo?.BeatmapSet?.Ratings; Scheduler.AddOnce(updateStatistics); @@ -182,7 +183,7 @@ namespace osu.Game.Screens.Select tags.Text = BeatmapInfo?.Metadata?.Tags; // metrics may have been previously fetched - if (ratings != null && metrics != null) + if (ratings != null && failTimes != null) { updateMetrics(); return; @@ -208,7 +209,7 @@ namespace osu.Game.Screens.Select return; ratings = res.BeatmapSet?.Ratings; - metrics = res.Metrics; + failTimes = res.FailTimes; updateMetrics(); }); @@ -232,7 +233,7 @@ namespace osu.Game.Screens.Select private void updateMetrics() { - var hasMetrics = (metrics?.Retries?.Any() ?? false) || (metrics?.Fails?.Any() ?? false); + var hasMetrics = (failTimes?.Retries?.Any() ?? false) || (failTimes?.Fails?.Any() ?? false); if (ratings?.Any() ?? false) { @@ -248,12 +249,12 @@ namespace osu.Game.Screens.Select if (hasMetrics) { - failRetryGraph.Metrics = metrics; + failRetryGraph.FailTimes = failTimes; failRetryContainer.FadeIn(transition_duration); } else { - failRetryGraph.Metrics = new BeatmapMetrics + failRetryGraph.FailTimes = new APIFailTimes { Fails = new int[100], Retries = new int[100], diff --git a/osu.Game/Screens/Select/Details/FailRetryGraph.cs b/osu.Game/Screens/Select/Details/FailRetryGraph.cs index 7cc80acfd3..ecaf02cb30 100644 --- a/osu.Game/Screens/Select/Details/FailRetryGraph.cs +++ b/osu.Game/Screens/Select/Details/FailRetryGraph.cs @@ -16,19 +16,19 @@ namespace osu.Game.Screens.Select.Details { private readonly BarGraph retryGraph, failGraph; - private BeatmapMetrics metrics; + private APIFailTimes failTimes; - public BeatmapMetrics Metrics + public APIFailTimes FailTimes { - get => metrics; + get => failTimes; set { - if (value == metrics) return; + if (value == failTimes) return; - metrics = value; + failTimes = value; - var retries = Metrics?.Retries ?? Array.Empty(); - var fails = Metrics?.Fails ?? Array.Empty(); + var retries = FailTimes?.Retries ?? Array.Empty(); + var fails = FailTimes?.Fails ?? Array.Empty(); var retriesAndFails = sumRetriesAndFails(retries, fails); float maxValue = retriesAndFails.Any() ? retriesAndFails.Max() : 0; From 67ef1c40e64ca935a5f4b9ee666b72cb15da9182 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 25 Oct 2021 15:59:40 +0900 Subject: [PATCH 070/260] Allow API beatmap requests using interface type --- osu.Game/Online/API/Requests/GetBeatmapRequest.cs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/osu.Game/Online/API/Requests/GetBeatmapRequest.cs b/osu.Game/Online/API/Requests/GetBeatmapRequest.cs index 901f7365b8..aa0d51641e 100644 --- a/osu.Game/Online/API/Requests/GetBeatmapRequest.cs +++ b/osu.Game/Online/API/Requests/GetBeatmapRequest.cs @@ -4,17 +4,23 @@ using osu.Game.Beatmaps; using osu.Game.Online.API.Requests.Responses; +#nullable enable + namespace osu.Game.Online.API.Requests { public class GetBeatmapRequest : APIRequest { - private readonly BeatmapInfo beatmapInfo; + private readonly IBeatmapInfo beatmapInfo; - public GetBeatmapRequest(BeatmapInfo beatmapInfo) + private readonly string filename; + + public GetBeatmapRequest(IBeatmapInfo beatmapInfo) { this.beatmapInfo = beatmapInfo; + + filename = (beatmapInfo as BeatmapInfo)?.Path ?? string.Empty; } - protected override string Target => $@"beatmaps/lookup?id={beatmapInfo.OnlineBeatmapID}&checksum={beatmapInfo.MD5Hash}&filename={System.Uri.EscapeUriString(beatmapInfo.Path ?? string.Empty)}"; + protected override string Target => $@"beatmaps/lookup?id={beatmapInfo.OnlineID}&checksum={beatmapInfo.MD5Hash}&filename={System.Uri.EscapeUriString(filename)}"; } } From 7a92c7ab7e0c2c888845f0a16dbf8189ecc50ea2 Mon Sep 17 00:00:00 2001 From: Roxie Wattz Date: Mon, 25 Oct 2021 00:25:32 -0700 Subject: [PATCH 071/260] Move condition to separate switch statement No clue if this is how peppy wanted me to do it but it still works --- .../Mods/TestSceneOsuModHidden.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs | 107 +++++++++++------- 2 files changed, 65 insertions(+), 44 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModHidden.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModHidden.cs index f25df90737..1fd601cec5 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModHidden.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModHidden.cs @@ -120,7 +120,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods [Test] public void OuendanTwoHidden() => CreateModTest(new ModTestData { - Mod = new OsuModHidden { NoHitCircles = { Value = true } }, + Mod = new OsuModHidden { OnlyFadeApproachCircles = { Value = true } }, Autoplay = true, Beatmap = new Beatmap { diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs index 59ac6746a0..12e2035e80 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs @@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Osu.Mods public class OsuModHidden : ModHidden, IHidesApproachCircles { [SettingSource("Only fade approach circles", "The main object body will not fade when enabled.")] - public Bindable NoHitCircles { get; } = new BindableBool(false); + public Bindable OnlyFadeApproachCircles { get; } = new BindableBool(false); public override string Description => @"Play with no approach circles and fading circles/sliders."; public override double ScoreMultiplier => 1.06; @@ -67,69 +67,90 @@ namespace osu.Game.Rulesets.Osu.Mods (double fadeStartTime, double fadeDuration) = getFadeOutParameters(drawableOsuObject); - switch (drawableObject) + if (!OnlyFadeApproachCircles.Value) { - case DrawableSliderTail _: - using (drawableObject.BeginAbsoluteSequence(fadeStartTime)) - if (!NoHitCircles.Value) + switch (drawableObject) + { + case DrawableSliderTail _: + using (drawableObject.BeginAbsoluteSequence(fadeStartTime)) drawableObject.FadeOut(fadeDuration); - break; + break; - case DrawableSliderRepeat sliderRepeat: - using (drawableObject.BeginAbsoluteSequence(fadeStartTime)) - // only apply to circle piece – reverse arrow is not affected by hidden. - if (!NoHitCircles.Value) + case DrawableSliderRepeat sliderRepeat: + using (drawableObject.BeginAbsoluteSequence(fadeStartTime)) + // only apply to circle piece – reverse arrow is not affected by hidden. sliderRepeat.CirclePiece.FadeOut(fadeDuration); - break; + break; - case DrawableHitCircle circle: - Drawable fadeTarget = circle; + case DrawableHitCircle circle: + Drawable fadeTarget = circle; - if (increaseVisibility) - { - // only fade the circle piece (not the approach circle) for the increased visibility object. - if (!NoHitCircles.Value) + if (increaseVisibility) + { + // only fade the circle piece (not the approach circle) for the increased visibility object. fadeTarget = circle.CirclePiece; - } - else - { - // we don't want to see the approach circle - using (circle.BeginAbsoluteSequence(hitObject.StartTime - hitObject.TimePreempt)) - circle.ApproachCircle.Hide(); - } + } + else + { + // we don't want to see the approach circle + using (circle.BeginAbsoluteSequence(hitObject.StartTime - hitObject.TimePreempt)) + circle.ApproachCircle.Hide(); + } - using (drawableObject.BeginAbsoluteSequence(fadeStartTime)) - if (!NoHitCircles.Value) + using (drawableObject.BeginAbsoluteSequence(fadeStartTime)) fadeTarget.FadeOut(fadeDuration); - break; + break; - case DrawableSlider slider: - using (slider.BeginAbsoluteSequence(fadeStartTime)) - if (!NoHitCircles.Value) + case DrawableSlider slider: + using (slider.BeginAbsoluteSequence(fadeStartTime)) slider.Body.FadeOut(fadeDuration, Easing.Out); - break; + break; - case DrawableSliderTick sliderTick: - using (sliderTick.BeginAbsoluteSequence(fadeStartTime)) - if (!NoHitCircles.Value) + case DrawableSliderTick sliderTick: + using (sliderTick.BeginAbsoluteSequence(fadeStartTime)) sliderTick.FadeOut(fadeDuration); - break; + break; - case DrawableSpinner spinner: - // hide elements we don't care about. - // todo: hide background + case DrawableSpinner spinner: + // hide elements we don't care about. + // todo: hide background - spinner.Body.OnSkinChanged += () => hideSpinnerApproachCircle(spinner); - hideSpinnerApproachCircle(spinner); + spinner.Body.OnSkinChanged += () => hideSpinnerApproachCircle(spinner); + hideSpinnerApproachCircle(spinner); - using (spinner.BeginAbsoluteSequence(fadeStartTime)) - spinner.FadeOut(fadeDuration); + using (spinner.BeginAbsoluteSequence(fadeStartTime)) + spinner.FadeOut(fadeDuration); - break; + break; + } + } else + { + switch(drawableObject) + { + case DrawableHitCircle circle: + if (!increaseVisibility) + { + // we don't want to see the approach circle + using (circle.BeginAbsoluteSequence(hitObject.StartTime - hitObject.TimePreempt)) + circle.ApproachCircle.Hide(); + } + break; + + case DrawableSpinner spinner: + // hide elements we don't care about. + // todo: hide background + + spinner.Body.OnSkinChanged += () => hideSpinnerApproachCircle(spinner); + hideSpinnerApproachCircle(spinner); + break; + + default: + break; + } } } From 6d1cd0a3a14d28b237c827cf8ba62ca5c2658824 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Mon, 25 Oct 2021 16:37:44 +0900 Subject: [PATCH 072/260] Add distance snapping to catch editor --- .../Edit/CatchHitObjectComposer.cs | 116 +++++++++++++++++- 1 file changed, 115 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs b/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs index 050c2f625d..9b779d3370 100644 --- a/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs +++ b/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs @@ -2,14 +2,23 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; +using System.Linq; +using JetBrains.Annotations; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Input; using osu.Game.Beatmaps; +using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Catch.UI; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit.Tools; using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.UI; +using osu.Game.Screens.Edit.Components.TernaryButtons; using osu.Game.Screens.Edit.Compose.Components; using osuTK; @@ -17,6 +26,14 @@ namespace osu.Game.Rulesets.Catch.Edit { public class CatchHitObjectComposer : HitObjectComposer { + private const float distance_snap_radius = 50; + + private CatchDistanceSnapGrid distanceSnapGrid; + + private readonly Bindable distanceSnapToggle = new Bindable(); + + private InputManager inputManager; + public CatchHitObjectComposer(CatchRuleset ruleset) : base(ruleset) { @@ -30,6 +47,27 @@ namespace osu.Game.Rulesets.Catch.Edit RelativeSizeAxes = Axes.Both, PlayfieldBorderStyle = { Value = PlayfieldBorderStyle.Corners } }); + + LayerBelowRuleset.Add(distanceSnapGrid = new CatchDistanceSnapGrid(new[] + { + 0.0, + Catcher.BASE_SPEED, -Catcher.BASE_SPEED, + Catcher.BASE_SPEED / 2, -Catcher.BASE_SPEED / 2, + })); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + inputManager = GetContainingInputManager(); + } + + protected override void Update() + { + base.Update(); + + updateDistanceSnapGrid(); } protected override DrawableRuleset CreateDrawableRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods = null) => @@ -42,14 +80,90 @@ namespace osu.Game.Rulesets.Catch.Edit new BananaShowerCompositionTool() }; + protected override IEnumerable CreateTernaryButtons() => base.CreateTernaryButtons().Concat(new[] + { + new TernaryButton(distanceSnapToggle, "Distance Snap", () => new SpriteIcon { Icon = FontAwesome.Solid.Ruler }) + }); + public override SnapResult SnapScreenSpacePositionToValidTime(Vector2 screenSpacePosition) { var result = base.SnapScreenSpacePositionToValidTime(screenSpacePosition); - // TODO: implement position snap result.ScreenSpacePosition.X = screenSpacePosition.X; + + if (distanceSnapGrid.IsPresent && distanceSnapGrid.GetSnappedPosition(result.ScreenSpacePosition) is SnapResult snapResult && + Vector2.Distance(snapResult.ScreenSpacePosition, result.ScreenSpacePosition) < distance_snap_radius) + { + result = snapResult; + } + return result; } protected override ComposeBlueprintContainer CreateBlueprintContainer() => new CatchBlueprintContainer(this); + + [CanBeNull] + private PalpableCatchHitObject getPreviousHitObject(double time) + { + var hitObject = EditorBeatmap.HitObjects.OfType().LastOrDefault(h => h.GetEndTime() < time && !(h is BananaShower)); + + switch (hitObject) + { + case Fruit fruit: + return fruit; + + case JuiceStream juiceStream: + return juiceStream.NestedHitObjects.OfType().LastOrDefault(h => !(h is TinyDroplet)); + + default: + return null; + } + } + + [CanBeNull] + private PalpableCatchHitObject getDistanceSnapGridSourceHitObject() + { + switch (BlueprintContainer.CurrentTool) + { + case SelectTool _: + if (EditorBeatmap.SelectedHitObjects.Count == 0) + return null; + + double minTime = EditorBeatmap.SelectedHitObjects.Min(hitObject => hitObject.StartTime); + return getPreviousHitObject(minTime); + + case FruitCompositionTool _: + case JuiceStreamCompositionTool _: + if (!CursorInPlacementArea) + return null; + + if (EditorBeatmap.PlacementObject.Value is JuiceStream) + { + // Juice stream path is not subject to snapping. + return null; + } + + double timeAtCursor = ((CatchPlayfield)Playfield).TimeAtScreenSpacePosition(inputManager.CurrentState.Mouse.Position); + return getPreviousHitObject(timeAtCursor); + + default: + return null; + } + } + + private void updateDistanceSnapGrid() + { + var sourceHitObject = getDistanceSnapGridSourceHitObject(); + + if (distanceSnapToggle.Value != TernaryState.True || sourceHitObject == null) + { + distanceSnapGrid.Hide(); + } + else + { + distanceSnapGrid.Show(); + distanceSnapGrid.StartTime = sourceHitObject.GetEndTime(); + distanceSnapGrid.StartX = sourceHitObject.EffectiveX; + } + } } } From 7c99193ada60807bab91c9b27e0eb37e3f01c174 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 25 Oct 2021 16:45:46 +0900 Subject: [PATCH 073/260] Add tournament `LadderInfo` serialisation tests --- .../NonVisual/LadderInfoSerialisationTest.cs | 65 +++++++++++++++++++ .../TournamentTestScene.cs | 12 +++- osu.Game.Tournament/TournamentSceneManager.cs | 2 +- 3 files changed, 77 insertions(+), 2 deletions(-) create mode 100644 osu.Game.Tournament.Tests/NonVisual/LadderInfoSerialisationTest.cs diff --git a/osu.Game.Tournament.Tests/NonVisual/LadderInfoSerialisationTest.cs b/osu.Game.Tournament.Tests/NonVisual/LadderInfoSerialisationTest.cs new file mode 100644 index 0000000000..13cbcd3caf --- /dev/null +++ b/osu.Game.Tournament.Tests/NonVisual/LadderInfoSerialisationTest.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 Newtonsoft.Json; +using NUnit.Framework; +using osu.Game.Tournament.Models; + +namespace osu.Game.Tournament.Tests.NonVisual +{ + [TestFixture] + public class LadderInfoSerialisationTest + { + [Test] + public void TestDeserialise() + { + var ladder = createSampleLadder(); + string serialised = JsonConvert.SerializeObject(ladder); + + JsonConvert.DeserializeObject(serialised, new JsonPointConverter()); + } + + [Test] + public void TestSerialise() + { + var ladder = createSampleLadder(); + JsonConvert.SerializeObject(ladder); + } + + private static LadderInfo createSampleLadder() + { + var match = TournamentTestScene.CreateSampleMatch(); + + return new LadderInfo + { + PlayersPerTeam = { Value = 4 }, + Teams = + { + match.Team1.Value, + match.Team2.Value, + }, + Rounds = + { + new TournamentRound + { + Beatmaps = + { + new RoundBeatmap { BeatmapInfo = TournamentTestScene.CreateSampleBeatmapInfo() }, + new RoundBeatmap { BeatmapInfo = TournamentTestScene.CreateSampleBeatmapInfo() }, + } + } + }, + + Matches = + { + match, + }, + Progressions = + { + new TournamentProgression(1, 2), + new TournamentProgression(1, 3, true), + } + }; + } + } +} diff --git a/osu.Game.Tournament.Tests/TournamentTestScene.cs b/osu.Game.Tournament.Tests/TournamentTestScene.cs index 93e1e018a5..3ea58e9867 100644 --- a/osu.Game.Tournament.Tests/TournamentTestScene.cs +++ b/osu.Game.Tournament.Tests/TournamentTestScene.cs @@ -8,6 +8,7 @@ using osu.Framework.Platform; using osu.Framework.Testing; using osu.Framework.Utils; using osu.Game.Beatmaps; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Rulesets; using osu.Game.Tests.Visual; using osu.Game.Tournament.IO; @@ -152,7 +153,16 @@ namespace osu.Game.Tournament.Tests }; public static BeatmapInfo CreateSampleBeatmapInfo() => - new BeatmapInfo { Metadata = new BeatmapMetadata { Title = "Test Title", Artist = "Test Artist", ID = RNG.Next(0, 1000000) } }; + new BeatmapInfo + { + Metadata = new BeatmapMetadata + { + Title = "Test Title", + Artist = "Test Artist", + ID = RNG.Next(0, 1000000) + }, + OnlineInfo = new BeatmapOnlineInfo(), + }; protected override ITestSceneTestRunner CreateRunner() => new TournamentTestSceneTestRunner(); diff --git a/osu.Game.Tournament/TournamentSceneManager.cs b/osu.Game.Tournament/TournamentSceneManager.cs index ced1a8ec72..914d1163ad 100644 --- a/osu.Game.Tournament/TournamentSceneManager.cs +++ b/osu.Game.Tournament/TournamentSceneManager.cs @@ -237,7 +237,7 @@ namespace osu.Game.Tournament { Type = type; BackgroundColour = OsuColour.Gray(0.2f); - Action = () => RequestSelection(type); + Action = () => RequestSelection?.Invoke(type); RelativeSizeAxes = Axes.X; } From 7111f58bfee77791652f4059b5e2032ea08a4bf9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 25 Oct 2021 16:46:07 +0900 Subject: [PATCH 074/260] Fix tournament client silently failing on error during parsing --- osu.Game.Tournament/TournamentGame.cs | 136 ++++++++++++---------- osu.Game.Tournament/TournamentGameBase.cs | 122 ++++++++++--------- 2 files changed, 142 insertions(+), 116 deletions(-) diff --git a/osu.Game.Tournament/TournamentGame.cs b/osu.Game.Tournament/TournamentGame.cs index 7a43fee013..f3927bb852 100644 --- a/osu.Game.Tournament/TournamentGame.cs +++ b/osu.Game.Tournament/TournamentGame.cs @@ -12,6 +12,7 @@ using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Handlers.Mouse; +using osu.Framework.Logging; using osu.Framework.Platform; using osu.Game.Graphics; using osu.Game.Graphics.Cursor; @@ -60,72 +61,89 @@ namespace osu.Game.Tournament loadingSpinner.Show(); - BracketLoadTask.ContinueWith(_ => LoadComponentsAsync(new[] + BracketLoadTask.ContinueWith(t => { - new Container + if (t.IsFaulted) { - CornerRadius = 10, - Depth = float.MinValue, - Position = new Vector2(5), - Masking = true, - AutoSizeAxes = Axes.Both, - Anchor = Anchor.BottomRight, - Origin = Anchor.BottomRight, - Children = new Drawable[] + Schedule(() => { - new Box - { - Colour = OsuColour.Gray(0.2f), - RelativeSizeAxes = Axes.Both, - }, - new TourneyButton - { - Text = "Save Changes", - Width = 140, - Height = 50, - Padding = new MarginPadding - { - Top = 10, - Left = 10, - }, - Margin = new MarginPadding - { - Right = 10, - Bottom = 10, - }, - Action = SaveChanges, - }, - } - }, - heightWarning = new WarningBox("Please make the window wider") - { - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - Margin = new MarginPadding(20), - }, - new OsuContextMenuContainer - { - RelativeSizeAxes = Axes.Both, - Child = new TournamentSceneManager() + loadingSpinner.Hide(); + loadingSpinner.Expire(); + + Logger.Error(t.Exception, "Couldn't load bracket with error"); + Add(new WarningBox("Your bracket.json file could not be parsed. Please check runtime.log for more details.")); + }); + + return; } - }, drawables => - { - loadingSpinner.Hide(); - loadingSpinner.Expire(); - AddRange(drawables); - - windowSize.BindValueChanged(size => ScheduleAfterChildren(() => + LoadComponentsAsync(new[] { - var minWidth = (int)(size.NewValue.Height / 768f * TournamentSceneManager.REQUIRED_WIDTH) - 1; - heightWarning.Alpha = size.NewValue.Width < minWidth ? 1 : 0; - }), true); - - windowMode.BindValueChanged(mode => ScheduleAfterChildren(() => + new Container + { + CornerRadius = 10, + Depth = float.MinValue, + Position = new Vector2(5), + Masking = true, + AutoSizeAxes = Axes.Both, + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, + Children = new Drawable[] + { + new Box + { + Colour = OsuColour.Gray(0.2f), + RelativeSizeAxes = Axes.Both, + }, + new TourneyButton + { + Text = "Save Changes", + Width = 140, + Height = 50, + Padding = new MarginPadding + { + Top = 10, + Left = 10, + }, + Margin = new MarginPadding + { + Right = 10, + Bottom = 10, + }, + Action = SaveChanges, + }, + } + }, + heightWarning = new WarningBox("Please make the window wider") + { + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Margin = new MarginPadding(20), + }, + new OsuContextMenuContainer + { + RelativeSizeAxes = Axes.Both, + Child = new TournamentSceneManager() + } + }, drawables => { - windowMode.Value = WindowMode.Windowed; - }), true); - })); + loadingSpinner.Hide(); + loadingSpinner.Expire(); + + AddRange(drawables); + + windowSize.BindValueChanged(size => ScheduleAfterChildren(() => + { + var minWidth = (int)(size.NewValue.Height / 768f * TournamentSceneManager.REQUIRED_WIDTH) - 1; + heightWarning.Alpha = size.NewValue.Width < minWidth ? 1 : 0; + }), true); + + windowMode.BindValueChanged(mode => ScheduleAfterChildren(() => + { + windowMode.Value = WindowMode.Windowed; + }), true); + }); + }); } } } diff --git a/osu.Game.Tournament/TournamentGameBase.cs b/osu.Game.Tournament/TournamentGameBase.cs index bdf7269c83..a6b0fa5cfc 100644 --- a/osu.Game.Tournament/TournamentGameBase.cs +++ b/osu.Game.Tournament/TournamentGameBase.cs @@ -58,75 +58,83 @@ namespace osu.Game.Tournament private void readBracket() { - if (storage.Exists(bracket_filename)) + try { - using (Stream stream = storage.GetStream(bracket_filename, FileAccess.Read, FileMode.Open)) - using (var sr = new StreamReader(stream)) - ladder = JsonConvert.DeserializeObject(sr.ReadToEnd(), new JsonPointConverter()); - } - - ladder ??= new LadderInfo(); - - ladder.Ruleset.Value = RulesetStore.GetRuleset(ladder.Ruleset.Value?.ShortName) - ?? RulesetStore.AvailableRulesets.First(); - - bool addedInfo = false; - - // assign teams - foreach (var match in ladder.Matches) - { - match.Team1.Value = ladder.Teams.FirstOrDefault(t => t.Acronym.Value == match.Team1Acronym); - match.Team2.Value = ladder.Teams.FirstOrDefault(t => t.Acronym.Value == match.Team2Acronym); - - foreach (var conditional in match.ConditionalMatches) + if (storage.Exists(bracket_filename)) { - conditional.Team1.Value = ladder.Teams.FirstOrDefault(t => t.Acronym.Value == conditional.Team1Acronym); - conditional.Team2.Value = ladder.Teams.FirstOrDefault(t => t.Acronym.Value == conditional.Team2Acronym); - conditional.Round.Value = match.Round.Value; + using (Stream stream = storage.GetStream(bracket_filename, FileAccess.Read, FileMode.Open)) + using (var sr = new StreamReader(stream)) + ladder = JsonConvert.DeserializeObject(sr.ReadToEnd(), new JsonPointConverter()); } - } - // assign progressions - foreach (var pair in ladder.Progressions) - { - var src = ladder.Matches.FirstOrDefault(p => p.ID == pair.SourceID); - var dest = ladder.Matches.FirstOrDefault(p => p.ID == pair.TargetID); + ladder ??= new LadderInfo(); - if (src == null) - continue; + ladder.Ruleset.Value = RulesetStore.GetRuleset(ladder.Ruleset.Value?.ShortName) + ?? RulesetStore.AvailableRulesets.First(); - if (dest != null) + bool addedInfo = false; + + // assign teams + foreach (var match in ladder.Matches) { - if (pair.Losers) - src.LosersProgression.Value = dest; - else - src.Progression.Value = dest; - } - } + match.Team1.Value = ladder.Teams.FirstOrDefault(t => t.Acronym.Value == match.Team1Acronym); + match.Team2.Value = ladder.Teams.FirstOrDefault(t => t.Acronym.Value == match.Team2Acronym); - // link matches to rounds - foreach (var round in ladder.Rounds) - { - foreach (var id in round.Matches) - { - var found = ladder.Matches.FirstOrDefault(p => p.ID == id); - - if (found != null) + foreach (var conditional in match.ConditionalMatches) { - found.Round.Value = round; - if (round.StartDate.Value > found.Date.Value) - found.Date.Value = round.StartDate.Value; + conditional.Team1.Value = ladder.Teams.FirstOrDefault(t => t.Acronym.Value == conditional.Team1Acronym); + conditional.Team2.Value = ladder.Teams.FirstOrDefault(t => t.Acronym.Value == conditional.Team2Acronym); + conditional.Round.Value = match.Round.Value; } } + + // assign progressions + foreach (var pair in ladder.Progressions) + { + var src = ladder.Matches.FirstOrDefault(p => p.ID == pair.SourceID); + var dest = ladder.Matches.FirstOrDefault(p => p.ID == pair.TargetID); + + if (src == null) + continue; + + if (dest != null) + { + if (pair.Losers) + src.LosersProgression.Value = dest; + else + src.Progression.Value = dest; + } + } + + // link matches to rounds + foreach (var round in ladder.Rounds) + { + foreach (var id in round.Matches) + { + var found = ladder.Matches.FirstOrDefault(p => p.ID == id); + + if (found != null) + { + found.Round.Value = round; + if (round.StartDate.Value > found.Date.Value) + found.Date.Value = round.StartDate.Value; + } + } + } + + addedInfo |= addPlayers(); + addedInfo |= addBeatmaps(); + + if (addedInfo) + SaveChanges(); + + ladder.CurrentMatch.Value = ladder.Matches.FirstOrDefault(p => p.Current.Value); + } + catch (Exception e) + { + taskCompletionSource.SetException(e); + return; } - - addedInfo |= addPlayers(); - addedInfo |= addBeatmaps(); - - if (addedInfo) - SaveChanges(); - - ladder.CurrentMatch.Value = ladder.Matches.FirstOrDefault(p => p.Current.Value); Schedule(() => { From 6adb0f068b340672aa7218c1129a6bd466037acd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 25 Oct 2021 16:44:52 +0900 Subject: [PATCH 075/260] Fix multiple issues with json serialisation of online info (causing tournament failures) --- osu.Game.Tournament.Tests/TournamentTestScene.cs | 2 +- osu.Game/Beatmaps/BeatmapInfo.cs | 3 ++- osu.Game/Online/API/Requests/Responses/APIBeatmap.cs | 4 +++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tournament.Tests/TournamentTestScene.cs b/osu.Game.Tournament.Tests/TournamentTestScene.cs index 3ea58e9867..ce9fd91ff1 100644 --- a/osu.Game.Tournament.Tests/TournamentTestScene.cs +++ b/osu.Game.Tournament.Tests/TournamentTestScene.cs @@ -161,7 +161,7 @@ namespace osu.Game.Tournament.Tests Artist = "Test Artist", ID = RNG.Next(0, 1000000) }, - OnlineInfo = new BeatmapOnlineInfo(), + OnlineInfo = new APIBeatmap(), }; protected override ITestSceneTestRunner CreateRunner() => new TournamentTestSceneTestRunner(); diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs index 571809db36..9069ea4404 100644 --- a/osu.Game/Beatmaps/BeatmapInfo.cs +++ b/osu.Game/Beatmaps/BeatmapInfo.cs @@ -9,6 +9,7 @@ using System.Linq; using Newtonsoft.Json; using osu.Framework.Testing; using osu.Game.Database; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Rulesets; using osu.Game.Scoring; @@ -47,7 +48,7 @@ namespace osu.Game.Beatmaps public BeatmapDifficulty BaseDifficulty { get; set; } [NotMapped] - public IBeatmapOnlineInfo OnlineInfo { get; set; } + public APIBeatmap OnlineInfo { get; set; } [NotMapped] public int? MaxCombo { get; set; } diff --git a/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs b/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs index de86f0f875..a677844d66 100644 --- a/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs +++ b/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs @@ -74,6 +74,8 @@ namespace osu.Game.Online.API.Requests.Responses [JsonProperty(@"max_combo")] public int? MaxCombo { get; set; } + public double BPM { get; set; } + public virtual BeatmapInfo ToBeatmapInfo(RulesetStore rulesets) { var set = BeatmapSet?.ToBeatmapSet(rulesets); @@ -120,7 +122,7 @@ namespace osu.Game.Online.API.Requests.Responses public IRulesetInfo Ruleset => new RulesetInfo { ID = RulesetID }; - public double BPM => throw new NotImplementedException(); + [JsonIgnore] public string Hash => throw new NotImplementedException(); #endregion From b65d199f6ab7e54695b9a1b459c988a53b55d2de Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 25 Oct 2021 17:01:47 +0900 Subject: [PATCH 076/260] Fix incorrect length when calling `APIBeatmap.ToBeatmapInfo` --- osu.Game/Online/API/Requests/Responses/APIBeatmap.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs b/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs index 6e43ac5504..fee3e56859 100644 --- a/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs +++ b/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs @@ -54,7 +54,7 @@ namespace osu.Game.Online.API.Requests.Responses [JsonProperty(@"accuracy")] private float overallDifficulty { get; set; } - public double Length => lengthInSeconds * 1000; + public double Length => TimeSpan.FromSeconds(lengthInSeconds).TotalMilliseconds; [JsonProperty(@"total_length")] private double lengthInSeconds { get; set; } @@ -86,7 +86,7 @@ namespace osu.Game.Online.API.Requests.Responses OnlineBeatmapID = OnlineID, Version = DifficultyName, // this is actually an incorrect mapping (Length is calculated as drain length in lazer's import process, see BeatmapManager.calculateLength). - Length = TimeSpan.FromSeconds(Length).TotalMilliseconds, + Length = Length, Status = Status, MD5Hash = Checksum, BeatmapSet = set, From 3f91e74e576a9a18594a7b2830ee5c35c57a20c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 25 Oct 2021 19:26:52 +0200 Subject: [PATCH 077/260] Remove unused using directive --- osu.Game.Tournament.Tests/TournamentTestScene.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Tournament.Tests/TournamentTestScene.cs b/osu.Game.Tournament.Tests/TournamentTestScene.cs index 3ea58e9867..bd079eb8de 100644 --- a/osu.Game.Tournament.Tests/TournamentTestScene.cs +++ b/osu.Game.Tournament.Tests/TournamentTestScene.cs @@ -8,7 +8,6 @@ using osu.Framework.Platform; using osu.Framework.Testing; using osu.Framework.Utils; using osu.Game.Beatmaps; -using osu.Game.Online.API.Requests.Responses; using osu.Game.Rulesets; using osu.Game.Tests.Visual; using osu.Game.Tournament.IO; From 6802e9ec109c34309529a10b950038e54cc62015 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 25 Oct 2021 20:20:40 +0200 Subject: [PATCH 078/260] Remove `FixedWidth` and add `AutoSizeAxes` specs to all usages --- .../TestSceneBeatmapSetOnlineStatusPill.cs | 9 +++++-- .../Drawables/BeatmapSetOnlineStatusPill.cs | 26 ------------------- .../BeatmapListing/Panels/GridBeatmapPanel.cs | 1 + .../BeatmapListing/Panels/ListBeatmapPanel.cs | 1 + .../BeatmapSet/BeatmapSetHeaderContent.cs | 1 + osu.Game/Screens/Select/BeatmapInfoWedge.cs | 1 + .../Select/Carousel/SetPanelContent.cs | 1 + 7 files changed, 12 insertions(+), 28 deletions(-) diff --git a/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapSetOnlineStatusPill.cs b/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapSetOnlineStatusPill.cs index 2d61385269..c48b63ac89 100644 --- a/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapSetOnlineStatusPill.cs +++ b/osu.Game.Tests/Visual/Beatmaps/TestSceneBeatmapSetOnlineStatusPill.cs @@ -28,6 +28,7 @@ namespace osu.Game.Tests.Visual.Beatmaps Spacing = new Vector2(0, 10), ChildrenEnumerable = Enum.GetValues(typeof(BeatmapSetOnlineStatus)).Cast().Select(status => new BeatmapSetOnlineStatusPill { + AutoSizeAxes = Axes.Both, Anchor = Anchor.Centre, Origin = Anchor.Centre, Status = status @@ -41,8 +42,12 @@ namespace osu.Game.Tests.Visual.Beatmaps { AddStep("create themed content", () => CreateThemedContent(OverlayColourScheme.Red)); - AddStep("set fixed width", () => statusPills.ForEach(pill => pill.FixedWidth = 90)); - AddStep("unset fixed width", () => statusPills.ForEach(pill => pill.FixedWidth = null)); + AddStep("set fixed width", () => statusPills.ForEach(pill => + { + pill.AutoSizeAxes = Axes.Y; + pill.Width = 90; + })); + AddStep("unset fixed width", () => statusPills.ForEach(pill => pill.AutoSizeAxes = Axes.Both)); } } } diff --git a/osu.Game/Beatmaps/Drawables/BeatmapSetOnlineStatusPill.cs b/osu.Game/Beatmaps/Drawables/BeatmapSetOnlineStatusPill.cs index 848bab6394..ec098f4ca2 100644 --- a/osu.Game/Beatmaps/Drawables/BeatmapSetOnlineStatusPill.cs +++ b/osu.Game/Beatmaps/Drawables/BeatmapSetOnlineStatusPill.cs @@ -47,31 +47,6 @@ namespace osu.Game.Beatmaps.Drawables set => statusText.Padding = value; } - private float? fixedWidth; - - /// - /// When set to a non- value, the pill will be forcibly sized to the given width. - /// When set to a value, the pill will autosize to its contents. - /// - public float? FixedWidth - { - get => fixedWidth; - set - { - fixedWidth = value; - - if (fixedWidth == null) - { - AutoSizeAxes = Axes.Both; - } - else - { - AutoSizeAxes = Axes.Y; - Width = fixedWidth.Value; - } - } - } - private readonly OsuSpriteText statusText; private readonly Box background; @@ -83,7 +58,6 @@ namespace osu.Game.Beatmaps.Drawables public BeatmapSetOnlineStatusPill() { - AutoSizeAxes = Axes.Both; Masking = true; Children = new Drawable[] diff --git a/osu.Game/Overlays/BeatmapListing/Panels/GridBeatmapPanel.cs b/osu.Game/Overlays/BeatmapListing/Panels/GridBeatmapPanel.cs index c078127353..4a0fa59c31 100644 --- a/osu.Game/Overlays/BeatmapListing/Panels/GridBeatmapPanel.cs +++ b/osu.Game/Overlays/BeatmapListing/Panels/GridBeatmapPanel.cs @@ -243,6 +243,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels statusContainer.Add(new BeatmapSetOnlineStatusPill { + AutoSizeAxes = Axes.Both, TextSize = 12, TextPadding = new MarginPadding { Horizontal = 10, Vertical = 5 }, Status = SetInfo.OnlineInfo?.Status ?? BeatmapSetOnlineStatus.None, diff --git a/osu.Game/Overlays/BeatmapListing/Panels/ListBeatmapPanel.cs b/osu.Game/Overlays/BeatmapListing/Panels/ListBeatmapPanel.cs index 5011749c5f..63d651f9de 100644 --- a/osu.Game/Overlays/BeatmapListing/Panels/ListBeatmapPanel.cs +++ b/osu.Game/Overlays/BeatmapListing/Panels/ListBeatmapPanel.cs @@ -257,6 +257,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels statusContainer.Add(new BeatmapSetOnlineStatusPill { + AutoSizeAxes = Axes.Both, TextSize = 12, TextPadding = new MarginPadding { Horizontal = 10, Vertical = 4 }, Status = SetInfo.OnlineInfo?.Status ?? BeatmapSetOnlineStatus.None, diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs b/osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs index df88b06879..6f85846720 100644 --- a/osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs +++ b/osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs @@ -198,6 +198,7 @@ namespace osu.Game.Overlays.BeatmapSet { onlineStatusPill = new BeatmapSetOnlineStatusPill { + AutoSizeAxes = Axes.Both, Anchor = Anchor.TopRight, Origin = Anchor.TopRight, TextSize = 14, diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index ac191a38f2..2de72beaad 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -257,6 +257,7 @@ namespace osu.Game.Screens.Select }, StatusPill = new BeatmapSetOnlineStatusPill { + AutoSizeAxes = Axes.Both, Anchor = Anchor.TopRight, Origin = Anchor.TopRight, Shear = -wedged_container_shear, diff --git a/osu.Game/Screens/Select/Carousel/SetPanelContent.cs b/osu.Game/Screens/Select/Carousel/SetPanelContent.cs index 9fb640ba1a..f2054677b0 100644 --- a/osu.Game/Screens/Select/Carousel/SetPanelContent.cs +++ b/osu.Game/Screens/Select/Carousel/SetPanelContent.cs @@ -60,6 +60,7 @@ namespace osu.Game.Screens.Select.Carousel { new BeatmapSetOnlineStatusPill { + AutoSizeAxes = Axes.Both, Origin = Anchor.CentreLeft, Anchor = Anchor.CentreLeft, Margin = new MarginPadding { Right = 5 }, From 0fb2307420e8a8affa2c36e9da722f1884c9881b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 25 Oct 2021 21:00:05 +0200 Subject: [PATCH 079/260] Add back using directive Was removed for being unneeded in a prerequisite PR but then ended up leading to compilation failures in the dependent PR. --- osu.Game.Tournament.Tests/TournamentTestScene.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Tournament.Tests/TournamentTestScene.cs b/osu.Game.Tournament.Tests/TournamentTestScene.cs index e941c9e2bd..ce9fd91ff1 100644 --- a/osu.Game.Tournament.Tests/TournamentTestScene.cs +++ b/osu.Game.Tournament.Tests/TournamentTestScene.cs @@ -8,6 +8,7 @@ using osu.Framework.Platform; using osu.Framework.Testing; using osu.Framework.Utils; using osu.Game.Beatmaps; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Rulesets; using osu.Game.Tests.Visual; using osu.Game.Tournament.IO; From c9a2c6c031e4be032e9b9ecfdf0f9e5faf3be63b Mon Sep 17 00:00:00 2001 From: Roxie Wattz Date: Mon, 25 Oct 2021 14:09:22 -0700 Subject: [PATCH 080/260] Reworking the Ouendan 2 Hidden code AGAIN Not finished. Will be finished in a later commit. --- osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs | 117 ++++++++------------- 1 file changed, 45 insertions(+), 72 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs index 12e2035e80..e93120b321 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs @@ -50,15 +50,15 @@ namespace osu.Game.Rulesets.Osu.Mods protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state) { - applyState(hitObject, true); + applyHiddenState(hitObject, true, OnlyFadeApproachCircles.Value); } protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state) { - applyState(hitObject, false); + applyHiddenState(hitObject, false, OnlyFadeApproachCircles.Value); } - private void applyState(DrawableHitObject drawableObject, bool increaseVisibility) + private void applyHiddenState(DrawableHitObject drawableObject, bool hideApproachCircle, bool hideCirclePiece) { if (!(drawableObject is DrawableOsuHitObject drawableOsuObject)) return; @@ -67,90 +67,63 @@ namespace osu.Game.Rulesets.Osu.Mods (double fadeStartTime, double fadeDuration) = getFadeOutParameters(drawableOsuObject); - if (!OnlyFadeApproachCircles.Value) + switch (drawableObject) { - switch (drawableObject) - { - case DrawableSliderTail _: - using (drawableObject.BeginAbsoluteSequence(fadeStartTime)) - drawableObject.FadeOut(fadeDuration); + case DrawableSliderTail _: + using (drawableObject.BeginAbsoluteSequence(fadeStartTime)) + drawableObject.FadeOut(fadeDuration); - break; + break; - case DrawableSliderRepeat sliderRepeat: - using (drawableObject.BeginAbsoluteSequence(fadeStartTime)) - // only apply to circle piece – reverse arrow is not affected by hidden. - sliderRepeat.CirclePiece.FadeOut(fadeDuration); + case DrawableSliderRepeat sliderRepeat: + using (drawableObject.BeginAbsoluteSequence(fadeStartTime)) + // only apply to circle piece – reverse arrow is not affected by hidden. + sliderRepeat.CirclePiece.FadeOut(fadeDuration); - break; + break; - case DrawableHitCircle circle: - Drawable fadeTarget = circle; + case DrawableHitCircle circle: + Drawable fadeTarget = circle; - if (increaseVisibility) - { - // only fade the circle piece (not the approach circle) for the increased visibility object. - fadeTarget = circle.CirclePiece; - } - else - { - // we don't want to see the approach circle - using (circle.BeginAbsoluteSequence(hitObject.StartTime - hitObject.TimePreempt)) - circle.ApproachCircle.Hide(); - } + if (hideApproachCircle) + { + // only fade the circle piece (not the approach circle) for the increased visibility object. + fadeTarget = circle.CirclePiece; + } + else + { + // we don't want to see the approach circle + using (circle.BeginAbsoluteSequence(hitObject.StartTime - hitObject.TimePreempt)) + circle.ApproachCircle.Hide(); + } - using (drawableObject.BeginAbsoluteSequence(fadeStartTime)) - fadeTarget.FadeOut(fadeDuration); - break; + using (drawableObject.BeginAbsoluteSequence(fadeStartTime)) + fadeTarget.FadeOut(fadeDuration); + break; - case DrawableSlider slider: - using (slider.BeginAbsoluteSequence(fadeStartTime)) - slider.Body.FadeOut(fadeDuration, Easing.Out); + case DrawableSlider slider: + using (slider.BeginAbsoluteSequence(fadeStartTime)) + slider.Body.FadeOut(fadeDuration, Easing.Out); - break; + break; - case DrawableSliderTick sliderTick: - using (sliderTick.BeginAbsoluteSequence(fadeStartTime)) - sliderTick.FadeOut(fadeDuration); + case DrawableSliderTick sliderTick: + using (sliderTick.BeginAbsoluteSequence(fadeStartTime)) + sliderTick.FadeOut(fadeDuration); - break; + break; - case DrawableSpinner spinner: - // hide elements we don't care about. - // todo: hide background + case DrawableSpinner spinner: + // hide elements we don't care about. + // todo: hide background - spinner.Body.OnSkinChanged += () => hideSpinnerApproachCircle(spinner); - hideSpinnerApproachCircle(spinner); + spinner.Body.OnSkinChanged += () => hideSpinnerApproachCircle(spinner); + hideSpinnerApproachCircle(spinner); - using (spinner.BeginAbsoluteSequence(fadeStartTime)) - spinner.FadeOut(fadeDuration); + using (spinner.BeginAbsoluteSequence(fadeStartTime)) + spinner.FadeOut(fadeDuration); - break; - } - } else - { - switch(drawableObject) - { - case DrawableHitCircle circle: - if (!increaseVisibility) - { - // we don't want to see the approach circle - using (circle.BeginAbsoluteSequence(hitObject.StartTime - hitObject.TimePreempt)) - circle.ApproachCircle.Hide(); - } - break; - - case DrawableSpinner spinner: - // hide elements we don't care about. - // todo: hide background - - spinner.Body.OnSkinChanged += () => hideSpinnerApproachCircle(spinner); - hideSpinnerApproachCircle(spinner); - break; - - default: - break; - } + break; } } From 3687f830e8d12f49ad33b49fb7d2792408bcf7b2 Mon Sep 17 00:00:00 2001 From: Roxie Wattz Date: Mon, 25 Oct 2021 16:57:07 -0700 Subject: [PATCH 081/260] Rename my test scene As requested by bdach --- .../{TestSceneSpinnerSpam.cs => TestSceneNoSpinnerStacking.cs} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename osu.Game.Rulesets.Osu.Tests/{TestSceneSpinnerSpam.cs => TestSceneNoSpinnerStacking.cs} (93%) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerSpam.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneNoSpinnerStacking.cs similarity index 93% rename from osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerSpam.cs rename to osu.Game.Rulesets.Osu.Tests/TestSceneNoSpinnerStacking.cs index 26729979f9..52597385bb 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerSpam.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneNoSpinnerStacking.cs @@ -10,7 +10,7 @@ using osuTK; namespace osu.Game.Rulesets.Osu.Tests { [TestFixture] - public class TestSceneSpinnerSpam : TestSceneOsuPlayer + public class TestSceneNoSpinnerStacking : TestSceneOsuPlayer { protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) { From cf7fbf4b635e5803ddf55036fb705ae5c9420fcf Mon Sep 17 00:00:00 2001 From: Roxie Wattz Date: Mon, 25 Oct 2021 17:15:43 -0700 Subject: [PATCH 082/260] updated bdach's suggestion --- osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs | 4 ++-- osu.Game.Rulesets.Osu/Objects/Spinner.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs index a9b4027ea7..8b7de9e109 100644 --- a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs @@ -49,7 +49,7 @@ namespace osu.Game.Rulesets.Osu.Objects public virtual Vector2 EndPosition => Position; - public virtual Vector2 StackedEndPosition => EndPosition + StackOffset; + public Vector2 StackedEndPosition => EndPosition + StackOffset; public readonly Bindable StackHeightBindable = new Bindable(); @@ -59,7 +59,7 @@ namespace osu.Game.Rulesets.Osu.Objects set => StackHeightBindable.Value = value; } - public Vector2 StackOffset => new Vector2(StackHeight * Scale * -6.4f); + public virtual Vector2 StackOffset => new Vector2(StackHeight * Scale * -6.4f); public double Radius => OBJECT_RADIUS * Scale; diff --git a/osu.Game.Rulesets.Osu/Objects/Spinner.cs b/osu.Game.Rulesets.Osu/Objects/Spinner.cs index efaea88674..0ad8e4ea68 100644 --- a/osu.Game.Rulesets.Osu/Objects/Spinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Spinner.cs @@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.Osu.Objects ///
public int MaximumBonusSpins { get; protected set; } = 1; - public override Vector2 StackedEndPosition => EndPosition; + public override Vector2 StackOffset => Vector2.Zero; protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, IBeatmapDifficultyInfo difficulty) { From 2c92f1a0b70ab1ef912d148fd08ae53a3cfc4b34 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 26 Oct 2021 10:41:17 +0900 Subject: [PATCH 083/260] Use array for fixed-size list --- osu.Game.Rulesets.Catch/Edit/CatchDistanceSnapGrid.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Edit/CatchDistanceSnapGrid.cs b/osu.Game.Rulesets.Catch/Edit/CatchDistanceSnapGrid.cs index 5808b332b0..1b9705e8dc 100644 --- a/osu.Game.Rulesets.Catch/Edit/CatchDistanceSnapGrid.cs +++ b/osu.Game.Rulesets.Catch/Edit/CatchDistanceSnapGrid.cs @@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Catch.Edit private readonly List verticalPaths = new List(); - private readonly List> verticalLineVertices = new List>(); + private readonly List verticalLineVertices = new List(); [Resolved] private Playfield playfield { get; set; } @@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.Catch.Edit Alpha = 0.5f, }); - verticalLineVertices.Add(new List { Vector2.Zero, Vector2.Zero }); + verticalLineVertices.Add(new[] { Vector2.Zero, Vector2.Zero }); } AddRangeInternal(verticalPaths); @@ -70,7 +70,7 @@ namespace osu.Game.Rulesets.Catch.Edit float x = (float)((endTime - StartTime) * velocity); float y = hitObjectContainer.PositionAtTime(endTime, StartTime); - List lineVertices = verticalLineVertices[i]; + Vector2[] lineVertices = verticalLineVertices[i]; lineVertices[0] = new Vector2(StartX, startY); lineVertices[1] = lineVertices[0] + new Vector2(x, y); From 865be5a10b1405c185e80a9c32ca7bb2ee57bc23 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 26 Oct 2021 11:00:44 +0900 Subject: [PATCH 084/260] Fix distance snap grid rendering inverted when out of bounds --- osu.Game.Rulesets.Catch/Edit/CatchDistanceSnapGrid.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Edit/CatchDistanceSnapGrid.cs b/osu.Game.Rulesets.Catch/Edit/CatchDistanceSnapGrid.cs index 1b9705e8dc..db7b910f96 100644 --- a/osu.Game.Rulesets.Catch/Edit/CatchDistanceSnapGrid.cs +++ b/osu.Game.Rulesets.Catch/Edit/CatchDistanceSnapGrid.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.Collections.Generic; using System.Linq; using JetBrains.Annotations; @@ -63,9 +64,11 @@ namespace osu.Game.Rulesets.Catch.Edit for (int i = 0; i < velocities.Length; i++) { double velocity = velocities[i]; + var verticalPath = verticalPaths[i]; // The line ends at the top of the screen. - double endTime = hitObjectContainer.TimeAtPosition(-hitObjectContainer.DrawHeight, hitObjectContainer.Time.Current); + double topScreenTime = hitObjectContainer.TimeAtPosition(-hitObjectContainer.DrawHeight, hitObjectContainer.Time.Current); + double endTime = Math.Max(StartTime, topScreenTime); float x = (float)((endTime - StartTime) * velocity); float y = hitObjectContainer.PositionAtTime(endTime, StartTime); @@ -74,7 +77,6 @@ namespace osu.Game.Rulesets.Catch.Edit lineVertices[0] = new Vector2(StartX, startY); lineVertices[1] = lineVertices[0] + new Vector2(x, y); - var verticalPath = verticalPaths[i]; verticalPath.Vertices = verticalLineVertices[i]; verticalPath.OriginPosition = verticalPath.PositionInBoundingBox(Vector2.Zero); } From 9f49a1df8beba494a89dd432ee19959ed04a0aae Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 26 Oct 2021 11:00:55 +0900 Subject: [PATCH 085/260] Handle the case of cursor below the start time of distance snap --- osu.Game.Rulesets.Catch/Edit/CatchDistanceSnapGrid.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/osu.Game.Rulesets.Catch/Edit/CatchDistanceSnapGrid.cs b/osu.Game.Rulesets.Catch/Edit/CatchDistanceSnapGrid.cs index db7b910f96..667cb4a9b5 100644 --- a/osu.Game.Rulesets.Catch/Edit/CatchDistanceSnapGrid.cs +++ b/osu.Game.Rulesets.Catch/Edit/CatchDistanceSnapGrid.cs @@ -87,6 +87,16 @@ namespace osu.Game.Rulesets.Catch.Edit { double time = hitObjectContainer.TimeAtScreenSpacePosition(screenSpacePosition); + // If the cursor is below the distance snap grid, snap to the origin. + // Not returning `null` to retain the continuous snapping behavior when the cursor is slightly below the origin. + // This behavior is not currently visible in the editor because editor chooses the snap start time based on the mouse position. + if (time <= StartTime) + { + float y = hitObjectContainer.PositionAtTime(StartTime); + Vector2 originPosition = hitObjectContainer.ToScreenSpace(new Vector2(StartX, y)); + return new SnapResult(originPosition, StartTime); + } + return enumerateSnappingCandidates(time) .OrderBy(pos => Vector2.DistanceSquared(screenSpacePosition, pos.ScreenSpacePosition)) .FirstOrDefault(); From 068217557e686660854a78bcafbf024b3301b5f6 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 26 Oct 2021 11:12:10 +0900 Subject: [PATCH 086/260] Add doc comment on catch distance snap grid --- osu.Game.Rulesets.Catch/Edit/CatchDistanceSnapGrid.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game.Rulesets.Catch/Edit/CatchDistanceSnapGrid.cs b/osu.Game.Rulesets.Catch/Edit/CatchDistanceSnapGrid.cs index 667cb4a9b5..d999115d81 100644 --- a/osu.Game.Rulesets.Catch/Edit/CatchDistanceSnapGrid.cs +++ b/osu.Game.Rulesets.Catch/Edit/CatchDistanceSnapGrid.cs @@ -17,6 +17,10 @@ using osuTK; namespace osu.Game.Rulesets.Catch.Edit { + /// + /// The guide lines used in the osu!catch editor to compose patterns that can be caught with constant speed. + /// Currently, only forward placement (an object is snapped based on the previous object, not the opposite) is supported. + /// public class CatchDistanceSnapGrid : CompositeDrawable { public double StartTime { get; set; } From b7a719de716f611443fed8f67e9c92a4a2fddc4c Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 26 Oct 2021 11:14:12 +0900 Subject: [PATCH 087/260] Rename method --- osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs b/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs index 9b779d3370..9c67e4600b 100644 --- a/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs +++ b/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs @@ -102,7 +102,7 @@ namespace osu.Game.Rulesets.Catch.Edit protected override ComposeBlueprintContainer CreateBlueprintContainer() => new CatchBlueprintContainer(this); [CanBeNull] - private PalpableCatchHitObject getPreviousHitObject(double time) + private PalpableCatchHitObject getLastSnappableHitObject(double time) { var hitObject = EditorBeatmap.HitObjects.OfType().LastOrDefault(h => h.GetEndTime() < time && !(h is BananaShower)); @@ -129,7 +129,7 @@ namespace osu.Game.Rulesets.Catch.Edit return null; double minTime = EditorBeatmap.SelectedHitObjects.Min(hitObject => hitObject.StartTime); - return getPreviousHitObject(minTime); + return getLastSnappableHitObject(minTime); case FruitCompositionTool _: case JuiceStreamCompositionTool _: @@ -143,7 +143,7 @@ namespace osu.Game.Rulesets.Catch.Edit } double timeAtCursor = ((CatchPlayfield)Playfield).TimeAtScreenSpacePosition(inputManager.CurrentState.Mouse.Position); - return getPreviousHitObject(timeAtCursor); + return getLastSnappableHitObject(timeAtCursor); default: return null; From c5d765029ce2383f5d0fe22977acd617a981dcb3 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 26 Oct 2021 11:17:04 +0900 Subject: [PATCH 088/260] Don't compute source object for distance snap grid when it is not enabled --- .../Edit/CatchHitObjectComposer.cs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs b/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs index 9c67e4600b..67055fb5e0 100644 --- a/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs +++ b/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs @@ -152,18 +152,23 @@ namespace osu.Game.Rulesets.Catch.Edit private void updateDistanceSnapGrid() { - var sourceHitObject = getDistanceSnapGridSourceHitObject(); - - if (distanceSnapToggle.Value != TernaryState.True || sourceHitObject == null) + if (distanceSnapToggle.Value != TernaryState.True) { distanceSnapGrid.Hide(); + return; } - else + + var sourceHitObject = getDistanceSnapGridSourceHitObject(); + + if (sourceHitObject == null) { - distanceSnapGrid.Show(); - distanceSnapGrid.StartTime = sourceHitObject.GetEndTime(); - distanceSnapGrid.StartX = sourceHitObject.EffectiveX; + distanceSnapGrid.Hide(); + return; } + + distanceSnapGrid.Show(); + distanceSnapGrid.StartTime = sourceHitObject.GetEndTime(); + distanceSnapGrid.StartX = sourceHitObject.EffectiveX; } } } From e7b19cb72489a32ac725ddbdfc25cfa2539056c9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 Oct 2021 11:30:26 +0900 Subject: [PATCH 089/260] Rename test and fix incorrect layout --- .../Mods/TestSceneOsuModHidden.cs | 12 ++++++------ osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs | 1 - 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModHidden.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModHidden.cs index 1fd601cec5..71b575abe2 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModHidden.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModHidden.cs @@ -118,7 +118,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods }); [Test] - public void OuendanTwoHidden() => CreateModTest(new ModTestData + public void TestApproachCirclesOnly() => CreateModTest(new ModTestData { Mod = new OsuModHidden { OnlyFadeApproachCircles = { Value = true } }, Autoplay = true, @@ -129,22 +129,22 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods new HitCircle { StartTime = 1000, - Position = new Vector2(206,142) + Position = new Vector2(206, 142) }, new HitCircle { StartTime = 2000, - Position = new Vector2(306,142) + Position = new Vector2(306, 142) }, new Slider { StartTime = 3000, - Position = new Vector2(156,242), - Path = new SliderPath(PathType.Linear, new[] { Vector2.Zero, new Vector2(200,0), }) + Position = new Vector2(156, 242), + Path = new SliderPath(PathType.Linear, new[] { Vector2.Zero, new Vector2(200, 0), }) }, new Spinner { - Position = new Vector2(256,192), + Position = new Vector2(256, 192), StartTime = 7000, EndTime = 9000 } diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs index e93120b321..ed329f02b3 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs @@ -15,7 +15,6 @@ using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Skinning; - namespace osu.Game.Rulesets.Osu.Mods { public class OsuModHidden : ModHidden, IHidesApproachCircles From 0757044b1571bc5f9f2bec360296814f85a0f408 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 Oct 2021 11:42:15 +0900 Subject: [PATCH 090/260] Refactor logic to work --- osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs | 27 ++++++++++++++-------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs index ed329f02b3..c4444bf4f8 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs @@ -49,15 +49,15 @@ namespace osu.Game.Rulesets.Osu.Mods protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state) { - applyHiddenState(hitObject, true, OnlyFadeApproachCircles.Value); + applyHiddenState(hitObject, true); } protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state) { - applyHiddenState(hitObject, false, OnlyFadeApproachCircles.Value); + applyHiddenState(hitObject, false); } - private void applyHiddenState(DrawableHitObject drawableObject, bool hideApproachCircle, bool hideCirclePiece) + private void applyHiddenState(DrawableHitObject drawableObject, bool increaseVisibility) { if (!(drawableObject is DrawableOsuHitObject drawableOsuObject)) return; @@ -66,6 +66,19 @@ namespace osu.Game.Rulesets.Osu.Mods (double fadeStartTime, double fadeDuration) = getFadeOutParameters(drawableOsuObject); + // process approach circle hiding first (to allow for early return below). + if (!increaseVisibility) + { + if (drawableObject is DrawableHitCircle circle) + { + using (circle.BeginAbsoluteSequence(hitObject.StartTime - hitObject.TimePreempt)) + circle.ApproachCircle.Hide(); + } + } + + if (OnlyFadeApproachCircles.Value) + return; + switch (drawableObject) { case DrawableSliderTail _: @@ -84,17 +97,11 @@ namespace osu.Game.Rulesets.Osu.Mods case DrawableHitCircle circle: Drawable fadeTarget = circle; - if (hideApproachCircle) + if (increaseVisibility) { // only fade the circle piece (not the approach circle) for the increased visibility object. fadeTarget = circle.CirclePiece; } - else - { - // we don't want to see the approach circle - using (circle.BeginAbsoluteSequence(hitObject.StartTime - hitObject.TimePreempt)) - circle.ApproachCircle.Hide(); - } using (drawableObject.BeginAbsoluteSequence(fadeStartTime)) fadeTarget.FadeOut(fadeDuration); From 5d1170aaf4879459d027e03d15b33c4a3bf28b6f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 Oct 2021 11:57:51 +0900 Subject: [PATCH 091/260] Remove unnecessary null check --- osu.Game/Audio/PreviewTrackManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Audio/PreviewTrackManager.cs b/osu.Game/Audio/PreviewTrackManager.cs index 26787c362b..ca63add31d 100644 --- a/osu.Game/Audio/PreviewTrackManager.cs +++ b/osu.Game/Audio/PreviewTrackManager.cs @@ -114,7 +114,7 @@ namespace osu.Game.Audio Logger.Log($"A {nameof(PreviewTrack)} was created without a containing {nameof(IPreviewTrackOwner)}. An owner should be added for correct behaviour."); } - protected override Track GetTrack() => trackManager.Get($"https://b.ppy.sh/preview/{beatmapSetInfo?.OnlineID}.mp3"); + protected override Track GetTrack() => trackManager.Get($"https://b.ppy.sh/preview/{beatmapSetInfo.OnlineID}.mp3"); } private class PreviewTrackStore : AudioCollectionManager, ITrackStore From d7ac94038a1d01e5240e0340cf15bb6890f8a6a1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 Oct 2021 12:04:18 +0900 Subject: [PATCH 092/260] Update `GetBeatmapRequest` to use `AddParameter` --- osu.Game/Online/API/Requests/GetBeatmapRequest.cs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/osu.Game/Online/API/Requests/GetBeatmapRequest.cs b/osu.Game/Online/API/Requests/GetBeatmapRequest.cs index aa0d51641e..6cd45a41df 100644 --- a/osu.Game/Online/API/Requests/GetBeatmapRequest.cs +++ b/osu.Game/Online/API/Requests/GetBeatmapRequest.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 osu.Framework.IO.Network; using osu.Game.Beatmaps; using osu.Game.Online.API.Requests.Responses; @@ -21,6 +22,17 @@ namespace osu.Game.Online.API.Requests filename = (beatmapInfo as BeatmapInfo)?.Path ?? string.Empty; } - protected override string Target => $@"beatmaps/lookup?id={beatmapInfo.OnlineID}&checksum={beatmapInfo.MD5Hash}&filename={System.Uri.EscapeUriString(filename)}"; + protected override WebRequest CreateWebRequest() + { + var request = base.CreateWebRequest(); + + request.AddParameter(@"id", beatmapInfo.OnlineID.ToString()); + request.AddParameter(@"checksum", beatmapInfo.MD5Hash); + request.AddParameter(@"filename", filename); + + return request; + } + + protected override string Target => @"beatmaps/lookup"; } } From 8b85c78b0defe810b34a324f5a0c6b85bb35e18f Mon Sep 17 00:00:00 2001 From: Roxie Wattz Date: Mon, 25 Oct 2021 20:16:45 -0700 Subject: [PATCH 093/260] Add processing of Spinner approach circles --- osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs index c4444bf4f8..e5cc08c886 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs @@ -74,6 +74,11 @@ namespace osu.Game.Rulesets.Osu.Mods using (circle.BeginAbsoluteSequence(hitObject.StartTime - hitObject.TimePreempt)) circle.ApproachCircle.Hide(); } + else if (drawableObject is DrawableSpinner spinner) + { + spinner.Body.OnSkinChanged += () => hideSpinnerApproachCircle(spinner); + hideSpinnerApproachCircle(spinner); + } } if (OnlyFadeApproachCircles.Value) @@ -123,9 +128,6 @@ namespace osu.Game.Rulesets.Osu.Mods // hide elements we don't care about. // todo: hide background - spinner.Body.OnSkinChanged += () => hideSpinnerApproachCircle(spinner); - hideSpinnerApproachCircle(spinner); - using (spinner.BeginAbsoluteSequence(fadeStartTime)) spinner.FadeOut(fadeDuration); From a582976851306ea1f567768381b70df8e26fc9e3 Mon Sep 17 00:00:00 2001 From: Jason Won Date: Tue, 26 Oct 2021 00:05:46 -0400 Subject: [PATCH 094/260] reversing slider maintains shape --- osu.Game/Rulesets/Objects/SliderPathExtensions.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Objects/SliderPathExtensions.cs b/osu.Game/Rulesets/Objects/SliderPathExtensions.cs index 663746bfca..052fc7c775 100644 --- a/osu.Game/Rulesets/Objects/SliderPathExtensions.cs +++ b/osu.Game/Rulesets/Objects/SliderPathExtensions.cs @@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Objects public static void Reverse(this SliderPath sliderPath, out Vector2 positionalOffset) { var points = sliderPath.ControlPoints.ToArray(); - positionalOffset = points.Last().Position; + positionalOffset = sliderPath.PositionAt(1); sliderPath.ControlPoints.Clear(); @@ -32,7 +32,10 @@ namespace osu.Game.Rulesets.Objects // propagate types forwards to last null type if (i == points.Length - 1) + { p.Type = lastType; + p.Position = Vector2.Zero; + } else if (p.Type != null) (p.Type, lastType) = (lastType, p.Type); From 6c38028d068e796fbeeda82587c22d9a7da4c6c2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 Oct 2021 13:55:02 +0900 Subject: [PATCH 095/260] Fix gameplay chat display not losing focus on escape key press Closes #15293. --- .../Multiplayer/TestSceneGameplayChatDisplay.cs | 12 ++++++++++++ .../OnlinePlay/Multiplayer/GameplayChatDisplay.cs | 10 ++++++++++ 2 files changed, 22 insertions(+) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneGameplayChatDisplay.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneGameplayChatDisplay.cs index a3a1cacb0d..512d206a06 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneGameplayChatDisplay.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneGameplayChatDisplay.cs @@ -92,6 +92,18 @@ namespace osu.Game.Tests.Visual.Multiplayer assertChatFocused(true); } + [Test] + public void TestFocusLostOnBackKey() + { + setLocalUserPlaying(true); + + assertChatFocused(false); + AddStep("press tab", () => InputManager.Key(Key.Tab)); + assertChatFocused(true); + AddStep("press escape", () => InputManager.Key(Key.Escape)); + assertChatFocused(false); + } + [Test] public void TestFocusOnTabKeyWhenNotExpanded() { diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/GameplayChatDisplay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/GameplayChatDisplay.cs index af0c50a848..ff24035d0e 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/GameplayChatDisplay.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/GameplayChatDisplay.cs @@ -10,6 +10,7 @@ using osu.Game.Input.Bindings; using osu.Game.Online.Rooms; using osu.Game.Screens.OnlinePlay.Match.Components; using osu.Game.Screens.Play; +using osuTK.Input; namespace osu.Game.Screens.OnlinePlay.Multiplayer { @@ -75,6 +76,15 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer { switch (e.Action) { + case GlobalAction.Back: + if (Textbox.HasFocus) + { + Schedule(() => Textbox.KillFocus()); + return true; + } + + break; + case GlobalAction.ToggleChatFocus: if (Textbox.HasFocus) { From 26fb9af0218fadff0bfebfa2985525db253cb236 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 Oct 2021 13:58:07 +0900 Subject: [PATCH 096/260] 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 f552aff2f2..9e0d31e7e5 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 32d6eeab29..a9555b555e 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index 92abab036a..1669573c26 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -93,7 +93,7 @@ - + From 370775af68ec8cdab445182c2b8d457c1f932eef Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 Oct 2021 14:05:07 +0900 Subject: [PATCH 097/260] Fix some surrounding code formatting --- osu.Game/Database/ArchiveModelManager.cs | 6 ++--- osu.Game/Graphics/ScreenshotManager.cs | 34 +++++++++++++----------- 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index a0ca23eaec..f3ed2d735b 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -462,10 +462,10 @@ namespace osu.Game.Database if (retrievedItem == null) throw new ArgumentException(@"Specified model could not be found", nameof(item)); - var filename = $"{getValidFilename(item.ToString())}{HandledExtensions.First()}"; + string filename = $"{getValidFilename(item.ToString())}{HandledExtensions.First()}"; - using (var outputStream = exportStorage.GetStream(filename, FileAccess.Write, FileMode.Create)) - ExportModelTo(retrievedItem, outputStream); + using (var stream = exportStorage.GetStream(filename, FileAccess.Write, FileMode.Create)) + ExportModelTo(retrievedItem, stream); exportStorage.PresentFileExternally(filename); } diff --git a/osu.Game/Graphics/ScreenshotManager.cs b/osu.Game/Graphics/ScreenshotManager.cs index c21986f6c4..e652f07239 100644 --- a/osu.Game/Graphics/ScreenshotManager.cs +++ b/osu.Game/Graphics/ScreenshotManager.cs @@ -109,40 +109,42 @@ namespace osu.Game.Graphics if (Interlocked.Decrement(ref screenShotTasks) == 0 && cursorVisibility.Value == false) cursorVisibility.Value = true; - var fileName = getFileName(); - if (fileName == null) return; + string filename = getFilename(); - var stream = storage.GetStream(fileName, FileAccess.Write); + if (filename == null) return; - switch (screenshotFormat.Value) + using (var stream = storage.GetStream(filename, FileAccess.Write)) { - case ScreenshotFormat.Png: - await image.SaveAsPngAsync(stream).ConfigureAwait(false); - break; + switch (screenshotFormat.Value) + { + case ScreenshotFormat.Png: + await image.SaveAsPngAsync(stream).ConfigureAwait(false); + break; - case ScreenshotFormat.Jpg: - const int jpeg_quality = 92; + case ScreenshotFormat.Jpg: + const int jpeg_quality = 92; - await image.SaveAsJpegAsync(stream, new JpegEncoder { Quality = jpeg_quality }).ConfigureAwait(false); - break; + await image.SaveAsJpegAsync(stream, new JpegEncoder { Quality = jpeg_quality }).ConfigureAwait(false); + break; - default: - throw new InvalidOperationException($"Unknown enum member {nameof(ScreenshotFormat)} {screenshotFormat.Value}."); + default: + throw new InvalidOperationException($"Unknown enum member {nameof(ScreenshotFormat)} {screenshotFormat.Value}."); + } } notificationOverlay.Post(new SimpleNotification { - Text = $"{fileName} saved!", + Text = $"{filename} saved!", Activated = () => { - storage.PresentFileExternally(fileName); + storage.PresentFileExternally(filename); return true; } }); } }); - private string getFileName() + private string getFilename() { var dt = DateTime.Now; var fileExt = screenshotFormat.ToString().ToLowerInvariant(); From de7a908be29e2dfb6dbddc3837f894467e07233d Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 26 Oct 2021 14:51:52 +0900 Subject: [PATCH 098/260] Cut distance snap lines at playfield border in catch editor --- .../Edit/CatchDistanceSnapGrid.cs | 36 ++++++++++++++----- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Edit/CatchDistanceSnapGrid.cs b/osu.Game.Rulesets.Catch/Edit/CatchDistanceSnapGrid.cs index d999115d81..137ac1fc59 100644 --- a/osu.Game.Rulesets.Catch/Edit/CatchDistanceSnapGrid.cs +++ b/osu.Game.Rulesets.Catch/Edit/CatchDistanceSnapGrid.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Lines; using osu.Framework.Graphics.Primitives; +using osu.Game.Rulesets.Catch.UI; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI.Scrolling; @@ -27,6 +28,8 @@ namespace osu.Game.Rulesets.Catch.Edit public float StartX { get; set; } + private const double max_vertical_line_length_in_time = CatchPlayfield.WIDTH / Catcher.BASE_SPEED * 2; + private readonly double[] velocities; private readonly List verticalPaths = new List(); @@ -63,27 +66,42 @@ namespace osu.Game.Rulesets.Catch.Edit { base.Update(); - float startY = hitObjectContainer.PositionAtTime(StartTime); + double currentTime = hitObjectContainer.Time.Current; for (int i = 0; i < velocities.Length; i++) { double velocity = velocities[i]; - var verticalPath = verticalPaths[i]; - // The line ends at the top of the screen. - double topScreenTime = hitObjectContainer.TimeAtPosition(-hitObjectContainer.DrawHeight, hitObjectContainer.Time.Current); - double endTime = Math.Max(StartTime, topScreenTime); + // The line ends at the top of the playfield. + double endTime = hitObjectContainer.TimeAtPosition(-hitObjectContainer.DrawHeight, currentTime); - float x = (float)((endTime - StartTime) * velocity); - float y = hitObjectContainer.PositionAtTime(endTime, StartTime); + // Non-vertical lines are cut at the sides of the playfield. + // Vertical lines are cut at some reasonable length. + if (velocity > 0) + endTime = Math.Min(endTime, StartTime + (CatchPlayfield.WIDTH - StartX) / velocity); + else if (velocity < 0) + endTime = Math.Min(endTime, StartTime + StartX / -velocity); + else + endTime = Math.Min(endTime, StartTime + max_vertical_line_length_in_time); Vector2[] lineVertices = verticalLineVertices[i]; - lineVertices[0] = new Vector2(StartX, startY); - lineVertices[1] = lineVertices[0] + new Vector2(x, y); + lineVertices[0] = calculatePosition(velocity, StartTime); + lineVertices[1] = calculatePosition(velocity, endTime); + var verticalPath = verticalPaths[i]; verticalPath.Vertices = verticalLineVertices[i]; verticalPath.OriginPosition = verticalPath.PositionInBoundingBox(Vector2.Zero); } + + Vector2 calculatePosition(double velocity, double time) + { + // Don't draw inverted lines. + time = Math.Max(time, StartTime); + + float x = StartX + (float)((time - StartTime) * velocity); + float y = hitObjectContainer.PositionAtTime(time, currentTime); + return new Vector2(x, y); + } } [CanBeNull] From 81fee02c2bc90553093304cfc938abd883faad45 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 Oct 2021 15:46:15 +0900 Subject: [PATCH 099/260] Fix slider paths being extended even when the last two points are equal --- osu.Game/Rulesets/Objects/SliderPath.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/osu.Game/Rulesets/Objects/SliderPath.cs b/osu.Game/Rulesets/Objects/SliderPath.cs index 9cc215589b..0dec0655b9 100644 --- a/osu.Game/Rulesets/Objects/SliderPath.cs +++ b/osu.Game/Rulesets/Objects/SliderPath.cs @@ -280,6 +280,13 @@ namespace osu.Game.Rulesets.Objects if (ExpectedDistance.Value is double expectedDistance && calculatedLength != expectedDistance) { + // In osu-stable, if the last two control points of a slider are equal, extension is not performed. + if (ControlPoints.Count >= 2 && ControlPoints[^1].Position == ControlPoints[^2].Position && expectedDistance > calculatedLength) + { + cumulativeLength.Add(calculatedLength); + return; + } + // The last length is always incorrect cumulativeLength.RemoveAt(cumulativeLength.Count - 1); From b259192a8aac895db3f2f1b2c1f6c21ca3ebb485 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 Oct 2021 16:06:02 +0900 Subject: [PATCH 100/260] Add failing test coverage showing hitobject local control points are defaults --- osu.Game.Tests/Visual/Editing/TestSceneEditorSaving.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneEditorSaving.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorSaving.cs index ab2bc4649a..af3d9beb69 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneEditorSaving.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneEditorSaving.cs @@ -56,6 +56,11 @@ namespace osu.Game.Tests.Visual.Editing checkMutations(); + // After placement these must be non-default as defaults are read-only. + AddAssert("Placed object has non-default control points", () => + editorBeatmap.HitObjects[0].SampleControlPoint != SampleControlPoint.DEFAULT && + editorBeatmap.HitObjects[0].DifficultyControlPoint != DifficultyControlPoint.DEFAULT); + AddStep("Save", () => InputManager.Keys(PlatformAction.Save)); checkMutations(); From dae7cdc4c50efeaec5d2fab48d10e25360f7f669 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 Oct 2021 15:59:48 +0900 Subject: [PATCH 101/260] Ensure `HitObject`-local control points are non-default after `ApplyDefaults` We will probably want to make this whole flow more efficient in the future, but this should suffice for now. --- osu.Game/Rulesets/Objects/HitObject.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Rulesets/Objects/HitObject.cs b/osu.Game/Rulesets/Objects/HitObject.cs index 035ebe10cb..a80b3d0fa5 100644 --- a/osu.Game/Rulesets/Objects/HitObject.cs +++ b/osu.Game/Rulesets/Objects/HitObject.cs @@ -119,6 +119,8 @@ namespace osu.Game.Rulesets.Objects DifficultyControlPoint = (DifficultyControlPoint)legacyInfo.DifficultyPointAt(StartTime).DeepClone(); DifficultyControlPoint.Time = StartTime; } + else if (DifficultyControlPoint == DifficultyControlPoint.DEFAULT) + DifficultyControlPoint = new DifficultyControlPoint(); ApplyDefaultsToSelf(controlPointInfo, difficulty); @@ -128,6 +130,8 @@ namespace osu.Game.Rulesets.Objects SampleControlPoint = (SampleControlPoint)legacyInfo.SamplePointAt(this.GetEndTime() + control_point_leniency).DeepClone(); SampleControlPoint.Time = this.GetEndTime() + control_point_leniency; } + else if (SampleControlPoint == SampleControlPoint.DEFAULT) + SampleControlPoint = new SampleControlPoint(); nestedHitObjects.Clear(); From ea55ba7ba9f4526129e37c484773d7a64c8568eb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 Oct 2021 16:08:21 +0900 Subject: [PATCH 102/260] Remove unused using statement --- osu.Game/Screens/OnlinePlay/Multiplayer/GameplayChatDisplay.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/GameplayChatDisplay.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/GameplayChatDisplay.cs index ff24035d0e..0e73f65f8b 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/GameplayChatDisplay.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/GameplayChatDisplay.cs @@ -10,7 +10,6 @@ using osu.Game.Input.Bindings; using osu.Game.Online.Rooms; using osu.Game.Screens.OnlinePlay.Match.Components; using osu.Game.Screens.Play; -using osuTK.Input; namespace osu.Game.Screens.OnlinePlay.Multiplayer { From e92def0ba28ef701f576d83e1cf8cd32ee34319f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 Oct 2021 17:19:29 +0900 Subject: [PATCH 103/260] Fix `LegacyBeatmapEncoder` using incorrect path length value --- osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index 1dc270ee63..7cd4244cd0 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -460,7 +460,7 @@ namespace osu.Game.Beatmaps.Formats var curveData = pathData as IHasPathWithRepeats; writer.Write(FormattableString.Invariant($"{(curveData?.RepeatCount ?? 0) + 1},")); - writer.Write(FormattableString.Invariant($"{pathData.Path.Distance},")); + writer.Write(FormattableString.Invariant($"{pathData.Path.ExpectedDistance.Value ?? pathData.Path.Distance},")); if (curveData != null) { From aa0762ebe20a1153dfdbadc50f517ac5642ac995 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 Oct 2021 17:35:03 +0900 Subject: [PATCH 104/260] Add test coverage for failing slider case --- .../Formats/LegacyBeatmapDecoderTest.cs | 17 +++++++++++++++++ .../duplicate-last-position-slider.osu | 19 +++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 osu.Game.Tests/Resources/duplicate-last-position-slider.osu diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs index cb12d03620..d37e09aa29 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs @@ -775,5 +775,22 @@ namespace osu.Game.Tests.Beatmaps.Formats Assert.That(seventh.ControlPoints[4].Type == null); } } + + [Test] + public void TestSliderLengthExtensionEdgeCase() + { + var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false }; + + using (var resStream = TestResources.OpenResource("duplicate-last-position-slider.osu")) + using (var stream = new LineBufferedReader(resStream)) + { + var decoded = decoder.Decode(stream); + + var path = ((IHasPath)decoded.HitObjects[0]).Path; + + Assert.That(path.ExpectedDistance.Value, Is.EqualTo(2)); + Assert.That(path.Distance, Is.EqualTo(1)); + } + } } } diff --git a/osu.Game.Tests/Resources/duplicate-last-position-slider.osu b/osu.Game.Tests/Resources/duplicate-last-position-slider.osu new file mode 100644 index 0000000000..782dd4263e --- /dev/null +++ b/osu.Game.Tests/Resources/duplicate-last-position-slider.osu @@ -0,0 +1,19 @@ +osu file format v14 + +[Difficulty] +HPDrainRate:7 +CircleSize:10 +OverallDifficulty:9 +ApproachRate:10 +SliderMultiplier:0.4 +SliderTickRate:1 + +[TimingPoints] +382,923.076923076923,3,2,1,75,1,0 +382,-1000,3,2,1,75,0,0 + +[HitObjects] + +// Importantly, the last position is specified twice. +// In this case, osu-stable doesn't extend the slider length even when the "expected" length is higher than the actual. +261,171,25305,6,0,B|262:171|262:171|262:171,1,2,8|0,0:0|0:0,0:0:0:0: From 708d40931b8009d3c036df32ad0487ab43abc6f1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 Oct 2021 16:36:54 +0900 Subject: [PATCH 105/260] Add back remaining attempt display on playlist room screen --- .../Playlists/PlaylistsRoomSubScreen.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs index d5e423a438..5f2e444165 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs @@ -153,6 +153,22 @@ namespace osu.Game.Screens.OnlinePlay.Playlists }, }, new Drawable[] + { + new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Alpha = Room.MaxAttempts.Value != null ? 1 : 0, + Margin = new MarginPadding { Bottom = 10 }, + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + new OverlinedHeader("Progress"), + new RoomLocalUserInfo(), + } + }, + }, + new Drawable[] { new OverlinedHeader("Leaderboard") }, @@ -162,6 +178,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists }, RowDimensions = new[] { + new Dimension(GridSizeMode.AutoSize), new Dimension(GridSizeMode.AutoSize), new Dimension(GridSizeMode.AutoSize), new Dimension(), From bffd9162ba15055260021053a2bcfc5b23f13fd2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 Oct 2021 16:40:45 +0900 Subject: [PATCH 106/260] Tint attempt count red when no attempts remain --- osu.Game/Screens/OnlinePlay/Components/RoomLocalUserInfo.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game/Screens/OnlinePlay/Components/RoomLocalUserInfo.cs b/osu.Game/Screens/OnlinePlay/Components/RoomLocalUserInfo.cs index 1fcf7f2277..3bad6cb183 100644 --- a/osu.Game/Screens/OnlinePlay/Components/RoomLocalUserInfo.cs +++ b/osu.Game/Screens/OnlinePlay/Components/RoomLocalUserInfo.cs @@ -14,6 +14,9 @@ namespace osu.Game.Screens.OnlinePlay.Components { private OsuSpriteText attemptDisplay; + [Resolved] + private OsuColour colours { get; set; } + public RoomLocalUserInfo() { AutoSizeAxes = Axes.Both; @@ -54,6 +57,9 @@ namespace osu.Game.Screens.OnlinePlay.Components { int remaining = MaxAttempts.Value.Value - UserScore.Value.PlaylistItemAttempts.Sum(a => a.Attempts); attemptDisplay.Text += $" ({remaining} remaining)"; + + if (remaining == 0) + attemptDisplay.Colour = colours.RedLight; } } else From 1c578f285accf3f17168708ba5c6aa1fd402f6ec Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 Oct 2021 16:54:55 +0900 Subject: [PATCH 107/260] Disable playlist start button when attempts have been exhausted --- .../Playlists/PlaylistsReadyButton.cs | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsReadyButton.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsReadyButton.cs index 9ac1fe1722..ea85a8bcc1 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsReadyButton.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsReadyButton.cs @@ -32,11 +32,32 @@ namespace osu.Game.Screens.OnlinePlay.Playlists Triangles.ColourLight = colours.GreenLight; } + private bool hasRemainingAttempts = true; + + protected override void LoadComplete() + { + base.LoadComplete(); + + userScore.BindValueChanged(aggregate => + { + if (maxAttempts.Value == null) + return; + + int remaining = maxAttempts.Value.Value - aggregate.NewValue.PlaylistItemAttempts.Sum(a => a.Attempts); + + hasRemainingAttempts = remaining > 0; + }); + } + protected override void Update() { base.Update(); - Enabled.Value = endDate.Value != null && DateTimeOffset.UtcNow.AddSeconds(30).AddMilliseconds(gameBeatmap.Value.Track.Length) < endDate.Value; + Enabled.Value = hasRemainingAttempts && enoughTimeLeft; } + + private bool enoughTimeLeft => + // This should probably consider the length of the currently selected item, rather than a constant 30 seconds. + endDate.Value != null && DateTimeOffset.UtcNow.AddSeconds(30).AddMilliseconds(gameBeatmap.Value.Track.Length) < endDate.Value; } } From 42d8d4fd380bcdc2fdd4a7b96178b7f4a551386f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 Oct 2021 16:55:24 +0900 Subject: [PATCH 108/260] Add tooltips to start button to explain why it's not clickable --- .../OnlinePlay/Components/ReadyButton.cs | 18 +++++++++++-- .../Playlists/PlaylistsReadyButton.cs | 25 +++++++++++++++++++ 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Components/ReadyButton.cs b/osu.Game/Screens/OnlinePlay/Components/ReadyButton.cs index 8f85608b29..5a4a0a0fba 100644 --- a/osu.Game/Screens/OnlinePlay/Components/ReadyButton.cs +++ b/osu.Game/Screens/OnlinePlay/Components/ReadyButton.cs @@ -3,13 +3,15 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Graphics.Cursor; +using osu.Framework.Localisation; using osu.Game.Graphics.UserInterface; using osu.Game.Online; using osu.Game.Online.Rooms; namespace osu.Game.Screens.OnlinePlay.Components { - public abstract class ReadyButton : TriangleButton + public abstract class ReadyButton : TriangleButton, IHasTooltip { public new readonly BindableBool Enabled = new BindableBool(); @@ -24,6 +26,18 @@ namespace osu.Game.Screens.OnlinePlay.Components Enabled.BindValueChanged(_ => updateState(), true); } - private void updateState() => base.Enabled.Value = availability.Value.State == DownloadState.LocallyAvailable && Enabled.Value; + private void updateState() => + base.Enabled.Value = availability.Value.State == DownloadState.LocallyAvailable && Enabled.Value; + + public virtual LocalisableString TooltipText + { + get + { + if (Enabled.Value) + return string.Empty; + + return "Beatmap not downloaded"; + } + } } } diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsReadyButton.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsReadyButton.cs index ea85a8bcc1..24f112ef0c 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsReadyButton.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsReadyButton.cs @@ -2,8 +2,10 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Localisation; using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Online.Rooms; @@ -16,6 +18,12 @@ namespace osu.Game.Screens.OnlinePlay.Playlists [Resolved(typeof(Room), nameof(Room.EndDate))] private Bindable endDate { get; set; } + [Resolved(typeof(Room), nameof(Room.MaxAttempts))] + private Bindable maxAttempts { get; set; } + + [Resolved(typeof(Room), nameof(Room.UserScore))] + private Bindable userScore { get; set; } + [Resolved] private IBindable gameBeatmap { get; set; } @@ -56,6 +64,23 @@ namespace osu.Game.Screens.OnlinePlay.Playlists Enabled.Value = hasRemainingAttempts && enoughTimeLeft; } + public override LocalisableString TooltipText + { + get + { + if (Enabled.Value) + return string.Empty; + + if (!enoughTimeLeft) + return "No time left!"; + + if (!hasRemainingAttempts) + return "Attempts exhausted!"; + + return base.TooltipText; + } + } + private bool enoughTimeLeft => // This should probably consider the length of the currently selected item, rather than a constant 30 seconds. endDate.Value != null && DateTimeOffset.UtcNow.AddSeconds(30).AddMilliseconds(gameBeatmap.Value.Track.Length) < endDate.Value; From c15bfb2cf44d569062961a7ed59aee738b7684bc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 Oct 2021 18:13:28 +0900 Subject: [PATCH 109/260] Handle bindable changes to fix new playlist creation not showing correct details --- .../OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs index 5f2e444165..6d2a426e70 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs @@ -37,6 +37,8 @@ namespace osu.Game.Screens.OnlinePlay.Playlists private MatchLeaderboard leaderboard; private SelectionPollingComponent selectionPollingComponent; + private FillFlowContainer progressSection; + public PlaylistsRoomSubScreen(Room room) : base(room, false) // Editing is temporarily not allowed. { @@ -67,6 +69,8 @@ namespace osu.Game.Screens.OnlinePlay.Playlists Schedule(() => SelectedItem.Value = Room.Playlist.FirstOrDefault()); } }, true); + + Room.MaxAttempts.BindValueChanged(attempts => progressSection.Alpha = Room.MaxAttempts.Value != null ? 1 : 0, true); } protected override Drawable CreateMainContent() => new GridContainer @@ -154,11 +158,11 @@ namespace osu.Game.Screens.OnlinePlay.Playlists }, new Drawable[] { - new FillFlowContainer + progressSection = new FillFlowContainer { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - Alpha = Room.MaxAttempts.Value != null ? 1 : 0, + Alpha = 0, Margin = new MarginPadding { Bottom = 10 }, Direction = FillDirection.Vertical, Children = new Drawable[] From 1147c6cbfc60584536237a15459b101c248a3e3c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 26 Oct 2021 19:56:54 +0900 Subject: [PATCH 110/260] Remove unnecessary default value --- osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs index e5cc08c886..e162f805a1 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs @@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Osu.Mods public class OsuModHidden : ModHidden, IHidesApproachCircles { [SettingSource("Only fade approach circles", "The main object body will not fade when enabled.")] - public Bindable OnlyFadeApproachCircles { get; } = new BindableBool(false); + public Bindable OnlyFadeApproachCircles { get; } = new BindableBool(); public override string Description => @"Play with no approach circles and fading circles/sliders."; public override double ScoreMultiplier => 1.06; From e5050b90a56a08bffbacb951d91cd83a4a30fba0 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 26 Oct 2021 20:09:48 +0900 Subject: [PATCH 111/260] Don't assume catcher dash speed is 2x the walk speed --- .../Beatmaps/CatchBeatmapProcessor.cs | 2 +- .../Edit/CatchHitObjectComposer.cs | 4 ++-- .../Replays/CatchAutoGenerator.cs | 11 ++++------- osu.Game.Rulesets.Catch/UI/Catcher.cs | 15 ++++++++++----- 4 files changed, 17 insertions(+), 15 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs index 87cc2c45e8..346a09cac8 100644 --- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs @@ -233,7 +233,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps int thisDirection = nextObject.EffectiveX > currentObject.EffectiveX ? 1 : -1; double timeToNext = nextObject.StartTime - currentObject.StartTime - 1000f / 60f / 4; // 1/4th of a frame of grace time, taken from osu-stable double distanceToNext = Math.Abs(nextObject.EffectiveX - currentObject.EffectiveX) - (lastDirection == thisDirection ? lastExcess : halfCatcherWidth); - float distanceToHyper = (float)(timeToNext * Catcher.BASE_SPEED - distanceToNext); + float distanceToHyper = (float)(timeToNext * Catcher.BASE_DASH_SPEED - distanceToNext); if (distanceToHyper < 0) { diff --git a/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs b/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs index 67055fb5e0..164f465438 100644 --- a/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs +++ b/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs @@ -51,8 +51,8 @@ namespace osu.Game.Rulesets.Catch.Edit LayerBelowRuleset.Add(distanceSnapGrid = new CatchDistanceSnapGrid(new[] { 0.0, - Catcher.BASE_SPEED, -Catcher.BASE_SPEED, - Catcher.BASE_SPEED / 2, -Catcher.BASE_SPEED / 2, + Catcher.BASE_DASH_SPEED, -Catcher.BASE_DASH_SPEED, + Catcher.BASE_WALK_SPEED, -Catcher.BASE_WALK_SPEED, })); } diff --git a/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs b/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs index 2fc05701db..7c84cb24f3 100644 --- a/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs +++ b/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs @@ -26,9 +26,6 @@ namespace osu.Game.Rulesets.Catch.Replays if (Beatmap.HitObjects.Count == 0) return; - // todo: add support for HT DT - const double dash_speed = Catcher.BASE_SPEED; - const double movement_speed = dash_speed / 2; float lastPosition = CatchPlayfield.CENTER_X; double lastTime = 0; @@ -47,8 +44,8 @@ namespace osu.Game.Rulesets.Catch.Replays // The case where positionChange > 0 and timeAvailable == 0 results in PositiveInfinity which provides expected beheaviour. double speedRequired = positionChange == 0 ? 0 : positionChange / timeAvailable; - bool dashRequired = speedRequired > movement_speed; - bool impossibleJump = speedRequired > movement_speed * 2; + bool dashRequired = speedRequired > Catcher.BASE_WALK_SPEED; + bool impossibleJump = speedRequired > Catcher.BASE_DASH_SPEED; // todo: get correct catcher size, based on difficulty CS. const float catcher_width_half = Catcher.BASE_SIZE * 0.3f * 0.5f; @@ -73,7 +70,7 @@ namespace osu.Game.Rulesets.Catch.Replays else if (dashRequired) { // we do a movement in two parts - the dash part then the normal part... - double timeAtNormalSpeed = positionChange / movement_speed; + double timeAtNormalSpeed = positionChange / Catcher.BASE_WALK_SPEED; double timeWeNeedToSave = timeAtNormalSpeed - timeAvailable; double timeAtDashSpeed = timeWeNeedToSave / 2; @@ -86,7 +83,7 @@ namespace osu.Game.Rulesets.Catch.Replays } else { - double timeBefore = positionChange / movement_speed; + double timeBefore = positionChange / Catcher.BASE_WALK_SPEED; addFrame(h.StartTime - timeBefore, lastPosition); addFrame(h.StartTime, h.EffectiveX); diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index 3745099010..a70dbcf25e 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -57,14 +57,19 @@ namespace osu.Game.Rulesets.Catch.UI public bool CatchFruitOnPlate { get; set; } = true; /// - /// The relative space to cover in 1 millisecond. based on 1 game pixel per millisecond as in osu-stable. + /// The speed of the catcher when the catcher is dashing. /// - public const double BASE_SPEED = 1.0; + public const double BASE_DASH_SPEED = 1.0; /// - /// The current speed of the catcher. + /// The speed of the catcher when the catcher is not dashing. /// - public double Speed => (Dashing ? 1 : 0.5) * BASE_SPEED * hyperDashModifier; + public const double BASE_WALK_SPEED = 0.5; + + /// + /// The current speed of the catcher with the hyper-dash modifier applied. + /// + public double Speed => (Dashing ? BASE_DASH_SPEED : BASE_WALK_SPEED) * hyperDashModifier; /// /// The amount by which caught fruit should be scaled down to fit on the plate. @@ -230,7 +235,7 @@ namespace osu.Game.Rulesets.Catch.UI double positionDifference = target.EffectiveX - X; var velocity = positionDifference / Math.Max(1.0, timeDifference - 1000.0 / 60.0); - SetHyperDashState(Math.Abs(velocity), target.EffectiveX); + SetHyperDashState(Math.Abs(velocity) / BASE_DASH_SPEED, target.EffectiveX); } else SetHyperDashState(); From 393d118eef783c14f5534710bdc7fbf99c971c76 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 26 Oct 2021 20:20:43 +0900 Subject: [PATCH 112/260] Fix compilation failure overlooked in merge --- osu.Game.Rulesets.Catch/Edit/CatchDistanceSnapGrid.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/Edit/CatchDistanceSnapGrid.cs b/osu.Game.Rulesets.Catch/Edit/CatchDistanceSnapGrid.cs index 137ac1fc59..9a78c7ff86 100644 --- a/osu.Game.Rulesets.Catch/Edit/CatchDistanceSnapGrid.cs +++ b/osu.Game.Rulesets.Catch/Edit/CatchDistanceSnapGrid.cs @@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Catch.Edit public float StartX { get; set; } - private const double max_vertical_line_length_in_time = CatchPlayfield.WIDTH / Catcher.BASE_SPEED * 2; + private const double max_vertical_line_length_in_time = CatchPlayfield.WIDTH / Catcher.BASE_WALK_SPEED; private readonly double[] velocities; From 411586bac36ae84807ab3f8076b280c5a0b8dcbc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 Oct 2021 22:12:56 +0900 Subject: [PATCH 113/260] 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 9e0d31e7e5..552675d706 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 a9555b555e..8ba6e41d53 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -37,7 +37,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 1669573c26..e55dbb3bfe 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -71,7 +71,7 @@ - + From 670fcea68d07b2f89ae2a70414ba6d960fca1228 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 26 Oct 2021 18:56:34 +0200 Subject: [PATCH 114/260] Rename some variables --- osu.Game.Rulesets.Osu/Mods/OsuModNoScope.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModNoScope.cs b/osu.Game.Rulesets.Osu/Mods/OsuModNoScope.cs index 026bb6c1cb..10de06a550 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModNoScope.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModNoScope.cs @@ -42,7 +42,7 @@ namespace osu.Game.Rulesets.Osu.Mods private IBindable isBreakTime; private PeriodTracker spinnerPeriods; - private float targetAlpha; + private float comboBasedAlpha; [SettingSource( "Hidden at combo", @@ -76,15 +76,15 @@ namespace osu.Game.Rulesets.Osu.Mods currentCombo = scoreProcessor.Combo.GetBoundCopy(); currentCombo.BindValueChanged(combo => { - targetAlpha = Math.Max(min_alpha, 1 - (float)combo.NewValue / HiddenComboCount.Value); + comboBasedAlpha = Math.Max(min_alpha, 1 - (float)combo.NewValue / HiddenComboCount.Value); }, true); } public virtual void Update(Playfield playfield) { - var overrideAlpha = isBreakTime.Value || spinnerPeriods.IsInAny(playfield.Clock.CurrentTime); - var targetOverriddenAlpha = overrideAlpha ? 1 : targetAlpha; - playfield.Cursor.Alpha = (float)Interpolation.Lerp(playfield.Cursor.Alpha, targetOverriddenAlpha, Math.Clamp(playfield.Time.Elapsed / transition_duration, 0, 1)); + var shouldAlwaysShowCursor = isBreakTime.Value || spinnerPeriods.IsInAny(playfield.Clock.CurrentTime); + var targetAlpha = shouldAlwaysShowCursor ? 1 : comboBasedAlpha; + playfield.Cursor.Alpha = (float)Interpolation.Lerp(playfield.Cursor.Alpha, targetAlpha, Math.Clamp(playfield.Time.Elapsed / transition_duration, 0, 1)); } } From 1a77e98537b30269e32472edd3aa504034a541eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 17 Oct 2021 15:52:40 +0200 Subject: [PATCH 115/260] Implement difficulty spectrum display --- .../TestSceneDifficultySpectrumDisplay.cs | 84 +++++++++++++ .../Drawables/DifficultySpectrumDisplay.cs | 114 ++++++++++++++++++ 2 files changed, 198 insertions(+) create mode 100644 osu.Game.Tests/Visual/Beatmaps/TestSceneDifficultySpectrumDisplay.cs create mode 100644 osu.Game/Beatmaps/Drawables/DifficultySpectrumDisplay.cs diff --git a/osu.Game.Tests/Visual/Beatmaps/TestSceneDifficultySpectrumDisplay.cs b/osu.Game.Tests/Visual/Beatmaps/TestSceneDifficultySpectrumDisplay.cs new file mode 100644 index 0000000000..8925c68ef4 --- /dev/null +++ b/osu.Game.Tests/Visual/Beatmaps/TestSceneDifficultySpectrumDisplay.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 System.Linq; +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.Drawables; +using osu.Game.Online.API.Requests.Responses; +using osuTK; + +namespace osu.Game.Tests.Visual.Beatmaps +{ + public class TestSceneDifficultySpectrumDisplay : OsuTestScene + { + private static APIBeatmapSet createBeatmapSetWith(params (int rulesetId, double stars)[] difficulties) => new APIBeatmapSet + { + Beatmaps = difficulties.Select(difficulty => new APIBeatmap + { + RulesetID = difficulty.rulesetId, + StarRating = difficulty.stars + }).ToList() + }; + + [Test] + public void TestSingleRuleset() + { + var beatmapSet = createBeatmapSetWith( + (rulesetId: 0, stars: 2.0), + (rulesetId: 0, stars: 3.2), + (rulesetId: 0, stars: 5.6)); + + createDisplay(beatmapSet); + } + + [Test] + public void TestMultipleRulesets() + { + var beatmapSet = createBeatmapSetWith( + (rulesetId: 0, stars: 2.0), + (rulesetId: 3, stars: 2.3), + (rulesetId: 0, stars: 3.2), + (rulesetId: 1, stars: 4.3), + (rulesetId: 0, stars: 5.6)); + + createDisplay(beatmapSet); + } + + [Test] + public void TestUnknownRuleset() + { + var beatmapSet = createBeatmapSetWith( + (rulesetId: 0, stars: 2.0), + (rulesetId: 3, stars: 2.3), + (rulesetId: 0, stars: 3.2), + (rulesetId: 1, stars: 4.3), + (rulesetId: 0, stars: 5.6), + (rulesetId: 15, stars: 7.8)); + + createDisplay(beatmapSet); + } + + [Test] + public void TestMaximumUncollapsed() + { + var beatmapSet = createBeatmapSetWith(Enumerable.Range(0, 12).Select(i => (rulesetId: i % 4, stars: 2.5 + i * 0.25)).ToArray()); + createDisplay(beatmapSet); + } + + [Test] + public void TestMinimumCollapsed() + { + var beatmapSet = createBeatmapSetWith(Enumerable.Range(0, 13).Select(i => (rulesetId: i % 4, stars: 2.5 + i * 0.25)).ToArray()); + createDisplay(beatmapSet); + } + + private void createDisplay(IBeatmapSetInfo beatmapSetInfo) => AddStep("create spectrum display", () => Child = new DifficultySpectrumDisplay(beatmapSetInfo) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Scale = new Vector2(3) + }); + } +} diff --git a/osu.Game/Beatmaps/Drawables/DifficultySpectrumDisplay.cs b/osu.Game/Beatmaps/Drawables/DifficultySpectrumDisplay.cs new file mode 100644 index 0000000000..a773f6b12c --- /dev/null +++ b/osu.Game/Beatmaps/Drawables/DifficultySpectrumDisplay.cs @@ -0,0 +1,114 @@ +// 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.Extensions.LocalisationExtensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Rulesets; +using osuTK; + +namespace osu.Game.Beatmaps.Drawables +{ + public class DifficultySpectrumDisplay : CompositeDrawable + { + public DifficultySpectrumDisplay(IBeatmapSetInfo beatmapSet) + { + AutoSizeAxes = Axes.Both; + + FillFlowContainer flow; + + InternalChild = flow = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Spacing = new Vector2(10, 0), + Direction = FillDirection.Horizontal, + }; + + // matching web: https://github.com/ppy/osu-web/blob/d06d8c5e735eb1f48799b1654b528e9a7afb0a35/resources/assets/lib/beatmapset-panel.tsx#L127 + bool collapsed = beatmapSet.Beatmaps.Count() > 12; + + foreach (var rulesetGrouping in beatmapSet.Beatmaps.GroupBy(beatmap => beatmap.Ruleset.OnlineID)) + { + flow.Add(new RulesetDifficultyGroup(rulesetGrouping.Key, rulesetGrouping, collapsed)); + } + } + + private class RulesetDifficultyGroup : FillFlowContainer + { + private readonly int rulesetId; + private readonly IEnumerable beatmapInfos; + private readonly bool collapsed; + + public RulesetDifficultyGroup(int rulesetId, IEnumerable beatmapInfos, bool collapsed) + { + this.rulesetId = rulesetId; + this.beatmapInfos = beatmapInfos; + this.collapsed = collapsed; + } + + [BackgroundDependencyLoader] + private void load(RulesetStore rulesets) + { + AutoSizeAxes = Axes.Both; + Spacing = new Vector2(1, 0); + Direction = FillDirection.Horizontal; + + var icon = rulesets.GetRuleset(rulesetId)?.CreateInstance()?.CreateIcon() ?? new SpriteIcon { Icon = FontAwesome.Regular.QuestionCircle }; + Add(icon.With(i => + { + i.Size = new Vector2(14); + i.Anchor = i.Origin = Anchor.Centre; + })); + + if (!collapsed) + { + foreach (var beatmapInfo in beatmapInfos.OrderBy(bi => bi.StarRating)) + Add(new DifficultyDot(beatmapInfo.StarRating)); + } + else + { + Add(new OsuSpriteText + { + Text = beatmapInfos.Count().ToLocalisableString(@"N0"), + Font = OsuFont.Default.With(size: 12), + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Padding = new MarginPadding { Bottom = 1 } + }); + } + } + } + + private class DifficultyDot : CircularContainer + { + private readonly double starDifficulty; + + public DifficultyDot(double starDifficulty) + { + this.starDifficulty = starDifficulty; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + Width = 4; + Height = 8; + Anchor = Origin = Anchor.Centre; + Masking = true; + + Child = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colours.ForStarDifficulty(starDifficulty) + }; + } + } + } +} From 3de994449df8831228ff1b0ed096b948732baf04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 17 Oct 2021 16:05:55 +0200 Subject: [PATCH 116/260] Add support for changing dimensions of difficulty spectrum display --- .../TestSceneDifficultySpectrumDisplay.cs | 28 ++++++++- .../Drawables/DifficultySpectrumDisplay.cs | 63 +++++++++++++++++-- 2 files changed, 86 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Beatmaps/TestSceneDifficultySpectrumDisplay.cs b/osu.Game.Tests/Visual/Beatmaps/TestSceneDifficultySpectrumDisplay.cs index 8925c68ef4..1f38b05879 100644 --- a/osu.Game.Tests/Visual/Beatmaps/TestSceneDifficultySpectrumDisplay.cs +++ b/osu.Game.Tests/Visual/Beatmaps/TestSceneDifficultySpectrumDisplay.cs @@ -13,6 +13,8 @@ namespace osu.Game.Tests.Visual.Beatmaps { public class TestSceneDifficultySpectrumDisplay : OsuTestScene { + private DifficultySpectrumDisplay display; + private static APIBeatmapSet createBeatmapSetWith(params (int rulesetId, double stars)[] difficulties) => new APIBeatmapSet { Beatmaps = difficulties.Select(difficulty => new APIBeatmap @@ -74,7 +76,31 @@ namespace osu.Game.Tests.Visual.Beatmaps createDisplay(beatmapSet); } - private void createDisplay(IBeatmapSetInfo beatmapSetInfo) => AddStep("create spectrum display", () => Child = new DifficultySpectrumDisplay(beatmapSetInfo) + [Test] + public void TestAdjustableDotSize() + { + var beatmapSet = createBeatmapSetWith( + (rulesetId: 0, stars: 2.0), + (rulesetId: 3, stars: 2.3), + (rulesetId: 0, stars: 3.2), + (rulesetId: 1, stars: 4.3), + (rulesetId: 0, stars: 5.6)); + + createDisplay(beatmapSet); + + AddStep("change dot dimensions", () => + { + display.DotSize = new Vector2(8, 12); + display.DotSpacing = 2; + }); + AddStep("change dot dimensions back", () => + { + display.DotSize = new Vector2(4, 8); + display.DotSpacing = 1; + }); + } + + private void createDisplay(IBeatmapSetInfo beatmapSetInfo) => AddStep("create spectrum display", () => Child = display = new DifficultySpectrumDisplay(beatmapSetInfo) { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game/Beatmaps/Drawables/DifficultySpectrumDisplay.cs b/osu.Game/Beatmaps/Drawables/DifficultySpectrumDisplay.cs index a773f6b12c..1feaa88350 100644 --- a/osu.Game/Beatmaps/Drawables/DifficultySpectrumDisplay.cs +++ b/osu.Game/Beatmaps/Drawables/DifficultySpectrumDisplay.cs @@ -18,12 +18,40 @@ namespace osu.Game.Beatmaps.Drawables { public class DifficultySpectrumDisplay : CompositeDrawable { + private Vector2 dotSize = new Vector2(4, 8); + + public Vector2 DotSize + { + get => dotSize; + set + { + dotSize = value; + + if (IsLoaded) + updateDotDimensions(); + } + } + + private float dotSpacing = 1; + + public float DotSpacing + { + get => dotSpacing; + set + { + dotSpacing = value; + + if (IsLoaded) + updateDotDimensions(); + } + } + + private readonly FillFlowContainer flow; + public DifficultySpectrumDisplay(IBeatmapSetInfo beatmapSet) { AutoSizeAxes = Axes.Both; - FillFlowContainer flow; - InternalChild = flow = new FillFlowContainer { AutoSizeAxes = Axes.Both, @@ -40,6 +68,21 @@ namespace osu.Game.Beatmaps.Drawables } } + protected override void LoadComplete() + { + base.LoadComplete(); + updateDotDimensions(); + } + + private void updateDotDimensions() + { + foreach (var group in flow) + { + group.DotSize = DotSize; + group.DotSpacing = DotSpacing; + } + } + private class RulesetDifficultyGroup : FillFlowContainer { private readonly int rulesetId; @@ -53,6 +96,20 @@ namespace osu.Game.Beatmaps.Drawables this.collapsed = collapsed; } + public Vector2 DotSize + { + set + { + foreach (var dot in Children.OfType()) + dot.Size = value; + } + } + + public float DotSpacing + { + set => Spacing = new Vector2(value, 0); + } + [BackgroundDependencyLoader] private void load(RulesetStore rulesets) { @@ -98,8 +155,6 @@ namespace osu.Game.Beatmaps.Drawables [BackgroundDependencyLoader] private void load(OsuColour colours) { - Width = 4; - Height = 8; Anchor = Origin = Anchor.Centre; Masking = true; From d80a73b63da0c275e0818c3de814db8f5ce38388 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 Oct 2021 12:53:02 +0900 Subject: [PATCH 117/260] Use explicit primitive types --- osu.Game.Rulesets.Osu/Mods/OsuModNoScope.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModNoScope.cs b/osu.Game.Rulesets.Osu/Mods/OsuModNoScope.cs index 10de06a550..501c0a55bd 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModNoScope.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModNoScope.cs @@ -82,8 +82,8 @@ namespace osu.Game.Rulesets.Osu.Mods public virtual void Update(Playfield playfield) { - var shouldAlwaysShowCursor = isBreakTime.Value || spinnerPeriods.IsInAny(playfield.Clock.CurrentTime); - var targetAlpha = shouldAlwaysShowCursor ? 1 : comboBasedAlpha; + bool shouldAlwaysShowCursor = isBreakTime.Value || spinnerPeriods.IsInAny(playfield.Clock.CurrentTime); + float targetAlpha = shouldAlwaysShowCursor ? 1 : comboBasedAlpha; playfield.Cursor.Alpha = (float)Interpolation.Lerp(playfield.Cursor.Alpha, targetAlpha, Math.Clamp(playfield.Time.Elapsed / transition_duration, 0, 1)); } } From 1d7e97625aa14927701eb3487721fe0c68d26778 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 Oct 2021 12:57:43 +0900 Subject: [PATCH 118/260] Update `var` usage inspections to disallow for built-in types --- .editorconfig | 2 +- osu.sln.DotSettings | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.editorconfig b/.editorconfig index 3c4997c88d..be5652954b 100644 --- a/.editorconfig +++ b/.editorconfig @@ -113,7 +113,7 @@ dotnet_style_qualification_for_event = false:warning dotnet_style_predefined_type_for_locals_parameters_members = true:warning dotnet_style_predefined_type_for_member_access = true:warning csharp_style_var_when_type_is_apparent = true:none -csharp_style_var_for_built_in_types = true:none +csharp_style_var_for_built_in_types = false:warning csharp_style_var_elsewhere = true:silent #Style - modifiers diff --git a/osu.sln.DotSettings b/osu.sln.DotSettings index 3af986543e..f35bdfce66 100644 --- a/osu.sln.DotSettings +++ b/osu.sln.DotSettings @@ -207,7 +207,7 @@ HINT WARNING WARNING - DO_NOT_SHOW + SUGGESTION DO_NOT_SHOW DO_NOT_SHOW WARNING @@ -301,6 +301,9 @@ True 200 CHOP_IF_LONG + UseExplicitType + UseVarWhenEvident + UseVarWhenEvident False False AABB From 6944151486e677bfd11f2390163aca9161defbbf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 Oct 2021 13:04:41 +0900 Subject: [PATCH 119/260] Apply batch fixing of built-in types using `var` --- osu.Desktop/OsuGameDesktop.cs | 4 +- osu.Desktop/Program.cs | 12 +-- .../TestSceneJuiceStreamSelectionBlueprint.cs | 2 +- .../TestSceneCatcher.cs | 4 +- .../TestSceneDrawableHitObjects.cs | 2 +- .../Preprocessing/CatchDifficultyHitObject.cs | 2 +- .../Mods/CatchModHidden.cs | 4 +- .../Replays/CatchFramedReplayInputHandler.cs | 2 +- .../Legacy/CatchLegacySkinTransformer.cs | 2 +- osu.Game.Rulesets.Catch/UI/Catcher.cs | 6 +- .../ManiaPlacementBlueprintTestScene.cs | 2 +- .../ManiaBeatmapConversionTest.cs | 2 +- .../ManiaColumnTypeTest.cs | 2 +- .../Beatmaps/ManiaBeatmapConverter.cs | 6 +- .../Legacy/DistanceObjectPatternGenerator.cs | 2 +- .../Legacy/HitObjectPatternGenerator.cs | 2 +- .../Difficulty/Skills/Strain.cs | 4 +- osu.Game.Rulesets.Mania/ManiaRuleset.cs | 2 +- .../MathUtils/LegacySortHelper.cs | 2 +- .../Mods/ManiaModMirror.cs | 2 +- .../Mods/ManiaModRandom.cs | 2 +- .../Objects/Drawables/DrawableHoldNoteTick.cs | 2 +- .../Replays/ManiaAutoGenerator.cs | 2 +- .../Legacy/ManiaLegacySkinTransformer.cs | 2 +- .../TestSceneHitCircleArea.cs | 2 +- .../TestSceneSpinnerRotation.cs | 2 +- .../Preprocessing/OsuDifficultyHitObject.cs | 2 +- .../Difficulty/Skills/Aim.cs | 2 +- .../Components/PathControlPointPiece.cs | 4 +- .../Edit/Checks/CheckLowDiffOverlaps.cs | 8 +- .../Edit/Checks/CheckTimeDistanceEquality.cs | 8 +- .../Edit/OsuRectangularPositionSnapGrid.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs | 8 +- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 8 +- osu.Game.Rulesets.Osu/Mods/OsuModSpunOut.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs | 28 ++--- .../Connections/FollowPointConnection.cs | 2 +- .../Connections/FollowPointLifetimeEntry.cs | 2 +- .../Connections/FollowPointRenderer.cs | 2 +- .../Skinning/Default/SnakingSliderBody.cs | 8 +- .../Default/SpinnerRotationTracker.cs | 4 +- .../Skinning/Legacy/LegacyMainCirclePiece.cs | 2 +- .../Skinning/Legacy/LegacyOldStyleSpinner.cs | 2 +- .../Statistics/AccuracyHeatmap.cs | 2 +- .../Utils/OsuHitObjectGenerationUtils.cs | 10 +- .../TestSceneSampleOutput.cs | 2 +- .../Beatmaps/TaikoBeatmapConverter.cs | 2 +- .../Objects/Drawables/DrawableHit.cs | 4 +- .../Objects/Drawables/DrawableSwell.cs | 6 +- .../Legacy/TaikoLegacySkinTransformer.cs | 6 +- .../UI/DrumRollHitContainer.cs | 2 +- .../UI/TaikoMascotAnimation.cs | 2 +- .../Beatmaps/Formats/LegacyDecoderTest.cs | 2 +- .../Beatmaps/IO/ImportBeatmapTest.cs | 30 +++--- .../Beatmaps/IO/LineBufferedReaderTest.cs | 2 +- .../Beatmaps/IO/OszArchiveReaderTest.cs | 4 +- .../Database/BeatmapImporterTests.cs | 26 ++--- .../NonVisual/BarLineGeneratorTest.cs | 2 +- .../NonVisual/CustomDataDirectoryTest.cs | 6 +- .../NonVisual/FramedReplayInputHandlerTest.cs | 2 +- .../NonVisual/LimitedCapacityQueueTest.cs | 6 +- .../StatefulMultiplayerClientTest.cs | 2 +- osu.Game.Tests/NonVisual/ReverseQueueTest.cs | 6 +- ...TestMultiplayerMessagePackSerialization.cs | 8 +- osu.Game.Tests/Resources/TestResources.cs | 4 +- .../Editing/TestSceneBeatDivisorControl.cs | 2 +- .../Editing/TestSceneEditorBeatmapCreation.cs | 2 +- .../Gameplay/TestSceneGameplayMenuOverlay.cs | 2 +- .../TestSceneMultiSpectatorLeaderboard.cs | 4 +- .../TestSceneMultiSpectatorScreen.cs | 4 +- .../Multiplayer/TestSceneMultiplayer.cs | 2 +- ...TestSceneMultiplayerGameplayLeaderboard.cs | 6 +- ...ceneMultiplayerGameplayLeaderboardTeams.cs | 2 +- .../TestSceneMultiplayerParticipantsList.cs | 2 +- .../Visual/Online/TestSceneChatOverlay.cs | 4 +- .../Visual/Online/TestSceneRankGraph.cs | 8 +- .../Online/TestSceneStandAloneChatDisplay.cs | 2 +- .../Visual/Online/TestSceneWikiHeader.cs | 2 +- .../Visual/Online/TestSceneWikiSidebar.cs | 4 +- .../TestSceneBreadcrumbControlHeader.cs | 8 +- .../TestSceneFooterButtonMods.cs | 4 +- .../Visual/UserInterface/TestSceneOsuIcon.cs | 2 +- osu.Game.Tournament/Components/SongBar.cs | 10 +- osu.Game.Tournament/IPC/FileBasedIPC.cs | 14 +-- osu.Game.Tournament/JsonPointConverter.cs | 2 +- osu.Game.Tournament/Models/TournamentTeam.cs | 8 +- .../Components/TournamentMatchScoreDisplay.cs | 2 +- .../Components/DrawableTournamentMatch.cs | 6 +- .../Screens/Ladder/LadderDragContainer.cs | 2 +- .../Screens/Setup/ResolutionSelector.cs | 2 +- .../Screens/Setup/StablePathSelectScreen.cs | 4 +- osu.Game.Tournament/TournamentGame.cs | 2 +- osu.Game.Tournament/TournamentGameBase.cs | 2 +- osu.Game/Audio/Effects/AudioFilter.cs | 2 +- osu.Game/Beatmaps/Beatmap.cs | 2 +- osu.Game/Beatmaps/BeatmapInfoExtensions.cs | 2 +- .../Beatmaps/BeatmapMetadataInfoExtensions.cs | 4 +- .../ControlPoints/ControlPointInfo.cs | 4 +- osu.Game/Beatmaps/DifficultyRecommender.cs | 4 +- .../Beatmaps/Formats/LegacyBeatmapEncoder.cs | 2 +- osu.Game/Beatmaps/Formats/LegacyDecoder.cs | 4 +- .../Formats/LegacyStoryboardDecoder.cs | 102 +++++++++--------- osu.Game/Beatmaps/Formats/Parsing.cs | 6 +- osu.Game/Beatmaps/MetadataUtils.cs | 2 +- osu.Game/Beatmaps/WorkingBeatmapCache.cs | 2 +- osu.Game/Collections/CollectionManager.cs | 8 +- osu.Game/Configuration/OsuConfigManager.cs | 4 +- osu.Game/Database/ArchiveModelManager.cs | 2 +- osu.Game/Database/DatabaseBackedStore.cs | 2 +- osu.Game/Database/RealmContextFactory.cs | 6 +- osu.Game/Database/StableImportManager.cs | 2 +- osu.Game/Database/UserLookupCache.cs | 2 +- osu.Game/Graphics/Backgrounds/Triangles.cs | 2 +- .../Containers/ConstrainedIconContainer.cs | 2 +- .../Containers/LogoTrackingContainer.cs | 2 +- .../Graphics/Containers/SectionsContainer.cs | 4 +- .../SelectionCycleFillFlowContainer.cs | 2 +- osu.Game/Graphics/Cursor/MenuCursor.cs | 2 +- osu.Game/Graphics/ErrorTextFlowContainer.cs | 2 +- osu.Game/Graphics/ParticleExplosion.cs | 4 +- osu.Game/Graphics/ParticleSpewer.cs | 14 +-- osu.Game/Graphics/ScreenshotManager.cs | 6 +- .../UserInterface/BreadcrumbControl.cs | 4 +- .../Graphics/UserInterface/OsuSliderBar.cs | 4 +- .../Graphics/UserInterface/StarCounter.cs | 4 +- .../Graphics/UserInterface/TwoLayerButton.cs | 2 +- osu.Game/IO/LineBufferedReader.cs | 4 +- .../Converters/TypedListConverter.cs | 4 +- osu.Game/IO/StableStorage.cs | 4 +- osu.Game/IO/WrappedStorage.cs | 2 +- .../Bindings/DatabasedKeyBindingContainer.cs | 2 +- osu.Game/Input/RealmKeyBindingStore.cs | 2 +- .../ResourceManagerLocalisationStore.cs | 2 +- osu.Game/Online/API/APIDownloadRequest.cs | 2 +- osu.Game/Online/Chat/Channel.cs | 4 +- osu.Game/Online/Chat/ChannelManager.cs | 4 +- osu.Game/Online/Chat/MessageFormatter.cs | 22 ++-- osu.Game/Online/Chat/NowPlayingCommand.cs | 2 +- osu.Game/Online/Chat/StandAloneChatDisplay.cs | 2 +- osu.Game/Online/Leaderboards/Leaderboard.cs | 8 +- .../Rooms/IndexPlaylistScoresRequest.cs | 2 +- osu.Game/Online/Spectator/SpectatorClient.cs | 2 +- osu.Game/OsuGame.cs | 10 +- .../BeatmapSet/BeatmapRulesetTabItem.cs | 2 +- osu.Game/Overlays/BeatmapSet/Info.cs | 2 +- .../Scores/ScoreTableRowBackground.cs | 2 +- osu.Game/Overlays/BeatmapSet/SuccessRate.cs | 2 +- osu.Game/Overlays/ChatOverlay.cs | 2 +- .../Comments/CommentMarkdownContainer.cs | 2 +- osu.Game/Overlays/Comments/DrawableComment.cs | 4 +- osu.Game/Overlays/Comments/VotePill.cs | 2 +- .../Dashboard/CurrentlyPlayingDisplay.cs | 4 +- .../Friends/FriendOnlineStreamControl.cs | 4 +- osu.Game/Overlays/HoldToConfirmOverlay.cs | 2 +- osu.Game/Overlays/Mods/ModSection.cs | 4 +- osu.Game/Overlays/MusicController.cs | 6 +- osu.Game/Overlays/News/Sidebar/NewsSidebar.cs | 4 +- osu.Game/Overlays/News/Sidebar/YearsPanel.cs | 4 +- .../Header/Components/PreviousUsernames.cs | 2 +- .../Profile/Header/Components/RankGraph.cs | 2 +- .../Profile/Header/MedalHeaderContainer.cs | 2 +- .../Sections/Historical/ProfileLineChart.cs | 18 ++-- .../Sections/Audio/AudioDevicesSettings.cs | 2 +- .../Sections/Input/KeyBindingsSubsection.cs | 2 +- .../Settings/Sections/Input/MouseSettings.cs | 4 +- .../Sections/Input/RotationPresetButtons.cs | 2 +- .../Sections/Input/RulesetBindingsSection.cs | 2 +- .../Overlays/Settings/Sections/SkinSection.cs | 2 +- .../Overlays/Settings/SettingsNumberBox.cs | 2 +- osu.Game/Overlays/TabControlOverlayHeader.cs | 2 +- osu.Game/Overlays/Toolbar/ToolbarButton.cs | 2 +- osu.Game/Overlays/Volume/VolumeMeter.cs | 2 +- .../Wiki/Markdown/WikiNoticeContainer.cs | 2 +- osu.Game/Overlays/Wiki/WikiMainPage.cs | 4 +- osu.Game/Overlays/WikiOverlay.cs | 2 +- .../Difficulty/DifficultyCalculator.cs | 4 +- .../Rulesets/Edit/Checks/CheckAudioInVideo.cs | 2 +- .../Rulesets/Edit/Checks/CheckAudioQuality.cs | 2 +- .../Edit/Checks/CheckBackgroundQuality.cs | 2 +- .../Rulesets/Edit/Checks/CheckFewHitsounds.cs | 6 +- .../Rulesets/Edit/Checks/CheckFilePresence.cs | 4 +- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 4 +- osu.Game/Rulesets/Mods/ModBarrelRoll.cs | 4 +- osu.Game/Rulesets/Objects/BarLineGenerator.cs | 2 +- .../Objects/Drawables/DrawableHitObject.cs | 2 +- .../Objects/Legacy/ConvertHitObjectParser.cs | 2 +- .../Rulesets/Objects/SliderEventGenerator.cs | 16 +-- .../Rulesets/Objects/SliderPathExtensions.cs | 2 +- osu.Game/Rulesets/RulesetInfo.cs | 2 +- osu.Game/Rulesets/RulesetStore.cs | 6 +- osu.Game/Rulesets/Scoring/HitWindows.cs | 2 +- .../Algorithms/ConstantScrollAlgorithm.cs | 2 +- .../Algorithms/SequentialScrollAlgorithm.cs | 4 +- osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs | 16 +-- osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs | 2 +- .../Scoring/Legacy/ScoreInfoExtensions.cs | 2 +- osu.Game/Scoring/LegacyDatabasedScore.cs | 2 +- osu.Game/Scoring/ScoreManager.cs | 2 +- osu.Game/Screens/Edit/BindableBeatDivisor.cs | 2 +- .../Compose/Components/BeatDivisorControl.cs | 4 +- .../Compose/Components/BlueprintContainer.cs | 6 +- .../Compose/Components/DistanceSnapGrid.cs | 4 +- .../Components/EditorSelectionHandler.cs | 4 +- .../Timeline/TimelineBlueprintContainer.cs | 2 +- .../Timeline/TimelineHitObjectBlueprint.cs | 8 +- .../Timeline/TimelineTickDisplay.cs | 6 +- osu.Game/Screens/Edit/Editor.cs | 2 +- osu.Game/Screens/Edit/EditorBeatmap.cs | 4 +- osu.Game/Screens/Edit/EditorChangeHandler.cs | 2 +- .../Edit/LegacyEditorBeatmapPatcher.cs | 2 +- osu.Game/Screens/Edit/Timing/GroupSection.cs | 2 +- osu.Game/Screens/Menu/IntroScreen.cs | 2 +- osu.Game/Screens/Menu/OsuLogo.cs | 4 +- .../Components/OnlinePlayBackgroundScreen.cs | 2 +- .../Participants/ParticipantPanel.cs | 2 +- .../Spectate/MultiSpectatorScreen.cs | 10 +- osu.Game/Screens/Play/Break/BreakInfoLine.cs | 2 +- osu.Game/Screens/Play/BreakTracker.cs | 2 +- osu.Game/Screens/Play/HUD/FailingLayer.cs | 2 +- .../HUD/HitErrorMeters/BarHitErrorMeter.cs | 6 +- .../Screens/Play/HUD/MatchScoreDisplay.cs | 2 +- .../HUD/MultiplayerGameplayLeaderboard.cs | 2 +- osu.Game/Screens/Play/SongProgressGraph.cs | 8 +- osu.Game/Screens/Play/SongProgressInfo.cs | 2 +- osu.Game/Screens/Play/SquareGraph.cs | 2 +- osu.Game/Screens/Play/SubmittingPlayer.cs | 2 +- .../Expanded/ExpandedPanelMiddleContent.cs | 4 +- osu.Game/Screens/Ranking/ResultsScreen.cs | 2 +- .../Ranking/Statistics/UnstableRate.cs | 8 +- osu.Game/Screens/Select/BeatmapDetails.cs | 2 +- .../Select/Carousel/CarouselBeatmap.cs | 6 +- .../Select/Carousel/CarouselBeatmapSet.cs | 4 +- .../Carousel/DrawableCarouselBeatmapSet.cs | 2 +- .../Screens/Select/Details/FailRetryGraph.cs | 8 +- .../Screens/Select/Details/UserRatings.cs | 4 +- osu.Game/Screens/Select/FilterControl.cs | 2 +- osu.Game/Screens/Select/FilterQueryParser.cs | 8 +- osu.Game/Screens/Spectate/SpectatorScreen.cs | 14 +-- osu.Game/Skinning/DefaultSkin.cs | 2 +- .../Skinning/Editor/SkinBlueprintContainer.cs | 2 +- osu.Game/Skinning/LegacyManiaSkinDecoder.cs | 2 +- osu.Game/Skinning/LegacySkin.cs | 10 +- osu.Game/Skinning/LegacySkinDecoder.cs | 2 +- osu.Game/Skinning/LegacySkinResourceStore.cs | 4 +- osu.Game/Skinning/LegacySpriteText.cs | 2 +- osu.Game/Skinning/ResourceStoreBackedSkin.cs | 2 +- osu.Game/Skinning/Skin.cs | 2 +- osu.Game/Stores/BeatmapImporter.cs | 2 +- osu.Game/Stores/RealmArchiveModelImporter.cs | 2 +- osu.Game/Stores/RealmRulesetStore.cs | 6 +- osu.Game/Storyboards/CommandLoop.cs | 4 +- osu.Game/Storyboards/CommandTimeline.cs | 2 +- osu.Game/Storyboards/CommandTimelineGroup.cs | 2 +- .../Drawables/DrawableStoryboardVideo.cs | 2 +- osu.Game/Storyboards/Storyboard.cs | 4 +- .../Tests/Beatmaps/BeatmapConversionTest.cs | 4 +- .../Beatmaps/DifficultyCalculatorTest.cs | 2 +- .../Visual/DependencyProvidingContainer.cs | 2 +- osu.Game/Tests/Visual/SkinnableTestScene.cs | 4 +- osu.Game/Updater/SimpleUpdateManager.cs | 2 +- osu.Game/Updater/UpdateManager.cs | 4 +- osu.Game/Utils/StatelessRNG.cs | 2 +- 263 files changed, 572 insertions(+), 572 deletions(-) diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index 4de1e84fbf..19e1252a4d 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -156,7 +156,7 @@ namespace osu.Desktop { lock (importableFiles) { - var firstExtension = Path.GetExtension(filePaths.First()); + string firstExtension = Path.GetExtension(filePaths.First()); if (filePaths.Any(f => Path.GetExtension(f) != firstExtension)) return; @@ -177,7 +177,7 @@ namespace osu.Desktop { Logger.Log($"Handling batch import of {importableFiles.Count} files"); - var paths = importableFiles.ToArray(); + string[] paths = importableFiles.ToArray(); importableFiles.Clear(); Task.Factory.StartNew(() => Import(paths), TaskCreationOptions.LongRunning); diff --git a/osu.Desktop/Program.cs b/osu.Desktop/Program.cs index 8ccd23b418..898f7d5105 100644 --- a/osu.Desktop/Program.cs +++ b/osu.Desktop/Program.cs @@ -22,17 +22,17 @@ namespace osu.Desktop public static int Main(string[] args) { // Back up the cwd before DesktopGameHost changes it - var cwd = Environment.CurrentDirectory; + string cwd = Environment.CurrentDirectory; string gameName = base_game_name; bool tournamentClient = false; - foreach (var arg in args) + foreach (string arg in args) { - var split = arg.Split('='); + string[] split = arg.Split('='); - var key = split[0]; - var val = split.Length > 1 ? split[1] : string.Empty; + string key = split[0]; + string val = split.Length > 1 ? split[1] : string.Empty; switch (key) { @@ -62,7 +62,7 @@ namespace osu.Desktop { var importer = new ArchiveImportIPCChannel(host); - foreach (var file in args) + foreach (string file in args) { Console.WriteLine(@"Importing {0}", file); if (!importer.ImportAsync(Path.GetFullPath(file, cwd)).Wait(3000)) diff --git a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs index 155d033dd0..fb77fb1efd 100644 --- a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs @@ -49,7 +49,7 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor AddAssert("correct outline count", () => { - var expected = hitObject.NestedHitObjects.Count(h => !(h is TinyDroplet)); + int expected = hitObject.NestedHitObjects.Count(h => !(h is TinyDroplet)); return this.ChildrenOfType().Count() == expected; }); AddAssert("correct vertex piece count", () => diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs index f291bfed13..29a7b03ad3 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatcher.cs @@ -103,7 +103,7 @@ namespace osu.Game.Rulesets.Catch.Tests [Test] public void TestCatcherCatchWidth() { - var halfWidth = Catcher.CalculateCatchWidth(new BeatmapDifficulty { CircleSize = 0 }) / 2; + float halfWidth = Catcher.CalculateCatchWidth(new BeatmapDifficulty { CircleSize = 0 }) / 2; AddStep("catch fruit", () => { attemptCatch(new Fruit { X = -halfWidth + 1 }); @@ -237,7 +237,7 @@ namespace osu.Game.Rulesets.Catch.Tests private void attemptCatch(Func hitObject, int count) { - for (var i = 0; i < count; i++) + for (int i = 0; i < count; i++) attemptCatch(hitObject(), out _, out _); } diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjects.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjects.cs index e7c7dc3c98..3c61eb19e5 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjects.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjects.cs @@ -118,7 +118,7 @@ namespace osu.Game.Rulesets.Catch.Tests private void spawnJuiceStream(bool hit = false) { - var xCoords = getXCoords(hit); + float xCoords = getXCoords(hit); var juice = new JuiceStream { diff --git a/osu.Game.Rulesets.Catch/Difficulty/Preprocessing/CatchDifficultyHitObject.cs b/osu.Game.Rulesets.Catch/Difficulty/Preprocessing/CatchDifficultyHitObject.cs index e19098c580..b22ec93b43 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/Preprocessing/CatchDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/Preprocessing/CatchDifficultyHitObject.cs @@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Preprocessing : base(hitObject, lastObject, clockRate) { // We will scale everything by this factor, so we can assume a uniform CircleSize among beatmaps. - var scalingFactor = normalized_hitobject_radius / halfCatcherWidth; + float scalingFactor = normalized_hitobject_radius / halfCatcherWidth; NormalizedPosition = BaseObject.EffectiveX * scalingFactor; LastNormalizedPosition = LastObject.EffectiveX * scalingFactor; diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModHidden.cs b/osu.Game.Rulesets.Catch/Mods/CatchModHidden.cs index d48edbcd74..4b6f79df88 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModHidden.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModHidden.cs @@ -52,8 +52,8 @@ namespace osu.Game.Rulesets.Catch.Mods { var hitObject = drawable.HitObject; - var offset = hitObject.TimePreempt * fade_out_offset_multiplier; - var duration = offset - hitObject.TimePreempt * fade_out_duration_multiplier; + double offset = hitObject.TimePreempt * fade_out_offset_multiplier; + double duration = offset - hitObject.TimePreempt * fade_out_duration_multiplier; using (drawable.BeginAbsoluteSequence(hitObject.StartTime - offset)) drawable.FadeOut(duration); diff --git a/osu.Game.Rulesets.Catch/Replays/CatchFramedReplayInputHandler.cs b/osu.Game.Rulesets.Catch/Replays/CatchFramedReplayInputHandler.cs index 137328b1c3..bd742ce6a6 100644 --- a/osu.Game.Rulesets.Catch/Replays/CatchFramedReplayInputHandler.cs +++ b/osu.Game.Rulesets.Catch/Replays/CatchFramedReplayInputHandler.cs @@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Catch.Replays public override void CollectPendingInputs(List inputs) { - var position = Interpolation.ValueAt(CurrentTime, StartFrame.Position, EndFrame.Position, StartFrame.Time, EndFrame.Time); + float position = Interpolation.ValueAt(CurrentTime, StartFrame.Position, EndFrame.Position, StartFrame.Time, EndFrame.Time); inputs.Add(new CatchReplayState { diff --git a/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs b/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs index 10fc4e78b2..7177a18cc3 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs @@ -66,7 +66,7 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy return null; case CatchSkinComponents.Catcher: - var version = GetConfig(LegacySkinConfiguration.LegacySetting.Version)?.Value ?? 1; + decimal version = GetConfig(LegacySkinConfiguration.LegacySetting.Version)?.Value ?? 1; if (version < 2.3m) { diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index 3745099010..1061bd2fc9 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -226,9 +226,9 @@ namespace osu.Game.Rulesets.Catch.UI if (result.IsHit && hitObject.HyperDash) { var target = hitObject.HyperDashTarget; - var timeDifference = target.StartTime - hitObject.StartTime; + double timeDifference = target.StartTime - hitObject.StartTime; double positionDifference = target.EffectiveX - X; - var velocity = positionDifference / Math.Max(1.0, timeDifference - 1000.0 / 60.0); + double velocity = positionDifference / Math.Max(1.0, timeDifference - 1000.0 / 60.0); SetHyperDashState(Math.Abs(velocity), target.EffectiveX); } @@ -266,7 +266,7 @@ namespace osu.Game.Rulesets.Catch.UI /// When this catcher crosses this position, this catcher ends hyper-dashing. public void SetHyperDashState(double modifier = 1, float targetPosition = -1) { - var wasHyperDashing = HyperDashing; + bool wasHyperDashing = HyperDashing; if (modifier <= 1 || X == targetPosition) { diff --git a/osu.Game.Rulesets.Mania.Tests/Editor/ManiaPlacementBlueprintTestScene.cs b/osu.Game.Rulesets.Mania.Tests/Editor/ManiaPlacementBlueprintTestScene.cs index ece523e84c..94f385bbf1 100644 --- a/osu.Game.Rulesets.Mania.Tests/Editor/ManiaPlacementBlueprintTestScene.cs +++ b/osu.Game.Rulesets.Mania.Tests/Editor/ManiaPlacementBlueprintTestScene.cs @@ -43,7 +43,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Editor protected override SnapResult SnapForBlueprint(PlacementBlueprint blueprint) { - var time = column.TimeAtScreenSpacePosition(InputManager.CurrentState.Mouse.Position); + double time = column.TimeAtScreenSpacePosition(InputManager.CurrentState.Mouse.Position); var pos = column.ScreenSpacePositionAtTime(time); return new SnapResult(pos, time, column); diff --git a/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapConversionTest.cs b/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapConversionTest.cs index 3d4bc4748b..948f088b4e 100644 --- a/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapConversionTest.cs +++ b/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapConversionTest.cs @@ -111,7 +111,7 @@ namespace osu.Game.Rulesets.Mania.Tests public int CompareTo(ConvertValue other) { - var result = StartTime.CompareTo(other.StartTime); + int result = StartTime.CompareTo(other.StartTime); if (result != 0) return result; diff --git a/osu.Game.Rulesets.Mania.Tests/ManiaColumnTypeTest.cs b/osu.Game.Rulesets.Mania.Tests/ManiaColumnTypeTest.cs index 40a6e1fdae..66fe6d8cc5 100644 --- a/osu.Game.Rulesets.Mania.Tests/ManiaColumnTypeTest.cs +++ b/osu.Game.Rulesets.Mania.Tests/ManiaColumnTypeTest.cs @@ -43,7 +43,7 @@ namespace osu.Game.Rulesets.Mania.Tests private IEnumerable getResults(StageDefinition definition) { - for (var i = 0; i < definition.Columns; i++) + for (int i = 0; i < definition.Columns; i++) yield return definition.GetTypeOfColumn(i); } } diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs index ebfbaccd31..9d0aaec2ba 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs @@ -42,8 +42,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps { IsForCurrentRuleset = beatmap.BeatmapInfo.Ruleset.Equals(ruleset.RulesetInfo); - var roundedCircleSize = Math.Round(beatmap.Difficulty.CircleSize); - var roundedOverallDifficulty = Math.Round(beatmap.Difficulty.OverallDifficulty); + double roundedCircleSize = Math.Round(beatmap.Difficulty.CircleSize); + double roundedOverallDifficulty = Math.Round(beatmap.Difficulty.OverallDifficulty); if (IsForCurrentRuleset) { @@ -73,7 +73,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps public static int GetColumnCountForNonConvert(BeatmapInfo beatmapInfo) { - var roundedCircleSize = Math.Round(beatmapInfo.BaseDifficulty.CircleSize); + double roundedCircleSize = Math.Round(beatmapInfo.BaseDifficulty.CircleSize); return (int)Math.Max(1, roundedCircleSize); } diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs index 1ed045f7e0..77538cc2ac 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs @@ -493,7 +493,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy if (!(HitObject is IHasPathWithRepeats curveData)) return null; - var index = SegmentDuration == 0 ? 0 : (time - StartTime) / SegmentDuration; + int index = SegmentDuration == 0 ? 0 : (time - StartTime) / SegmentDuration; // avoid slicing the list & creating copies, if at all possible. return index == 0 ? curveData.NodeSamples : curveData.NodeSamples.Skip(index).ToList(); diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs index 54c37e9742..53b059b4e2 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs @@ -302,7 +302,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy var pattern = new Pattern(); - int noteCount = getRandomNoteCountMirrored(centreProbability, p2, p3, out var addToCentre); + int noteCount = getRandomNoteCountMirrored(centreProbability, p2, p3, out bool addToCentre); int columnLimit = (TotalColumns % 2 == 0 ? TotalColumns : TotalColumns - 1) / 2; int nextColumn = GetRandomColumn(upperBound: columnLimit); diff --git a/osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs b/osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs index 01d930d585..ab6bd78ece 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs @@ -35,8 +35,8 @@ namespace osu.Game.Rulesets.Mania.Difficulty.Skills protected override double StrainValueOf(DifficultyHitObject current) { var maniaCurrent = (ManiaDifficultyHitObject)current; - var endTime = maniaCurrent.EndTime; - var column = maniaCurrent.BaseObject.Column; + double endTime = maniaCurrent.EndTime; + int column = maniaCurrent.BaseObject.Column; double holdFactor = 1.0; // Factor to all additional strains in case something else is held double holdAddition = 0; // Addition to the current note in case it's a hold and has to be released awkwardly diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index 1f79dae280..a9b16c61d4 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -316,7 +316,7 @@ namespace osu.Game.Rulesets.Mania case PlayfieldType.Dual: { - var keys = getDualStageKeyCount(variant); + int keys = getDualStageKeyCount(variant); return $"{keys}K + {keys}K"; } } diff --git a/osu.Game.Rulesets.Mania/MathUtils/LegacySortHelper.cs b/osu.Game.Rulesets.Mania/MathUtils/LegacySortHelper.cs index 0f4829028f..a0e2958bae 100644 --- a/osu.Game.Rulesets.Mania/MathUtils/LegacySortHelper.cs +++ b/osu.Game.Rulesets.Mania/MathUtils/LegacySortHelper.cs @@ -122,7 +122,7 @@ namespace osu.Game.Rulesets.Mania.MathUtils while (i <= n / 2) { - var child = 2 * i; + int child = 2 * i; if (child < n && comparer.Compare(keys[lo + child - 1], keys[lo + child]) < 0) { diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs index f01884c97f..9c3744ea98 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs @@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Mania.Mods public void ApplyToBeatmap(IBeatmap beatmap) { - var availableColumns = ((ManiaBeatmap)beatmap).TotalColumns; + int availableColumns = ((ManiaBeatmap)beatmap).TotalColumns; beatmap.HitObjects.OfType().ForEach(h => h.Column = availableColumns - 1 - h.Column); } diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModRandom.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModRandom.cs index 6f2d4fe91e..f5d1a34d73 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModRandom.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModRandom.cs @@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Mania.Mods Seed.Value ??= RNG.Next(); var rng = new Random((int)Seed.Value); - var availableColumns = ((ManiaBeatmap)beatmap).TotalColumns; + int availableColumns = ((ManiaBeatmap)beatmap).TotalColumns; var shuffledColumns = Enumerable.Range(0, availableColumns).OrderBy(item => rng.Next()).ToList(); beatmap.HitObjects.OfType().ForEach(h => h.Column = shuffledColumns[h.Column]); diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs index f040dad135..cffa79322e 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs @@ -97,7 +97,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables if (Time.Current < HitObject.StartTime) return; - var startTime = holdStartTime?.Invoke(); + double? startTime = holdStartTime?.Invoke(); if (startTime == null || startTime > HitObject.StartTime) ApplyResult(r => r.Type = r.Judgement.MinResult); diff --git a/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs b/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs index 517b708691..efe144ac03 100644 --- a/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs +++ b/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs @@ -75,7 +75,7 @@ namespace osu.Game.Rulesets.Mania.Replays { var currentObject = Beatmap.HitObjects[i]; var nextObjectInColumn = GetNextObject(i); // Get the next object that requires pressing the same button - var releaseTime = calculateReleaseTime(currentObject, nextObjectInColumn); + double releaseTime = calculateReleaseTime(currentObject, nextObjectInColumn); yield return new HitPoint { Time = currentObject.StartTime, Column = currentObject.Column }; diff --git a/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs index 814a737034..cc3b3f348f 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/ManiaLegacySkinTransformer.cs @@ -66,7 +66,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy isLegacySkin = new Lazy(() => GetConfig(LegacySkinConfiguration.LegacySetting.Version) != null); hasKeyTexture = new Lazy(() => { - var keyImage = this.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.KeyImage, 0)?.Value ?? "mania-key1"; + string keyImage = this.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.KeyImage, 0)?.Value ?? "mania-key1"; return this.GetAnimation(keyImage, true, true) != null; }); } diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleArea.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleArea.cs index 575523b168..07bbd6379a 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleArea.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleArea.cs @@ -97,7 +97,7 @@ namespace osu.Game.Rulesets.Osu.Tests private void scheduleHit() => AddStep("schedule action", () => { - var delay = hitCircle.StartTime - hitCircle.HitWindows.WindowFor(HitResult.Great) - Time.Current; + double delay = hitCircle.StartTime - hitCircle.HitWindows.WindowFor(HitResult.Great) - Time.Current; Scheduler.AddDelayed(() => hitAreaReceptor.OnPressed(new KeyBindingPressEvent(GetContainingInputManager().CurrentState, OsuAction.LeftButton)), delay); }); } diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs index 52ab39cfbd..de795241bf 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs @@ -148,7 +148,7 @@ namespace osu.Game.Rulesets.Osu.Tests AddAssert("player score matching expected bonus score", () => { // multipled by 2 to nullify the score multiplier. (autoplay mod selected) - var totalScore = ((ScoreExposedPlayer)Player).ScoreProcessor.TotalScore.Value * 2; + double totalScore = ((ScoreExposedPlayer)Player).ScoreProcessor.TotalScore.Value * 2; return totalScore == (int)(drawableSpinner.Result.RateAdjustedRotation / 360) * new SpinnerTick().CreateJudgement().MaxNumericResult; }); diff --git a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs index 5e5993aefe..49ac6a7af3 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs @@ -123,7 +123,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing // Skip the head circle var scoringTimes = slider.NestedHitObjects.Skip(1).Select(t => t.StartTime); - foreach (var time in scoringTimes) + foreach (double time in scoringTimes) computeVertex(time); } diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs index d8f4aa1229..64ca567a15 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs @@ -44,7 +44,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills { const double scale = 90; - var angleBonus = Math.Sqrt( + double angleBonus = Math.Sqrt( Math.Max(osuPrevious.JumpDistance - scale, 0) * Math.Pow(Math.Sin(osuCurrent.Angle.Value - angle_bonus_begin), 2) * Math.Max(osuCurrent.JumpDistance - scale, 0)); 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 2cc95e1891..b3e0566a98 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs @@ -184,7 +184,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components { Vector2[] oldControlPoints = slider.Path.ControlPoints.Select(cp => cp.Position).ToArray(); var oldPosition = slider.Position; - var oldStartTime = slider.StartTime; + double oldStartTime = slider.StartTime; if (ControlPoint == slider.Path.ControlPoints[0]) { @@ -205,7 +205,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components if (!slider.Path.HasValidLength) { - for (var i = 0; i < slider.Path.ControlPoints.Count; i++) + for (int i = 0; i < slider.Path.ControlPoints.Count; i++) slider.Path.ControlPoints[i].Position = oldControlPoints[i]; slider.Position = oldPosition; diff --git a/osu.Game.Rulesets.Osu/Edit/Checks/CheckLowDiffOverlaps.cs b/osu.Game.Rulesets.Osu/Edit/Checks/CheckLowDiffOverlaps.cs index 1dd859b5b8..084a3e5ea1 100644 --- a/osu.Game.Rulesets.Osu/Edit/Checks/CheckLowDiffOverlaps.cs +++ b/osu.Game.Rulesets.Osu/Edit/Checks/CheckLowDiffOverlaps.cs @@ -47,14 +47,14 @@ namespace osu.Game.Rulesets.Osu.Edit.Checks if (!(hitObjects[i + 1] is OsuHitObject nextHitObject) || nextHitObject is Spinner) continue; - var deltaTime = nextHitObject.StartTime - hitObject.GetEndTime(); + double deltaTime = nextHitObject.StartTime - hitObject.GetEndTime(); if (deltaTime >= hitObject.TimeFadeIn + hitObject.TimePreempt) // The objects are not visible at the same time (without mods), hence skipping. continue; - var distanceSq = (hitObject.StackedEndPosition - nextHitObject.StackedPosition).LengthSquared; - var diameter = (hitObject.Radius - overlap_leniency) * 2; - var diameterSq = diameter * diameter; + float distanceSq = (hitObject.StackedEndPosition - nextHitObject.StackedPosition).LengthSquared; + double diameter = (hitObject.Radius - overlap_leniency) * 2; + double diameterSq = diameter * diameter; bool areOverlapping = distanceSq < diameterSq; diff --git a/osu.Game.Rulesets.Osu/Edit/Checks/CheckTimeDistanceEquality.cs b/osu.Game.Rulesets.Osu/Edit/Checks/CheckTimeDistanceEquality.cs index 6420d9558e..585bd35bd9 100644 --- a/osu.Game.Rulesets.Osu/Edit/Checks/CheckTimeDistanceEquality.cs +++ b/osu.Game.Rulesets.Osu/Edit/Checks/CheckTimeDistanceEquality.cs @@ -88,21 +88,21 @@ namespace osu.Game.Rulesets.Osu.Edit.Checks if (!(hitObjects[i + 1] is OsuHitObject nextHitObject) || nextHitObject is Spinner) continue; - var deltaTime = nextHitObject.StartTime - hitObject.GetEndTime(); + double deltaTime = nextHitObject.StartTime - hitObject.GetEndTime(); // Ignore objects that are far enough apart in time to not be considered the same pattern. if (deltaTime > pattern_lifetime) continue; // Relying on FastInvSqrt is probably good enough here. We'll be taking the difference between distances later, hence square not being sufficient. - var distance = (hitObject.StackedEndPosition - nextHitObject.StackedPosition).LengthFast; + float distance = (hitObject.StackedEndPosition - nextHitObject.StackedPosition).LengthFast; // Ignore stacks and half-stacks, as these are close enough to where they can't be confused for being time-distanced. if (distance < stack_leniency) continue; var observedTimeDistance = new ObservedTimeDistance(nextHitObject.StartTime, deltaTime, distance); - var expectedDistance = getExpectedDistance(prevObservedTimeDistances, observedTimeDistance); + double expectedDistance = getExpectedDistance(prevObservedTimeDistances, observedTimeDistance); if (expectedDistance == 0) { @@ -125,7 +125,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Checks private double getExpectedDistance(IEnumerable prevObservedTimeDistances, ObservedTimeDistance observedTimeDistance) { - var observations = prevObservedTimeDistances.Count(); + int observations = prevObservedTimeDistances.Count(); int count = 0; double sum = 0; diff --git a/osu.Game.Rulesets.Osu/Edit/OsuRectangularPositionSnapGrid.cs b/osu.Game.Rulesets.Osu/Edit/OsuRectangularPositionSnapGrid.cs index b8ff92bd37..d1c81b51bc 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuRectangularPositionSnapGrid.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuRectangularPositionSnapGrid.cs @@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Osu.Edit [BackgroundDependencyLoader] private void load() { - var gridSizeIndex = Array.IndexOf(grid_sizes, editorBeatmap.BeatmapInfo.GridSize); + int gridSizeIndex = Array.IndexOf(grid_sizes, editorBeatmap.BeatmapInfo.GridSize); if (gridSizeIndex >= 0) currentGridSizeIndex = gridSizeIndex; updateSpacing(); diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs index 3102db270e..ad4c5dfd5d 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs @@ -170,7 +170,7 @@ namespace osu.Game.Rulesets.Osu.Mods base.LoadComplete(); var firstObj = beatmap.HitObjects[0]; - var startDelay = firstObj.StartTime - firstObj.TimePreempt; + double startDelay = firstObj.StartTime - firstObj.TimePreempt; using (BeginAbsoluteSequence(startDelay + break_close_late)) leaveBreak(); diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs index e162f805a1..d602fe67ee 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs @@ -155,11 +155,11 @@ namespace osu.Game.Rulesets.Osu.Mods static (double fadeStartTime, double fadeDuration) getParameters(OsuHitObject hitObject) { - var fadeOutStartTime = hitObject.StartTime - hitObject.TimePreempt + hitObject.TimeFadeIn; - var fadeOutDuration = hitObject.TimePreempt * fade_out_duration_multiplier; + double fadeOutStartTime = hitObject.StartTime - hitObject.TimePreempt + hitObject.TimeFadeIn; + double fadeOutDuration = hitObject.TimePreempt * fade_out_duration_multiplier; // new duration from completed fade in to end (before fading out) - var longFadeDuration = hitObject.GetEndTime() - fadeOutStartTime; + double longFadeDuration = hitObject.GetEndTime() - fadeOutStartTime; switch (hitObject) { @@ -167,7 +167,7 @@ namespace osu.Game.Rulesets.Osu.Mods return (fadeOutStartTime, longFadeDuration); case SliderTick _: - var tickFadeOutDuration = Math.Min(hitObject.TimePreempt - DrawableSliderTick.ANIM_DURATION, 1000); + double tickFadeOutDuration = Math.Min(hitObject.TimePreempt - DrawableSliderTick.ANIM_DURATION, 1000); return (hitObject.StartTime - tickFadeOutDuration, tickFadeOutDuration); case Spinner _: diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index 1a2e5d92b4..9b9ebcad04 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -124,7 +124,7 @@ namespace osu.Game.Rulesets.Osu.Mods // to allow jumps and prevent too sharp turns during streams. // Allow maximum jump angle when jump distance is more than half of playfield diagonal length - var randomAngleRad = rateOfChangeMultiplier * 2 * Math.PI * Math.Min(1f, distanceToPrev / (playfield_diagonal * 0.5f)); + double randomAngleRad = rateOfChangeMultiplier * 2 * Math.PI * Math.Min(1f, distanceToPrev / (playfield_diagonal * 0.5f)); current.AngleRad = (float)randomAngleRad + previous.AngleRad; if (current.AngleRad < 0) @@ -171,11 +171,11 @@ namespace osu.Game.Rulesets.Osu.Mods // Clamp slider position to the placement area // If the slider is larger than the playfield, force it to stay at the original position - var newX = possibleMovementBounds.Width < 0 + float newX = possibleMovementBounds.Width < 0 ? objectInfo.PositionOriginal.X : Math.Clamp(previousPosition.X, possibleMovementBounds.Left, possibleMovementBounds.Right); - var newY = possibleMovementBounds.Height < 0 + float newY = possibleMovementBounds.Height < 0 ? objectInfo.PositionOriginal.Y : Math.Clamp(previousPosition.Y, possibleMovementBounds.Top, possibleMovementBounds.Bottom); @@ -235,7 +235,7 @@ namespace osu.Game.Rulesets.Osu.Mods } // Take the circle radius into account. - var radius = (float)slider.Radius; + float radius = (float)slider.Radius; minX -= radius; minY -= radius; diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModSpunOut.cs b/osu.Game.Rulesets.Osu/Mods/OsuModSpunOut.cs index c7f4811701..31474e8fbb 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModSpunOut.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModSpunOut.cs @@ -44,7 +44,7 @@ namespace osu.Game.Rulesets.Osu.Mods // because the spinner is under the gameplay clock, it is affected by rate adjustments on the track; // for that reason using ElapsedFrameTime directly leads to fewer SPM with Half Time and more SPM with Double Time. // for spinners we want the real (wall clock) elapsed time; to achieve that, unapply the clock rate locally here. - var rateIndependentElapsedTime = spinner.Clock.ElapsedFrameTime / spinner.Clock.Rate; + double rateIndependentElapsedTime = spinner.Clock.ElapsedFrameTime / spinner.Clock.Rate; spinner.RotationTracker.AddRotation(MathUtils.RadiansToDegrees((float)rateIndependentElapsedTime * 0.03f)); } } diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs index bf70a63ab5..5285380097 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTarget.cs @@ -193,8 +193,8 @@ namespace osu.Game.Rulesets.Osu.Mods private IEnumerable generateBeats(IBeatmap beatmap) { - var startTime = originalHitObjects.First().StartTime; - var endTime = originalHitObjects.Last().GetEndTime(); + double startTime = originalHitObjects.First().StartTime; + double endTime = originalHitObjects.Last().GetEndTime(); var beats = beatmap.ControlPointInfo.TimingPoints // Ignore timing points after endTime @@ -208,9 +208,9 @@ namespace osu.Game.Rulesets.Osu.Mods .ToList(); // Remove beats that are too close to the next one (e.g. due to timing point changes) - for (var i = beats.Count - 2; i >= 0; i--) + for (int i = beats.Count - 2; i >= 0; i--) { - var beat = beats[i]; + double beat = beats[i]; if (!definitelyBigger(beats[i + 1] - beat, beatmap.ControlPointInfo.TimingPointAt(beat).BeatLength / 2)) beats.RemoveAt(i); @@ -250,13 +250,13 @@ namespace osu.Game.Rulesets.Osu.Mods // Other kinds of combo info are also added in the process var combos = hitObjects.GroupBy(x => x.ComboIndex).ToList(); - for (var i = 0; i < combos.Count; i++) + for (int i = 0; i < combos.Count; i++) { var group = combos[i].ToList(); group.First().NewCombo = true; group.Last().LastInCombo = true; - for (var j = 0; j < group.Count; j++) + for (int j = 0; j < group.Count; j++) { var x = group[j]; x.ComboIndex = i; @@ -273,17 +273,17 @@ namespace osu.Game.Rulesets.Osu.Mods const float two_pi = MathF.PI * 2; - var direction = two_pi * nextSingle(); - var maxComboIndex = hitObjects.Last().ComboIndex; + float direction = two_pi * nextSingle(); + int maxComboIndex = hitObjects.Last().ComboIndex; - for (var i = 0; i < hitObjects.Count; i++) + for (int i = 0; i < hitObjects.Count; i++) { var obj = hitObjects[i]; var lastPos = i == 0 ? Vector2.Divide(OsuPlayfield.BASE_SIZE, 2) : hitObjects[i - 1].Position; - var distance = maxComboIndex == 0 + float distance = maxComboIndex == 0 ? (float)obj.Radius : mapRange(obj.ComboIndex, 0, maxComboIndex, (float)obj.Radius, max_base_distance); if (obj.NewCombo) distance *= 1.5f; @@ -292,7 +292,7 @@ namespace osu.Game.Rulesets.Osu.Mods // Attempt to place the circle at a place that does not overlap with previous ones - var tryCount = 0; + int tryCount = 0; // for checking overlap var precedingObjects = hitObjects.SkipLast(hitObjects.Count - i).TakeLast(overlap_check_count).ToList(); @@ -363,7 +363,7 @@ namespace osu.Game.Rulesets.Osu.Mods { var beats = new List(); int i = 0; - var currentTime = timingPoint.Time; + double currentTime = timingPoint.Time; while (!definitelyBigger(currentTime, mapEndTime) && controlPointInfo.TimingPointAt(currentTime) == timingPoint) { @@ -377,7 +377,7 @@ namespace osu.Game.Rulesets.Osu.Mods private OsuHitObject getClosestHitObject(List hitObjects, double time) { - var precedingIndex = hitObjects.FindLastIndex(h => h.StartTime < time); + int precedingIndex = hitObjects.FindLastIndex(h => h.StartTime < time); if (precedingIndex == hitObjects.Count - 1) return hitObjects[precedingIndex]; @@ -457,7 +457,7 @@ namespace osu.Game.Rulesets.Osu.Mods private void clampToPlayfield(OsuHitObject obj) { var position = obj.Position; - var radius = (float)obj.Radius; + float radius = (float)obj.Radius; if (position.Y < radius) position.Y = radius; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs index 001ea6c4ad..e88d09e6d9 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs @@ -67,7 +67,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections Vector2 pointStartPosition = startPosition + (fraction - 0.1f) * distanceVector; Vector2 pointEndPosition = startPosition + fraction * distanceVector; - GetFadeTimes(start, end, (float)d / distance, out var fadeInTime, out var fadeOutTime); + GetFadeTimes(start, end, (float)d / distance, out double fadeInTime, out double fadeOutTime); FollowPoint fp; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointLifetimeEntry.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointLifetimeEntry.cs index 30ff6b8984..ad656d2085 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointLifetimeEntry.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointLifetimeEntry.cs @@ -93,7 +93,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections // The lifetime start will match the fade-in time of the first follow point. float fraction = (int)(FollowPointConnection.SPACING * 1.5) / distanceVector.Length; - FollowPointConnection.GetFadeTimes(Start, End, fraction, out var fadeInTime, out _); + FollowPointConnection.GetFadeTimes(Start, End, fraction, out double fadeInTime, out _); LifetimeStart = fadeInTime; LifetimeEnd = double.MaxValue; // This will be set by the connection. diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs index 21e6619444..011e9fa85d 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs @@ -56,7 +56,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections { var newEntry = new FollowPointLifetimeEntry(hitObject); - var index = lifetimeEntries.AddInPlace(newEntry, Comparer.Create((e1, e2) => + int index = lifetimeEntries.AddInPlace(newEntry, Comparer.Create((e1, e2) => { int comp = e1.Start.StartTime.CompareTo(e2.Start.StartTime); diff --git a/osu.Game.Rulesets.Osu/Skinning/Default/SnakingSliderBody.cs b/osu.Game.Rulesets.Osu/Skinning/Default/SnakingSliderBody.cs index 7b7a89d5e2..42d3840158 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Default/SnakingSliderBody.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Default/SnakingSliderBody.cs @@ -70,8 +70,8 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default Slider slider = drawableSlider.HitObject; - var span = slider.SpanAt(completionProgress); - var spanProgress = slider.ProgressAt(completionProgress); + int span = slider.SpanAt(completionProgress); + double spanProgress = slider.ProgressAt(completionProgress); double start = 0; double end = SnakingIn.Value ? Math.Clamp((Time.Current - (slider.StartTime - slider.TimePreempt)) / (slider.TimePreempt / 3), 0, 1) : 1; @@ -110,8 +110,8 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default snakedPosition = Path.PositionInBoundingBox(Vector2.Zero); snakedPathOffset = Path.PositionInBoundingBox(Path.Vertices[0]); - var lastSnakedStart = SnakedStart ?? 0; - var lastSnakedEnd = SnakedEnd ?? 0; + double lastSnakedStart = SnakedStart ?? 0; + double lastSnakedEnd = SnakedEnd ?? 0; SnakedStart = null; SnakedEnd = null; diff --git a/osu.Game.Rulesets.Osu/Skinning/Default/SpinnerRotationTracker.cs b/osu.Game.Rulesets.Osu/Skinning/Default/SpinnerRotationTracker.cs index 9393a589eb..4313e99b13 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Default/SpinnerRotationTracker.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Default/SpinnerRotationTracker.cs @@ -62,9 +62,9 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default protected override void Update() { base.Update(); - var thisAngle = -MathUtils.RadiansToDegrees(MathF.Atan2(mousePosition.X - DrawSize.X / 2, mousePosition.Y - DrawSize.Y / 2)); + float thisAngle = -MathUtils.RadiansToDegrees(MathF.Atan2(mousePosition.X - DrawSize.X / 2, mousePosition.Y - DrawSize.Y / 2)); - var delta = thisAngle - lastAngle; + float delta = thisAngle - lastAngle; if (Tracking) AddRotation(delta); diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs index d1c9b1bf92..f4422a4806 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs @@ -158,7 +158,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy if (hasNumber) { - var legacyVersion = skin.GetConfig(LegacySetting.Version)?.Value; + decimal? legacyVersion = skin.GetConfig(LegacySetting.Version)?.Value; if (legacyVersion >= 2.0m) // legacy skins of version 2.0 and newer only apply very short fade out to the number piece. diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyOldStyleSpinner.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyOldStyleSpinner.cs index e3e8f3ce88..ea122d47bb 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyOldStyleSpinner.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyOldStyleSpinner.cs @@ -103,7 +103,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy // careful: need to call this exactly once for all calculations in a frame // as the function has a random factor in it - var metreHeight = getMetreHeight(DrawableSpinner.Progress); + float metreHeight = getMetreHeight(DrawableSpinner.Progress); // hack to make the metre blink up from below than down from above. // move down the container to be able to apply masking for the metre, diff --git a/osu.Game.Rulesets.Osu/Statistics/AccuracyHeatmap.cs b/osu.Game.Rulesets.Osu/Statistics/AccuracyHeatmap.cs index 24a660e69e..3c2077b3c8 100644 --- a/osu.Game.Rulesets.Osu/Statistics/AccuracyHeatmap.cs +++ b/osu.Game.Rulesets.Osu/Statistics/AccuracyHeatmap.cs @@ -216,7 +216,7 @@ namespace osu.Game.Rulesets.Osu.Statistics // Likewise sin(pi/2)=1 and sin(3pi/2)=-1, whereas we actually want these values to appear on the bottom/top respectively, so the y-coordinate also needs to be inverted. // // We also need to apply the anti-clockwise rotation. - var rotatedAngle = finalAngle - MathUtils.DegreesToRadians(rotation); + double rotatedAngle = finalAngle - MathUtils.DegreesToRadians(rotation); var rotatedCoordinate = -1 * new Vector2((float)Math.Cos(rotatedAngle), (float)Math.Sin(rotatedAngle)); Vector2 localCentre = new Vector2(points_per_dimension - 1) / 2; diff --git a/osu.Game.Rulesets.Osu/Utils/OsuHitObjectGenerationUtils.cs b/osu.Game.Rulesets.Osu/Utils/OsuHitObjectGenerationUtils.cs index bfd6ac3ad3..97a4b14a62 100644 --- a/osu.Game.Rulesets.Osu/Utils/OsuHitObjectGenerationUtils.cs +++ b/osu.Game.Rulesets.Osu/Utils/OsuHitObjectGenerationUtils.cs @@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Osu.Utils /// The new position of the hit object, relative to the previous one. public static Vector2 RotateAwayFromEdge(Vector2 prevObjectPos, Vector2 posRelativeToPrev, float rotationRatio = 0.5f) { - var relativeRotationDistance = 0f; + float relativeRotationDistance = 0f; if (prevObjectPos.X < playfield_middle.X) { @@ -88,16 +88,16 @@ namespace osu.Game.Rulesets.Osu.Utils /// The rotated vector. public static Vector2 RotateVectorTowardsVector(Vector2 initial, Vector2 destination, float rotationRatio) { - var initialAngleRad = MathF.Atan2(initial.Y, initial.X); - var destAngleRad = MathF.Atan2(destination.Y, destination.X); + float initialAngleRad = MathF.Atan2(initial.Y, initial.X); + float destAngleRad = MathF.Atan2(destination.Y, destination.X); - var diff = destAngleRad - initialAngleRad; + float diff = destAngleRad - initialAngleRad; while (diff < -MathF.PI) diff += 2 * MathF.PI; while (diff > MathF.PI) diff -= 2 * MathF.PI; - var finalAngleRad = initialAngleRad + rotationRatio * diff; + float finalAngleRad = initialAngleRad + rotationRatio * diff; return new Vector2( initial.Length * MathF.Cos(finalAngleRad), diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneSampleOutput.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneSampleOutput.cs index 296468d98d..97c8d9eeb5 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TestSceneSampleOutput.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneSampleOutput.cs @@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Taiko.Tests { base.SetUpSteps(); - var expectedSampleNames = new[] + string[] expectedSampleNames = new[] { string.Empty, string.Empty, diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs index 32aad6c36a..5c5b3f9daa 100644 --- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs +++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs @@ -79,7 +79,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps { case IHasDistance distanceData: { - if (shouldConvertSliderToHits(obj, beatmap, distanceData, out var taikoDuration, out var tickSpacing)) + if (shouldConvertSliderToHits(obj, beatmap, distanceData, out int taikoDuration, out double tickSpacing)) { List> allSamples = obj is IHasPathWithRepeats curveData ? curveData.NodeSamples : new List>(new[] { samples }); diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs index 97adc5f197..863672b3fa 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs @@ -109,7 +109,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables var corrected = samples.ToList(); - for (var i = 0; i < corrected.Count; i++) + for (int i = 0; i < corrected.Count; i++) { var s = corrected[i]; @@ -156,7 +156,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables validActionPressed = HitActions.Contains(e.Action); // Only count this as handled if the new judgement is a hit - var result = UpdateResult(true); + bool result = UpdateResult(true); if (IsHit) HitAction = e.Action; diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs index 2d19296d06..77243218ce 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs @@ -185,9 +185,9 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables nextTick?.TriggerResult(true); - var numHits = ticks.Count(r => r.IsHit); + int numHits = ticks.Count(r => r.IsHit); - var completion = (float)numHits / HitObject.RequiredHits; + float completion = (float)numHits / HitObject.RequiredHits; expandingRing .FadeTo(expandingRing.Alpha + Math.Clamp(completion / 16, 0.1f, 0.6f), 50) @@ -273,7 +273,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables if (Time.Current < HitObject.StartTime) return false; - var isCentre = e.Action == TaikoAction.LeftCentre || e.Action == TaikoAction.RightCentre; + bool isCentre = e.Action == TaikoAction.LeftCentre || e.Action == TaikoAction.RightCentre; // Ensure alternating centre and rim hits if (lastWasCentre == isCentre) diff --git a/osu.Game.Rulesets.Taiko/Skinning/Legacy/TaikoLegacySkinTransformer.cs b/osu.Game.Rulesets.Taiko/Skinning/Legacy/TaikoLegacySkinTransformer.cs index a3ecbbc436..bbc8f0abea 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/Legacy/TaikoLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/Legacy/TaikoLegacySkinTransformer.cs @@ -91,7 +91,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy case TaikoSkinComponents.TaikoExplosionOk: case TaikoSkinComponents.TaikoExplosionGreat: - var hitName = getHitName(taikoComponent.Component); + string hitName = getHitName(taikoComponent.Component); var hitSprite = this.GetAnimation(hitName, true, false); if (hitSprite != null) @@ -162,10 +162,10 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy { get { - foreach (var name in base.LookupNames) + foreach (string name in base.LookupNames) yield return name.Insert(name.LastIndexOf('/') + 1, "taiko-"); - foreach (var name in base.LookupNames) + foreach (string name in base.LookupNames) yield return name; } } diff --git a/osu.Game.Rulesets.Taiko/UI/DrumRollHitContainer.cs b/osu.Game.Rulesets.Taiko/UI/DrumRollHitContainer.cs index 263454c78a..ae37840825 100644 --- a/osu.Game.Rulesets.Taiko/UI/DrumRollHitContainer.cs +++ b/osu.Game.Rulesets.Taiko/UI/DrumRollHitContainer.cs @@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Taiko.UI base.Update(); // Remove any auxiliary hit notes that were spawned during a drum roll but subsequently rewound. - for (var i = AliveInternalChildren.Count - 1; i >= 0; i--) + for (int i = AliveInternalChildren.Count - 1; i >= 0; i--) { var flyingHit = (DrawableFlyingHit)AliveInternalChildren[i]; if (Time.Current <= flyingHit.HitObject.StartTime) diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimation.cs b/osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimation.cs index 3706acbe23..c496c05236 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimation.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoMascotAnimation.cs @@ -122,7 +122,7 @@ namespace osu.Game.Rulesets.Taiko.UI if (skin == null) return; - foreach (var frameIndex in clear_animation_sequence) + foreach (int frameIndex in clear_animation_sequence) { var texture = getAnimationFrame(skin, TaikoMascotAnimationState.Clear, frameIndex); diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyDecoderTest.cs index 335a6aeeb0..4334c4d7a2 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyDecoderTest.cs @@ -40,7 +40,7 @@ namespace osu.Game.Tests.Beatmaps.Formats protected override bool ShouldSkipLine(string line) { - var result = base.ShouldSkipLine(line); + bool result = base.ShouldSkipLine(line); if (!result) ParsedLines.Add(line); diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs index 4cc71717ff..935194db58 100644 --- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs @@ -82,7 +82,7 @@ namespace osu.Game.Tests.Beatmaps.IO { var osu = LoadOsuIntoHost(host); - var tempPath = TestResources.GetTestBeatmapForImport(); + string tempPath = TestResources.GetTestBeatmapForImport(); var manager = osu.Dependencies.Get(); @@ -144,7 +144,7 @@ namespace osu.Game.Tests.Beatmaps.IO { var osu = LoadOsuIntoHost(host); - var temp = TestResources.GetTestBeatmapForImport(); + string temp = TestResources.GetTestBeatmapForImport(); string extractedFolder = $"{temp}_extracted"; Directory.CreateDirectory(extractedFolder); @@ -196,7 +196,7 @@ namespace osu.Game.Tests.Beatmaps.IO { var osu = LoadOsuIntoHost(host); - var temp = TestResources.GetTestBeatmapForImport(); + string temp = TestResources.GetTestBeatmapForImport(); string extractedFolder = $"{temp}_extracted"; Directory.CreateDirectory(extractedFolder); @@ -251,7 +251,7 @@ namespace osu.Game.Tests.Beatmaps.IO { var osu = LoadOsuIntoHost(host); - var temp = TestResources.GetTestBeatmapForImport(); + string temp = TestResources.GetTestBeatmapForImport(); string extractedFolder = $"{temp}_extracted"; Directory.CreateDirectory(extractedFolder); @@ -302,7 +302,7 @@ namespace osu.Game.Tests.Beatmaps.IO { var osu = LoadOsuIntoHost(host); - var temp = TestResources.GetTestBeatmapForImport(); + string temp = TestResources.GetTestBeatmapForImport(); string extractedFolder = $"{temp}_extracted"; Directory.CreateDirectory(extractedFolder); @@ -424,7 +424,7 @@ namespace osu.Game.Tests.Beatmaps.IO checkBeatmapCount(osu, 12); checkSingleReferencedFileCount(osu, 18); - var brokenTempFilename = TestResources.GetTestBeatmapForImport(); + string brokenTempFilename = TestResources.GetTestBeatmapForImport(); MemoryStream brokenOsu = new MemoryStream(); MemoryStream brokenOsz = new MemoryStream(await File.ReadAllBytesAsync(brokenTempFilename)); @@ -594,7 +594,7 @@ namespace osu.Game.Tests.Beatmaps.IO var osu = LoadOsuIntoHost(host); - var temp = TestResources.GetTestBeatmapForImport(); + string temp = TestResources.GetTestBeatmapForImport(); var importer = new ArchiveImportIPCChannel(client); if (!importer.ImportAsync(temp).Wait(10000)) @@ -619,7 +619,7 @@ namespace osu.Game.Tests.Beatmaps.IO try { var osu = LoadOsuIntoHost(host); - var temp = TestResources.GetTestBeatmapForImport(); + string temp = TestResources.GetTestBeatmapForImport(); using (File.OpenRead(temp)) await osu.Dependencies.Get().Import(temp); ensureLoaded(osu); @@ -642,7 +642,7 @@ namespace osu.Game.Tests.Beatmaps.IO { var osu = LoadOsuIntoHost(host); - var temp = TestResources.GetTestBeatmapForImport(); + string temp = TestResources.GetTestBeatmapForImport(); string extractedFolder = $"{temp}_extracted"; Directory.CreateDirectory(extractedFolder); @@ -684,7 +684,7 @@ namespace osu.Game.Tests.Beatmaps.IO { var osu = LoadOsuIntoHost(host); - var temp = TestResources.GetTestBeatmapForImport(); + string temp = TestResources.GetTestBeatmapForImport(); string extractedFolder = $"{temp}_extracted"; string subfolder = Path.Combine(extractedFolder, "subfolder"); @@ -729,7 +729,7 @@ namespace osu.Game.Tests.Beatmaps.IO { var osu = LoadOsuIntoHost(host); - var temp = TestResources.GetTestBeatmapForImport(); + string temp = TestResources.GetTestBeatmapForImport(); string extractedFolder = $"{temp}_extracted"; string dataFolder = Path.Combine(extractedFolder, "actual_data"); @@ -784,7 +784,7 @@ namespace osu.Game.Tests.Beatmaps.IO var osu = LoadOsuIntoHost(host); var manager = osu.Dependencies.Get(); - var temp = TestResources.GetTestBeatmapForImport(); + string temp = TestResources.GetTestBeatmapForImport(); await osu.Dependencies.Get().Import(temp); // Update via the beatmap, not the beatmap info, to ensure correct linking @@ -814,7 +814,7 @@ namespace osu.Game.Tests.Beatmaps.IO var osu = LoadOsuIntoHost(host); var manager = osu.Dependencies.Get(); - var temp = TestResources.GetTestBeatmapForImport(); + string temp = TestResources.GetTestBeatmapForImport(); await osu.Dependencies.Get().Import(temp); BeatmapSetInfo setToUpdate = manager.GetAllUsableBeatmapSets()[0]; @@ -905,7 +905,7 @@ namespace osu.Game.Tests.Beatmaps.IO public static async Task LoadQuickOszIntoOsu(OsuGameBase osu) { - var temp = TestResources.GetQuickTestBeatmapForImport(); + string temp = TestResources.GetQuickTestBeatmapForImport(); var manager = osu.Dependencies.Get(); @@ -920,7 +920,7 @@ namespace osu.Game.Tests.Beatmaps.IO public static async Task LoadOszIntoOsu(OsuGameBase osu, string path = null, bool virtualTrack = false) { - var temp = path ?? TestResources.GetTestBeatmapForImport(virtualTrack); + string temp = path ?? TestResources.GetTestBeatmapForImport(virtualTrack); var manager = osu.Dependencies.Get(); diff --git a/osu.Game.Tests/Beatmaps/IO/LineBufferedReaderTest.cs b/osu.Game.Tests/Beatmaps/IO/LineBufferedReaderTest.cs index 25517ad615..5e37f01c81 100644 --- a/osu.Game.Tests/Beatmaps/IO/LineBufferedReaderTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/LineBufferedReaderTest.cs @@ -114,7 +114,7 @@ namespace osu.Game.Tests.Beatmaps.IO Assert.AreEqual("this line is gone", bufferedReader.ReadLine()); Assert.AreEqual("this one shouldn't be", bufferedReader.PeekLine()); - var endingLines = bufferedReader.ReadToEnd().Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); + string[] endingLines = bufferedReader.ReadToEnd().Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); Assert.AreEqual(3, endingLines.Length); Assert.AreEqual("this one shouldn't be", endingLines[0]); Assert.AreEqual("these ones", endingLines[1]); diff --git a/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs b/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs index 2c1e39c2cf..d11da5e2a3 100644 --- a/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs @@ -36,8 +36,8 @@ namespace osu.Game.Tests.Beatmaps.IO "Soleily - Renatus (MMzz) [Muzukashii].osu", "Soleily - Renatus (MMzz) [Oni].osu" }; - var maps = reader.Filenames.ToArray(); - foreach (var map in expected) + string[] maps = reader.Filenames.ToArray(); + foreach (string map in expected) Assert.Contains(map, maps); } } diff --git a/osu.Game.Tests/Database/BeatmapImporterTests.cs b/osu.Game.Tests/Database/BeatmapImporterTests.cs index 4cdcf507b6..a21f935f6b 100644 --- a/osu.Game.Tests/Database/BeatmapImporterTests.cs +++ b/osu.Game.Tests/Database/BeatmapImporterTests.cs @@ -94,7 +94,7 @@ namespace osu.Game.Tests.Database using var importer = new BeatmapImporter(realmFactory, storage); using var store = new RealmRulesetStore(realmFactory, storage); - var tempPath = TestResources.GetTestBeatmapForImport(); + string? tempPath = TestResources.GetTestBeatmapForImport(); ILive? importedSet; @@ -144,7 +144,7 @@ namespace osu.Game.Tests.Database using var importer = new BeatmapImporter(realmFactory, storage); using var store = new RealmRulesetStore(realmFactory, storage); - var temp = TestResources.GetTestBeatmapForImport(); + string? temp = TestResources.GetTestBeatmapForImport(); string extractedFolder = $"{temp}_extracted"; Directory.CreateDirectory(extractedFolder); @@ -193,7 +193,7 @@ namespace osu.Game.Tests.Database using var importer = new BeatmapImporter(realmFactory, storage); using var store = new RealmRulesetStore(realmFactory, storage); - var temp = TestResources.GetTestBeatmapForImport(); + string? temp = TestResources.GetTestBeatmapForImport(); string extractedFolder = $"{temp}_extracted"; Directory.CreateDirectory(extractedFolder); @@ -245,7 +245,7 @@ namespace osu.Game.Tests.Database using var importer = new BeatmapImporter(realmFactory, storage); using var store = new RealmRulesetStore(realmFactory, storage); - var temp = TestResources.GetTestBeatmapForImport(); + string? temp = TestResources.GetTestBeatmapForImport(); string extractedFolder = $"{temp}_extracted"; Directory.CreateDirectory(extractedFolder); @@ -293,7 +293,7 @@ namespace osu.Game.Tests.Database using var importer = new BeatmapImporter(realmFactory, storage); using var store = new RealmRulesetStore(realmFactory, storage); - var temp = TestResources.GetTestBeatmapForImport(); + string? temp = TestResources.GetTestBeatmapForImport(); string extractedFolder = $"{temp}_extracted"; Directory.CreateDirectory(extractedFolder); @@ -391,7 +391,7 @@ namespace osu.Game.Tests.Database checkBeatmapCount(realmFactory.Context, 12); checkSingleReferencedFileCount(realmFactory.Context, 18); - var brokenTempFilename = TestResources.GetTestBeatmapForImport(); + string? brokenTempFilename = TestResources.GetTestBeatmapForImport(); MemoryStream brokenOsu = new MemoryStream(); MemoryStream brokenOsz = new MemoryStream(await File.ReadAllBytesAsync(brokenTempFilename)); @@ -522,7 +522,7 @@ namespace osu.Game.Tests.Database using var importer = new BeatmapImporter(realmFactory, storage); using var store = new RealmRulesetStore(realmFactory, storage); - var temp = TestResources.GetTestBeatmapForImport(); + string? temp = TestResources.GetTestBeatmapForImport(); using (File.OpenRead(temp)) await importer.Import(temp); ensureLoaded(realmFactory.Context); @@ -539,7 +539,7 @@ namespace osu.Game.Tests.Database using var importer = new BeatmapImporter(realmFactory, storage); using var store = new RealmRulesetStore(realmFactory, storage); - var temp = TestResources.GetTestBeatmapForImport(); + string? temp = TestResources.GetTestBeatmapForImport(); string extractedFolder = $"{temp}_extracted"; Directory.CreateDirectory(extractedFolder); @@ -575,7 +575,7 @@ namespace osu.Game.Tests.Database using var importer = new BeatmapImporter(realmFactory, storage); using var store = new RealmRulesetStore(realmFactory, storage); - var temp = TestResources.GetTestBeatmapForImport(); + string? temp = TestResources.GetTestBeatmapForImport(); string extractedFolder = $"{temp}_extracted"; string subfolder = Path.Combine(extractedFolder, "subfolder"); @@ -617,7 +617,7 @@ namespace osu.Game.Tests.Database using var importer = new BeatmapImporter(realmFactory, storage); using var store = new RealmRulesetStore(realmFactory, storage); - var temp = TestResources.GetTestBeatmapForImport(); + string? temp = TestResources.GetTestBeatmapForImport(); string extractedFolder = $"{temp}_extracted"; string dataFolder = Path.Combine(extractedFolder, "actual_data"); @@ -668,7 +668,7 @@ namespace osu.Game.Tests.Database using var importer = new BeatmapImporter(realmFactory, storage); using var store = new RealmRulesetStore(realmFactory, storage); - var temp = TestResources.GetTestBeatmapForImport(); + string? temp = TestResources.GetTestBeatmapForImport(); await importer.Import(temp); // Update via the beatmap, not the beatmap info, to ensure correct linking @@ -685,7 +685,7 @@ namespace osu.Game.Tests.Database public static async Task LoadQuickOszIntoOsu(BeatmapImporter importer, Realm realm) { - var temp = TestResources.GetQuickTestBeatmapForImport(); + string? temp = TestResources.GetQuickTestBeatmapForImport(); var importedSet = await importer.Import(new ImportTask(temp)); @@ -700,7 +700,7 @@ namespace osu.Game.Tests.Database public static async Task LoadOszIntoStore(BeatmapImporter importer, Realm realm, string? path = null, bool virtualTrack = false) { - var temp = path ?? TestResources.GetTestBeatmapForImport(virtualTrack); + string? temp = path ?? TestResources.GetTestBeatmapForImport(virtualTrack); var importedSet = await importer.Import(new ImportTask(temp)); diff --git a/osu.Game.Tests/NonVisual/BarLineGeneratorTest.cs b/osu.Game.Tests/NonVisual/BarLineGeneratorTest.cs index e663e1128e..834c05fd08 100644 --- a/osu.Game.Tests/NonVisual/BarLineGeneratorTest.cs +++ b/osu.Game.Tests/NonVisual/BarLineGeneratorTest.cs @@ -49,7 +49,7 @@ namespace osu.Game.Tests.NonVisual for (int i = 0; i * beat_length_denominator < barLines.Count; i++) { var barLine = barLines[i * beat_length_denominator]; - var expectedTime = beat_length_numerator * (int)signature * i; + int expectedTime = beat_length_numerator * (int)signature * i; // every seventh bar's start time should be at least greater than the whole number we expect. // It cannot be less, as that can affect overlapping scroll algorithms diff --git a/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs b/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs index 5e14af5c27..8a063b3c6e 100644 --- a/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs +++ b/osu.Game.Tests/NonVisual/CustomDataDirectoryTest.cs @@ -87,7 +87,7 @@ namespace osu.Game.Tests.NonVisual File.WriteAllText(actualTestFile, "test"); var rulesetStorage = storage.GetStorageForDirectory("rulesets"); - var lookupPath = rulesetStorage.GetFiles(".").Single(); + string lookupPath = rulesetStorage.GetFiles(".").Single(); Assert.That(lookupPath, Is.EqualTo("test")); } @@ -140,7 +140,7 @@ namespace osu.Game.Tests.NonVisual Assert.That(osuStorage, Is.Not.Null); - foreach (var file in osuStorage.IgnoreFiles) + foreach (string file in osuStorage.IgnoreFiles) { // avoid touching realm files which may be a pipe and break everything. // this is also done locally inside OsuStorage via the IgnoreFiles list. @@ -149,7 +149,7 @@ namespace osu.Game.Tests.NonVisual Assert.That(storage.Exists(file), Is.False); } - foreach (var dir in osuStorage.IgnoreDirectories) + foreach (string dir in osuStorage.IgnoreDirectories) { Assert.That(Directory.Exists(Path.Combine(originalDirectory, dir))); Assert.That(storage.ExistsDirectory(dir), Is.False); diff --git a/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs b/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs index 407dec936b..99d394b454 100644 --- a/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs +++ b/osu.Game.Tests/NonVisual/FramedReplayInputHandlerTest.cs @@ -347,7 +347,7 @@ namespace osu.Game.Tests.NonVisual { for (int i = 0; i < 1000; i++) { - var time = handler.SetFrameFromTime(destination); + double? time = handler.SetFrameFromTime(destination); if (time == null || time == destination) return; } diff --git a/osu.Game.Tests/NonVisual/LimitedCapacityQueueTest.cs b/osu.Game.Tests/NonVisual/LimitedCapacityQueueTest.cs index a04415bc7f..e1eaf213d6 100644 --- a/osu.Game.Tests/NonVisual/LimitedCapacityQueueTest.cs +++ b/osu.Game.Tests/NonVisual/LimitedCapacityQueueTest.cs @@ -30,7 +30,7 @@ namespace osu.Game.Tests.NonVisual Assert.Throws(() => _ = queue.Dequeue()); int count = 0; - foreach (var _ in queue) + foreach (int _ in queue) count++; Assert.AreEqual(0, count); @@ -50,7 +50,7 @@ namespace osu.Game.Tests.NonVisual Assert.AreEqual(i, queue[i]); int j = 0; - foreach (var item in queue) + foreach (int item in queue) Assert.AreEqual(j++, item); for (int i = queue.Count; i < queue.Count + capacity; i++) @@ -71,7 +71,7 @@ namespace osu.Game.Tests.NonVisual Assert.AreEqual(count - capacity + i, queue[i]); int j = count - capacity; - foreach (var item in queue) + foreach (int item in queue) Assert.AreEqual(j++, item); for (int i = queue.Count; i < queue.Count + capacity; i++) diff --git a/osu.Game.Tests/NonVisual/Multiplayer/StatefulMultiplayerClientTest.cs b/osu.Game.Tests/NonVisual/Multiplayer/StatefulMultiplayerClientTest.cs index 07ec86b0e7..8e2259bce8 100644 --- a/osu.Game.Tests/NonVisual/Multiplayer/StatefulMultiplayerClientTest.cs +++ b/osu.Game.Tests/NonVisual/Multiplayer/StatefulMultiplayerClientTest.cs @@ -84,7 +84,7 @@ namespace osu.Game.Tests.NonVisual.Multiplayer { for (int i = 0; i < userCount; ++i) { - var userId = Client.Room?.Users[i].UserID ?? throw new AssertionException("Room cannot be null!"); + int userId = Client.Room?.Users[i].UserID ?? throw new AssertionException("Room cannot be null!"); Client.ChangeUserState(userId, state); } }); diff --git a/osu.Game.Tests/NonVisual/ReverseQueueTest.cs b/osu.Game.Tests/NonVisual/ReverseQueueTest.cs index 93cd9403ce..808c8d14f0 100644 --- a/osu.Game.Tests/NonVisual/ReverseQueueTest.cs +++ b/osu.Game.Tests/NonVisual/ReverseQueueTest.cs @@ -29,7 +29,7 @@ namespace osu.Game.Tests.NonVisual }); int count = 0; - foreach (var unused in queue) + foreach (char unused in queue) count++; Assert.AreEqual(0, count); @@ -72,7 +72,7 @@ namespace osu.Game.Tests.NonVisual // Assert correct item return and no longer in queue after dequeueing Assert.AreEqual('a', queue[5]); - var dequeuedItem = queue.Dequeue(); + char dequeuedItem = queue.Dequeue(); Assert.AreEqual('a', dequeuedItem); Assert.AreEqual(5, queue.Count); @@ -133,7 +133,7 @@ namespace osu.Game.Tests.NonVisual int expectedValueIndex = 0; // Assert items are enumerated in correct order - foreach (var item in queue) + foreach (char item in queue) { Assert.AreEqual(expectedValues[expectedValueIndex], item); expectedValueIndex++; diff --git a/osu.Game.Tests/Online/TestMultiplayerMessagePackSerialization.cs b/osu.Game.Tests/Online/TestMultiplayerMessagePackSerialization.cs index 5491774e26..1027b722d1 100644 --- a/osu.Game.Tests/Online/TestMultiplayerMessagePackSerialization.cs +++ b/osu.Game.Tests/Online/TestMultiplayerMessagePackSerialization.cs @@ -20,7 +20,7 @@ namespace osu.Game.Tests.Online MatchState = new TeamVersusRoomState() }; - var serialized = MessagePackSerializer.Serialize(room); + byte[] serialized = MessagePackSerializer.Serialize(room); var deserialized = MessagePackSerializer.Deserialize(serialized); @@ -32,7 +32,7 @@ namespace osu.Game.Tests.Online { var state = new TeamVersusUserState(); - var serialized = MessagePackSerializer.Serialize(typeof(MatchUserState), state); + byte[] serialized = MessagePackSerializer.Serialize(typeof(MatchUserState), state); var deserialized = MessagePackSerializer.Deserialize(serialized); Assert.IsTrue(deserialized is TeamVersusUserState); @@ -44,7 +44,7 @@ namespace osu.Game.Tests.Online var state = new TeamVersusUserState(); // SignalR serialises using the actual type, rather than a base specification. - var serialized = MessagePackSerializer.Serialize(typeof(TeamVersusUserState), state); + byte[] serialized = MessagePackSerializer.Serialize(typeof(TeamVersusUserState), state); // works with explicit type specified. MessagePackSerializer.Deserialize(serialized); @@ -59,7 +59,7 @@ namespace osu.Game.Tests.Online var state = new TeamVersusUserState(); // SignalR serialises using the actual type, rather than a base specification. - var serialized = MessagePackSerializer.Serialize(typeof(TeamVersusUserState), state, SignalRUnionWorkaroundResolver.OPTIONS); + byte[] serialized = MessagePackSerializer.Serialize(typeof(TeamVersusUserState), state, SignalRUnionWorkaroundResolver.OPTIONS); // works with explicit type specified. MessagePackSerializer.Deserialize(serialized); diff --git a/osu.Game.Tests/Resources/TestResources.cs b/osu.Game.Tests/Resources/TestResources.cs index 839366d98e..dff9478852 100644 --- a/osu.Game.Tests/Resources/TestResources.cs +++ b/osu.Game.Tests/Resources/TestResources.cs @@ -29,7 +29,7 @@ namespace osu.Game.Tests.Resources /// A path to a copy of a beatmap archive (osz). Should be deleted after use. public static string GetQuickTestBeatmapForImport() { - var tempPath = getTempFilename(); + string tempPath = getTempFilename(); using (var stream = OpenResource("Archives/241526 Soleily - Renatus_virtual_quick.osz")) using (var newFile = File.Create(tempPath)) stream.CopyTo(newFile); @@ -45,7 +45,7 @@ namespace osu.Game.Tests.Resources /// A path to a copy of a beatmap archive (osz). Should be deleted after use. public static string GetTestBeatmapForImport(bool virtualTrack = false) { - var tempPath = getTempFilename(); + string tempPath = getTempFilename(); using (var stream = GetTestBeatmapStream(virtualTrack)) using (var newFile = File.Create(tempPath)) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneBeatDivisorControl.cs b/osu.Game.Tests/Visual/Editing/TestSceneBeatDivisorControl.cs index 6cf5e6a987..ed7bb9e301 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneBeatDivisorControl.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneBeatDivisorControl.cs @@ -72,7 +72,7 @@ namespace osu.Game.Tests.Visual.Editing private Vector2 getPositionForDivisor(int divisor) { - var relativePosition = (float)Math.Clamp(divisor, 0, 16) / 16; + float relativePosition = (float)Math.Clamp(divisor, 0, 16) / 16; var sliderDrawQuad = tickSliderBar.ScreenSpaceDrawQuad; return new Vector2( sliderDrawQuad.TopLeft.X + sliderDrawQuad.Width * relativePosition, diff --git a/osu.Game.Tests/Visual/Editing/TestSceneEditorBeatmapCreation.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorBeatmapCreation.cs index 440d66ff9f..92c8131568 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneEditorBeatmapCreation.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneEditorBeatmapCreation.cs @@ -76,7 +76,7 @@ namespace osu.Game.Tests.Visual.Editing { var setup = Editor.ChildrenOfType().First(); - var temp = TestResources.GetTestBeatmapForImport(); + string temp = TestResources.GetTestBeatmapForImport(); string extractedFolder = $"{temp}_extracted"; Directory.CreateDirectory(extractedFolder); diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayMenuOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayMenuOverlay.cs index 477ac70501..abd43e7427 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayMenuOverlay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayMenuOverlay.cs @@ -57,7 +57,7 @@ namespace osu.Game.Tests.Visual.Gameplay { showOverlay(); - var retryCount = 0; + int retryCount = 0; AddRepeatStep("Add retry", () => { diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorLeaderboard.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorLeaderboard.cs index ade24b8740..bd5320354e 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorLeaderboard.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorLeaderboard.cs @@ -32,7 +32,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { PLAYER_2_ID, new ManualClock() } }; - foreach (var (userId, _) in clocks) + foreach ((int userId, var _) in clocks) { SpectatorClient.StartPlay(userId, 0); OnlinePlayDependencies.Client.AddUser(new User { Id = userId }); @@ -53,7 +53,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("add clock sources", () => { - foreach (var (userId, clock) in clocks) + foreach ((int userId, var clock) in clocks) leaderboard.AddClock(userId, clock); }); } diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs index bfcb55ce33..8d878b993c 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs @@ -289,7 +289,7 @@ namespace osu.Game.Tests.Visual.Multiplayer [Test] public void TestSpectatingDuringGameplay() { - var players = new[] { PLAYER_1_ID, PLAYER_2_ID }; + int[] players = new[] { PLAYER_1_ID, PLAYER_2_ID }; start(players); sendFrames(players, 300); @@ -326,7 +326,7 @@ namespace osu.Game.Tests.Visual.Multiplayer for (int count = 3; count >= 0; count--) { - var id = PLAYER_1_ID + count; + int id = PLAYER_1_ID + count; end(id); AddUntilStep($"{id} area grayed", () => getInstance(id).Colour != Color4.White); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs index 2bb77395ef..f4a72dd7e7 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs @@ -582,7 +582,7 @@ namespace osu.Game.Tests.Visual.Multiplayer // Gameplay runs in real-time, so we need to incrementally check if gameplay has finished in order to not time out. for (double i = 1000; i < TestResources.QUICK_BEATMAP_LENGTH; i += 1000) { - var time = i; + double time = i; AddUntilStep($"wait for time > {i}", () => this.ChildrenOfType().SingleOrDefault()?.GameplayClock.CurrentTime > time); } diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs index 3317ddc767..eff107faee 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs @@ -55,7 +55,7 @@ namespace osu.Game.Tests.Visual.Multiplayer var playableBeatmap = Beatmap.Value.GetPlayableBeatmap(Ruleset.Value); var multiplayerUsers = new List(); - foreach (var user in users) + foreach (int user in users) { SpectatorClient.StartPlay(user, Beatmap.Value.BeatmapInfo.OnlineBeatmapID ?? 0); multiplayerUsers.Add(OnlinePlayDependencies.Client.AddUser(new User { Id = user }, true)); @@ -89,7 +89,7 @@ namespace osu.Game.Tests.Visual.Multiplayer [Test] public void TestUserQuit() { - foreach (var user in users) + foreach (int user in users) AddStep($"mark user {user} quit", () => Client.RemoveUser(LookupCache.GetUserAsync(user).Result.AsNonNull())); } @@ -114,7 +114,7 @@ namespace osu.Game.Tests.Visual.Multiplayer public void RandomlyUpdateState() { - foreach (var userId in PlayingUsers) + foreach (int userId in PlayingUsers) { if (RNG.NextBool()) continue; diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs index dfaf2f1dc3..32114fa500 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs @@ -59,7 +59,7 @@ namespace osu.Game.Tests.Visual.Multiplayer var playableBeatmap = Beatmap.Value.GetPlayableBeatmap(Ruleset.Value); var multiplayerUsers = new List(); - foreach (var user in users) + foreach (int user in users) { SpectatorClient.StartPlay(user, Beatmap.Value.BeatmapInfo.OnlineBeatmapID ?? 0); var roomUser = OnlinePlayDependencies.Client.AddUser(new User { Id = user }, true); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerParticipantsList.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerParticipantsList.cs index d1980b03c7..e50b150f94 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerParticipantsList.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerParticipantsList.cs @@ -112,7 +112,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddRepeatStep("increment progress", () => { - var progress = this.ChildrenOfType().Single().User.BeatmapAvailability.DownloadProgress ?? 0; + float progress = this.ChildrenOfType().Single().User.BeatmapAvailability.DownloadProgress ?? 0; Client.ChangeBeatmapAvailability(BeatmapAvailability.Downloading(progress + RNG.NextSingle(0.1f))); }, 25); diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs index ab4e1b4457..99c3b398ab 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs @@ -190,8 +190,8 @@ namespace osu.Game.Tests.Visual.Online for (int zeroBasedIndex = 0; zeroBasedIndex < 10; ++zeroBasedIndex) { - var oneBasedIndex = zeroBasedIndex + 1; - var targetNumberKey = oneBasedIndex % 10; + int oneBasedIndex = zeroBasedIndex + 1; + int targetNumberKey = oneBasedIndex % 10; var targetChannel = channels[zeroBasedIndex]; AddStep($"Press Alt+{targetNumberKey}", () => pressChannelHotkey(targetNumberKey)); AddAssert($"Channel #{oneBasedIndex} is selected", () => currentChannel == targetChannel); diff --git a/osu.Game.Tests/Visual/Online/TestSceneRankGraph.cs b/osu.Game.Tests/Visual/Online/TestSceneRankGraph.cs index 5bf9e31309..f577140e17 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneRankGraph.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneRankGraph.cs @@ -24,10 +24,10 @@ namespace osu.Game.Tests.Visual.Online { RankGraph graph; - var data = new int[89]; - var dataWithZeros = new int[89]; - var smallData = new int[89]; - var edgyData = new int[89]; + int[] data = new int[89]; + int[] dataWithZeros = new int[89]; + int[] smallData = new int[89]; + int[] edgyData = new int[89]; for (int i = 0; i < 89; i++) data[i] = dataWithZeros[i] = (i + 1) * 1000; diff --git a/osu.Game.Tests/Visual/Online/TestSceneStandAloneChatDisplay.cs b/osu.Game.Tests/Visual/Online/TestSceneStandAloneChatDisplay.cs index 165fff99dd..b3b8d75c46 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneStandAloneChatDisplay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneStandAloneChatDisplay.cs @@ -171,7 +171,7 @@ namespace osu.Game.Tests.Visual.Online { var indices = chatDisplay.FillFlow.OfType().Select(ds => chatDisplay.FillFlow.IndexOf(ds)); - foreach (var i in indices) + foreach (int i in indices) { if (i < chatDisplay.FillFlow.Count && chatDisplay.FillFlow[i + 1] is DrawableChannel.DaySeparator) return false; diff --git a/osu.Game.Tests/Visual/Online/TestSceneWikiHeader.cs b/osu.Game.Tests/Visual/Online/TestSceneWikiHeader.cs index 08e61d19f4..27e989df76 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneWikiHeader.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneWikiHeader.cs @@ -75,7 +75,7 @@ namespace osu.Game.Tests.Visual.Online private bool checkBreadcrumb() { - var result = header.TabControlItems.Contains(wikiPageData.Value.Title); + bool result = header.TabControlItems.Contains(wikiPageData.Value.Title); if (wikiPageData.Value.Subtitle != null) result = header.TabControlItems.Contains(wikiPageData.Value.Subtitle) && result; diff --git a/osu.Game.Tests/Visual/Online/TestSceneWikiSidebar.cs b/osu.Game.Tests/Visual/Online/TestSceneWikiSidebar.cs index b4f1997bb0..862b3667b1 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneWikiSidebar.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneWikiSidebar.cs @@ -36,7 +36,7 @@ namespace osu.Game.Tests.Visual.Online { AddStep("Add TOC", () => { - for (var i = 0; i < 10; i++) + for (int i = 0; i < 10; i++) addTitle($"This is a very long title {i + 1}"); }); } @@ -46,7 +46,7 @@ namespace osu.Game.Tests.Visual.Online { AddStep("Add TOC", () => { - for (var i = 0; i < 10; i++) + for (int i = 0; i < 10; i++) addTitle($"This is a very long title {i + 1}", i % 4 != 0); }); } diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneBreadcrumbControlHeader.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneBreadcrumbControlHeader.cs index 90c3e142df..7bc75f1c44 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneBreadcrumbControlHeader.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneBreadcrumbControlHeader.cs @@ -31,18 +31,18 @@ namespace osu.Game.Tests.Visual.UserInterface [Test] public void TestAddAndRemoveItem() { - foreach (var item in items.Skip(1)) + foreach (string item in items.Skip(1)) AddStep($"Add {item} item", () => header.AddItem(item)); - foreach (var item in items.Reverse().SkipLast(3)) + foreach (string item in items.Reverse().SkipLast(3)) AddStep($"Remove {item} item", () => header.RemoveItem(item)); AddStep("Clear items", () => header.ClearItems()); - foreach (var item in items) + foreach (string item in items) AddStep($"Add {item} item", () => header.AddItem(item)); - foreach (var item in items) + foreach (string item in items) AddStep($"Remove {item} item", () => header.RemoveItem(item)); } diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneFooterButtonMods.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneFooterButtonMods.cs index 8d1572769f..0631059d1a 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneFooterButtonMods.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneFooterButtonMods.cs @@ -73,8 +73,8 @@ namespace osu.Game.Tests.Visual.UserInterface private bool assertModsMultiplier(IEnumerable mods) { - var multiplier = mods.Aggregate(1.0, (current, mod) => current * mod.ScoreMultiplier); - var expectedValue = multiplier.Equals(1.0) ? string.Empty : $"{multiplier:N2}x"; + double multiplier = mods.Aggregate(1.0, (current, mod) => current * mod.ScoreMultiplier); + string expectedValue = multiplier.Equals(1.0) ? string.Empty : $"{multiplier:N2}x"; return expectedValue == footerButtonMods.MultiplierText.Current.Value; } diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneOsuIcon.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneOsuIcon.cs index 096bccae9e..0bc4ac12d6 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneOsuIcon.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneOsuIcon.cs @@ -48,7 +48,7 @@ namespace osu.Game.Tests.Visual.UserInterface foreach (var p in typeof(OsuIcon).GetProperties(BindingFlags.Public | BindingFlags.Static)) { - var propValue = p.GetValue(null); + object propValue = p.GetValue(null); Debug.Assert(propValue != null); flow.Add(new Icon($"{nameof(OsuIcon)}.{p.Name}", (IconUsage)propValue)); diff --git a/osu.Game.Tournament/Components/SongBar.cs b/osu.Game.Tournament/Components/SongBar.cs index 13888699ef..60be7dec91 100644 --- a/osu.Game.Tournament/Components/SongBar.cs +++ b/osu.Game.Tournament/Components/SongBar.cs @@ -101,12 +101,12 @@ namespace osu.Game.Tournament.Components return; } - var bpm = beatmapInfo.BeatmapSet.OnlineInfo.BPM; - var length = beatmapInfo.Length; + double bpm = beatmapInfo.BeatmapSet.OnlineInfo.BPM; + double length = beatmapInfo.Length; string hardRockExtra = ""; string srExtra = ""; - var ar = beatmapInfo.BaseDifficulty.ApproachRate; + float ar = beatmapInfo.BaseDifficulty.ApproachRate; if ((mods & LegacyMods.HardRock) > 0) { @@ -252,9 +252,9 @@ namespace osu.Game.Tournament.Components s.Font = OsuFont.Torus.With(weight: bold ? FontWeight.Bold : FontWeight.Regular, size: 15); } - for (var i = 0; i < tuples.Length; i++) + for (int i = 0; i < tuples.Length; i++) { - var (heading, content) = tuples[i]; + (string heading, string content) = tuples[i]; if (i > 0) { diff --git a/osu.Game.Tournament/IPC/FileBasedIPC.cs b/osu.Game.Tournament/IPC/FileBasedIPC.cs index 7010a30eb7..6dde265bd6 100644 --- a/osu.Game.Tournament/IPC/FileBasedIPC.cs +++ b/osu.Game.Tournament/IPC/FileBasedIPC.cs @@ -46,7 +46,7 @@ namespace osu.Game.Tournament.IPC [BackgroundDependencyLoader] private void load() { - var stablePath = stableInfo.StablePath ?? findStablePath(); + string stablePath = stableInfo.StablePath ?? findStablePath(); initialiseIPCStorage(stablePath); } @@ -78,8 +78,8 @@ namespace osu.Game.Tournament.IPC using (var stream = IPCStorage.GetStream(file_ipc_filename)) using (var sr = new StreamReader(stream)) { - var beatmapId = int.Parse(sr.ReadLine().AsNonNull()); - var mods = int.Parse(sr.ReadLine().AsNonNull()); + int beatmapId = int.Parse(sr.ReadLine().AsNonNull()); + int mods = int.Parse(sr.ReadLine().AsNonNull()); if (lastBeatmapId != beatmapId) { @@ -187,10 +187,10 @@ namespace osu.Game.Tournament.IPC [CanBeNull] private string findStablePath() { - var stableInstallPath = findFromEnvVar() ?? - findFromRegistry() ?? - findFromLocalAppData() ?? - findFromDotFolder(); + string stableInstallPath = findFromEnvVar() ?? + findFromRegistry() ?? + findFromLocalAppData() ?? + findFromDotFolder(); Logger.Log($"Stable path for tourney usage: {stableInstallPath}"); return stableInstallPath; diff --git a/osu.Game.Tournament/JsonPointConverter.cs b/osu.Game.Tournament/JsonPointConverter.cs index 9c82f8ac06..32bbe2dc18 100644 --- a/osu.Game.Tournament/JsonPointConverter.cs +++ b/osu.Game.Tournament/JsonPointConverter.cs @@ -40,7 +40,7 @@ namespace osu.Game.Tournament if (reader.TokenType == JsonToken.PropertyName) { - var name = reader.Value?.ToString(); + string name = reader.Value?.ToString(); int? val = reader.ReadAsInt32(); if (val == null) diff --git a/osu.Game.Tournament/Models/TournamentTeam.cs b/osu.Game.Tournament/Models/TournamentTeam.cs index 7074ae413c..d895e4b538 100644 --- a/osu.Game.Tournament/Models/TournamentTeam.cs +++ b/osu.Game.Tournament/Models/TournamentTeam.cs @@ -36,10 +36,10 @@ namespace osu.Game.Tournament.Models { get { - var ranks = Players.Select(p => p.Statistics?.GlobalRank) - .Where(i => i.HasValue) - .Select(i => i.Value) - .ToArray(); + int[] ranks = Players.Select(p => p.Statistics?.GlobalRank) + .Where(i => i.HasValue) + .Select(i => i.Value) + .ToArray(); if (ranks.Length == 0) return 0; diff --git a/osu.Game.Tournament/Screens/Gameplay/Components/TournamentMatchScoreDisplay.cs b/osu.Game.Tournament/Screens/Gameplay/Components/TournamentMatchScoreDisplay.cs index 77101e4023..813bed86ae 100644 --- a/osu.Game.Tournament/Screens/Gameplay/Components/TournamentMatchScoreDisplay.cs +++ b/osu.Game.Tournament/Screens/Gameplay/Components/TournamentMatchScoreDisplay.cs @@ -114,7 +114,7 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components var winningBar = score1.Value > score2.Value ? score1Bar : score2Bar; var losingBar = score1.Value <= score2.Value ? score1Bar : score2Bar; - var diff = Math.Max(score1.Value, score2.Value) - Math.Min(score1.Value, score2.Value); + int diff = Math.Max(score1.Value, score2.Value) - Math.Min(score1.Value, score2.Value); losingBar.ResizeWidthTo(0, 400, Easing.OutQuint); winningBar.ResizeWidthTo(Math.Min(0.4f, MathF.Pow(diff / 1500000f, 0.5f) / 2), 400, Easing.OutQuint); diff --git a/osu.Game.Tournament/Screens/Ladder/Components/DrawableTournamentMatch.cs b/osu.Game.Tournament/Screens/Ladder/Components/DrawableTournamentMatch.cs index 6937c69dbf..3e950310cf 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/DrawableTournamentMatch.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/DrawableTournamentMatch.cs @@ -208,7 +208,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components { if (Match.Round.Value == null) return; - var instaWinAmount = Match.Round.Value.BestOf.Value / 2; + int instaWinAmount = Match.Round.Value.BestOf.Value / 2; Match.Completed.Value = Match.Round.Value.BestOf.Value > 0 && (Match.Team1Score.Value + Match.Team2Score.Value >= Match.Round.Value.BestOf.Value || Match.Team1Score.Value > instaWinAmount || Match.Team2Score.Value > instaWinAmount); @@ -243,8 +243,8 @@ namespace osu.Game.Tournament.Screens.Ladder.Components { foreach (var conditional in Match.ConditionalMatches) { - var team1Match = conditional.Acronyms.Contains(Match.Team1Acronym); - var team2Match = conditional.Acronyms.Contains(Match.Team2Acronym); + bool team1Match = conditional.Acronyms.Contains(Match.Team1Acronym); + bool team2Match = conditional.Acronyms.Contains(Match.Team2Acronym); if (team1Match && team2Match) Match.Date.Value = conditional.Date.Value; diff --git a/osu.Game.Tournament/Screens/Ladder/LadderDragContainer.cs b/osu.Game.Tournament/Screens/Ladder/LadderDragContainer.cs index fa03518c47..f98bfd087d 100644 --- a/osu.Game.Tournament/Screens/Ladder/LadderDragContainer.cs +++ b/osu.Game.Tournament/Screens/Ladder/LadderDragContainer.cs @@ -34,7 +34,7 @@ namespace osu.Game.Tournament.Screens.Ladder protected override bool OnScroll(ScrollEvent e) { - var newScale = Math.Clamp(scale + e.ScrollDelta.Y / 15 * scale, min_scale, max_scale); + float newScale = Math.Clamp(scale + e.ScrollDelta.Y / 15 * scale, min_scale, max_scale); this.MoveTo(target -= e.MousePosition * (newScale - scale), 2000, Easing.OutQuint); this.ScaleTo(scale = newScale, 2000, Easing.OutQuint); diff --git a/osu.Game.Tournament/Screens/Setup/ResolutionSelector.cs b/osu.Game.Tournament/Screens/Setup/ResolutionSelector.cs index 4b518ea7c7..e53110651b 100644 --- a/osu.Game.Tournament/Screens/Setup/ResolutionSelector.cs +++ b/osu.Game.Tournament/Screens/Setup/ResolutionSelector.cs @@ -31,7 +31,7 @@ namespace osu.Game.Tournament.Screens.Setup return; // box contains text - if (!int.TryParse(numberBox.Text, out var number)) + if (!int.TryParse(numberBox.Text, out int number)) { // at this point, the only reason we can arrive here is if the input number was too big to parse into an int // so clamp to max allowed value diff --git a/osu.Game.Tournament/Screens/Setup/StablePathSelectScreen.cs b/osu.Game.Tournament/Screens/Setup/StablePathSelectScreen.cs index 3752d9d3be..8e9b32231f 100644 --- a/osu.Game.Tournament/Screens/Setup/StablePathSelectScreen.cs +++ b/osu.Game.Tournament/Screens/Setup/StablePathSelectScreen.cs @@ -37,7 +37,7 @@ namespace osu.Game.Tournament.Screens.Setup private void load(Storage storage, OsuColour colours) { var initialStorage = (ipc as FileBasedIPC)?.IPCStorage ?? storage; - var initialPath = new DirectoryInfo(initialStorage.GetFullPath(string.Empty)).Parent?.FullName; + string initialPath = new DirectoryInfo(initialStorage.GetFullPath(string.Empty)).Parent?.FullName; AddRangeInternal(new Drawable[] { @@ -129,7 +129,7 @@ namespace osu.Game.Tournament.Screens.Setup protected virtual void ChangePath() { - var target = directorySelector.CurrentPath.Value.FullName; + string target = directorySelector.CurrentPath.Value.FullName; var fileBasedIpc = ipc as FileBasedIPC; Logger.Log($"Changing Stable CE location to {target}"); diff --git a/osu.Game.Tournament/TournamentGame.cs b/osu.Game.Tournament/TournamentGame.cs index f3927bb852..f03f815b83 100644 --- a/osu.Game.Tournament/TournamentGame.cs +++ b/osu.Game.Tournament/TournamentGame.cs @@ -134,7 +134,7 @@ namespace osu.Game.Tournament windowSize.BindValueChanged(size => ScheduleAfterChildren(() => { - var minWidth = (int)(size.NewValue.Height / 768f * TournamentSceneManager.REQUIRED_WIDTH) - 1; + int minWidth = (int)(size.NewValue.Height / 768f * TournamentSceneManager.REQUIRED_WIDTH) - 1; heightWarning.Alpha = size.NewValue.Width < minWidth ? 1 : 0; }), true); diff --git a/osu.Game.Tournament/TournamentGameBase.cs b/osu.Game.Tournament/TournamentGameBase.cs index a6b0fa5cfc..978be720df 100644 --- a/osu.Game.Tournament/TournamentGameBase.cs +++ b/osu.Game.Tournament/TournamentGameBase.cs @@ -109,7 +109,7 @@ namespace osu.Game.Tournament // link matches to rounds foreach (var round in ladder.Rounds) { - foreach (var id in round.Matches) + foreach (int id in round.Matches) { var found = ladder.Matches.FirstOrDefault(p => p.ID == id); diff --git a/osu.Game/Audio/Effects/AudioFilter.cs b/osu.Game/Audio/Effects/AudioFilter.cs index d2a39e9db7..9446967173 100644 --- a/osu.Game/Audio/Effects/AudioFilter.cs +++ b/osu.Game/Audio/Effects/AudioFilter.cs @@ -103,7 +103,7 @@ namespace osu.Game.Audio.Effects ensureAttached(); - var filterIndex = mixer.Effects.IndexOf(filter); + int filterIndex = mixer.Effects.IndexOf(filter); if (filterIndex < 0) return; diff --git a/osu.Game/Beatmaps/Beatmap.cs b/osu.Game/Beatmaps/Beatmap.cs index b2211e26cf..98087994b7 100644 --- a/osu.Game/Beatmaps/Beatmap.cs +++ b/osu.Game/Beatmaps/Beatmap.cs @@ -93,7 +93,7 @@ namespace osu.Game.Beatmaps if (t.Time > lastTime) return (beatLength: t.BeatLength, 0); - var nextTime = i == ControlPointInfo.TimingPoints.Count - 1 ? lastTime : ControlPointInfo.TimingPoints[i + 1].Time; + double nextTime = i == ControlPointInfo.TimingPoints.Count - 1 ? lastTime : ControlPointInfo.TimingPoints[i + 1].Time; return (beatLength: t.BeatLength, duration: nextTime - t.Time); }) // Aggregate durations into a set of (beatLength, duration) tuples for each beat length diff --git a/osu.Game/Beatmaps/BeatmapInfoExtensions.cs b/osu.Game/Beatmaps/BeatmapInfoExtensions.cs index 836302c424..c35370d572 100644 --- a/osu.Game/Beatmaps/BeatmapInfoExtensions.cs +++ b/osu.Game/Beatmaps/BeatmapInfoExtensions.cs @@ -22,7 +22,7 @@ namespace osu.Game.Beatmaps if (includeDifficultyName) { - var versionString = getVersionString(beatmapInfo); + string versionString = getVersionString(beatmapInfo); return new RomanisableString($"{metadata.GetPreferred(true)} {versionString}".Trim(), $"{metadata.GetPreferred(false)} {versionString}".Trim()); } diff --git a/osu.Game/Beatmaps/BeatmapMetadataInfoExtensions.cs b/osu.Game/Beatmaps/BeatmapMetadataInfoExtensions.cs index ee946eeeec..732b76e967 100644 --- a/osu.Game/Beatmaps/BeatmapMetadataInfoExtensions.cs +++ b/osu.Game/Beatmaps/BeatmapMetadataInfoExtensions.cs @@ -37,8 +37,8 @@ namespace osu.Game.Beatmaps public static RomanisableString GetDisplayTitleRomanisable(this IBeatmapMetadataInfo metadataInfo) { string author = string.IsNullOrEmpty(metadataInfo.Author) ? string.Empty : $"({metadataInfo.Author})"; - var artistUnicode = string.IsNullOrEmpty(metadataInfo.ArtistUnicode) ? metadataInfo.Artist : metadataInfo.ArtistUnicode; - var titleUnicode = string.IsNullOrEmpty(metadataInfo.TitleUnicode) ? metadataInfo.Title : metadataInfo.TitleUnicode; + string artistUnicode = string.IsNullOrEmpty(metadataInfo.ArtistUnicode) ? metadataInfo.Artist : metadataInfo.ArtistUnicode; + string titleUnicode = string.IsNullOrEmpty(metadataInfo.TitleUnicode) ? metadataInfo.Title : metadataInfo.TitleUnicode; return new RomanisableString($"{artistUnicode} - {titleUnicode} {author}".Trim(), $"{metadataInfo.Artist} - {metadataInfo.Title} {author}".Trim()); } diff --git a/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs b/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs index 9d738ecbfb..246d1f8af5 100644 --- a/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs +++ b/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs @@ -180,8 +180,8 @@ namespace osu.Game.Beatmaps.ControlPoints private static double getClosestSnappedTime(TimingControlPoint timingPoint, double time, int beatDivisor) { - var beatLength = timingPoint.BeatLength / beatDivisor; - var beatLengths = (int)Math.Round((time - timingPoint.Time) / beatLength, MidpointRounding.AwayFromZero); + double beatLength = timingPoint.BeatLength / beatDivisor; + int beatLengths = (int)Math.Round((time - timingPoint.Time) / beatLength, MidpointRounding.AwayFromZero); return timingPoint.Time + beatLengths * beatLength; } diff --git a/osu.Game/Beatmaps/DifficultyRecommender.cs b/osu.Game/Beatmaps/DifficultyRecommender.cs index b1b1e58ab7..86f5e0dabf 100644 --- a/osu.Game/Beatmaps/DifficultyRecommender.cs +++ b/osu.Game/Beatmaps/DifficultyRecommender.cs @@ -59,12 +59,12 @@ namespace osu.Game.Beatmaps { foreach (var r in orderedRulesets) { - if (!recommendedDifficultyMapping.TryGetValue(r, out var recommendation)) + if (!recommendedDifficultyMapping.TryGetValue(r, out double recommendation)) continue; BeatmapInfo beatmapInfo = beatmaps.Where(b => b.Ruleset.Equals(r)).OrderBy(b => { - var difference = b.StarDifficulty - recommendation; + double difference = b.StarDifficulty - recommendation; return difference >= 0 ? difference * 2 : difference * -1; // prefer easier over harder }).FirstOrDefault(); diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index 7cd4244cd0..89541a0845 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -295,7 +295,7 @@ namespace osu.Game.Beatmaps.Formats writer.WriteLine("[Colours]"); - for (var i = 0; i < colours.Count; i++) + for (int i = 0; i < colours.Count; i++) { var comboColour = colours[i]; diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs index cf6c827af5..56525ddb14 100644 --- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs @@ -89,7 +89,7 @@ namespace osu.Game.Beatmaps.Formats protected string StripComments(string line) { - var index = line.AsSpan().IndexOf("//".AsSpan()); + int index = line.AsSpan().IndexOf("//".AsSpan()); if (index > 0) return line.Substring(0, index); @@ -135,7 +135,7 @@ namespace osu.Game.Beatmaps.Formats protected KeyValuePair SplitKeyVal(string line, char separator = ':') { - var split = line.Split(separator, 2); + string[] split = line.Split(separator, 2); return new KeyValuePair ( diff --git a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs index 0f15e28c00..90a96e2ac8 100644 --- a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs @@ -77,7 +77,7 @@ namespace osu.Game.Beatmaps.Formats private void handleEvents(string line) { - var depth = 0; + int depth = 0; foreach (char c in line) { @@ -104,8 +104,8 @@ namespace osu.Game.Beatmaps.Formats { case LegacyEventType.Video: { - var offset = Parsing.ParseInt(split[1]); - var path = CleanFilename(split[2]); + int offset = Parsing.ParseInt(split[1]); + string path = CleanFilename(split[2]); storyboard.GetLayer("Video").Add(new StoryboardVideo(path, offset)); break; @@ -113,11 +113,11 @@ namespace osu.Game.Beatmaps.Formats case LegacyEventType.Sprite: { - var layer = parseLayer(split[1]); + string layer = parseLayer(split[1]); var origin = parseOrigin(split[2]); - var path = CleanFilename(split[3]); - var x = Parsing.ParseFloat(split[4], Parsing.MAX_COORDINATE_VALUE); - var y = Parsing.ParseFloat(split[5], Parsing.MAX_COORDINATE_VALUE); + string path = CleanFilename(split[3]); + float x = Parsing.ParseFloat(split[4], Parsing.MAX_COORDINATE_VALUE); + float y = Parsing.ParseFloat(split[5], Parsing.MAX_COORDINATE_VALUE); storyboardSprite = new StoryboardSprite(path, origin, new Vector2(x, y)); storyboard.GetLayer(layer).Add(storyboardSprite); break; @@ -125,13 +125,13 @@ namespace osu.Game.Beatmaps.Formats case LegacyEventType.Animation: { - var layer = parseLayer(split[1]); + string layer = parseLayer(split[1]); var origin = parseOrigin(split[2]); - var path = CleanFilename(split[3]); - var x = Parsing.ParseFloat(split[4], Parsing.MAX_COORDINATE_VALUE); - var y = Parsing.ParseFloat(split[5], Parsing.MAX_COORDINATE_VALUE); - var frameCount = Parsing.ParseInt(split[6]); - var frameDelay = Parsing.ParseDouble(split[7]); + string path = CleanFilename(split[3]); + float x = Parsing.ParseFloat(split[4], Parsing.MAX_COORDINATE_VALUE); + float y = Parsing.ParseFloat(split[5], Parsing.MAX_COORDINATE_VALUE); + int frameCount = Parsing.ParseInt(split[6]); + double frameDelay = Parsing.ParseDouble(split[7]); if (FormatVersion < 6) // this is random as hell but taken straight from osu-stable. @@ -145,10 +145,10 @@ namespace osu.Game.Beatmaps.Formats case LegacyEventType.Sample: { - var time = Parsing.ParseDouble(split[1]); - var layer = parseLayer(split[2]); - var path = CleanFilename(split[3]); - var volume = split.Length > 4 ? Parsing.ParseFloat(split[4]) : 100; + double time = Parsing.ParseDouble(split[1]); + string layer = parseLayer(split[2]); + string path = CleanFilename(split[3]); + float volume = split.Length > 4 ? Parsing.ParseFloat(split[4]) : 100; storyboard.GetLayer(layer).Add(new StoryboardSampleInfo(path, time, (int)volume)); break; } @@ -159,24 +159,24 @@ namespace osu.Game.Beatmaps.Formats if (depth < 2) timelineGroup = storyboardSprite?.TimelineGroup; - var commandType = split[0]; + string commandType = split[0]; switch (commandType) { case "T": { - var triggerName = split[1]; - var startTime = split.Length > 2 ? Parsing.ParseDouble(split[2]) : double.MinValue; - var endTime = split.Length > 3 ? Parsing.ParseDouble(split[3]) : double.MaxValue; - var groupNumber = split.Length > 4 ? Parsing.ParseInt(split[4]) : 0; + string triggerName = split[1]; + double startTime = split.Length > 2 ? Parsing.ParseDouble(split[2]) : double.MinValue; + double endTime = split.Length > 3 ? Parsing.ParseDouble(split[3]) : double.MaxValue; + int groupNumber = split.Length > 4 ? Parsing.ParseInt(split[4]) : 0; timelineGroup = storyboardSprite?.AddTrigger(triggerName, startTime, endTime, groupNumber); break; } case "L": { - var startTime = Parsing.ParseDouble(split[1]); - var repeatCount = Parsing.ParseInt(split[2]); + double startTime = Parsing.ParseDouble(split[1]); + int repeatCount = Parsing.ParseInt(split[2]); timelineGroup = storyboardSprite?.AddLoop(startTime, Math.Max(0, repeatCount - 1)); break; } @@ -187,51 +187,51 @@ namespace osu.Game.Beatmaps.Formats split[3] = split[2]; var easing = (Easing)Parsing.ParseInt(split[1]); - var startTime = Parsing.ParseDouble(split[2]); - var endTime = Parsing.ParseDouble(split[3]); + double startTime = Parsing.ParseDouble(split[2]); + double endTime = Parsing.ParseDouble(split[3]); switch (commandType) { case "F": { - var startValue = Parsing.ParseFloat(split[4]); - var endValue = split.Length > 5 ? Parsing.ParseFloat(split[5]) : startValue; + float startValue = Parsing.ParseFloat(split[4]); + float endValue = split.Length > 5 ? Parsing.ParseFloat(split[5]) : startValue; timelineGroup?.Alpha.Add(easing, startTime, endTime, startValue, endValue); break; } case "S": { - var startValue = Parsing.ParseFloat(split[4]); - var endValue = split.Length > 5 ? Parsing.ParseFloat(split[5]) : startValue; + float startValue = Parsing.ParseFloat(split[4]); + float endValue = split.Length > 5 ? Parsing.ParseFloat(split[5]) : startValue; timelineGroup?.Scale.Add(easing, startTime, endTime, startValue, endValue); break; } case "V": { - var startX = Parsing.ParseFloat(split[4]); - var startY = Parsing.ParseFloat(split[5]); - var endX = split.Length > 6 ? Parsing.ParseFloat(split[6]) : startX; - var endY = split.Length > 7 ? Parsing.ParseFloat(split[7]) : startY; + float startX = Parsing.ParseFloat(split[4]); + float startY = Parsing.ParseFloat(split[5]); + float endX = split.Length > 6 ? Parsing.ParseFloat(split[6]) : startX; + float endY = split.Length > 7 ? Parsing.ParseFloat(split[7]) : startY; timelineGroup?.VectorScale.Add(easing, startTime, endTime, new Vector2(startX, startY), new Vector2(endX, endY)); break; } case "R": { - var startValue = Parsing.ParseFloat(split[4]); - var endValue = split.Length > 5 ? Parsing.ParseFloat(split[5]) : startValue; + float startValue = Parsing.ParseFloat(split[4]); + float endValue = split.Length > 5 ? Parsing.ParseFloat(split[5]) : startValue; timelineGroup?.Rotation.Add(easing, startTime, endTime, MathUtils.RadiansToDegrees(startValue), MathUtils.RadiansToDegrees(endValue)); break; } case "M": { - var startX = Parsing.ParseFloat(split[4]); - var startY = Parsing.ParseFloat(split[5]); - var endX = split.Length > 6 ? Parsing.ParseFloat(split[6]) : startX; - var endY = split.Length > 7 ? Parsing.ParseFloat(split[7]) : startY; + float startX = Parsing.ParseFloat(split[4]); + float startY = Parsing.ParseFloat(split[5]); + float endX = split.Length > 6 ? Parsing.ParseFloat(split[6]) : startX; + float endY = split.Length > 7 ? Parsing.ParseFloat(split[7]) : startY; timelineGroup?.X.Add(easing, startTime, endTime, startX, endX); timelineGroup?.Y.Add(easing, startTime, endTime, startY, endY); break; @@ -239,28 +239,28 @@ namespace osu.Game.Beatmaps.Formats case "MX": { - var startValue = Parsing.ParseFloat(split[4]); - var endValue = split.Length > 5 ? Parsing.ParseFloat(split[5]) : startValue; + float startValue = Parsing.ParseFloat(split[4]); + float endValue = split.Length > 5 ? Parsing.ParseFloat(split[5]) : startValue; timelineGroup?.X.Add(easing, startTime, endTime, startValue, endValue); break; } case "MY": { - var startValue = Parsing.ParseFloat(split[4]); - var endValue = split.Length > 5 ? Parsing.ParseFloat(split[5]) : startValue; + float startValue = Parsing.ParseFloat(split[4]); + float endValue = split.Length > 5 ? Parsing.ParseFloat(split[5]) : startValue; timelineGroup?.Y.Add(easing, startTime, endTime, startValue, endValue); break; } case "C": { - var startRed = Parsing.ParseFloat(split[4]); - var startGreen = Parsing.ParseFloat(split[5]); - var startBlue = Parsing.ParseFloat(split[6]); - var endRed = split.Length > 7 ? Parsing.ParseFloat(split[7]) : startRed; - var endGreen = split.Length > 8 ? Parsing.ParseFloat(split[8]) : startGreen; - var endBlue = split.Length > 9 ? Parsing.ParseFloat(split[9]) : startBlue; + float startRed = Parsing.ParseFloat(split[4]); + float startGreen = Parsing.ParseFloat(split[5]); + float startBlue = Parsing.ParseFloat(split[6]); + float endRed = split.Length > 7 ? Parsing.ParseFloat(split[7]) : startRed; + float endGreen = split.Length > 8 ? Parsing.ParseFloat(split[8]) : startGreen; + float endBlue = split.Length > 9 ? Parsing.ParseFloat(split[9]) : startBlue; timelineGroup?.Colour.Add(easing, startTime, endTime, new Color4(startRed / 255f, startGreen / 255f, startBlue / 255f, 1), new Color4(endRed / 255f, endGreen / 255f, endBlue / 255f, 1)); @@ -269,7 +269,7 @@ namespace osu.Game.Beatmaps.Formats case "P": { - var type = split[4]; + string type = split[4]; switch (type) { diff --git a/osu.Game/Beatmaps/Formats/Parsing.cs b/osu.Game/Beatmaps/Formats/Parsing.cs index c4795a6931..4d512fdeed 100644 --- a/osu.Game/Beatmaps/Formats/Parsing.cs +++ b/osu.Game/Beatmaps/Formats/Parsing.cs @@ -17,7 +17,7 @@ namespace osu.Game.Beatmaps.Formats public static float ParseFloat(string input, float parseLimit = (float)MAX_PARSE_VALUE) { - var output = float.Parse(input, CultureInfo.InvariantCulture); + float output = float.Parse(input, CultureInfo.InvariantCulture); if (output < -parseLimit) throw new OverflowException("Value is too low"); if (output > parseLimit) throw new OverflowException("Value is too high"); @@ -29,7 +29,7 @@ namespace osu.Game.Beatmaps.Formats public static double ParseDouble(string input, double parseLimit = MAX_PARSE_VALUE) { - var output = double.Parse(input, CultureInfo.InvariantCulture); + double output = double.Parse(input, CultureInfo.InvariantCulture); if (output < -parseLimit) throw new OverflowException("Value is too low"); if (output > parseLimit) throw new OverflowException("Value is too high"); @@ -41,7 +41,7 @@ namespace osu.Game.Beatmaps.Formats public static int ParseInt(string input, int parseLimit = (int)MAX_PARSE_VALUE) { - var output = int.Parse(input, CultureInfo.InvariantCulture); + int output = int.Parse(input, CultureInfo.InvariantCulture); if (output < -parseLimit) throw new OverflowException("Value is too low"); if (output > parseLimit) throw new OverflowException("Value is too high"); diff --git a/osu.Game/Beatmaps/MetadataUtils.cs b/osu.Game/Beatmaps/MetadataUtils.cs index 56f5e3fe35..b27c59b4de 100644 --- a/osu.Game/Beatmaps/MetadataUtils.cs +++ b/osu.Game/Beatmaps/MetadataUtils.cs @@ -35,7 +35,7 @@ namespace osu.Game.Beatmaps var stringBuilder = new StringBuilder(str.Length); - foreach (var c in str) + foreach (char c in str) { if (IsRomanised(c)) stringBuilder.Append(c); diff --git a/osu.Game/Beatmaps/WorkingBeatmapCache.cs b/osu.Game/Beatmaps/WorkingBeatmapCache.cs index cf83345e2a..65f84984c2 100644 --- a/osu.Game/Beatmaps/WorkingBeatmapCache.cs +++ b/osu.Game/Beatmaps/WorkingBeatmapCache.cs @@ -206,7 +206,7 @@ namespace osu.Game.Beatmaps { var decoder = Decoder.GetDecoder(stream); - var storyboardFilename = BeatmapSetInfo?.Files.FirstOrDefault(f => f.Filename.EndsWith(".osb", StringComparison.OrdinalIgnoreCase))?.Filename; + string storyboardFilename = BeatmapSetInfo?.Files.FirstOrDefault(f => f.Filename.EndsWith(".osb", StringComparison.OrdinalIgnoreCase))?.Filename; // todo: support loading from both set-wide storyboard *and* beatmap specific. if (string.IsNullOrEmpty(storyboardFilename)) diff --git a/osu.Game/Collections/CollectionManager.cs b/osu.Game/Collections/CollectionManager.cs index 6f9d9cd8a8..9ff92032b7 100644 --- a/osu.Game/Collections/CollectionManager.cs +++ b/osu.Game/Collections/CollectionManager.cs @@ -250,7 +250,7 @@ namespace osu.Game.Collections /// private void backgroundSave() { - var current = Interlocked.Increment(ref lastSave); + int current = Interlocked.Increment(ref lastSave); Task.Delay(100).ContinueWith(task => { if (current != lastSave) @@ -270,7 +270,7 @@ namespace osu.Game.Collections // This is NOT thread-safe!! try { - var tempPath = Path.GetTempFileName(); + string tempPath = Path.GetTempFileName(); using (var ms = new MemoryStream()) { @@ -296,8 +296,8 @@ namespace osu.Game.Collections using (var fs = File.OpenWrite(tempPath)) ms.WriteTo(fs); - var databasePath = storage.GetFullPath(database_name); - var databaseBackupPath = storage.GetFullPath(database_backup_name); + string databasePath = storage.GetFullPath(database_name); + string databaseBackupPath = storage.GetFullPath(database_backup_name); // Back up the existing database, clearing any existing backup. if (File.Exists(databaseBackupPath)) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index 6d37f68473..1beef89b51 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -160,12 +160,12 @@ namespace osu.Game.Configuration public void Migrate() { // arrives as 2020.123.0 - var rawVersion = Get(OsuSetting.Version); + string rawVersion = Get(OsuSetting.Version); if (rawVersion.Length < 6) return; - var pieces = rawVersion.Split('.'); + string[] pieces = rawVersion.Split('.'); // on a fresh install or when coming from a non-release build, execution will end here. // we don't want to run migrations in such cases. diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index f3ed2d735b..c180edbed2 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -675,7 +675,7 @@ namespace osu.Game.Database { MemoryStream hashable = new MemoryStream(); - foreach (var file in reader.Filenames.Where(f => HashableFileTypes.Any(ext => f.EndsWith(ext, StringComparison.OrdinalIgnoreCase))).OrderBy(f => f)) + foreach (string file in reader.Filenames.Where(f => HashableFileTypes.Any(ext => f.EndsWith(ext, StringComparison.OrdinalIgnoreCase))).OrderBy(f => f)) { using (Stream s = reader.GetStream(file)) s.CopyTo(hashable); diff --git a/osu.Game/Database/DatabaseBackedStore.cs b/osu.Game/Database/DatabaseBackedStore.cs index e6b6a0ac2f..a11efba54b 100644 --- a/osu.Game/Database/DatabaseBackedStore.cs +++ b/osu.Game/Database/DatabaseBackedStore.cs @@ -27,7 +27,7 @@ namespace osu.Game.Database if (context.Entry(obj).State != EntityState.Detached) return; - var id = obj.ID; + int id = obj.ID; var foundObject = lookupSource?.SingleOrDefault(t => t.ID == id) ?? context.Find(id); if (foundObject != null) obj = foundObject; diff --git a/osu.Game/Database/RealmContextFactory.cs b/osu.Game/Database/RealmContextFactory.cs index 3d0bb34dc1..ffb1c96261 100644 --- a/osu.Game/Database/RealmContextFactory.cs +++ b/osu.Game/Database/RealmContextFactory.cs @@ -156,7 +156,7 @@ namespace osu.Game.Database void convertOnlineIDs() where T : RealmObject { - var className = typeof(T).Name.Replace(@"Realm", string.Empty); + string? className = typeof(T).Name.Replace(@"Realm", string.Empty); // version was not bumped when the beatmap/ruleset models were added // therefore we must manually check for their presence to avoid throwing on the `DynamicApi` calls. @@ -170,8 +170,8 @@ namespace osu.Game.Database for (int i = 0; i < itemCount; i++) { - var oldItem = oldItems.ElementAt(i); - var newItem = newItems.ElementAt(i); + dynamic? oldItem = oldItems.ElementAt(i); + dynamic? newItem = newItems.ElementAt(i); long? nullableOnlineID = oldItem?.OnlineID; newItem.OnlineID = (int)(nullableOnlineID ?? -1); diff --git a/osu.Game/Database/StableImportManager.cs b/osu.Game/Database/StableImportManager.cs index 63a6db35c0..fe8c14c085 100644 --- a/osu.Game/Database/StableImportManager.cs +++ b/osu.Game/Database/StableImportManager.cs @@ -78,7 +78,7 @@ namespace osu.Game.Database var taskCompletionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); Schedule(() => dialogOverlay.Push(new StableDirectoryLocationDialog(taskCompletionSource))); - var stablePath = await taskCompletionSource.Task.ConfigureAwait(false); + string stablePath = await taskCompletionSource.Task.ConfigureAwait(false); return cachedStorage = new StableStorage(stablePath, desktopGameHost); } diff --git a/osu.Game/Database/UserLookupCache.cs b/osu.Game/Database/UserLookupCache.cs index ff81637efb..3626f5e83a 100644 --- a/osu.Game/Database/UserLookupCache.cs +++ b/osu.Game/Database/UserLookupCache.cs @@ -37,7 +37,7 @@ namespace osu.Game.Database { var userLookupTasks = new List>(); - foreach (var u in userIds) + foreach (int u in userIds) { userLookupTasks.Add(GetUserAsync(u, token).ContinueWith(task => { diff --git a/osu.Game/Graphics/Backgrounds/Triangles.cs b/osu.Game/Graphics/Backgrounds/Triangles.cs index ab8763e576..450c93f37c 100644 --- a/osu.Game/Graphics/Backgrounds/Triangles.cs +++ b/osu.Game/Graphics/Backgrounds/Triangles.cs @@ -214,7 +214,7 @@ namespace osu.Game.Graphics.Backgrounds float u1 = 1 - nextRandom(); //uniform(0,1] random floats float u2 = 1 - nextRandom(); float randStdNormal = (float)(Math.Sqrt(-2.0 * Math.Log(u1)) * Math.Sin(2.0 * Math.PI * u2)); // random normal(0,1) - var scale = Math.Max(triangleScale * (mean + std_dev * randStdNormal), 0.1f); // random normal(mean,stdDev^2) + float scale = Math.Max(triangleScale * (mean + std_dev * randStdNormal), 0.1f); // random normal(mean,stdDev^2) return new TriangleParticle { Scale = scale }; } diff --git a/osu.Game/Graphics/Containers/ConstrainedIconContainer.cs b/osu.Game/Graphics/Containers/ConstrainedIconContainer.cs index 9d3f342a70..9e1af1944c 100644 --- a/osu.Game/Graphics/Containers/ConstrainedIconContainer.cs +++ b/osu.Game/Graphics/Containers/ConstrainedIconContainer.cs @@ -43,7 +43,7 @@ namespace osu.Game.Graphics.Containers // - If we were to use RelativeSize/FillMode, we'd need to set the Icon's RelativeSizeAxes directly. // We can't do this because we would need access to AutoSizeAxes to set it to none. // Other issues come up along the way too, so it's not a good solution. - var fitScale = Math.Min(DrawSize.X / InternalChild.DrawSize.X, DrawSize.Y / InternalChild.DrawSize.Y); + float fitScale = Math.Min(DrawSize.X / InternalChild.DrawSize.X, DrawSize.Y / InternalChild.DrawSize.Y); InternalChild.Scale = new Vector2(fitScale); InternalChild.Anchor = Anchor.Centre; InternalChild.Origin = Anchor.Centre; diff --git a/osu.Game/Graphics/Containers/LogoTrackingContainer.cs b/osu.Game/Graphics/Containers/LogoTrackingContainer.cs index dadd7d5240..f89f3a5e76 100644 --- a/osu.Game/Graphics/Containers/LogoTrackingContainer.cs +++ b/osu.Game/Graphics/Containers/LogoTrackingContainer.cs @@ -109,7 +109,7 @@ namespace osu.Game.Graphics.Containers { double elapsedDuration = (double)(Time.Current - startTime); - var amount = (float)Interpolation.ApplyEasing(easing, Math.Min(elapsedDuration / duration, 1)); + float amount = (float)Interpolation.ApplyEasing(easing, Math.Min(elapsedDuration / duration, 1)); // Interpolate the position of the logo, where amount 0 is where the logo was when it first began interpolating, and amount 1 is the target location. Logo.Position = Vector2.Lerp(startPosition.Value, localPos, amount); diff --git a/osu.Game/Graphics/Containers/SectionsContainer.cs b/osu.Game/Graphics/Containers/SectionsContainer.cs index 76492cab55..540ca85809 100644 --- a/osu.Game/Graphics/Containers/SectionsContainer.cs +++ b/osu.Game/Graphics/Containers/SectionsContainer.cs @@ -182,7 +182,7 @@ namespace osu.Game.Graphics.Containers protected override bool OnInvalidate(Invalidation invalidation, InvalidationSource source) { - var result = base.OnInvalidate(invalidation, source); + bool result = base.OnInvalidate(invalidation, source); if (source == InvalidationSource.Child && (invalidation & Invalidation.DrawSize) != 0) { @@ -240,7 +240,7 @@ namespace osu.Game.Graphics.Containers headerBackgroundContainer.Height = expandableHeaderSize + fixedHeaderSize; headerBackgroundContainer.Y = ExpandableHeader?.Y ?? 0; - var smallestSectionHeight = Children.Count > 0 ? Children.Min(d => d.Height) : 0; + float smallestSectionHeight = Children.Count > 0 ? Children.Min(d => d.Height) : 0; // scroll offset is our fixed header height if we have it plus 10% of content height // plus 5% to fix floating point errors and to not have a section instantly unselect when scrolling upwards diff --git a/osu.Game/Graphics/Containers/SelectionCycleFillFlowContainer.cs b/osu.Game/Graphics/Containers/SelectionCycleFillFlowContainer.cs index 90b2d20e4d..4ddaa09be6 100644 --- a/osu.Game/Graphics/Containers/SelectionCycleFillFlowContainer.cs +++ b/osu.Game/Graphics/Containers/SelectionCycleFillFlowContainer.cs @@ -40,7 +40,7 @@ namespace osu.Game.Graphics.Containers public void Select(T item) { - var newIndex = IndexOf(item); + int newIndex = IndexOf(item); if (newIndex < 0) setSelected(null); diff --git a/osu.Game/Graphics/Cursor/MenuCursor.cs b/osu.Game/Graphics/Cursor/MenuCursor.cs index fd8f016860..3fa90e2330 100644 --- a/osu.Game/Graphics/Cursor/MenuCursor.cs +++ b/osu.Game/Graphics/Cursor/MenuCursor.cs @@ -44,7 +44,7 @@ namespace osu.Game.Graphics.Cursor if (dragRotationState != DragRotationState.NotDragging) { var position = e.MousePosition; - var distance = Vector2Extensions.Distance(position, positionMouseDown); + float distance = Vector2Extensions.Distance(position, positionMouseDown); // don't start rotating until we're moved a minimum distance away from the mouse down location, // else it can have an annoying effect. diff --git a/osu.Game/Graphics/ErrorTextFlowContainer.cs b/osu.Game/Graphics/ErrorTextFlowContainer.cs index f17a2a2c3d..486382bf33 100644 --- a/osu.Game/Graphics/ErrorTextFlowContainer.cs +++ b/osu.Game/Graphics/ErrorTextFlowContainer.cs @@ -28,7 +28,7 @@ namespace osu.Game.Graphics if (errors == null) return; - foreach (var error in errors) + foreach (string error in errors) errorDrawables.AddRange(AddParagraph(error, cp => cp.Colour = Color4.Red)); } } diff --git a/osu.Game/Graphics/ParticleExplosion.cs b/osu.Game/Graphics/ParticleExplosion.cs index 094cc87bbe..ec1077eb81 100644 --- a/osu.Game/Graphics/ParticleExplosion.cs +++ b/osu.Game/Graphics/ParticleExplosion.cs @@ -89,7 +89,7 @@ namespace osu.Game.Graphics protected override void Blit(Action vertexAction) { - var time = currentTime - startTime; + double time = currentTime - startTime; foreach (var p in parts) { @@ -136,7 +136,7 @@ namespace osu.Game.Graphics public Vector2 PositionAtTime(double time) { - var travelledDistance = distance * progressAtTime(time); + float travelledDistance = distance * progressAtTime(time); return new Vector2(0.5f) + travelledDistance * new Vector2(MathF.Sin(direction), MathF.Cos(direction)); } diff --git a/osu.Game/Graphics/ParticleSpewer.cs b/osu.Game/Graphics/ParticleSpewer.cs index 54a2b1e890..4fc6c4527f 100644 --- a/osu.Game/Graphics/ParticleSpewer.cs +++ b/osu.Game/Graphics/ParticleSpewer.cs @@ -109,18 +109,18 @@ namespace osu.Game.Graphics { foreach (var p in particles) { - var timeSinceStart = currentTime - p.StartTime; + float timeSinceStart = currentTime - p.StartTime; // ignore particles from the future. // these can appear when seeking in replays. if (timeSinceStart < 0) continue; - var alpha = p.AlphaAtTime(timeSinceStart); + float alpha = p.AlphaAtTime(timeSinceStart); if (alpha <= 0) continue; var pos = p.PositionAtTime(timeSinceStart, gravity, maxDuration); - var scale = p.ScaleAtTime(timeSinceStart); - var angle = p.AngleAtTime(timeSinceStart); + float scale = p.ScaleAtTime(timeSinceStart); + float angle = p.AngleAtTime(timeSinceStart); var rect = createDrawRect(pos, scale); @@ -139,8 +139,8 @@ namespace osu.Game.Graphics private RectangleF createDrawRect(Vector2 position, float scale) { - var width = Texture.DisplayWidth * scale; - var height = Texture.DisplayHeight * scale; + float width = Texture.DisplayWidth * scale; + float height = Texture.DisplayHeight * scale; if (relativePositionAxes.HasFlagFast(Axes.X)) position.X *= sourceSize.X; @@ -188,7 +188,7 @@ namespace osu.Game.Graphics public Vector2 PositionAtTime(float timeSinceStart, float gravity, float maxDuration) { - var progress = progressAtTime(timeSinceStart); + float progress = progressAtTime(timeSinceStart); var currentGravity = new Vector2(0, gravity * Duration / maxDuration * progress); return StartPosition + (Velocity + currentGravity) * timeSinceStart / maxDuration; diff --git a/osu.Game/Graphics/ScreenshotManager.cs b/osu.Game/Graphics/ScreenshotManager.cs index e652f07239..1f7f93b3c3 100644 --- a/osu.Game/Graphics/ScreenshotManager.cs +++ b/osu.Game/Graphics/ScreenshotManager.cs @@ -147,15 +147,15 @@ namespace osu.Game.Graphics private string getFilename() { var dt = DateTime.Now; - var fileExt = screenshotFormat.ToString().ToLowerInvariant(); + string fileExt = screenshotFormat.ToString().ToLowerInvariant(); - var withoutIndex = $"osu_{dt:yyyy-MM-dd_HH-mm-ss}.{fileExt}"; + string withoutIndex = $"osu_{dt:yyyy-MM-dd_HH-mm-ss}.{fileExt}"; if (!storage.Exists(withoutIndex)) return withoutIndex; for (ulong i = 1; i < ulong.MaxValue; i++) { - var indexedName = $"osu_{dt:yyyy-MM-dd_HH-mm-ss}-{i}.{fileExt}"; + string indexedName = $"osu_{dt:yyyy-MM-dd_HH-mm-ss}-{i}.{fileExt}"; if (!storage.Exists(indexedName)) return indexedName; } diff --git a/osu.Game/Graphics/UserInterface/BreadcrumbControl.cs b/osu.Game/Graphics/UserInterface/BreadcrumbControl.cs index fb5ff4aad3..d06c227d4b 100644 --- a/osu.Game/Graphics/UserInterface/BreadcrumbControl.cs +++ b/osu.Game/Graphics/UserInterface/BreadcrumbControl.cs @@ -33,8 +33,8 @@ namespace osu.Game.Graphics.UserInterface { foreach (var t in TabContainer.Children.OfType()) { - var tIndex = TabContainer.IndexOf(t); - var tabIndex = TabContainer.IndexOf(TabMap[index.NewValue]); + int tIndex = TabContainer.IndexOf(t); + int tabIndex = TabContainer.IndexOf(TabMap[index.NewValue]); t.State = tIndex > tabIndex ? Visibility.Hidden : Visibility.Visible; t.Chevron.FadeTo(tIndex >= tabIndex ? 0f : 1f, 500, Easing.OutQuint); diff --git a/osu.Game/Graphics/UserInterface/OsuSliderBar.cs b/osu.Game/Graphics/UserInterface/OsuSliderBar.cs index 6963f7335e..d4310dc901 100644 --- a/osu.Game/Graphics/UserInterface/OsuSliderBar.cs +++ b/osu.Game/Graphics/UserInterface/OsuSliderBar.cs @@ -216,10 +216,10 @@ namespace osu.Game.Graphics.UserInterface } else { - var decimalPrecision = normalise(CurrentNumber.Precision.ToDecimal(NumberFormatInfo.InvariantInfo), max_decimal_digits); + decimal decimalPrecision = normalise(CurrentNumber.Precision.ToDecimal(NumberFormatInfo.InvariantInfo), max_decimal_digits); // Find the number of significant digits (we could have less than 5 after normalize()) - var significantDigits = findPrecision(decimalPrecision); + int significantDigits = findPrecision(decimalPrecision); TooltipText = floatValue.ToString($"N{significantDigits}"); } diff --git a/osu.Game/Graphics/UserInterface/StarCounter.cs b/osu.Game/Graphics/UserInterface/StarCounter.cs index 32b788b5dc..b66f371801 100644 --- a/osu.Game/Graphics/UserInterface/StarCounter.cs +++ b/osu.Game/Graphics/UserInterface/StarCounter.cs @@ -89,7 +89,7 @@ namespace osu.Game.Graphics.UserInterface public void ReplayAnimation() { - var t = current; + float t = current; ResetCount(); Current = t; } @@ -105,7 +105,7 @@ namespace osu.Game.Graphics.UserInterface private void animate(float newValue) { - for (var i = 0; i < stars.Children.Count; i++) + for (int i = 0; i < stars.Children.Count; i++) { var star = stars.Children[i]; diff --git a/osu.Game/Graphics/UserInterface/TwoLayerButton.cs b/osu.Game/Graphics/UserInterface/TwoLayerButton.cs index 969309bc79..1f5d29571d 100644 --- a/osu.Game/Graphics/UserInterface/TwoLayerButton.cs +++ b/osu.Game/Graphics/UserInterface/TwoLayerButton.cs @@ -236,7 +236,7 @@ namespace osu.Game.Graphics.UserInterface { base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes); - var beatLength = timingPoint.BeatLength; + double beatLength = timingPoint.BeatLength; float amplitudeAdjust = Math.Min(1, 0.4f + amplitudes.Maximum); diff --git a/osu.Game/IO/LineBufferedReader.cs b/osu.Game/IO/LineBufferedReader.cs index 018321dc9a..a6b8c9492a 100644 --- a/osu.Game/IO/LineBufferedReader.cs +++ b/osu.Game/IO/LineBufferedReader.cs @@ -32,7 +32,7 @@ namespace osu.Game.IO if (lineBuffer.Count > 0) return lineBuffer.Peek(); - var line = streamReader.ReadLine(); + string line = streamReader.ReadLine(); if (line != null) lineBuffer.Enqueue(line); return line; @@ -50,7 +50,7 @@ namespace osu.Game.IO /// public string ReadToEnd() { - var remainingText = streamReader.ReadToEnd(); + string remainingText = streamReader.ReadToEnd(); if (lineBuffer.Count == 0) return remainingText; diff --git a/osu.Game/IO/Serialization/Converters/TypedListConverter.cs b/osu.Game/IO/Serialization/Converters/TypedListConverter.cs index 174fbf9983..715c83b07e 100644 --- a/osu.Game/IO/Serialization/Converters/TypedListConverter.cs +++ b/osu.Game/IO/Serialization/Converters/TypedListConverter.cs @@ -60,7 +60,7 @@ namespace osu.Game.IO.Serialization.Converters if (tok["$type"] == null) throw new JsonException("Expected $type token."); - var typeName = lookupTable[(int)tok["$type"]]; + string typeName = lookupTable[(int)tok["$type"]]; var instance = (T)Activator.CreateInstance(Type.GetType(typeName).AsNonNull()); serializer.Populate(itemReader, instance); @@ -80,7 +80,7 @@ namespace osu.Game.IO.Serialization.Converters var type = item.GetType(); var assemblyName = type.Assembly.GetName(); - var typeString = $"{type.FullName}, {assemblyName.Name}"; + string typeString = $"{type.FullName}, {assemblyName.Name}"; if (requiresTypeVersion) typeString += $", {assemblyName.Version}"; diff --git a/osu.Game/IO/StableStorage.cs b/osu.Game/IO/StableStorage.cs index d4b0d300ff..f5a8c4dc9e 100644 --- a/osu.Game/IO/StableStorage.cs +++ b/osu.Game/IO/StableStorage.cs @@ -34,7 +34,7 @@ namespace osu.Game.IO private string locateSongsDirectory() { - var configFile = GetFiles(".", $"osu!.{Environment.UserName}.cfg").SingleOrDefault(); + string configFile = GetFiles(".", $"osu!.{Environment.UserName}.cfg").SingleOrDefault(); if (configFile != null) { @@ -47,7 +47,7 @@ namespace osu.Game.IO { if (!line.StartsWith("BeatmapDirectory", StringComparison.OrdinalIgnoreCase)) continue; - var customDirectory = line.Split('=').LastOrDefault()?.Trim(); + string customDirectory = line.Split('=').LastOrDefault()?.Trim(); if (customDirectory != null && Path.IsPathFullyQualified(customDirectory)) return customDirectory; diff --git a/osu.Game/IO/WrappedStorage.cs b/osu.Game/IO/WrappedStorage.cs index aadc4e760b..6f0f898de3 100644 --- a/osu.Game/IO/WrappedStorage.cs +++ b/osu.Game/IO/WrappedStorage.cs @@ -60,7 +60,7 @@ namespace osu.Game.IO { string localRoot = GetFullPath(string.Empty); - foreach (var path in paths) + foreach (string path in paths) yield return Path.GetRelativePath(localRoot, UnderlyingStorage.GetFullPath(path)); } diff --git a/osu.Game/Input/Bindings/DatabasedKeyBindingContainer.cs b/osu.Game/Input/Bindings/DatabasedKeyBindingContainer.cs index 10376c1866..5dced23614 100644 --- a/osu.Game/Input/Bindings/DatabasedKeyBindingContainer.cs +++ b/osu.Game/Input/Bindings/DatabasedKeyBindingContainer.cs @@ -52,7 +52,7 @@ namespace osu.Game.Input.Bindings { if (ruleset == null || ruleset.ID.HasValue) { - var rulesetId = ruleset?.ID; + int? rulesetId = ruleset?.ID; realmKeyBindings = realmFactory.Context.All() .Where(b => b.RulesetID == rulesetId && b.Variant == variant); diff --git a/osu.Game/Input/RealmKeyBindingStore.cs b/osu.Game/Input/RealmKeyBindingStore.cs index 5fa3ccdeb9..c65e36e478 100644 --- a/osu.Game/Input/RealmKeyBindingStore.cs +++ b/osu.Game/Input/RealmKeyBindingStore.cs @@ -65,7 +65,7 @@ namespace osu.Game.Input foreach (var ruleset in rulesets) { var instance = ruleset.CreateInstance(); - foreach (var variant in instance.AvailableVariants) + foreach (int variant in instance.AvailableVariants) insertDefaults(realm, existingBindings, instance.GetDefaultKeyBindings(variant), ruleset.ID, variant); } diff --git a/osu.Game/Localisation/ResourceManagerLocalisationStore.cs b/osu.Game/Localisation/ResourceManagerLocalisationStore.cs index 6a4e38fb38..82dc0ad110 100644 --- a/osu.Game/Localisation/ResourceManagerLocalisationStore.cs +++ b/osu.Game/Localisation/ResourceManagerLocalisationStore.cs @@ -27,7 +27,7 @@ namespace osu.Game.Localisation public string Get(string lookup) { - var split = lookup.Split(':'); + string[] split = lookup.Split(':'); if (split.Length < 2) return null; diff --git a/osu.Game/Online/API/APIDownloadRequest.cs b/osu.Game/Online/API/APIDownloadRequest.cs index 63bb3e2287..11753e05ba 100644 --- a/osu.Game/Online/API/APIDownloadRequest.cs +++ b/osu.Game/Online/API/APIDownloadRequest.cs @@ -23,7 +23,7 @@ namespace osu.Game.Online.API protected override WebRequest CreateWebRequest() { - var file = Path.GetTempFileName(); + string file = Path.GetTempFileName(); File.Move(file, filename = Path.ChangeExtension(file, FileExtension)); diff --git a/osu.Game/Online/Chat/Channel.cs b/osu.Game/Online/Chat/Channel.cs index 187a3e5dfc..9b463a6348 100644 --- a/osu.Game/Online/Chat/Channel.cs +++ b/osu.Game/Online/Chat/Channel.cs @@ -26,7 +26,7 @@ namespace osu.Game.Online.Chat { set { - foreach (var id in value) + foreach (int id in value) Users.Add(new User { Id = id }); } } @@ -131,7 +131,7 @@ namespace osu.Game.Online.Chat Messages.AddRange(messages); - var maxMessageId = messages.Max(m => m.Id); + long? maxMessageId = messages.Max(m => m.Id); if (maxMessageId > LastMessageId) LastMessageId = maxMessageId; diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index 47d5955fb0..52c9387185 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -218,7 +218,7 @@ namespace osu.Game.Online.Chat if (target == null) return; - var parameters = text.Split(' ', 2); + string[] parameters = text.Split(' ', 2); string command = parameters[0]; string content = parameters.Length == 2 ? parameters[1] : string.Empty; @@ -306,7 +306,7 @@ namespace osu.Game.Online.Chat { var req = new ListChannelsRequest(); - var joinDefaults = JoinedChannels.Count == 0; + bool joinDefaults = JoinedChannels.Count == 0; req.Success += channels => { diff --git a/osu.Game/Online/Chat/MessageFormatter.cs b/osu.Game/Online/Chat/MessageFormatter.cs index 201ba6239b..5a90638dcd 100644 --- a/osu.Game/Online/Chat/MessageFormatter.cs +++ b/osu.Game/Online/Chat/MessageFormatter.cs @@ -70,14 +70,14 @@ namespace osu.Game.Online.Chat foreach (Match m in regex.Matches(result.Text, startIndex)) { - var index = m.Index - captureOffset; + int index = m.Index - captureOffset; - var displayText = string.Format(display, + string? displayText = string.Format(display, m.Groups[0], m.Groups["text"].Value, m.Groups["url"].Value).Trim(); - var linkText = string.Format(link, + string linkText = string.Format(link, m.Groups[0], m.Groups["text"].Value, m.Groups["url"].Value).Trim(); @@ -109,9 +109,9 @@ namespace osu.Game.Online.Chat { foreach (Match m in regex.Matches(result.Text, startIndex)) { - var index = m.Index; - var linkText = m.Groups["link"].Value; - var indexLength = linkText.Length; + int index = m.Index; + string? linkText = m.Groups["link"].Value; + int indexLength = linkText.Length; var details = GetLinkDetails(linkText); var link = new Link(linkText, index, indexLength, details.Action, details.Argument); @@ -126,7 +126,7 @@ namespace osu.Game.Online.Chat public static LinkDetails GetLinkDetails(string url) { - var args = url.Split('/', StringSplitOptions.RemoveEmptyEntries); + string[]? args = url.Split('/', StringSplitOptions.RemoveEmptyEntries); args[0] = args[0].TrimEnd(':'); switch (args[0]) @@ -136,7 +136,7 @@ namespace osu.Game.Online.Chat // length > 3 since all these links need another argument to work if (args.Length > 3 && args[1].EndsWith(websiteRootUrl, StringComparison.OrdinalIgnoreCase)) { - var mainArg = args[3]; + string? mainArg = args[3]; switch (args[2]) { @@ -145,7 +145,7 @@ namespace osu.Game.Online.Chat case "beatmaps": { string trimmed = mainArg.Split('?').First(); - if (int.TryParse(trimmed, out var id)) + if (int.TryParse(trimmed, out int id)) return new LinkDetails(LinkAction.OpenBeatmap, id.ToString()); break; @@ -159,7 +159,7 @@ namespace osu.Game.Online.Chat // handle discussion links externally for now return new LinkDetails(LinkAction.External, url); - if (args.Length > 4 && int.TryParse(args[4], out var id)) + if (args.Length > 4 && int.TryParse(args[4], out int id)) // https://osu.ppy.sh/beatmapsets/1154158#osu/2768184 return new LinkDetails(LinkAction.OpenBeatmap, id.ToString()); @@ -273,7 +273,7 @@ namespace osu.Game.Online.Chat // handle channels handleMatches(channel_regex, "{0}", "osu://chan/{0}", result, startIndex, LinkAction.OpenChannel); - var empty = ""; + string empty = ""; while (space-- > 0) empty += "\0"; diff --git a/osu.Game/Online/Chat/NowPlayingCommand.cs b/osu.Game/Online/Chat/NowPlayingCommand.cs index 89eb00a45a..adb3d88df6 100644 --- a/osu.Game/Online/Chat/NowPlayingCommand.cs +++ b/osu.Game/Online/Chat/NowPlayingCommand.cs @@ -57,7 +57,7 @@ namespace osu.Game.Online.Chat break; } - var beatmapString = beatmapInfo.OnlineBeatmapID.HasValue ? $"[{api.WebsiteRootUrl}/b/{beatmapInfo.OnlineBeatmapID} {beatmapInfo}]" : beatmapInfo.ToString(); + string beatmapString = beatmapInfo.OnlineBeatmapID.HasValue ? $"[{api.WebsiteRootUrl}/b/{beatmapInfo.OnlineBeatmapID} {beatmapInfo}]" : beatmapInfo.ToString(); channelManager.PostMessage($"is {verb} {beatmapString}", true, target); Expire(); diff --git a/osu.Game/Online/Chat/StandAloneChatDisplay.cs b/osu.Game/Online/Chat/StandAloneChatDisplay.cs index 6ed2055e65..ede76235b1 100644 --- a/osu.Game/Online/Chat/StandAloneChatDisplay.cs +++ b/osu.Game/Online/Chat/StandAloneChatDisplay.cs @@ -85,7 +85,7 @@ namespace osu.Game.Online.Chat private void postMessage(TextBox sender, bool newtext) { - var text = Textbox.Text.Trim(); + string text = Textbox.Text.Trim(); if (string.IsNullOrWhiteSpace(text)) return; diff --git a/osu.Game/Online/Leaderboards/Leaderboard.cs b/osu.Game/Online/Leaderboards/Leaderboard.cs index e3ac9f603d..515cc6fd73 100644 --- a/osu.Game/Online/Leaderboards/Leaderboard.cs +++ b/osu.Game/Online/Leaderboards/Leaderboard.cs @@ -350,8 +350,8 @@ namespace osu.Game.Online.Leaderboards { base.UpdateAfterChildren(); - var fadeBottom = scrollContainer.Current + scrollContainer.DrawHeight; - var fadeTop = scrollContainer.Current + LeaderboardScore.HEIGHT; + float fadeBottom = scrollContainer.Current + scrollContainer.DrawHeight; + float fadeTop = scrollContainer.Current + LeaderboardScore.HEIGHT; if (!scrollContainer.IsScrolledToEnd()) fadeBottom -= LeaderboardScore.HEIGHT; @@ -361,8 +361,8 @@ namespace osu.Game.Online.Leaderboards foreach (var c in scrollFlow.Children) { - var topY = c.ToSpaceOfOtherDrawable(Vector2.Zero, scrollFlow).Y; - var bottomY = topY + LeaderboardScore.HEIGHT; + float topY = c.ToSpaceOfOtherDrawable(Vector2.Zero, scrollFlow).Y; + float bottomY = topY + LeaderboardScore.HEIGHT; bool requireTopFade = FadeTop && topY <= fadeTop; bool requireBottomFade = FadeBottom && bottomY >= fadeBottom; diff --git a/osu.Game/Online/Rooms/IndexPlaylistScoresRequest.cs b/osu.Game/Online/Rooms/IndexPlaylistScoresRequest.cs index abce2093e3..bd9f254e1a 100644 --- a/osu.Game/Online/Rooms/IndexPlaylistScoresRequest.cs +++ b/osu.Game/Online/Rooms/IndexPlaylistScoresRequest.cs @@ -47,7 +47,7 @@ namespace osu.Game.Online.Rooms req.AddCursor(Cursor); - foreach (var (key, value) in IndexParams.Properties) + foreach ((string key, var value) in IndexParams.Properties) req.AddParameter(key, value.ToString()); } diff --git a/osu.Game/Online/Spectator/SpectatorClient.cs b/osu.Game/Online/Spectator/SpectatorClient.cs index b597b2f214..f9366674d8 100644 --- a/osu.Game/Online/Spectator/SpectatorClient.cs +++ b/osu.Game/Online/Spectator/SpectatorClient.cs @@ -80,7 +80,7 @@ namespace osu.Game.Online.Spectator watchingUsers.Clear(); // resubscribe to watched users. - foreach (var userId in users) + foreach (int userId in users) WatchUser(userId); // re-send state in case it wasn't received diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 985451fd6f..304904e776 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -336,7 +336,7 @@ namespace osu.Game ShowChangelogListing(); else { - var changelogArgs = link.Argument.Split("/"); + string[] changelogArgs = link.Argument.Split("/"); ShowChangelogBuild(changelogArgs[0], changelogArgs[1]); } @@ -622,7 +622,7 @@ namespace osu.Game foreach (var language in Enum.GetValues(typeof(Language)).OfType()) { - var cultureCode = language.ToCultureCode(); + string cultureCode = language.ToCultureCode(); try { @@ -870,7 +870,7 @@ namespace osu.Game { if (args?.Length > 0) { - var paths = args.Where(a => !a.StartsWith('-')).ToArray(); + string[] paths = args.Where(a => !a.StartsWith('-')).ToArray(); if (paths.Length > 0) Task.Run(() => Import(paths)); } @@ -913,7 +913,7 @@ namespace osu.Game } else if (recentLogCount == short_term_display_limit) { - var logFile = $@"{entry.Target.ToString().ToLowerInvariant()}.log"; + string logFile = $@"{entry.Target.ToString().ToLowerInvariant()}.log"; Schedule(() => Notifications.Post(new SimpleNotification { @@ -1059,7 +1059,7 @@ namespace osu.Game ScreenOffsetContainer.Padding = new MarginPadding { Top = toolbarOffset }; overlayOffsetContainer.Padding = new MarginPadding { Top = toolbarOffset }; - var horizontalOffset = 0f; + float horizontalOffset = 0f; // Content.ToLocalSpace() is used instead of this.ToLocalSpace() to correctly calculate the offset with scaling modes active. // Content is a child of a scaling container with ScalingMode.Everything set, while the game itself is never scaled. diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapRulesetTabItem.cs b/osu.Game/Overlays/BeatmapSet/BeatmapRulesetTabItem.cs index cb258edced..79c95d6646 100644 --- a/osu.Game/Overlays/BeatmapSet/BeatmapRulesetTabItem.cs +++ b/osu.Game/Overlays/BeatmapSet/BeatmapRulesetTabItem.cs @@ -64,7 +64,7 @@ namespace osu.Game.Overlays.BeatmapSet BeatmapSet.BindValueChanged(setInfo => { - var beatmapsCount = setInfo.NewValue?.Beatmaps.Count(b => b.Ruleset.Equals(Value)) ?? 0; + int beatmapsCount = setInfo.NewValue?.Beatmaps.Count(b => b.Ruleset.Equals(Value)) ?? 0; count.Text = beatmapsCount.ToString(); countContainer.FadeTo(beatmapsCount > 0 ? 1 : 0); diff --git a/osu.Game/Overlays/BeatmapSet/Info.cs b/osu.Game/Overlays/BeatmapSet/Info.cs index 8bc5c6d27e..970e9bbf42 100644 --- a/osu.Game/Overlays/BeatmapSet/Info.cs +++ b/osu.Game/Overlays/BeatmapSet/Info.cs @@ -119,7 +119,7 @@ namespace osu.Game.Overlays.BeatmapSet tags.Text = b.NewValue?.Metadata.Tags ?? string.Empty; genre.Text = b.NewValue?.OnlineInfo?.Genre.Name ?? string.Empty; language.Text = b.NewValue?.OnlineInfo?.Language.Name ?? string.Empty; - var setHasLeaderboard = b.NewValue?.OnlineInfo?.Status > 0; + bool setHasLeaderboard = b.NewValue?.OnlineInfo?.Status > 0; successRate.Alpha = setHasLeaderboard ? 1 : 0; notRankedPlaceholder.Alpha = setHasLeaderboard ? 0 : 1; Height = setHasLeaderboard ? 270 : base_height; diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTableRowBackground.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTableRowBackground.cs index d84e1eff8c..b6079b36ab 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTableRowBackground.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTableRowBackground.cs @@ -50,7 +50,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores [BackgroundDependencyLoader] private void load(OsuColour colours, OverlayColourProvider colourProvider, IAPIProvider api) { - var isOwnScore = api.LocalUser.Value.Id == score.UserID; + bool isOwnScore = api.LocalUser.Value.Id == score.UserID; if (isOwnScore) background.Colour = colours.GreenDarker; diff --git a/osu.Game/Overlays/BeatmapSet/SuccessRate.cs b/osu.Game/Overlays/BeatmapSet/SuccessRate.cs index 604c4e1949..40a3c9fe8b 100644 --- a/osu.Game/Overlays/BeatmapSet/SuccessRate.cs +++ b/osu.Game/Overlays/BeatmapSet/SuccessRate.cs @@ -43,7 +43,7 @@ namespace osu.Game.Overlays.BeatmapSet int passCount = beatmapInfo?.OnlineInfo?.PassCount ?? 0; int playCount = beatmapInfo?.OnlineInfo?.PlayCount ?? 0; - var rate = playCount != 0 ? (float)passCount / playCount : 0; + float rate = playCount != 0 ? (float)passCount / playCount : 0; successPercent.Text = rate.ToLocalisableString(@"0.#%"); successRate.Length = rate; percentContainer.ResizeWidthTo(successRate.Length, 250, Easing.InOutCubic); diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index 4b27335c7c..cc3ce63bf7 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -479,7 +479,7 @@ namespace osu.Game.Overlays private void postMessage(TextBox textbox, bool newText) { - var text = textbox.Text.Trim(); + string text = textbox.Text.Trim(); if (string.IsNullOrWhiteSpace(text)) return; diff --git a/osu.Game/Overlays/Comments/CommentMarkdownContainer.cs b/osu.Game/Overlays/Comments/CommentMarkdownContainer.cs index aeab292b0d..3971a61363 100644 --- a/osu.Game/Overlays/Comments/CommentMarkdownContainer.cs +++ b/osu.Game/Overlays/Comments/CommentMarkdownContainer.cs @@ -29,7 +29,7 @@ namespace osu.Game.Overlays.Comments protected override float GetFontSizeByLevel(int level) { - var defaultFontSize = base.GetFontSizeByLevel(6); + float defaultFontSize = base.GetFontSizeByLevel(6); switch (level) { diff --git a/osu.Game/Overlays/Comments/DrawableComment.cs b/osu.Game/Overlays/Comments/DrawableComment.cs index a44f3a7643..43f4177bd0 100644 --- a/osu.Game/Overlays/Comments/DrawableComment.cs +++ b/osu.Game/Overlays/Comments/DrawableComment.cs @@ -362,8 +362,8 @@ namespace osu.Game.Overlays.Comments private void updateButtonsState() { - var loadedReplesCount = loadedReplies.Count; - var hasUnloadedReplies = loadedReplesCount != Comment.RepliesCount; + int loadedReplesCount = loadedReplies.Count; + bool hasUnloadedReplies = loadedReplesCount != Comment.RepliesCount; loadRepliesButton.FadeTo(hasUnloadedReplies && loadedReplesCount == 0 ? 1 : 0); showMoreButton.FadeTo(hasUnloadedReplies && loadedReplesCount > 0 ? 1 : 0); diff --git a/osu.Game/Overlays/Comments/VotePill.cs b/osu.Game/Overlays/Comments/VotePill.cs index cf3c470f96..093cdce66e 100644 --- a/osu.Game/Overlays/Comments/VotePill.cs +++ b/osu.Game/Overlays/Comments/VotePill.cs @@ -67,7 +67,7 @@ namespace osu.Game.Overlays.Comments AccentColour = borderContainer.BorderColour = sideNumber.Colour = colours.GreenLight; hoverLayer.Colour = Color4.Black.Opacity(0.5f); - var ownComment = api.LocalUser.Value.Id == comment.UserId; + bool ownComment = api.LocalUser.Value.Id == comment.UserId; if (!ownComment) Action = onAction; diff --git a/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs b/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs index 3051ca7dbe..454dd500fe 100644 --- a/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs +++ b/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs @@ -61,7 +61,7 @@ namespace osu.Game.Overlays.Dashboard switch (e.Action) { case NotifyCollectionChangedAction.Add: - foreach (var id in e.NewItems.OfType().ToArray()) + foreach (int id in e.NewItems.OfType().ToArray()) { users.GetUserAsync(id).ContinueWith(u => { @@ -81,7 +81,7 @@ namespace osu.Game.Overlays.Dashboard break; case NotifyCollectionChangedAction.Remove: - foreach (var u in e.OldItems.OfType()) + foreach (int u in e.OldItems.OfType()) userFlow.FirstOrDefault(card => card.User.Id == u)?.Expire(); break; diff --git a/osu.Game/Overlays/Dashboard/Friends/FriendOnlineStreamControl.cs b/osu.Game/Overlays/Dashboard/Friends/FriendOnlineStreamControl.cs index 28546ceab8..d7f66e35b5 100644 --- a/osu.Game/Overlays/Dashboard/Friends/FriendOnlineStreamControl.cs +++ b/osu.Game/Overlays/Dashboard/Friends/FriendOnlineStreamControl.cs @@ -15,8 +15,8 @@ namespace osu.Game.Overlays.Dashboard.Friends { Clear(); - var userCount = users.Count; - var onlineUsersCount = users.Count(user => user.IsOnline); + int userCount = users.Count; + int onlineUsersCount = users.Count(user => user.IsOnline); AddItem(new FriendStream(OnlineStatus.All, userCount)); AddItem(new FriendStream(OnlineStatus.Online, onlineUsersCount)); diff --git a/osu.Game/Overlays/HoldToConfirmOverlay.cs b/osu.Game/Overlays/HoldToConfirmOverlay.cs index 0542f66b5b..cb6275bd7c 100644 --- a/osu.Game/Overlays/HoldToConfirmOverlay.cs +++ b/osu.Game/Overlays/HoldToConfirmOverlay.cs @@ -49,7 +49,7 @@ namespace osu.Game.Overlays Progress.ValueChanged += p => { - var target = p.NewValue * finalFillAlpha; + double target = p.NewValue * finalFillAlpha; audioVolume.Value = 1 - target; overlay.Alpha = (float)target; diff --git a/osu.Game/Overlays/Mods/ModSection.cs b/osu.Game/Overlays/Mods/ModSection.cs index faad23a4e1..5bf8cddd0c 100644 --- a/osu.Game/Overlays/Mods/ModSection.cs +++ b/osu.Game/Overlays/Mods/ModSection.cs @@ -96,7 +96,7 @@ namespace osu.Game.Overlays.Mods if (ToggleKeys != null) { - var index = Array.IndexOf(ToggleKeys, e.Key); + int index = Array.IndexOf(ToggleKeys, e.Key); if (index > -1 && index < Buttons.Count) Buttons[index].SelectNext(e.ShiftPressed ? -1 : 1); } @@ -196,7 +196,7 @@ namespace osu.Game.Overlays.Mods { foreach (var mod in newSelectedMods) { - var index = Array.FindIndex(button.Mods, m1 => mod.GetType() == m1.GetType()); + int index = Array.FindIndex(button.Mods, m1 => mod.GetType() == m1.GetType()); if (index < 0) continue; diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index 8fd50c3df2..97c7aaeaeb 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -238,7 +238,7 @@ namespace osu.Game.Overlays if (beatmap.Disabled) return PreviousTrackResult.None; - var currentTrackPosition = CurrentTrack.CurrentTime; + double currentTrackPosition = CurrentTrack.CurrentTime; if (currentTrackPosition >= restart_cutoff_point) { @@ -329,8 +329,8 @@ namespace osu.Game.Overlays else { // figure out the best direction based on order in playlist. - var last = BeatmapSets.TakeWhile(b => b.ID != current.BeatmapSetInfo?.ID).Count(); - var next = newWorking == null ? -1 : BeatmapSets.TakeWhile(b => b.ID != newWorking.BeatmapSetInfo?.ID).Count(); + int last = BeatmapSets.TakeWhile(b => b.ID != current.BeatmapSetInfo?.ID).Count(); + int next = newWorking == null ? -1 : BeatmapSets.TakeWhile(b => b.ID != newWorking.BeatmapSetInfo?.ID).Count(); direction = last > next ? TrackChangeDirection.Prev : TrackChangeDirection.Next; } diff --git a/osu.Game/Overlays/News/Sidebar/NewsSidebar.cs b/osu.Game/Overlays/News/Sidebar/NewsSidebar.cs index 35cd6eb03b..fe965385d8 100644 --- a/osu.Game/Overlays/News/Sidebar/NewsSidebar.cs +++ b/osu.Game/Overlays/News/Sidebar/NewsSidebar.cs @@ -61,11 +61,11 @@ namespace osu.Game.Overlays.News.Sidebar var keys = lookup.Select(kvp => kvp.Key); var sortedKeys = keys.OrderByDescending(k => k).ToList(); - var year = metadata.NewValue.CurrentYear; + int year = metadata.NewValue.CurrentYear; for (int i = 0; i < sortedKeys.Count; i++) { - var month = sortedKeys[i]; + int month = sortedKeys[i]; var posts = lookup[month]; monthsFlow.Add(new MonthSection(month, year, posts) diff --git a/osu.Game/Overlays/News/Sidebar/YearsPanel.cs b/osu.Game/Overlays/News/Sidebar/YearsPanel.cs index b07c9924b9..58c0f6ac82 100644 --- a/osu.Game/Overlays/News/Sidebar/YearsPanel.cs +++ b/osu.Game/Overlays/News/Sidebar/YearsPanel.cs @@ -69,9 +69,9 @@ namespace osu.Game.Overlays.News.Sidebar return; } - var currentYear = metadata.Value.CurrentYear; + int currentYear = metadata.Value.CurrentYear; - foreach (var y in metadata.Value.Years) + foreach (int y in metadata.Value.Years) yearsFlow.Add(new YearButton(y, y == currentYear)); Show(); diff --git a/osu.Game/Overlays/Profile/Header/Components/PreviousUsernames.cs b/osu.Game/Overlays/Profile/Header/Components/PreviousUsernames.cs index 14eeb4e5f0..b1076ba39b 100644 --- a/osu.Game/Overlays/Profile/Header/Components/PreviousUsernames.cs +++ b/osu.Game/Overlays/Profile/Header/Components/PreviousUsernames.cs @@ -111,7 +111,7 @@ namespace osu.Game.Overlays.Profile.Header.Components { text.Text = string.Empty; - var usernames = user.NewValue?.PreviousUsernames; + string[] usernames = user.NewValue?.PreviousUsernames; if (usernames?.Any() ?? false) { diff --git a/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs b/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs index ca5f26e375..d6e515d8a1 100644 --- a/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs +++ b/osu.Game/Overlays/Profile/Header/Components/RankGraph.cs @@ -62,7 +62,7 @@ namespace osu.Game.Overlays.Profile.Header.Components protected override UserGraphTooltipContent GetTooltipContent(int index, int rank) { - var days = ranked_days - index + 1; + int days = ranked_days - index + 1; return new UserGraphTooltipContent( UsersStrings.ShowRankGlobalSimple, diff --git a/osu.Game/Overlays/Profile/Header/MedalHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/MedalHeaderContainer.cs index e7df4eb5eb..f6419db540 100644 --- a/osu.Game/Overlays/Profile/Header/MedalHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/MedalHeaderContainer.cs @@ -72,7 +72,7 @@ namespace osu.Game.Overlays.Profile.Header { Show(); - for (var index = 0; index < badges.Length; index++) + for (int index = 0; index < badges.Length; index++) { int displayIndex = index; LoadComponentAsync(new DrawableBadge(badges[index]), asyncBadge => diff --git a/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs b/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs index a75235359a..d402438376 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/ProfileLineChart.cs @@ -116,10 +116,10 @@ namespace osu.Game.Overlays.Profile.Sections.Historical rowTicksContainer.Clear(); rowLinesContainer.Clear(); - var min = values.Select(v => v.Count).Min(); - var max = values.Select(v => v.Count).Max(); + long min = values.Select(v => v.Count).Min(); + long max = values.Select(v => v.Count).Max(); - var tickInterval = getTickInterval(max - min, 6); + long tickInterval = getTickInterval(max - min, 6); for (long currentTick = 0; currentTick <= max; currentTick += tickInterval) { @@ -145,7 +145,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical columnTicksContainer.Clear(); columnLinesContainer.Clear(); - var totalMonths = values.Length; + int totalMonths = values.Length; int monthsPerTick = 1; @@ -158,7 +158,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical for (int i = 0; i < totalMonths; i += monthsPerTick) { - var x = (float)i / (totalMonths - 1); + float x = (float)i / (totalMonths - 1); addColumnTick(x, values[i].Date); } } @@ -215,15 +215,15 @@ namespace osu.Game.Overlays.Profile.Sections.Historical { // this interval is what would be achieved if the interval was divided perfectly evenly into maxTicksCount ticks. // can contain ugly fractional parts. - var exactTickInterval = (float)range / (maxTicksCount - 1); + float exactTickInterval = (float)range / (maxTicksCount - 1); // the ideal ticks start with a 1, 2 or 5, and are multipliers of powers of 10. // first off, use log10 to calculate the number of digits in the "exact" interval. - var numberOfDigits = Math.Floor(Math.Log10(exactTickInterval)); - var tickBase = Math.Pow(10, numberOfDigits); + double numberOfDigits = Math.Floor(Math.Log10(exactTickInterval)); + double tickBase = Math.Pow(10, numberOfDigits); // then see how the exact tick relates to the power of 10. - var exactTickMultiplier = exactTickInterval / tickBase; + double exactTickMultiplier = exactTickInterval / tickBase; double tickMultiplier; diff --git a/osu.Game/Overlays/Settings/Sections/Audio/AudioDevicesSettings.cs b/osu.Game/Overlays/Settings/Sections/Audio/AudioDevicesSettings.cs index 0c54ae2763..858d555f06 100644 --- a/osu.Game/Overlays/Settings/Sections/Audio/AudioDevicesSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Audio/AudioDevicesSettings.cs @@ -47,7 +47,7 @@ namespace osu.Game.Overlays.Settings.Sections.Audio var deviceItems = new List { string.Empty }; deviceItems.AddRange(audio.AudioDeviceNames); - var preferredDeviceName = audio.AudioDevice.Value; + string preferredDeviceName = audio.AudioDevice.Value; if (deviceItems.All(kv => kv != preferredDeviceName)) deviceItems.Add(preferredDeviceName); diff --git a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingsSubsection.cs b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingsSubsection.cs index 2051af6f3c..be0830a7c2 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingsSubsection.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingsSubsection.cs @@ -32,7 +32,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input [BackgroundDependencyLoader] private void load(RealmContextFactory realmFactory) { - var rulesetId = Ruleset?.ID; + int? rulesetId = Ruleset?.ID; List bindings; diff --git a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs index a4da17c5cd..0334167759 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs @@ -86,7 +86,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input handlerSensitivity.BindValueChanged(val => { - var disabled = localSensitivity.Disabled; + bool disabled = localSensitivity.Disabled; localSensitivity.Disabled = false; localSensitivity.Value = val.NewValue; @@ -97,7 +97,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input windowMode.BindValueChanged(mode => { - var isFullscreen = mode.NewValue == WindowMode.Fullscreen; + bool isFullscreen = mode.NewValue == WindowMode.Fullscreen; if (isFullscreen) { diff --git a/osu.Game/Overlays/Settings/Sections/Input/RotationPresetButtons.cs b/osu.Game/Overlays/Settings/Sections/Input/RotationPresetButtons.cs index 3ef5ce8941..dbdf600002 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/RotationPresetButtons.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/RotationPresetButtons.cs @@ -59,7 +59,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input for (int i = 0; i < preset_count; i++) { - var rotationValue = i * 90; + int rotationValue = i * 90; var rotationPreset = new RotationButton(rotationValue) { diff --git a/osu.Game/Overlays/Settings/Sections/Input/RulesetBindingsSection.cs b/osu.Game/Overlays/Settings/Sections/Input/RulesetBindingsSection.cs index 5246051a4a..48cbe1b59e 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/RulesetBindingsSection.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/RulesetBindingsSection.cs @@ -26,7 +26,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input var r = ruleset.CreateInstance(); - foreach (var variant in r.AvailableVariants) + foreach (int variant in r.AvailableVariants) Add(new VariantBindingsSubsection(ruleset, variant)); } } diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs index 00198235c5..cf5d70ba91 100644 --- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs +++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs @@ -45,7 +45,7 @@ namespace osu.Game.Overlays.Settings.Sections { get { - var index = skinItems.FindIndex(s => s.ID > 0); + int index = skinItems.FindIndex(s => s.ID > 0); if (index < 0) index = skinItems.Count; diff --git a/osu.Game/Overlays/Settings/SettingsNumberBox.cs b/osu.Game/Overlays/Settings/SettingsNumberBox.cs index aca7a210b3..545f1050b2 100644 --- a/osu.Game/Overlays/Settings/SettingsNumberBox.cs +++ b/osu.Game/Overlays/Settings/SettingsNumberBox.cs @@ -46,7 +46,7 @@ namespace osu.Game.Overlays.Settings { int? value = null; - if (int.TryParse(e.NewValue, out var intVal)) + if (int.TryParse(e.NewValue, out int intVal)) value = intVal; current.Value = value; diff --git a/osu.Game/Overlays/TabControlOverlayHeader.cs b/osu.Game/Overlays/TabControlOverlayHeader.cs index e6f7e250a7..1b0bd658d9 100644 --- a/osu.Game/Overlays/TabControlOverlayHeader.cs +++ b/osu.Game/Overlays/TabControlOverlayHeader.cs @@ -111,7 +111,7 @@ namespace osu.Game.Overlays else { var localisableDescription = enumValue.GetLocalisableDescription(); - var nonLocalisableDescription = enumValue.GetDescription(); + string nonLocalisableDescription = enumValue.GetDescription(); // If localisable == non-localisable, then we must have a basic string, so .ToLower() is used. Text.Text = localisableDescription.Equals(nonLocalisableDescription) diff --git a/osu.Game/Overlays/Toolbar/ToolbarButton.cs b/osu.Game/Overlays/Toolbar/ToolbarButton.cs index dd554200ca..ab37b3b355 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarButton.cs @@ -207,7 +207,7 @@ namespace osu.Game.Overlays.Toolbar if (realmKeyBinding != null) { - var keyBindingString = realmKeyBinding.KeyCombination.ReadableString(); + string keyBindingString = realmKeyBinding.KeyCombination.ReadableString(); if (!string.IsNullOrEmpty(keyBindingString)) keyBindingTooltip.Text = $" ({keyBindingString})"; diff --git a/osu.Game/Overlays/Volume/VolumeMeter.cs b/osu.Game/Overlays/Volume/VolumeMeter.cs index ff28b45ebb..e2afd46c18 100644 --- a/osu.Game/Overlays/Volume/VolumeMeter.cs +++ b/osu.Game/Overlays/Volume/VolumeMeter.cs @@ -325,7 +325,7 @@ namespace osu.Game.Overlays.Volume delta *= accelerationModifier; accelerationModifier = Math.Min(max_acceleration, accelerationModifier * acceleration_multiplier); - var precision = Bindable.Precision; + double precision = Bindable.Precision; if (isPrecise) { diff --git a/osu.Game/Overlays/Wiki/Markdown/WikiNoticeContainer.cs b/osu.Game/Overlays/Wiki/Markdown/WikiNoticeContainer.cs index 421806eea8..a22c18b0a4 100644 --- a/osu.Game/Overlays/Wiki/Markdown/WikiNoticeContainer.cs +++ b/osu.Game/Overlays/Wiki/Markdown/WikiNoticeContainer.cs @@ -22,7 +22,7 @@ namespace osu.Game.Overlays.Wiki.Markdown AutoSizeAxes = Axes.Y; Direction = FillDirection.Vertical; - foreach (var line in yamlFrontMatterBlock.Lines) + foreach (object line in yamlFrontMatterBlock.Lines) { switch (line.ToString()) { diff --git a/osu.Game/Overlays/Wiki/WikiMainPage.cs b/osu.Game/Overlays/Wiki/WikiMainPage.cs index c9ee2cbfd5..9416ec77f1 100644 --- a/osu.Game/Overlays/Wiki/WikiMainPage.cs +++ b/osu.Game/Overlays/Wiki/WikiMainPage.cs @@ -72,11 +72,11 @@ namespace osu.Game.Overlays.Wiki Debug.Assert(panelsNode.Length > 1); - var i = 0; + int i = 0; while (i < panelsNode.Length) { - var isFullWidth = panelsNode[i].HasClass("wiki-main-page-panel--full"); + bool isFullWidth = panelsNode[i].HasClass("wiki-main-page-panel--full"); if (isFullWidth) { diff --git a/osu.Game/Overlays/WikiOverlay.cs b/osu.Game/Overlays/WikiOverlay.cs index bde73b6180..44713d637d 100644 --- a/osu.Game/Overlays/WikiOverlay.cs +++ b/osu.Game/Overlays/WikiOverlay.cs @@ -140,7 +140,7 @@ namespace osu.Game.Overlays private void showParentPage() { - var parentPath = string.Join("/", path.Value.Split('/').SkipLast(1)); + string parentPath = string.Join("/", path.Value.Split('/').SkipLast(1)); ShowPage(parentPath); } diff --git a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs index 200bbf3f92..eab81186d5 100644 --- a/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs +++ b/osu.Game/Rulesets/Difficulty/DifficultyCalculator.cs @@ -158,7 +158,7 @@ namespace osu.Game.Rulesets.Difficulty // Apply the rest of the remaining mods recursively. for (int i = 0; i < remainingMods.Length; i++) { - var (nextSet, nextCount) = flatten(remainingMods.Span[i]); + (var nextSet, int nextCount) = flatten(remainingMods.Span[i]); // Check if any mods in the next set are incompatible with any of the current set. if (currentSet.SelectMany(m => m.IncompatibleMods).Any(c => nextSet.Any(c.IsInstanceOfType))) @@ -185,7 +185,7 @@ namespace osu.Game.Rulesets.Difficulty foreach (var nested in multi.Mods) { - var (nestedSet, nestedCount) = flatten(nested); + (var nestedSet, int nestedCount) = flatten(nested); set = set.Concat(nestedSet); count += nestedCount; } diff --git a/osu.Game/Rulesets/Edit/Checks/CheckAudioInVideo.cs b/osu.Game/Rulesets/Edit/Checks/CheckAudioInVideo.cs index ac2542beb0..255671c807 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckAudioInVideo.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckAudioInVideo.cs @@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Edit.Checks } } - foreach (var filename in videoPaths) + foreach (string filename in videoPaths) { string storagePath = beatmapSet.GetPathForFile(filename); diff --git a/osu.Game/Rulesets/Edit/Checks/CheckAudioQuality.cs b/osu.Game/Rulesets/Edit/Checks/CheckAudioQuality.cs index 70d11883b7..08e0312b64 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckAudioQuality.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckAudioQuality.cs @@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Edit.Checks public IEnumerable Run(BeatmapVerifierContext context) { - var audioFile = context.Beatmap.Metadata?.AudioFile; + string audioFile = context.Beatmap.Metadata?.AudioFile; if (audioFile == null) yield break; diff --git a/osu.Game/Rulesets/Edit/Checks/CheckBackgroundQuality.cs b/osu.Game/Rulesets/Edit/Checks/CheckBackgroundQuality.cs index 085c558eaf..8fa79e2ee8 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckBackgroundQuality.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckBackgroundQuality.cs @@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Edit.Checks public IEnumerable Run(BeatmapVerifierContext context) { - var backgroundFile = context.Beatmap.Metadata?.BackgroundFile; + string backgroundFile = context.Beatmap.Metadata?.BackgroundFile; if (backgroundFile == null) yield break; diff --git a/osu.Game/Rulesets/Edit/Checks/CheckFewHitsounds.cs b/osu.Game/Rulesets/Edit/Checks/CheckFewHitsounds.cs index 5185ba6c99..3358e81d5f 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckFewHitsounds.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckFewHitsounds.cs @@ -67,7 +67,7 @@ namespace osu.Game.Rulesets.Edit.Checks } var hitObjectsByEndTime = hitObjectsIncludingNested.OrderBy(o => o.GetEndTime()).ToList(); - var hitObjectCount = hitObjectsByEndTime.Count; + int hitObjectCount = hitObjectsByEndTime.Count; for (int i = 0; i < hitObjectCount; ++i) { @@ -86,7 +86,7 @@ namespace osu.Game.Rulesets.Edit.Checks private IEnumerable applyHitsoundUpdate(HitObject hitObject, bool isLastObject = false) { - var time = hitObject.GetEndTime(); + double time = hitObject.GetEndTime(); bool hasHitsound = hitObject.Samples.Any(isHitsound); bool couldHaveHitsound = hitObject.Samples.Any(isHitnormal); @@ -94,7 +94,7 @@ namespace osu.Game.Rulesets.Edit.Checks // If there are no hitsounds we let the "No hitsounds" template take precedence. if (hasHitsound || (isLastObject && mapHasHitsounds)) { - var timeWithoutHitsounds = time - lastHitsoundTime; + double timeWithoutHitsounds = time - lastHitsoundTime; if (timeWithoutHitsounds > problem_threshold_time && objectsWithoutHitsounds > problem_threshold_objects) yield return new IssueTemplateLongPeriodProblem(this).Create(lastHitsoundTime, timeWithoutHitsounds); diff --git a/osu.Game/Rulesets/Edit/Checks/CheckFilePresence.cs b/osu.Game/Rulesets/Edit/Checks/CheckFilePresence.cs index 36a0bf8c5d..abedee143a 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckFilePresence.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckFilePresence.cs @@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Edit.Checks public IEnumerable Run(BeatmapVerifierContext context) { - var filename = GetFilename(context.Beatmap); + string filename = GetFilename(context.Beatmap); if (filename == null) { @@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Edit.Checks } // If the file is set, also make sure it still exists. - var storagePath = context.Beatmap.BeatmapInfo.BeatmapSet.GetPathForFile(filename); + string storagePath = context.Beatmap.BeatmapInfo.BeatmapSet.GetPathForFile(filename); if (storagePath != null) yield break; diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 91cc80e930..8bad9aa11f 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -223,7 +223,7 @@ namespace osu.Game.Rulesets.Edit if (e.ControlPressed || e.AltPressed || e.SuperPressed) return false; - if (checkLeftToggleFromKey(e.Key, out var leftIndex)) + if (checkLeftToggleFromKey(e.Key, out int leftIndex)) { var item = toolboxCollection.Items.ElementAtOrDefault(leftIndex); @@ -235,7 +235,7 @@ namespace osu.Game.Rulesets.Edit } } - if (checkRightToggleFromKey(e.Key, out var rightIndex)) + if (checkRightToggleFromKey(e.Key, out int rightIndex)) { var item = togglesCollection.ElementAtOrDefault(rightIndex); diff --git a/osu.Game/Rulesets/Mods/ModBarrelRoll.cs b/osu.Game/Rulesets/Mods/ModBarrelRoll.cs index 872daadd46..4acbcf3e74 100644 --- a/osu.Game/Rulesets/Mods/ModBarrelRoll.cs +++ b/osu.Game/Rulesets/Mods/ModBarrelRoll.cs @@ -49,8 +49,8 @@ namespace osu.Game.Rulesets.Mods // scale the playfield to allow all hitobjects to stay within the visible region. var playfieldSize = drawableRuleset.Playfield.DrawSize; - var minSide = MathF.Min(playfieldSize.X, playfieldSize.Y); - var maxSide = MathF.Max(playfieldSize.X, playfieldSize.Y); + float minSide = MathF.Min(playfieldSize.X, playfieldSize.Y); + float maxSide = MathF.Max(playfieldSize.X, playfieldSize.Y); drawableRuleset.Playfield.Scale = new Vector2(minSide / maxSide); } } diff --git a/osu.Game/Rulesets/Objects/BarLineGenerator.cs b/osu.Game/Rulesets/Objects/BarLineGenerator.cs index 9556b52735..e78aa5a5a0 100644 --- a/osu.Game/Rulesets/Objects/BarLineGenerator.cs +++ b/osu.Game/Rulesets/Objects/BarLineGenerator.cs @@ -47,7 +47,7 @@ namespace osu.Game.Rulesets.Objects for (double t = currentTimingPoint.Time; Precision.DefinitelyBigger(endTime, t); t += barLength, currentBeat++) { - var roundedTime = Math.Round(t, MidpointRounding.AwayFromZero); + double roundedTime = Math.Round(t, MidpointRounding.AwayFromZero); // in the case of some bar lengths, rounding errors can cause t to be slightly less than // the expected whole number value due to floating point inaccuracies. diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 29d8a475ef..01817147ae 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -568,7 +568,7 @@ namespace osu.Game.Rulesets.Objects.Drawables if (Result != null && Result.HasResult) { - var endTime = HitObject.GetEndTime(); + double endTime = HitObject.GetEndTime(); if (Result.TimeOffset + endTime > Time.Current) { diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs index 0942a7264d..b341d2ddab 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs @@ -130,7 +130,7 @@ namespace osu.Game.Rulesets.Objects.Legacy if (i >= adds.Length) break; - int.TryParse(adds[i], out var sound); + int.TryParse(adds[i], out int sound); nodeSoundTypes[i] = (LegacyHitSoundType)sound; } } diff --git a/osu.Game/Rulesets/Objects/SliderEventGenerator.cs b/osu.Game/Rulesets/Objects/SliderEventGenerator.cs index ba38c7f77d..bae5a5e8d9 100644 --- a/osu.Game/Rulesets/Objects/SliderEventGenerator.cs +++ b/osu.Game/Rulesets/Objects/SliderEventGenerator.cs @@ -18,10 +18,10 @@ namespace osu.Game.Rulesets.Objects // This exists for edge cases such as /b/1573664 where the beatmap has been edited by the user, and should never be reached in normal usage. const double max_length = 100000; - var length = Math.Min(max_length, totalDistance); + double length = Math.Min(max_length, totalDistance); tickDistance = Math.Clamp(tickDistance, 0, length); - var minDistanceFromEnd = velocity * 10; + double minDistanceFromEnd = velocity * 10; yield return new SliderEventDescriptor { @@ -34,10 +34,10 @@ namespace osu.Game.Rulesets.Objects if (tickDistance != 0) { - for (var span = 0; span < spanCount; span++) + for (int span = 0; span < spanCount; span++) { - var spanStartTime = startTime + span * spanDuration; - var reversed = span % 2 == 1; + double spanStartTime = startTime + span * spanDuration; + bool reversed = span % 2 == 1; var ticks = generateTicks(span, spanStartTime, spanDuration, reversed, length, tickDistance, minDistanceFromEnd, cancellationToken); @@ -115,7 +115,7 @@ namespace osu.Game.Rulesets.Objects private static IEnumerable generateTicks(int spanIndex, double spanStartTime, double spanDuration, bool reversed, double length, double tickDistance, double minDistanceFromEnd, CancellationToken cancellationToken = default) { - for (var d = tickDistance; d <= length; d += tickDistance) + for (double d = tickDistance; d <= length; d += tickDistance) { cancellationToken.ThrowIfCancellationRequested(); @@ -123,8 +123,8 @@ namespace osu.Game.Rulesets.Objects break; // Always generate ticks from the start of the path rather than the span to ensure that ticks in repeat spans are positioned identically to those in non-repeat spans - var pathProgress = d / length; - var timeProgress = reversed ? 1 - pathProgress : pathProgress; + double pathProgress = d / length; + double timeProgress = reversed ? 1 - pathProgress : pathProgress; yield return new SliderEventDescriptor { diff --git a/osu.Game/Rulesets/Objects/SliderPathExtensions.cs b/osu.Game/Rulesets/Objects/SliderPathExtensions.cs index 052fc7c775..1308fff7ae 100644 --- a/osu.Game/Rulesets/Objects/SliderPathExtensions.cs +++ b/osu.Game/Rulesets/Objects/SliderPathExtensions.cs @@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Objects PathType? lastType = null; - for (var i = 0; i < points.Length; i++) + for (int i = 0; i < points.Length; i++) { var p = points[i]; p.Position -= positionalOffset; diff --git a/osu.Game/Rulesets/RulesetInfo.cs b/osu.Game/Rulesets/RulesetInfo.cs index 8cd3fa8c63..8083041a3b 100644 --- a/osu.Game/Rulesets/RulesetInfo.cs +++ b/osu.Game/Rulesets/RulesetInfo.cs @@ -45,7 +45,7 @@ namespace osu.Game.Rulesets { unchecked { - var hashCode = ID.HasValue ? ID.GetHashCode() : 0; + int hashCode = ID.HasValue ? ID.GetHashCode() : 0; hashCode = (hashCode * 397) ^ (InstantiationInfo != null ? InstantiationInfo.GetHashCode() : 0); hashCode = (hashCode * 397) ^ (Name != null ? Name.GetHashCode() : 0); hashCode = (hashCode * 397) ^ Available.GetHashCode(); diff --git a/osu.Game/Rulesets/RulesetStore.cs b/osu.Game/Rulesets/RulesetStore.cs index a9e04a02b5..391bf2c07d 100644 --- a/osu.Game/Rulesets/RulesetStore.cs +++ b/osu.Game/Rulesets/RulesetStore.cs @@ -168,7 +168,7 @@ namespace osu.Game.Rulesets var rulesets = rulesetStorage.GetFiles(".", $"{ruleset_library_prefix}.*.dll"); - foreach (var ruleset in rulesets.Where(f => !f.Contains("Tests"))) + foreach (string ruleset in rulesets.Where(f => !f.Contains("Tests"))) loadRulesetFromFile(rulesetStorage.GetFullPath(ruleset)); } @@ -176,7 +176,7 @@ namespace osu.Game.Rulesets { try { - var files = Directory.GetFiles(RuntimeInfo.StartupDirectory, $"{ruleset_library_prefix}.*.dll"); + string[] files = Directory.GetFiles(RuntimeInfo.StartupDirectory, $"{ruleset_library_prefix}.*.dll"); foreach (string file in files.Where(f => !Path.GetFileName(f).Contains("Tests"))) loadRulesetFromFile(file); @@ -189,7 +189,7 @@ namespace osu.Game.Rulesets private void loadRulesetFromFile(string file) { - var filename = Path.GetFileNameWithoutExtension(file); + string filename = Path.GetFileNameWithoutExtension(file); if (loadedAssemblies.Values.Any(t => Path.GetFileNameWithoutExtension(t.Assembly.Location) == filename)) return; diff --git a/osu.Game/Rulesets/Scoring/HitWindows.cs b/osu.Game/Rulesets/Scoring/HitWindows.cs index 3ffd1eb66b..2d008b58ba 100644 --- a/osu.Game/Rulesets/Scoring/HitWindows.cs +++ b/osu.Game/Rulesets/Scoring/HitWindows.cs @@ -86,7 +86,7 @@ namespace osu.Game.Rulesets.Scoring { foreach (var range in GetRanges()) { - var value = IBeatmapDifficultyInfo.DifficultyRange(difficulty, (range.Min, range.Average, range.Max)); + double value = IBeatmapDifficultyInfo.DifficultyRange(difficulty, (range.Min, range.Average, range.Max)); switch (range.Result) { diff --git a/osu.Game/Rulesets/UI/Scrolling/Algorithms/ConstantScrollAlgorithm.cs b/osu.Game/Rulesets/UI/Scrolling/Algorithms/ConstantScrollAlgorithm.cs index 0d4283e319..ab6e07f424 100644 --- a/osu.Game/Rulesets/UI/Scrolling/Algorithms/ConstantScrollAlgorithm.cs +++ b/osu.Game/Rulesets/UI/Scrolling/Algorithms/ConstantScrollAlgorithm.cs @@ -7,7 +7,7 @@ namespace osu.Game.Rulesets.UI.Scrolling.Algorithms { public double GetDisplayStartTime(double originTime, float offset, double timeRange, float scrollLength) { - var adjustedTime = TimeAt(-offset, originTime, timeRange, scrollLength); + double adjustedTime = TimeAt(-offset, originTime, timeRange, scrollLength); return adjustedTime - timeRange; } diff --git a/osu.Game/Rulesets/UI/Scrolling/Algorithms/SequentialScrollAlgorithm.cs b/osu.Game/Rulesets/UI/Scrolling/Algorithms/SequentialScrollAlgorithm.cs index a1f68d7201..45d3b3bcd4 100644 --- a/osu.Game/Rulesets/UI/Scrolling/Algorithms/SequentialScrollAlgorithm.cs +++ b/osu.Game/Rulesets/UI/Scrolling/Algorithms/SequentialScrollAlgorithm.cs @@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.UI.Scrolling.Algorithms public float GetLength(double startTime, double endTime, double timeRange, float scrollLength) { - var objectLength = relativePositionAt(endTime, timeRange) - relativePositionAt(startTime, timeRange); + double objectLength = relativePositionAt(endTime, timeRange) - relativePositionAt(startTime, timeRange); return (float)(objectLength * scrollLength); } @@ -90,7 +90,7 @@ namespace osu.Game.Rulesets.UI.Scrolling.Algorithms { generatePositionMappings(timeRange); - var mappingIndex = positionMappings.BinarySearch(search, comparer ?? Comparer.Default); + int mappingIndex = positionMappings.BinarySearch(search, comparer ?? Comparer.Default); if (mappingIndex < 0) { diff --git a/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs b/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs index a1658b4cf3..379718195c 100644 --- a/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs +++ b/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs @@ -39,7 +39,7 @@ namespace osu.Game.Scoring.Legacy score.ScoreInfo = scoreInfo; - var version = sr.ReadInt32(); + int version = sr.ReadInt32(); workingBeatmap = GetBeatmap(sr.ReadString()); if (workingBeatmap is DummyWorkingBeatmap) @@ -77,7 +77,7 @@ namespace osu.Game.Scoring.Legacy scoreInfo.Date = sr.ReadDateTime(); - var compressedReplay = sr.ReadByteArray(); + byte[] compressedReplay = sr.ReadByteArray(); if (version >= 20140721) scoreInfo.OnlineScoreID = sr.ReadInt64(); @@ -228,11 +228,11 @@ namespace osu.Game.Scoring.Legacy float lastTime = 0; ReplayFrame currentFrame = null; - var frames = reader.ReadToEnd().Split(','); + string[] frames = reader.ReadToEnd().Split(','); - for (var i = 0; i < frames.Length; i++) + for (int i = 0; i < frames.Length; i++) { - var split = frames[i].Split('|'); + string[] split = frames[i].Split('|'); if (split.Length < 4) continue; @@ -243,9 +243,9 @@ namespace osu.Game.Scoring.Legacy continue; } - var diff = Parsing.ParseFloat(split[0]); - var mouseX = Parsing.ParseFloat(split[1], Parsing.MAX_COORDINATE_VALUE); - var mouseY = Parsing.ParseFloat(split[2], Parsing.MAX_COORDINATE_VALUE); + float diff = Parsing.ParseFloat(split[0]); + float mouseX = Parsing.ParseFloat(split[1], Parsing.MAX_COORDINATE_VALUE); + float mouseY = Parsing.ParseFloat(split[2], Parsing.MAX_COORDINATE_VALUE); lastTime += diff; diff --git a/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs b/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs index 58e4192f77..5769406948 100644 --- a/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs +++ b/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs @@ -72,7 +72,7 @@ namespace osu.Game.Scoring.Legacy private byte[] createReplayData() { - var content = new ASCIIEncoding().GetBytes(replayStringContent); + byte[] content = new ASCIIEncoding().GetBytes(replayStringContent); using (var outStream = new MemoryStream()) { diff --git a/osu.Game/Scoring/Legacy/ScoreInfoExtensions.cs b/osu.Game/Scoring/Legacy/ScoreInfoExtensions.cs index b58f65800d..fc27261225 100644 --- a/osu.Game/Scoring/Legacy/ScoreInfoExtensions.cs +++ b/osu.Game/Scoring/Legacy/ScoreInfoExtensions.cs @@ -130,7 +130,7 @@ namespace osu.Game.Scoring.Legacy private static int? getCount(ScoreInfo scoreInfo, HitResult result) { - if (scoreInfo.Statistics.TryGetValue(result, out var existing)) + if (scoreInfo.Statistics.TryGetValue(result, out int existing)) return existing; return null; diff --git a/osu.Game/Scoring/LegacyDatabasedScore.cs b/osu.Game/Scoring/LegacyDatabasedScore.cs index 8908775472..81892f65d0 100644 --- a/osu.Game/Scoring/LegacyDatabasedScore.cs +++ b/osu.Game/Scoring/LegacyDatabasedScore.cs @@ -16,7 +16,7 @@ namespace osu.Game.Scoring { ScoreInfo = score; - var replayFilename = score.Files.FirstOrDefault(f => f.Filename.EndsWith(".osr", StringComparison.InvariantCultureIgnoreCase))?.FileInfo.StoragePath; + string replayFilename = score.Files.FirstOrDefault(f => f.Filename.EndsWith(".osr", StringComparison.InvariantCultureIgnoreCase))?.FileInfo.StoragePath; if (replayFilename == null) return; diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs index a9791fba7e..253591eb56 100644 --- a/osu.Game/Scoring/ScoreManager.cs +++ b/osu.Game/Scoring/ScoreManager.cs @@ -72,7 +72,7 @@ namespace osu.Game.Scoring } } - var totalScores = await Task.WhenAll(scores.Select(s => GetTotalScoreAsync(s, cancellationToken: cancellationToken))).ConfigureAwait(false); + long[] totalScores = await Task.WhenAll(scores.Select(s => GetTotalScoreAsync(s, cancellationToken: cancellationToken))).ConfigureAwait(false); return scores.Select((score, index) => (score, totalScore: totalScores[index])) .OrderByDescending(g => g.totalScore) diff --git a/osu.Game/Screens/Edit/BindableBeatDivisor.cs b/osu.Game/Screens/Edit/BindableBeatDivisor.cs index dfe2992a7c..1a350d7261 100644 --- a/osu.Game/Screens/Edit/BindableBeatDivisor.cs +++ b/osu.Game/Screens/Edit/BindableBeatDivisor.cs @@ -92,7 +92,7 @@ namespace osu.Game.Screens.Edit { int beat = index % beatDivisor; - foreach (var divisor in BindableBeatDivisor.VALID_DIVISORS) + foreach (int divisor in BindableBeatDivisor.VALID_DIVISORS) { if ((beat * divisor) % beatDivisor == 0) return divisor; diff --git a/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs b/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs index 2dec3fd22e..de63b265d2 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs @@ -205,7 +205,7 @@ namespace osu.Game.Screens.Edit.Compose.Components [BackgroundDependencyLoader] private void load() { - foreach (var t in availableDivisors) + foreach (int t in availableDivisors) { AddInternal(new Tick { @@ -287,7 +287,7 @@ namespace osu.Game.Screens.Edit.Compose.Components private void handleMouseInput(Vector2 screenSpaceMousePosition) { // copied from SliderBar so we can do custom spacing logic. - var xPosition = (ToLocalSpace(screenSpaceMousePosition).X - RangePadding) / UsableWidth; + float xPosition = (ToLocalSpace(screenSpaceMousePosition).X - RangePadding) / UsableWidth; CurrentNumber.Value = availableDivisors.OrderBy(d => Math.Abs(getMappedPosition(d) - xPosition)).First(); OnUserChange(Current.Value); diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index 75d4d13f94..d7d4642a39 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -58,13 +58,13 @@ namespace osu.Game.Screens.Edit.Compose.Components switch (args.Action) { case NotifyCollectionChangedAction.Add: - foreach (var o in args.NewItems) + foreach (object o in args.NewItems) SelectionBlueprints.FirstOrDefault(b => b.Item == o)?.Select(); break; case NotifyCollectionChangedAction.Remove: - foreach (var o in args.OldItems) + foreach (object o in args.OldItems) SelectionBlueprints.FirstOrDefault(b => b.Item == o)?.Deselect(); break; @@ -468,7 +468,7 @@ namespace osu.Game.Screens.Edit.Compose.Components if (snapProvider != null) { // check for positional snap for every object in selection (for things like object-object snapping) - for (var i = 0; i < movementBlueprintOriginalPositions.Length; i++) + for (int i = 0; i < movementBlueprintOriginalPositions.Length; i++) { Vector2 originalPosition = movementBlueprintOriginalPositions[i]; var testPosition = originalPosition + distanceTravelled; diff --git a/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs b/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs index 9d43e3258a..05bf405f3c 100644 --- a/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs +++ b/osu.Game/Screens/Edit/Compose/Components/DistanceSnapGrid.cs @@ -132,8 +132,8 @@ namespace osu.Game.Screens.Edit.Compose.Components protected ColourInfo GetColourForIndexFromPlacement(int placementIndex) { var timingPoint = beatmap.ControlPointInfo.TimingPointAt(StartTime); - var beatLength = timingPoint.BeatLength / beatDivisor.Value; - var beatIndex = (int)Math.Round((StartTime - timingPoint.Time) / beatLength); + double beatLength = timingPoint.BeatLength / beatDivisor.Value; + int beatIndex = (int)Math.Round((StartTime - timingPoint.Time) / beatLength); var colour = BindableBeatDivisor.GetColourFor(BindableBeatDivisor.GetDivisorForBeatIndex(beatIndex + placementIndex + 1, beatDivisor.Value), Colours); diff --git a/osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs index 246d4aa8d7..5f6e8de557 100644 --- a/osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs +++ b/osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs @@ -55,7 +55,7 @@ namespace osu.Game.Screens.Edit.Compose.Components /// private void createStateBindables() { - foreach (var sampleName in HitSampleInfo.AllAdditions) + foreach (string sampleName in HitSampleInfo.AllAdditions) { var bindable = new Bindable { @@ -102,7 +102,7 @@ namespace osu.Game.Screens.Edit.Compose.Components { SelectionNewComboState.Value = GetStateFromSelection(SelectedItems.OfType(), h => h.NewCombo); - foreach (var (sampleName, bindable) in SelectionSampleStates) + foreach ((string sampleName, var bindable) in SelectionSampleStates) { bindable.Value = GetStateFromSelection(SelectedItems, h => h.Samples.Any(s => s.Name == sampleName)); } diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs index 73c38ba23f..a9e9ef5001 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs @@ -176,7 +176,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline if (timeline != null) { var timelineQuad = timeline.ScreenSpaceDrawQuad; - var mouseX = e.ScreenSpaceMousePosition.X; + float mouseX = e.ScreenSpaceMousePosition.X; // scroll if in a drag and dragging outside visible extents if (mouseX > timelineQuad.TopRight.X) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs index e2458d45c9..80aa6972b1 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs @@ -397,7 +397,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline if (hitObject.DifficultyControlPoint == DifficultyControlPoint.DEFAULT) hitObject.DifficultyControlPoint = new DifficultyControlPoint(); - var newVelocity = hitObject.DifficultyControlPoint.SliderVelocity * (repeatHitObject.Duration / proposedDuration); + double newVelocity = hitObject.DifficultyControlPoint.SliderVelocity * (repeatHitObject.Duration / proposedDuration); if (Precision.AlmostEquals(newVelocity, hitObject.DifficultyControlPoint.SliderVelocity)) return; @@ -408,8 +408,8 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline else { // find the number of repeats which can fit in the requested time. - var lengthOfOneRepeat = repeatHitObject.Duration / (repeatHitObject.RepeatCount + 1); - var proposedCount = Math.Max(0, (int)Math.Round(proposedDuration / lengthOfOneRepeat) - 1); + double lengthOfOneRepeat = repeatHitObject.Duration / (repeatHitObject.RepeatCount + 1); + int proposedCount = Math.Max(0, (int)Math.Round(proposedDuration / lengthOfOneRepeat) - 1); if (proposedCount == repeatHitObject.RepeatCount) return; @@ -421,7 +421,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline break; case IHasDuration endTimeHitObject: - var snappedTime = Math.Max(hitObject.StartTime, beatSnapProvider.SnapTime(time)); + double snappedTime = Math.Max(hitObject.StartTime, beatSnapProvider.SnapTime(time)); if (endTimeHitObject.EndTime == snappedTime || Precision.AlmostEquals(snappedTime, hitObject.StartTime, beatmap.GetBeatLengthAtTime(snappedTime))) return; diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineTickDisplay.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineTickDisplay.cs index 3aaf0451c8..1415014e59 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineTickDisplay.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineTickDisplay.cs @@ -104,10 +104,10 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline nextMinTick = null; nextMaxTick = null; - for (var i = 0; i < beatmap.ControlPointInfo.TimingPoints.Count; i++) + for (int i = 0; i < beatmap.ControlPointInfo.TimingPoints.Count; i++) { var point = beatmap.ControlPointInfo.TimingPoints[i]; - var until = i + 1 < beatmap.ControlPointInfo.TimingPoints.Count ? beatmap.ControlPointInfo.TimingPoints[i + 1].Time : working.Value.Track.Length; + double until = i + 1 < beatmap.ControlPointInfo.TimingPoints.Count ? beatmap.ControlPointInfo.TimingPoints[i + 1].Time : working.Value.Track.Length; int beat = 0; @@ -127,7 +127,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline int indexInBar = beat % ((int)point.TimeSignature * beatDivisor.Value); - var divisor = BindableBeatDivisor.GetDivisorForBeatIndex(beat, beatDivisor.Value); + int divisor = BindableBeatDivisor.GetDivisorForBeatIndex(beat, beatDivisor.Value); var colour = BindableBeatDivisor.GetColourFor(divisor, colours); // even though "bar lines" take up the full vertical space, we render them in two pieces because it allows for less anchor/origin churn. diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 512226413b..81b2847443 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -301,7 +301,7 @@ namespace osu.Game.Screens.Edit editorBeatmap.SelectedHitObjects.BindCollectionChanged((_, __) => { - var hasObjects = editorBeatmap.SelectedHitObjects.Count > 0; + bool hasObjects = editorBeatmap.SelectedHitObjects.Count > 0; cutMenuItem.Action.Disabled = !hasObjects; copyMenuItem.Action.Disabled = !hasObjects; diff --git a/osu.Game/Screens/Edit/EditorBeatmap.cs b/osu.Game/Screens/Edit/EditorBeatmap.cs index 2e84ef437a..98fad09192 100644 --- a/osu.Game/Screens/Edit/EditorBeatmap.cs +++ b/osu.Game/Screens/Edit/EditorBeatmap.cs @@ -183,7 +183,7 @@ namespace osu.Game.Screens.Edit public void Add(HitObject hitObject) { // Preserve existing sorting order in the beatmap - var insertionIndex = findInsertionIndex(PlayableBeatmap.HitObjects, hitObject.StartTime); + int insertionIndex = findInsertionIndex(PlayableBeatmap.HitObjects, hitObject.StartTime); Insert(insertionIndex + 1, hitObject); } @@ -332,7 +332,7 @@ namespace osu.Game.Screens.Edit // For now we'll remove and re-add the hitobject. This is not optimal and can be improved if required. mutableHitObjects.Remove(hitObject); - var insertionIndex = findInsertionIndex(PlayableBeatmap.HitObjects, hitObject.StartTime); + int insertionIndex = findInsertionIndex(PlayableBeatmap.HitObjects, hitObject.StartTime); mutableHitObjects.Insert(insertionIndex + 1, hitObject); Update(hitObject); diff --git a/osu.Game/Screens/Edit/EditorChangeHandler.cs b/osu.Game/Screens/Edit/EditorChangeHandler.cs index 2dcb416a03..333c518d3a 100644 --- a/osu.Game/Screens/Edit/EditorChangeHandler.cs +++ b/osu.Game/Screens/Edit/EditorChangeHandler.cs @@ -73,7 +73,7 @@ namespace osu.Game.Screens.Edit using (var sw = new StreamWriter(stream, Encoding.UTF8, 1024, true)) new LegacyBeatmapEncoder(editorBeatmap, editorBeatmap.BeatmapSkin).Encode(sw); - var newState = stream.ToArray(); + byte[] newState = stream.ToArray(); // if the previous state is binary equal we don't need to push a new one, unless this is the initial state. if (savedStates.Count > 0 && newState.SequenceEqual(savedStates[currentState])) return; diff --git a/osu.Game/Screens/Edit/LegacyEditorBeatmapPatcher.cs b/osu.Game/Screens/Edit/LegacyEditorBeatmapPatcher.cs index d26856365e..3ed2a7efe2 100644 --- a/osu.Game/Screens/Edit/LegacyEditorBeatmapPatcher.cs +++ b/osu.Game/Screens/Edit/LegacyEditorBeatmapPatcher.cs @@ -82,7 +82,7 @@ namespace osu.Game.Screens.Edit if (toAdd.Count > 0) { IBeatmap newBeatmap = readBeatmap(newState); - foreach (var i in toAdd) + foreach (int i in toAdd) editorBeatmap.Insert(i, newBeatmap.HitObjects[i]); } diff --git a/osu.Game/Screens/Edit/Timing/GroupSection.cs b/osu.Game/Screens/Edit/Timing/GroupSection.cs index 2e2c380d4a..03059ff6e1 100644 --- a/osu.Game/Screens/Edit/Timing/GroupSection.cs +++ b/osu.Game/Screens/Edit/Timing/GroupSection.cs @@ -68,7 +68,7 @@ namespace osu.Game.Screens.Edit.Timing if (!isNew) return; - if (double.TryParse(sender.Text, out var newTime)) + if (double.TryParse(sender.Text, out double newTime)) { changeSelectedGroupTime(newTime); } diff --git a/osu.Game/Screens/Menu/IntroScreen.cs b/osu.Game/Screens/Menu/IntroScreen.cs index 32fb9f1d6d..eb8d3dfea6 100644 --- a/osu.Game/Screens/Menu/IntroScreen.cs +++ b/osu.Game/Screens/Menu/IntroScreen.cs @@ -211,7 +211,7 @@ namespace osu.Game.Screens.Menu else { const int quick_appear = 350; - var initialMovementTime = logo.Alpha > 0.2f ? quick_appear : 0; + int initialMovementTime = logo.Alpha > 0.2f ? quick_appear : 0; logo.MoveTo(new Vector2(0.5f), initialMovementTime, Easing.OutQuint); diff --git a/osu.Game/Screens/Menu/OsuLogo.cs b/osu.Game/Screens/Menu/OsuLogo.cs index a9376325cd..f9388097ac 100644 --- a/osu.Game/Screens/Menu/OsuLogo.cs +++ b/osu.Game/Screens/Menu/OsuLogo.cs @@ -272,7 +272,7 @@ namespace osu.Game.Screens.Menu lastBeatIndex = beatIndex; - var beatLength = timingPoint.BeatLength; + double beatLength = timingPoint.BeatLength; float amplitudeAdjust = Math.Min(1, 0.4f + amplitudes.Maximum); @@ -337,7 +337,7 @@ namespace osu.Game.Screens.Menu if (musicController.CurrentTrack.IsRunning) { - var maxAmplitude = lastBeatIndex >= 0 ? musicController.CurrentTrack.CurrentAmplitudes.Maximum : 0; + float maxAmplitude = lastBeatIndex >= 0 ? musicController.CurrentTrack.CurrentAmplitudes.Maximum : 0; logoAmplitudeContainer.Scale = new Vector2((float)Interpolation.Damp(logoAmplitudeContainer.Scale.X, 1 - Math.Max(0, maxAmplitude - scale_adjust_cutoff) * 0.04f, 0.9f, Time.Elapsed)); if (maxAmplitude > velocity_adjust_cutoff) diff --git a/osu.Game/Screens/OnlinePlay/Components/OnlinePlayBackgroundScreen.cs b/osu.Game/Screens/OnlinePlay/Components/OnlinePlayBackgroundScreen.cs index 8b6077b9f2..e2088c77d5 100644 --- a/osu.Game/Screens/OnlinePlay/Components/OnlinePlayBackgroundScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Components/OnlinePlayBackgroundScreen.cs @@ -95,7 +95,7 @@ namespace osu.Game.Screens.OnlinePlay.Components public override bool OnExiting(IScreen next) { - var result = base.OnExiting(next); + bool result = base.OnExiting(next); this.MoveToX(0); return result; } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs index 79e305b765..e77b5d7b8c 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs @@ -186,7 +186,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants var ruleset = rulesets.GetRuleset(Room.Settings.RulesetID).CreateInstance(); - var currentModeRank = User.User?.RulesetsStatistics?.GetValueOrDefault(ruleset.ShortName)?.GlobalRank; + int? currentModeRank = User.User?.RulesetsStatistics?.GetValueOrDefault(ruleset.ShortName)?.GlobalRank; userRankText.Text = currentModeRank != null ? $"#{currentModeRank.Value:N0}" : string.Empty; userStateDisplay.UpdateStatus(User.State, User.BeatmapAvailability); diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs index 7bf8ce0e1a..57d0d2c198 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs @@ -180,11 +180,11 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate { // Seek the master clock to the gameplay time. // This is chosen as the first available frame in the players' replays, which matches the seek by each individual SpectatorPlayer. - var startTime = instances.Where(i => i.Score != null) - .SelectMany(i => i.Score.Replay.Frames) - .Select(f => f.Time) - .DefaultIfEmpty(0) - .Min(); + double startTime = instances.Where(i => i.Score != null) + .SelectMany(i => i.Score.Replay.Frames) + .Select(f => f.Time) + .DefaultIfEmpty(0) + .Min(); masterClockContainer.Seek(startTime); masterClockContainer.Start(); diff --git a/osu.Game/Screens/Play/Break/BreakInfoLine.cs b/osu.Game/Screens/Play/Break/BreakInfoLine.cs index 0bc79d6e77..87f514ffd5 100644 --- a/osu.Game/Screens/Play/Break/BreakInfoLine.cs +++ b/osu.Game/Screens/Play/Break/BreakInfoLine.cs @@ -56,7 +56,7 @@ namespace osu.Game.Screens.Play.Break private void currentValueChanged(ValueChangedEvent e) { - var newText = prefix + Format(e.NewValue); + string newText = prefix + Format(e.NewValue); if (valueText.Text == newText) return; diff --git a/osu.Game/Screens/Play/BreakTracker.cs b/osu.Game/Screens/Play/BreakTracker.cs index 2f3673e91f..8441b7657e 100644 --- a/osu.Game/Screens/Play/BreakTracker.cs +++ b/osu.Game/Screens/Play/BreakTracker.cs @@ -51,7 +51,7 @@ namespace osu.Game.Screens.Play private void updateBreakTime() { - var time = Clock.CurrentTime; + double time = Clock.CurrentTime; isBreakTime.Value = breaks?.IsInAny(time) == true || time < gameplayStartTime diff --git a/osu.Game/Screens/Play/HUD/FailingLayer.cs b/osu.Game/Screens/Play/HUD/FailingLayer.cs index 424ee55766..ceb81f6b8d 100644 --- a/osu.Game/Screens/Play/HUD/FailingLayer.cs +++ b/osu.Game/Screens/Play/HUD/FailingLayer.cs @@ -90,7 +90,7 @@ namespace osu.Game.Screens.Play.HUD private void updateState() { // Don't display ever if the ruleset is not using a draining health display. - var showLayer = HealthProcessor is DrainingHealthProcessor && fadePlayfieldWhenHealthLow.Value && ShowHealth.Value; + bool showLayer = HealthProcessor is DrainingHealthProcessor && fadePlayfieldWhenHealthLow.Value && ShowHealth.Value; this.FadeTo(showLayer ? 1 : 0, fade_time, Easing.OutQuint); } diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs index fb49dedce7..a8141c57da 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs @@ -157,11 +157,11 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters // max to avoid div-by-zero. maxHitWindow = Math.Max(1, windows.First().length); - for (var i = 0; i < windows.Length; i++) + for (int i = 0; i < windows.Length; i++) { - var (result, length) = windows[i]; + (var result, double length) = windows[i]; - var hitWindow = (float)(length / maxHitWindow); + float hitWindow = (float)(length / maxHitWindow); colourBarsEarly.Add(createColourBar(result, hitWindow, i == 0)); colourBarsLate.Add(createColourBar(result, hitWindow, i == 0)); diff --git a/osu.Game/Screens/Play/HUD/MatchScoreDisplay.cs b/osu.Game/Screens/Play/HUD/MatchScoreDisplay.cs index b1c07512dd..88cf9529bf 100644 --- a/osu.Game/Screens/Play/HUD/MatchScoreDisplay.cs +++ b/osu.Game/Screens/Play/HUD/MatchScoreDisplay.cs @@ -133,7 +133,7 @@ namespace osu.Game.Screens.Play.HUD var winningBar = Team1Score.Value > Team2Score.Value ? score1Bar : score2Bar; var losingBar = Team1Score.Value <= Team2Score.Value ? score1Bar : score2Bar; - var diff = Math.Max(Team1Score.Value, Team2Score.Value) - Math.Min(Team1Score.Value, Team2Score.Value); + int diff = Math.Max(Team1Score.Value, Team2Score.Value) - Math.Min(Team1Score.Value, Team2Score.Value); losingBar.ResizeWidthTo(0, 400, Easing.OutQuint); winningBar.ResizeWidthTo(Math.Min(0.4f, MathF.Pow(diff / 1500000f, 0.5f) / 2), 400, Easing.OutQuint); diff --git a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs index 19cb6aeb50..7caf90f610 100644 --- a/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/MultiplayerGameplayLeaderboard.cs @@ -148,7 +148,7 @@ namespace osu.Game.Screens.Play.HUD switch (e.Action) { case NotifyCollectionChangedAction.Remove: - foreach (var userId in e.OldItems.OfType()) + foreach (int userId in e.OldItems.OfType()) { spectatorClient.StopWatchingUser(userId); diff --git a/osu.Game/Screens/Play/SongProgressGraph.cs b/osu.Game/Screens/Play/SongProgressGraph.cs index 78eb456bb5..f96de149ba 100644 --- a/osu.Game/Screens/Play/SongProgressGraph.cs +++ b/osu.Game/Screens/Play/SongProgressGraph.cs @@ -24,17 +24,17 @@ namespace osu.Game.Screens.Play if (!objects.Any()) return; - var firstHit = objects.First().StartTime; - var lastHit = objects.Max(o => o.GetEndTime()); + double firstHit = objects.First().StartTime; + double lastHit = objects.Max(o => o.GetEndTime()); if (lastHit == 0) lastHit = objects.Last().StartTime; - var interval = (lastHit - firstHit + 1) / granularity; + double interval = (lastHit - firstHit + 1) / granularity; foreach (var h in objects) { - var endTime = h.GetEndTime(); + double endTime = h.GetEndTime(); Debug.Assert(endTime >= h.StartTime); diff --git a/osu.Game/Screens/Play/SongProgressInfo.cs b/osu.Game/Screens/Play/SongProgressInfo.cs index 7441c335d2..7a458cdde0 100644 --- a/osu.Game/Screens/Play/SongProgressInfo.cs +++ b/osu.Game/Screens/Play/SongProgressInfo.cs @@ -82,7 +82,7 @@ namespace osu.Game.Screens.Play { base.Update(); - var time = gameplayClock?.CurrentTime ?? Time.Current; + double time = gameplayClock?.CurrentTime ?? Time.Current; double songCurrentTime = time - startTime; int currentPercent = Math.Max(0, Math.Min(100, (int)(songCurrentTime / songLength * 100))); diff --git a/osu.Game/Screens/Play/SquareGraph.cs b/osu.Game/Screens/Play/SquareGraph.cs index 36ce131411..67abcb66e6 100644 --- a/osu.Game/Screens/Play/SquareGraph.cs +++ b/osu.Game/Screens/Play/SquareGraph.cs @@ -165,7 +165,7 @@ namespace osu.Game.Screens.Play return; } - var max = values.Max(); + int max = values.Max(); float step = values.Length / (float)ColumnCount; diff --git a/osu.Game/Screens/Play/SubmittingPlayer.cs b/osu.Game/Screens/Play/SubmittingPlayer.cs index 5faa384d03..76411c8c6b 100644 --- a/osu.Game/Screens/Play/SubmittingPlayer.cs +++ b/osu.Game/Screens/Play/SubmittingPlayer.cs @@ -116,7 +116,7 @@ namespace osu.Game.Screens.Play public override bool OnExiting(IScreen next) { - var exiting = base.OnExiting(next); + bool exiting = base.OnExiting(next); submitScore(Score.DeepClone()); diff --git a/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs b/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs index 262d1e8293..c27d5227b5 100644 --- a/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs +++ b/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs @@ -62,12 +62,12 @@ namespace osu.Game.Screens.Ranking.Expanded { var beatmap = score.BeatmapInfo; var metadata = beatmap.BeatmapSet?.Metadata ?? beatmap.Metadata; - var creator = metadata.Author?.Username; + string creator = metadata.Author?.Username; var topStatistics = new List { new AccuracyStatistic(score.Accuracy), - new ComboStatistic(score.MaxCombo, !score.Statistics.TryGetValue(HitResult.Miss, out var missCount) || missCount == 0), + new ComboStatistic(score.MaxCombo, !score.Statistics.TryGetValue(HitResult.Miss, out int missCount) || missCount == 0), new PerformanceStatistic(score), }; diff --git a/osu.Game/Screens/Ranking/ResultsScreen.cs b/osu.Game/Screens/Ranking/ResultsScreen.cs index af60296344..dacc4f5f9e 100644 --- a/osu.Game/Screens/Ranking/ResultsScreen.cs +++ b/osu.Game/Screens/Ranking/ResultsScreen.cs @@ -287,7 +287,7 @@ namespace osu.Game.Screens.Ranking detachedPanelContainer.Add(expandedPanel); // Move into its original location in the local container first, then to the final location. - var origLocation = detachedPanelContainer.ToLocalSpace(screenSpacePos).X; + float origLocation = detachedPanelContainer.ToLocalSpace(screenSpacePos).X; expandedPanel.MoveToX(origLocation) .Then() .MoveToX(StatisticsPanel.SIDE_PADDING, 150, Easing.OutQuint); diff --git a/osu.Game/Screens/Ranking/Statistics/UnstableRate.cs b/osu.Game/Screens/Ranking/Statistics/UnstableRate.cs index 055db143d1..cd2b292547 100644 --- a/osu.Game/Screens/Ranking/Statistics/UnstableRate.cs +++ b/osu.Game/Screens/Ranking/Statistics/UnstableRate.cs @@ -20,8 +20,8 @@ namespace osu.Game.Screens.Ranking.Statistics public UnstableRate(IEnumerable hitEvents) : base("Unstable Rate") { - var timeOffsets = hitEvents.Where(e => !(e.HitObject.HitWindows is HitWindows.EmptyHitWindows) && e.Result.IsHit()) - .Select(ev => ev.TimeOffset).ToArray(); + double[] timeOffsets = hitEvents.Where(e => !(e.HitObject.HitWindows is HitWindows.EmptyHitWindows) && e.Result.IsHit()) + .Select(ev => ev.TimeOffset).ToArray(); Value = 10 * standardDeviation(timeOffsets); } @@ -30,8 +30,8 @@ namespace osu.Game.Screens.Ranking.Statistics if (timeOffsets.Length == 0) return double.NaN; - var mean = timeOffsets.Average(); - var squares = timeOffsets.Select(offset => Math.Pow(offset - mean, 2)).Sum(); + double mean = timeOffsets.Average(); + double squares = timeOffsets.Select(offset => Math.Pow(offset - mean, 2)).Sum(); return Math.Sqrt(squares / timeOffsets.Length); } diff --git a/osu.Game/Screens/Select/BeatmapDetails.cs b/osu.Game/Screens/Select/BeatmapDetails.cs index 16455940bf..dfbaa9c6a5 100644 --- a/osu.Game/Screens/Select/BeatmapDetails.cs +++ b/osu.Game/Screens/Select/BeatmapDetails.cs @@ -233,7 +233,7 @@ namespace osu.Game.Screens.Select private void updateMetrics() { - var hasMetrics = (failTimes?.Retries?.Any() ?? false) || (failTimes?.Fails?.Any() ?? false); + bool hasMetrics = (failTimes?.Retries?.Any() ?? false) || (failTimes?.Fails?.Any() ?? false); if (ratings?.Any() ?? false) { diff --git a/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs b/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs index d8c5aa760e..9e057808a7 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs @@ -57,9 +57,9 @@ namespace osu.Game.Screens.Select.Carousel if (match) { - var terms = BeatmapInfo.GetSearchableTerms(); + string[] terms = BeatmapInfo.GetSearchableTerms(); - foreach (var criteriaTerm in criteria.SearchTerms) + foreach (string criteriaTerm in criteria.SearchTerms) match &= terms.Any(term => term.Contains(criteriaTerm, StringComparison.InvariantCultureIgnoreCase)); // if a match wasn't found via text matching of terms, do a second catch-all check matching against online IDs. @@ -89,7 +89,7 @@ namespace osu.Game.Screens.Select.Carousel { default: case SortMode.Difficulty: - var ruleset = BeatmapInfo.RulesetID.CompareTo(otherBeatmap.BeatmapInfo.RulesetID); + int ruleset = BeatmapInfo.RulesetID.CompareTo(otherBeatmap.BeatmapInfo.RulesetID); if (ruleset != 0) return ruleset; return BeatmapInfo.StarDifficulty.CompareTo(otherBeatmap.BeatmapInfo.StarDifficulty); diff --git a/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs index 0d7882bf17..e465f423bc 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs @@ -95,8 +95,8 @@ namespace osu.Game.Screens.Select.Carousel private int compareUsingAggregateMax(CarouselBeatmapSet other, Func func) { - var ourBeatmaps = ValidBeatmaps.Any(); - var otherBeatmaps = other.ValidBeatmaps.Any(); + bool ourBeatmaps = ValidBeatmaps.Any(); + bool otherBeatmaps = other.ValidBeatmaps.Any(); if (!ourBeatmaps && !otherBeatmaps) return 0; if (!ourBeatmaps) return -1; diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs index 9773bd5ce9..173b804d90 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs @@ -241,7 +241,7 @@ namespace osu.Game.Screens.Select.Carousel TernaryState state; - var countExisting = beatmapSet.Beatmaps.Count(b => collection.Beatmaps.Contains(b)); + int countExisting = beatmapSet.Beatmaps.Count(b => collection.Beatmaps.Contains(b)); if (countExisting == beatmapSet.Beatmaps.Count) state = TernaryState.True; diff --git a/osu.Game/Screens/Select/Details/FailRetryGraph.cs b/osu.Game/Screens/Select/Details/FailRetryGraph.cs index ecaf02cb30..312c55b242 100644 --- a/osu.Game/Screens/Select/Details/FailRetryGraph.cs +++ b/osu.Game/Screens/Select/Details/FailRetryGraph.cs @@ -27,9 +27,9 @@ namespace osu.Game.Screens.Select.Details failTimes = value; - var retries = FailTimes?.Retries ?? Array.Empty(); - var fails = FailTimes?.Fails ?? Array.Empty(); - var retriesAndFails = sumRetriesAndFails(retries, fails); + int[] retries = FailTimes?.Retries ?? Array.Empty(); + int[] fails = FailTimes?.Fails ?? Array.Empty(); + int[] retriesAndFails = sumRetriesAndFails(retries, fails); float maxValue = retriesAndFails.Any() ? retriesAndFails.Max() : 0; failGraph.MaxValue = maxValue; @@ -42,7 +42,7 @@ namespace osu.Game.Screens.Select.Details private int[] sumRetriesAndFails(int[] retries, int[] fails) { - var result = new int[Math.Max(retries.Length, fails.Length)]; + int[] result = new int[Math.Max(retries.Length, fails.Length)]; for (int i = 0; i < retries.Length; ++i) result[i] = retries[i]; diff --git a/osu.Game/Screens/Select/Details/UserRatings.cs b/osu.Game/Screens/Select/Details/UserRatings.cs index aa316d6e40..c2be3528fc 100644 --- a/osu.Game/Screens/Select/Details/UserRatings.cs +++ b/osu.Game/Screens/Select/Details/UserRatings.cs @@ -45,8 +45,8 @@ namespace osu.Game.Screens.Select.Details { var usableRange = Ratings.Skip(1).Take(rating_range); // adjust for API returning weird empty data at 0. - var negativeCount = usableRange.Take(rating_range / 2).Sum(); - var totalCount = usableRange.Sum(); + int negativeCount = usableRange.Take(rating_range / 2).Sum(); + int totalCount = usableRange.Sum(); negativeRatings.Text = negativeCount.ToLocalisableString(@"N0"); positiveRatings.Text = (totalCount - negativeCount).ToLocalisableString(@"N0"); diff --git a/osu.Game/Screens/Select/FilterControl.cs b/osu.Game/Screens/Select/FilterControl.cs index 298b6e49bd..e95bd7f653 100644 --- a/osu.Game/Screens/Select/FilterControl.cs +++ b/osu.Game/Screens/Select/FilterControl.cs @@ -39,7 +39,7 @@ namespace osu.Game.Screens.Select { Debug.Assert(ruleset.Value.ID != null); - var query = searchTextBox.Text; + string query = searchTextBox.Text; var criteria = new FilterCriteria { diff --git a/osu.Game/Screens/Select/FilterQueryParser.cs b/osu.Game/Screens/Select/FilterQueryParser.cs index a882148392..94df8addb3 100644 --- a/osu.Game/Screens/Select/FilterQueryParser.cs +++ b/osu.Game/Screens/Select/FilterQueryParser.cs @@ -22,9 +22,9 @@ namespace osu.Game.Screens.Select { foreach (Match match in query_syntax_regex.Matches(query)) { - var key = match.Groups["key"].Value.ToLower(); + string key = match.Groups["key"].Value.ToLower(); var op = parseOperator(match.Groups["op"].Value); - var value = match.Groups["value"].Value; + string value = match.Groups["value"].Value; if (tryParseKeywordCriteria(criteria, key, value, op)) query = query.Replace(match.ToString(), ""); @@ -310,10 +310,10 @@ namespace osu.Game.Screens.Select private static bool tryUpdateLengthRange(FilterCriteria criteria, Operator op, string val) { - if (!tryParseDoubleWithPoint(val.TrimEnd('m', 's', 'h'), out var length)) + if (!tryParseDoubleWithPoint(val.TrimEnd('m', 's', 'h'), out double length)) return false; - var scale = getLengthScale(val); + int scale = getLengthScale(val); return tryUpdateCriteriaRange(ref criteria.Length, op, length * scale, scale / 2.0); } } diff --git a/osu.Game/Screens/Spectate/SpectatorScreen.cs b/osu.Game/Screens/Spectate/SpectatorScreen.cs index 7861d4cb72..c8df01dae6 100644 --- a/osu.Game/Screens/Spectate/SpectatorScreen.cs +++ b/osu.Game/Screens/Spectate/SpectatorScreen.cs @@ -76,7 +76,7 @@ namespace osu.Game.Screens.Spectate managerUpdated = beatmaps.ItemUpdated.GetBoundCopy(); managerUpdated.BindValueChanged(beatmapUpdated); - foreach (var (id, _) in userMap) + foreach ((int id, var _) in userMap) spectatorClient.WatchUser(id); })); } @@ -86,7 +86,7 @@ namespace osu.Game.Screens.Spectate if (!e.NewValue.TryGetTarget(out var beatmapSet)) return; - foreach (var (userId, _) in userMap) + foreach ((int userId, var _) in userMap) { if (!playingUserStates.TryGetValue(userId, out var userState)) continue; @@ -101,20 +101,20 @@ namespace osu.Game.Screens.Spectate switch (e.Action) { case NotifyDictionaryChangedAction.Add: - foreach (var (userId, state) in e.NewItems.AsNonNull()) + foreach ((int userId, var state) in e.NewItems.AsNonNull()) onUserStateAdded(userId, state); break; case NotifyDictionaryChangedAction.Remove: - foreach (var (userId, _) in e.OldItems.AsNonNull()) + foreach ((int userId, var _) in e.OldItems.AsNonNull()) onUserStateRemoved(userId); break; case NotifyDictionaryChangedAction.Replace: - foreach (var (userId, _) in e.OldItems.AsNonNull()) + foreach ((int userId, var _) in e.OldItems.AsNonNull()) onUserStateRemoved(userId); - foreach (var (userId, state) in e.NewItems.AsNonNull()) + foreach ((int userId, var state) in e.NewItems.AsNonNull()) onUserStateAdded(userId, state); break; } @@ -219,7 +219,7 @@ namespace osu.Game.Screens.Spectate if (spectatorClient != null) { - foreach (var (userId, _) in userMap) + foreach ((int userId, var _) in userMap) spectatorClient.StopWatchingUser(userId); } diff --git a/osu.Game/Skinning/DefaultSkin.cs b/osu.Game/Skinning/DefaultSkin.cs index 8e03bddb4d..495f476417 100644 --- a/osu.Game/Skinning/DefaultSkin.cs +++ b/osu.Game/Skinning/DefaultSkin.cs @@ -42,7 +42,7 @@ namespace osu.Game.Skinning public override ISample GetSample(ISampleInfo sampleInfo) { - foreach (var lookup in sampleInfo.LookupNames) + foreach (string lookup in sampleInfo.LookupNames) { var sample = resources.AudioManager.Samples.Get(lookup); if (sample != null) diff --git a/osu.Game/Skinning/Editor/SkinBlueprintContainer.cs b/osu.Game/Skinning/Editor/SkinBlueprintContainer.cs index c0cc2ab40e..d67bfb89ab 100644 --- a/osu.Game/Skinning/Editor/SkinBlueprintContainer.cs +++ b/osu.Game/Skinning/Editor/SkinBlueprintContainer.cs @@ -41,7 +41,7 @@ namespace osu.Game.Skinning.Editor if (targetContainers.Length == 0) { - var targetScreen = target.ChildrenOfType().LastOrDefault()?.GetType().Name ?? "this screen"; + string targetScreen = target.ChildrenOfType().LastOrDefault()?.GetType().Name ?? "this screen"; AddInternal(new ScreenWhiteBox.UnderConstructionMessage(targetScreen, "doesn't support skin customisation just yet.")); return; diff --git a/osu.Game/Skinning/LegacyManiaSkinDecoder.cs b/osu.Game/Skinning/LegacyManiaSkinDecoder.cs index 5308640bdd..7214c847a7 100644 --- a/osu.Game/Skinning/LegacyManiaSkinDecoder.cs +++ b/osu.Game/Skinning/LegacyManiaSkinDecoder.cs @@ -66,7 +66,7 @@ namespace osu.Game.Skinning { Debug.Assert(currentConfig != null); - foreach (var line in pendingLines) + foreach (string line in pendingLines) { var pair = SplitKeyVal(line); diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index b09620411b..ad40f2c775 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -306,7 +306,7 @@ namespace osu.Game.Skinning => source.CustomColours.TryGetValue(lookup, out var col) ? new Bindable(col) : null; private IBindable getManiaImage(LegacyManiaSkinConfiguration source, string lookup) - => source.ImageLookups.TryGetValue(lookup, out var image) ? new Bindable(image) : null; + => source.ImageLookups.TryGetValue(lookup, out string image) ? new Bindable(image) : null; [CanBeNull] private IBindable legacySettingLookup(LegacySkinConfiguration.LegacySetting legacySetting) @@ -326,7 +326,7 @@ namespace osu.Game.Skinning { try { - if (Configuration.ConfigDictionary.TryGetValue(lookup.ToString(), out var val)) + if (Configuration.ConfigDictionary.TryGetValue(lookup.ToString(), out string val)) { // special case for handling skins which use 1 or 0 to signify a boolean state. if (typeof(TValue) == typeof(bool)) @@ -472,7 +472,7 @@ namespace osu.Game.Skinning public override Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) { - foreach (var name in getFallbackNames(componentName)) + foreach (string name in getFallbackNames(componentName)) { float ratio = 2; var texture = Textures?.Get($"{name}@2x", wrapModeS, wrapModeT); @@ -504,7 +504,7 @@ namespace osu.Game.Skinning lookupNames = sampleInfo.LookupNames.SelectMany(getFallbackNames); } - foreach (var lookup in lookupNames) + foreach (string lookup in lookupNames) { var sample = Samples?.Get(lookup); @@ -529,7 +529,7 @@ namespace osu.Game.Skinning lookupNames = lookupNames.Where(name => !name.EndsWith(hitSample.Suffix, StringComparison.Ordinal)); } - foreach (var l in lookupNames) + foreach (string l in lookupNames) yield return l; // also for compatibility, try falling back to non-bank samples (so-called "universal" samples) as the last resort. diff --git a/osu.Game/Skinning/LegacySkinDecoder.cs b/osu.Game/Skinning/LegacySkinDecoder.cs index 2700f84815..a4ce3d83ce 100644 --- a/osu.Game/Skinning/LegacySkinDecoder.cs +++ b/osu.Game/Skinning/LegacySkinDecoder.cs @@ -35,7 +35,7 @@ namespace osu.Game.Skinning case @"Version": if (pair.Value == "latest") skin.LegacyVersion = LegacySkinConfiguration.LATEST_VERSION; - else if (decimal.TryParse(pair.Value, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out var version)) + else if (decimal.TryParse(pair.Value, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out decimal version)) skin.LegacyVersion = version; return; diff --git a/osu.Game/Skinning/LegacySkinResourceStore.cs b/osu.Game/Skinning/LegacySkinResourceStore.cs index 05d0dee05f..fb06bb54d0 100644 --- a/osu.Game/Skinning/LegacySkinResourceStore.cs +++ b/osu.Game/Skinning/LegacySkinResourceStore.cs @@ -26,9 +26,9 @@ namespace osu.Game.Skinning if (source.Files == null) yield break; - foreach (var filename in base.GetFilenames(name)) + foreach (string filename in base.GetFilenames(name)) { - var path = getPathForFile(filename.ToStandardisedPath()); + string path = getPathForFile(filename.ToStandardisedPath()); if (path != null) yield return path; } diff --git a/osu.Game/Skinning/LegacySpriteText.cs b/osu.Game/Skinning/LegacySpriteText.cs index 8fc6cbde7d..94383834fc 100644 --- a/osu.Game/Skinning/LegacySpriteText.cs +++ b/osu.Game/Skinning/LegacySpriteText.cs @@ -49,7 +49,7 @@ namespace osu.Game.Skinning public ITexturedCharacterGlyph Get(string fontName, char character) { - var lookup = getLookupName(character); + string lookup = getLookupName(character); var texture = skin.GetTexture($"{fontName}-{lookup}"); diff --git a/osu.Game/Skinning/ResourceStoreBackedSkin.cs b/osu.Game/Skinning/ResourceStoreBackedSkin.cs index f041b82cf4..4787b5a4e9 100644 --- a/osu.Game/Skinning/ResourceStoreBackedSkin.cs +++ b/osu.Game/Skinning/ResourceStoreBackedSkin.cs @@ -36,7 +36,7 @@ namespace osu.Game.Skinning public ISample? GetSample(ISampleInfo sampleInfo) { - foreach (var lookup in sampleInfo.LookupNames) + foreach (string? lookup in sampleInfo.LookupNames) { ISample? sample = samples.Get(lookup); if (sample != null) diff --git a/osu.Game/Skinning/Skin.cs b/osu.Game/Skinning/Skin.cs index 92441f40da..f0413ff310 100644 --- a/osu.Game/Skinning/Skin.cs +++ b/osu.Game/Skinning/Skin.cs @@ -51,7 +51,7 @@ namespace osu.Game.Skinning if (fileInfo == null) continue; - var bytes = resources?.Files.Get(fileInfo.FileInfo.StoragePath); + byte[] bytes = resources?.Files.Get(fileInfo.FileInfo.StoragePath); if (bytes == null) continue; diff --git a/osu.Game/Stores/BeatmapImporter.cs b/osu.Game/Stores/BeatmapImporter.cs index 254127cc7e..787b1ddd60 100644 --- a/osu.Game/Stores/BeatmapImporter.cs +++ b/osu.Game/Stores/BeatmapImporter.cs @@ -123,7 +123,7 @@ namespace osu.Game.Stores // find any existing beatmaps in the database that have matching online ids List existingBeatmaps = new List(); - foreach (var id in beatmapIds) + foreach (int id in beatmapIds) existingBeatmaps.AddRange(realm.All().Where(b => b.OnlineID == id)); if (existingBeatmaps.Any()) diff --git a/osu.Game/Stores/RealmArchiveModelImporter.cs b/osu.Game/Stores/RealmArchiveModelImporter.cs index ec454d25fa..3398cc114d 100644 --- a/osu.Game/Stores/RealmArchiveModelImporter.cs +++ b/osu.Game/Stores/RealmArchiveModelImporter.cs @@ -426,7 +426,7 @@ namespace osu.Game.Stores { MemoryStream hashable = new MemoryStream(); - foreach (var file in reader.Filenames.Where(f => HashableFileTypes.Any(ext => f.EndsWith(ext, StringComparison.OrdinalIgnoreCase))).OrderBy(f => f)) + foreach (string? file in reader.Filenames.Where(f => HashableFileTypes.Any(ext => f.EndsWith(ext, StringComparison.OrdinalIgnoreCase))).OrderBy(f => f)) { using (Stream s = reader.GetStream(file)) s.CopyTo(hashable); diff --git a/osu.Game/Stores/RealmRulesetStore.cs b/osu.Game/Stores/RealmRulesetStore.cs index 27eb5d797f..eea9acea05 100644 --- a/osu.Game/Stores/RealmRulesetStore.cs +++ b/osu.Game/Stores/RealmRulesetStore.cs @@ -193,7 +193,7 @@ namespace osu.Game.Stores { var rulesets = rulesetStorage.GetFiles(@".", @$"{ruleset_library_prefix}.*.dll"); - foreach (var ruleset in rulesets.Where(f => !f.Contains(@"Tests"))) + foreach (string? ruleset in rulesets.Where(f => !f.Contains(@"Tests"))) loadRulesetFromFile(rulesetStorage.GetFullPath(ruleset)); } @@ -201,7 +201,7 @@ namespace osu.Game.Stores { try { - var files = Directory.GetFiles(RuntimeInfo.StartupDirectory, @$"{ruleset_library_prefix}.*.dll"); + string[]? files = Directory.GetFiles(RuntimeInfo.StartupDirectory, @$"{ruleset_library_prefix}.*.dll"); foreach (string file in files.Where(f => !Path.GetFileName(f).Contains("Tests"))) loadRulesetFromFile(file); @@ -214,7 +214,7 @@ namespace osu.Game.Stores private void loadRulesetFromFile(string file) { - var filename = Path.GetFileNameWithoutExtension(file); + string? filename = Path.GetFileNameWithoutExtension(file); if (loadedAssemblies.Values.Any(t => Path.GetFileNameWithoutExtension(t.Assembly.Location) == filename)) return; diff --git a/osu.Game/Storyboards/CommandLoop.cs b/osu.Game/Storyboards/CommandLoop.cs index 66db965803..0713cb8670 100644 --- a/osu.Game/Storyboards/CommandLoop.cs +++ b/osu.Game/Storyboards/CommandLoop.cs @@ -33,9 +33,9 @@ namespace osu.Game.Storyboards public override IEnumerable.TypedCommand> GetCommands(CommandTimelineSelector timelineSelector, double offset = 0) { - for (var loop = 0; loop < TotalIterations; loop++) + for (int loop = 0; loop < TotalIterations; loop++) { - var loopOffset = LoopStartTime + loop * CommandsDuration; + double loopOffset = LoopStartTime + loop * CommandsDuration; foreach (var command in base.GetCommands(timelineSelector, offset + loopOffset)) yield return command; } diff --git a/osu.Game/Storyboards/CommandTimeline.cs b/osu.Game/Storyboards/CommandTimeline.cs index c71806352d..8ded3ee975 100644 --- a/osu.Game/Storyboards/CommandTimeline.cs +++ b/osu.Game/Storyboards/CommandTimeline.cs @@ -57,7 +57,7 @@ namespace osu.Game.Storyboards public int CompareTo(ICommand other) { - var result = StartTime.CompareTo(other.StartTime); + int result = StartTime.CompareTo(other.StartTime); if (result != 0) return result; return EndTime.CompareTo(other.EndTime); diff --git a/osu.Game/Storyboards/CommandTimelineGroup.cs b/osu.Game/Storyboards/CommandTimelineGroup.cs index c478b91c22..e7de135ce8 100644 --- a/osu.Game/Storyboards/CommandTimelineGroup.cs +++ b/osu.Game/Storyboards/CommandTimelineGroup.cs @@ -65,7 +65,7 @@ namespace osu.Game.Storyboards { // if the first alpha command starts at zero it should be given priority over anything else. // this is due to it creating a state where the target is not present before that time, causing any other events to not be visible. - var earliestDisplay = EarliestDisplayedTime; + double? earliestDisplay = EarliestDisplayedTime; if (earliestDisplay != null) return earliestDisplay.Value; diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboardVideo.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboardVideo.cs index d746ff5ae5..d21616955a 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboardVideo.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboardVideo.cs @@ -29,7 +29,7 @@ namespace osu.Game.Storyboards.Drawables [BackgroundDependencyLoader(true)] private void load(IBindable beatmap, TextureStore textureStore) { - var path = beatmap.Value.BeatmapSetInfo?.Files.Find(f => f.Filename.Equals(Video.Path, StringComparison.OrdinalIgnoreCase))?.FileInfo.StoragePath; + string path = beatmap.Value.BeatmapSetInfo?.Files.Find(f => f.Filename.Equals(Video.Path, StringComparison.OrdinalIgnoreCase))?.FileInfo.StoragePath; if (path == null) return; diff --git a/osu.Game/Storyboards/Storyboard.cs b/osu.Game/Storyboards/Storyboard.cs index 38e0e4e38c..a25bf24491 100644 --- a/osu.Game/Storyboards/Storyboard.cs +++ b/osu.Game/Storyboards/Storyboard.cs @@ -77,7 +77,7 @@ namespace osu.Game.Storyboards { get { - var backgroundPath = BeatmapInfo.BeatmapSet?.Metadata?.BackgroundFile?.ToLowerInvariant(); + string backgroundPath = BeatmapInfo.BeatmapSet?.Metadata?.BackgroundFile?.ToLowerInvariant(); if (backgroundPath == null) return false; @@ -91,7 +91,7 @@ namespace osu.Game.Storyboards public Drawable CreateSpriteFromResourcePath(string path, TextureStore textureStore) { Drawable drawable = null; - var storyboardPath = BeatmapInfo.BeatmapSet?.Files.Find(f => f.Filename.Equals(path, StringComparison.OrdinalIgnoreCase))?.FileInfo.StoragePath; + string storyboardPath = BeatmapInfo.BeatmapSet?.Files.Find(f => f.Filename.Equals(path, StringComparison.OrdinalIgnoreCase))?.FileInfo.StoragePath; if (storyboardPath != null) drawable = new Sprite { Texture = textureStore.Get(storyboardPath) }; diff --git a/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs b/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs index 6d63525011..651874e4de 100644 --- a/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs +++ b/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs @@ -150,7 +150,7 @@ namespace osu.Game.Tests.Beatmaps using (var resStream = openResource($"{resource_namespace}.{name}{expected_conversion_suffix}.json")) using (var reader = new StreamReader(resStream)) { - var contents = reader.ReadToEnd(); + string contents = reader.ReadToEnd(); return JsonConvert.DeserializeObject(contents); } } @@ -173,7 +173,7 @@ namespace osu.Game.Tests.Beatmaps private Stream openResource(string name) { - var localPath = Path.GetDirectoryName(Uri.UnescapeDataString(new UriBuilder(Assembly.GetExecutingAssembly().CodeBase).Path)).AsNonNull(); + string localPath = Path.GetDirectoryName(Uri.UnescapeDataString(new UriBuilder(Assembly.GetExecutingAssembly().CodeBase).Path)).AsNonNull(); return Assembly.LoadFrom(Path.Combine(localPath, $"{ResourceAssembly}.dll")).GetManifestResourceStream($@"{ResourceAssembly}.Resources.{name}"); } diff --git a/osu.Game/Tests/Beatmaps/DifficultyCalculatorTest.cs b/osu.Game/Tests/Beatmaps/DifficultyCalculatorTest.cs index fdb3e1d465..e5b641b606 100644 --- a/osu.Game/Tests/Beatmaps/DifficultyCalculatorTest.cs +++ b/osu.Game/Tests/Beatmaps/DifficultyCalculatorTest.cs @@ -49,7 +49,7 @@ namespace osu.Game.Tests.Beatmaps private Stream openResource(string name) { - var localPath = Path.GetDirectoryName(Uri.UnescapeDataString(new UriBuilder(Assembly.GetExecutingAssembly().CodeBase).Path)).AsNonNull(); + string localPath = Path.GetDirectoryName(Uri.UnescapeDataString(new UriBuilder(Assembly.GetExecutingAssembly().CodeBase).Path)).AsNonNull(); return Assembly.LoadFrom(Path.Combine(localPath, $"{ResourceAssembly}.dll")).GetManifestResourceStream($@"{ResourceAssembly}.Resources.{name}"); } diff --git a/osu.Game/Tests/Visual/DependencyProvidingContainer.cs b/osu.Game/Tests/Visual/DependencyProvidingContainer.cs index c799cad61a..d1290fc5ac 100644 --- a/osu.Game/Tests/Visual/DependencyProvidingContainer.cs +++ b/osu.Game/Tests/Visual/DependencyProvidingContainer.cs @@ -25,7 +25,7 @@ namespace osu.Game.Tests.Visual { var dependencyContainer = new DependencyContainer(base.CreateChildDependencies(parent)); - foreach (var (type, value) in CachedDependencies) + foreach ((var type, object value) in CachedDependencies) dependencyContainer.CacheAs(type, value); return dependencyContainer; diff --git a/osu.Game/Tests/Visual/SkinnableTestScene.cs b/osu.Game/Tests/Visual/SkinnableTestScene.cs index ef44d0df24..000e7194bc 100644 --- a/osu.Game/Tests/Visual/SkinnableTestScene.cs +++ b/osu.Game/Tests/Visual/SkinnableTestScene.cs @@ -127,7 +127,7 @@ namespace osu.Game.Tests.Visual void updateSizing() { - var autoSize = created.RelativeSizeAxes == Axes.None; + bool autoSize = created.RelativeSizeAxes == Axes.None; foreach (var c in new[] { mainProvider, childContainer, skinProvider }) { @@ -202,7 +202,7 @@ namespace osu.Game.Tests.Visual { var match = Regex.Match(componentName, "-([0-9]*)"); - if (match.Length > 0 && int.TryParse(match.Groups[1].Value, out var number) && number < 60) + if (match.Length > 0 && int.TryParse(match.Groups[1].Value, out int number) && number < 60) return base.GetTexture(componentName.Replace($"-{number}", $"-{number % 2}"), wrapModeS, wrapModeT); } diff --git a/osu.Game/Updater/SimpleUpdateManager.cs b/osu.Game/Updater/SimpleUpdateManager.cs index e0409e34df..5e466cc57f 100644 --- a/osu.Game/Updater/SimpleUpdateManager.cs +++ b/osu.Game/Updater/SimpleUpdateManager.cs @@ -45,7 +45,7 @@ namespace osu.Game.Updater // avoid any discrepancies due to build suffixes for now. // eventually we will want to support release streams and consider these. version = version.Split('-').First(); - var latestTagName = latest.TagName.Split('-').First(); + string latestTagName = latest.TagName.Split('-').First(); if (latestTagName != version) { diff --git a/osu.Game/Updater/UpdateManager.cs b/osu.Game/Updater/UpdateManager.cs index 98ce2cb46c..28b828804c 100644 --- a/osu.Game/Updater/UpdateManager.cs +++ b/osu.Game/Updater/UpdateManager.cs @@ -39,9 +39,9 @@ namespace osu.Game.Updater Schedule(() => Task.Run(CheckForUpdateAsync)); - var version = game.Version; + string version = game.Version; - var lastVersion = config.Get(OsuSetting.Version); + string lastVersion = config.Get(OsuSetting.Version); if (game.IsDeployedBuild && version != lastVersion) { diff --git a/osu.Game/Utils/StatelessRNG.cs b/osu.Game/Utils/StatelessRNG.cs index cd169229e3..3db632fc42 100644 --- a/osu.Game/Utils/StatelessRNG.cs +++ b/osu.Game/Utils/StatelessRNG.cs @@ -37,7 +37,7 @@ namespace osu.Game.Utils { unchecked { - var combined = ((ulong)(uint)series << 32) | (uint)seed; + ulong combined = ((ulong)(uint)series << 32) | (uint)seed; // The xor operation is to not map (0, 0) to 0. return mix(combined ^ 0x12345678); } From 95837990f3a185abc9acf50f170ea387cdd8adcf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 Oct 2021 13:09:30 +0900 Subject: [PATCH 120/260] Apply some second-pass inspections that appeared after previous changes --- .../MathUtils/LegacySortHelper.cs | 16 +++------------- .../TestSceneSampleOutput.cs | 3 ++- .../Multiplayer/TestSceneMultiSpectatorScreen.cs | 2 +- osu.Game/Database/RealmContextFactory.cs | 2 +- osu.Game/Online/Chat/MessageFormatter.cs | 2 +- osu.Game/Stores/RealmRulesetStore.cs | 2 +- 6 files changed, 9 insertions(+), 18 deletions(-) diff --git a/osu.Game.Rulesets.Mania/MathUtils/LegacySortHelper.cs b/osu.Game.Rulesets.Mania/MathUtils/LegacySortHelper.cs index a0e2958bae..5c595323c3 100644 --- a/osu.Game.Rulesets.Mania/MathUtils/LegacySortHelper.cs +++ b/osu.Game.Rulesets.Mania/MathUtils/LegacySortHelper.cs @@ -62,9 +62,7 @@ namespace osu.Game.Rulesets.Mania.MathUtils if (i < j) { - T key = keys[i]; - keys[i] = keys[j]; - keys[j] = key; + (keys[i], keys[j]) = (keys[j], keys[i]); } i++; @@ -142,11 +140,7 @@ namespace osu.Game.Rulesets.Mania.MathUtils private static void swap(T[] a, int i, int j) { if (i != j) - { - T t = a[i]; - a[i] = a[j]; - a[j] = t; - } + (a[i], a[j]) = (a[j], a[i]); } private static void swapIfGreater(T[] keys, IComparer comparer, int a, int b) @@ -154,11 +148,7 @@ namespace osu.Game.Rulesets.Mania.MathUtils if (a != b) { if (comparer.Compare(keys[a], keys[b]) > 0) - { - T key = keys[a]; - keys[a] = keys[b]; - keys[b] = key; - } + (keys[a], keys[b]) = (keys[b], keys[a]); } } } diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneSampleOutput.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneSampleOutput.cs index 97c8d9eeb5..e5c9358c26 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TestSceneSampleOutput.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneSampleOutput.cs @@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Taiko.Tests { base.SetUpSteps(); - string[] expectedSampleNames = new[] + string[] expectedSampleNames = { string.Empty, string.Empty, @@ -31,6 +31,7 @@ namespace osu.Game.Rulesets.Taiko.Tests HitSampleInfo.HIT_WHISTLE, HitSampleInfo.HIT_WHISTLE, }; + var actualSampleNames = new List(); // due to pooling we can't access all samples right away due to object re-use, diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs index 8d878b993c..7ff8c82145 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiSpectatorScreen.cs @@ -289,7 +289,7 @@ namespace osu.Game.Tests.Visual.Multiplayer [Test] public void TestSpectatingDuringGameplay() { - int[] players = new[] { PLAYER_1_ID, PLAYER_2_ID }; + int[] players = { PLAYER_1_ID, PLAYER_2_ID }; start(players); sendFrames(players, 300); diff --git a/osu.Game/Database/RealmContextFactory.cs b/osu.Game/Database/RealmContextFactory.cs index ffb1c96261..013a2e9d64 100644 --- a/osu.Game/Database/RealmContextFactory.cs +++ b/osu.Game/Database/RealmContextFactory.cs @@ -156,7 +156,7 @@ namespace osu.Game.Database void convertOnlineIDs() where T : RealmObject { - string? className = typeof(T).Name.Replace(@"Realm", string.Empty); + string className = typeof(T).Name.Replace(@"Realm", string.Empty); // version was not bumped when the beatmap/ruleset models were added // therefore we must manually check for their presence to avoid throwing on the `DynamicApi` calls. diff --git a/osu.Game/Online/Chat/MessageFormatter.cs b/osu.Game/Online/Chat/MessageFormatter.cs index 5a90638dcd..5e0f66443b 100644 --- a/osu.Game/Online/Chat/MessageFormatter.cs +++ b/osu.Game/Online/Chat/MessageFormatter.cs @@ -136,7 +136,7 @@ namespace osu.Game.Online.Chat // length > 3 since all these links need another argument to work if (args.Length > 3 && args[1].EndsWith(websiteRootUrl, StringComparison.OrdinalIgnoreCase)) { - string? mainArg = args[3]; + string mainArg = args[3]; switch (args[2]) { diff --git a/osu.Game/Stores/RealmRulesetStore.cs b/osu.Game/Stores/RealmRulesetStore.cs index eea9acea05..e9c04f652d 100644 --- a/osu.Game/Stores/RealmRulesetStore.cs +++ b/osu.Game/Stores/RealmRulesetStore.cs @@ -201,7 +201,7 @@ namespace osu.Game.Stores { try { - string[]? files = Directory.GetFiles(RuntimeInfo.StartupDirectory, @$"{ruleset_library_prefix}.*.dll"); + string[] files = Directory.GetFiles(RuntimeInfo.StartupDirectory, @$"{ruleset_library_prefix}.*.dll"); foreach (string file in files.Where(f => !Path.GetFileName(f).Contains("Tests"))) loadRulesetFromFile(file); From 2bc242001217920324cbddb9bc462e005a2632f0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 Oct 2021 13:54:20 +0900 Subject: [PATCH 121/260] Rename test scene It's not testing the sub-screen. It's testing creating playlists. --- ...nePlaylistsRoomSubScreen.cs => TestScenePlaylistCreation.cs} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename osu.Game.Tests/Visual/Playlists/{TestScenePlaylistsRoomSubScreen.cs => TestScenePlaylistCreation.cs} (98%) diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistCreation.cs similarity index 98% rename from osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs rename to osu.Game.Tests/Visual/Playlists/TestScenePlaylistCreation.cs index deb9a22184..300a164644 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomSubScreen.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistCreation.cs @@ -22,7 +22,7 @@ using osuTK.Input; namespace osu.Game.Tests.Visual.Playlists { - public class TestScenePlaylistsRoomSubScreen : OnlinePlayTestScene + public class TestScenePlaylistCreation : OnlinePlayTestScene { private BeatmapManager manager; private RulesetStore rulesets; From c4a253cc75d5ece03ff3d44eb582c198877319f5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 Oct 2021 13:55:14 +0900 Subject: [PATCH 122/260] Replace local instantiation of `TestBeatmap` with helper method --- .../Visual/Playlists/TestScenePlaylistCreation.cs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistCreation.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistCreation.cs index 300a164644..a0b51610ec 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistCreation.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistCreation.cs @@ -16,7 +16,6 @@ using osu.Game.Rulesets; using osu.Game.Rulesets.Osu; using osu.Game.Screens.OnlinePlay.Playlists; using osu.Game.Screens.Play; -using osu.Game.Tests.Beatmaps; using osu.Game.Tests.Visual.OnlinePlay; using osuTK.Input; @@ -40,7 +39,7 @@ namespace osu.Game.Tests.Visual.Playlists public void SetupSteps() { AddStep("set room", () => SelectedRoom.Value = new Room()); - AddStep("ensure has beatmap", () => manager.Import(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo.BeatmapSet).Wait()); + AddStep("ensure has beatmap", () => manager.Import(CreateBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo.BeatmapSet).Wait()); AddStep("load match", () => LoadScreen(match = new TestPlaylistsRoomSubScreen(SelectedRoom.Value))); AddUntilStep("wait for load", () => match.IsCurrentScreen()); } @@ -57,7 +56,7 @@ namespace osu.Game.Tests.Visual.Playlists SelectedRoom.Value.EndDate.Value = DateTimeOffset.Now.AddMinutes(5); SelectedRoom.Value.Playlist.Add(new PlaylistItem { - Beatmap = { Value = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo }, + Beatmap = { Value = CreateBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo }, Ruleset = { Value = new OsuRuleset().RulesetInfo } }); }); @@ -75,7 +74,7 @@ namespace osu.Game.Tests.Visual.Playlists SelectedRoom.Value.Host.Value = API.LocalUser.Value; SelectedRoom.Value.Playlist.Add(new PlaylistItem { - Beatmap = { Value = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo }, + Beatmap = { Value = CreateBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo }, Ruleset = { Value = new OsuRuleset().RulesetInfo } }); }); @@ -94,13 +93,13 @@ namespace osu.Game.Tests.Visual.Playlists public void TestBeatmapUpdatedOnReImport() { BeatmapSetInfo importedSet = null; - TestBeatmap beatmap = null; + IBeatmap beatmap = null; // this step is required to make sure the further imports actually get online IDs. // all the playlist logic relies on online ID matching. AddStep("remove all matching online IDs", () => { - beatmap = new TestBeatmap(new OsuRuleset().RulesetInfo); + beatmap = CreateBeatmap(new OsuRuleset().RulesetInfo); var existing = manager.QueryBeatmapSets(s => s.OnlineBeatmapSetID == beatmap.BeatmapInfo.BeatmapSet.OnlineBeatmapSetID).ToList(); @@ -139,7 +138,7 @@ namespace osu.Game.Tests.Visual.Playlists AddAssert("match has altered beatmap", () => match.Beatmap.Value.Beatmap.Difficulty.CircleSize == 1); - AddStep("re-import original beatmap", () => manager.Import(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo.BeatmapSet).Wait()); + AddStep("re-import original beatmap", () => manager.Import(CreateBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo.BeatmapSet).Wait()); AddAssert("match has original beatmap", () => match.Beatmap.Value.Beatmap.Difficulty.CircleSize != 1); } From 84bb3a962b9def275ce92d29cb386b16913308a0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 Oct 2021 14:11:07 +0900 Subject: [PATCH 123/260] Centralise room "creation" --- .../Playlists/TestScenePlaylistCreation.cs | 46 +++++++++++++------ 1 file changed, 31 insertions(+), 15 deletions(-) diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistCreation.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistCreation.cs index a0b51610ec..9ee0fb4db5 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistCreation.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistCreation.cs @@ -47,18 +47,20 @@ namespace osu.Game.Tests.Visual.Playlists [Test] public void TestLoadSimpleMatch() { - AddStep("set room properties", () => + setupRoom(room => { - SelectedRoom.Value.RoomID.Value = 1; - SelectedRoom.Value.Name.Value = "my awesome room"; - SelectedRoom.Value.Host.Value = API.LocalUser.Value; - SelectedRoom.Value.RecentParticipants.Add(SelectedRoom.Value.Host.Value); - SelectedRoom.Value.EndDate.Value = DateTimeOffset.Now.AddMinutes(5); - SelectedRoom.Value.Playlist.Add(new PlaylistItem + room.RoomID.Value = 1; // forces room creation. + room.Name.Value = "my awesome room"; + room.Host.Value = API.LocalUser.Value; + room.RecentParticipants.Add(room.Host.Value); + room.EndDate.Value = DateTimeOffset.Now.AddMinutes(5); + room.Playlist.Add(new PlaylistItem { Beatmap = { Value = CreateBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo }, Ruleset = { Value = new OsuRuleset().RulesetInfo } }); + + OnlinePlayDependencies.RoomManager.CreateRoom(SelectedRoom.Value); }); AddStep("start match", () => match.ChildrenOfType().First().TriggerClick()); @@ -68,15 +70,17 @@ namespace osu.Game.Tests.Visual.Playlists [Test] public void TestPlaylistItemSelectedOnCreate() { - AddStep("set room properties", () => + setupRoom(room => { - SelectedRoom.Value.Name.Value = "my awesome room"; - SelectedRoom.Value.Host.Value = API.LocalUser.Value; - SelectedRoom.Value.Playlist.Add(new PlaylistItem + room.Name.Value = "my awesome room"; + room.Host.Value = API.LocalUser.Value; + room.Playlist.Add(new PlaylistItem { Beatmap = { Value = CreateBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo }, Ruleset = { Value = new OsuRuleset().RulesetInfo } }); + + OnlinePlayDependencies.RoomManager.CreateRoom(SelectedRoom.Value); }); AddStep("move mouse to create button", () => @@ -119,11 +123,11 @@ namespace osu.Game.Tests.Visual.Playlists importedSet = manager.Import(beatmap.BeatmapInfo.BeatmapSet).Result.Value; }); - AddStep("load room", () => + setupRoom(room => { - SelectedRoom.Value.Name.Value = "my awesome room"; - SelectedRoom.Value.Host.Value = API.LocalUser.Value; - SelectedRoom.Value.Playlist.Add(new PlaylistItem + room.Name.Value = "my awesome room"; + room.Host.Value = API.LocalUser.Value; + room.Playlist.Add(new PlaylistItem { Beatmap = { Value = importedSet.Beatmaps[0] }, Ruleset = { Value = new OsuRuleset().RulesetInfo } @@ -143,6 +147,18 @@ namespace osu.Game.Tests.Visual.Playlists AddAssert("match has original beatmap", () => match.Beatmap.Value.Beatmap.Difficulty.CircleSize != 1); } + private void setupRoom(Action room) + { + AddStep("setup room", () => + { + room(SelectedRoom.Value); + + // if this isn't done the test will crash when a poll kicks in. + // probably not correct, but works for now. + OnlinePlayDependencies.RoomManager.CreateRoom(SelectedRoom.Value); + }); + } + private class TestPlaylistsRoomSubScreen : PlaylistsRoomSubScreen { public new Bindable SelectedItem => base.SelectedItem; From 58237f6fee5b63e2d955389efe47377297f1c898 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 Oct 2021 14:26:37 +0900 Subject: [PATCH 124/260] Fix weird import flows --- .../Playlists/TestScenePlaylistCreation.cs | 43 +++++++++---------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistCreation.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistCreation.cs index 9ee0fb4db5..c4e6f41f27 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistCreation.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistCreation.cs @@ -11,6 +11,7 @@ using osu.Framework.Platform; using osu.Framework.Screens; using osu.Framework.Testing; using osu.Game.Beatmaps; +using osu.Game.Database; using osu.Game.Online.Rooms; using osu.Game.Rulesets; using osu.Game.Rulesets.Osu; @@ -28,6 +29,8 @@ namespace osu.Game.Tests.Visual.Playlists private TestPlaylistsRoomSubScreen match; + private ILive importedBeatmap; + [BackgroundDependencyLoader] private void load(GameHost host, AudioManager audio) { @@ -39,7 +42,9 @@ namespace osu.Game.Tests.Visual.Playlists public void SetupSteps() { AddStep("set room", () => SelectedRoom.Value = new Room()); - AddStep("ensure has beatmap", () => manager.Import(CreateBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo.BeatmapSet).Wait()); + + importBeatmap(); + AddStep("load match", () => LoadScreen(match = new TestPlaylistsRoomSubScreen(SelectedRoom.Value))); AddUntilStep("wait for load", () => match.IsCurrentScreen()); } @@ -47,7 +52,7 @@ namespace osu.Game.Tests.Visual.Playlists [Test] public void TestLoadSimpleMatch() { - setupRoom(room => + setupAndCreateRoom(room => { room.RoomID.Value = 1; // forces room creation. room.Name.Value = "my awesome room"; @@ -56,11 +61,9 @@ namespace osu.Game.Tests.Visual.Playlists room.EndDate.Value = DateTimeOffset.Now.AddMinutes(5); room.Playlist.Add(new PlaylistItem { - Beatmap = { Value = CreateBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo }, + Beatmap = { Value = importedBeatmap.Value.Beatmaps.First() }, Ruleset = { Value = new OsuRuleset().RulesetInfo } }); - - OnlinePlayDependencies.RoomManager.CreateRoom(SelectedRoom.Value); }); AddStep("start match", () => match.ChildrenOfType().First().TriggerClick()); @@ -70,17 +73,15 @@ namespace osu.Game.Tests.Visual.Playlists [Test] public void TestPlaylistItemSelectedOnCreate() { - setupRoom(room => + setupAndCreateRoom(room => { room.Name.Value = "my awesome room"; room.Host.Value = API.LocalUser.Value; room.Playlist.Add(new PlaylistItem { - Beatmap = { Value = CreateBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo }, + Beatmap = { Value = importedBeatmap.Value.Beatmaps.First() }, Ruleset = { Value = new OsuRuleset().RulesetInfo } }); - - OnlinePlayDependencies.RoomManager.CreateRoom(SelectedRoom.Value); }); AddStep("move mouse to create button", () => @@ -97,15 +98,12 @@ namespace osu.Game.Tests.Visual.Playlists public void TestBeatmapUpdatedOnReImport() { BeatmapSetInfo importedSet = null; - IBeatmap beatmap = null; // this step is required to make sure the further imports actually get online IDs. // all the playlist logic relies on online ID matching. AddStep("remove all matching online IDs", () => { - beatmap = CreateBeatmap(new OsuRuleset().RulesetInfo); - - var existing = manager.QueryBeatmapSets(s => s.OnlineBeatmapSetID == beatmap.BeatmapInfo.BeatmapSet.OnlineBeatmapSetID).ToList(); + var existing = manager.QueryBeatmapSets(s => s.OnlineBeatmapSetID == importedBeatmap.Value.OnlineBeatmapSetID).ToList(); foreach (var s in existing) { @@ -118,12 +116,14 @@ namespace osu.Game.Tests.Visual.Playlists AddStep("import altered beatmap", () => { + IBeatmap beatmap = CreateBeatmap(new OsuRuleset().RulesetInfo); + beatmap.BeatmapInfo.BaseDifficulty.CircleSize = 1; importedSet = manager.Import(beatmap.BeatmapInfo.BeatmapSet).Result.Value; }); - setupRoom(room => + setupAndCreateRoom(room => { room.Name.Value = "my awesome room"; room.Host.Value = API.LocalUser.Value; @@ -134,20 +134,14 @@ namespace osu.Game.Tests.Visual.Playlists }); }); - AddStep("create room", () => - { - InputManager.MoveMouseTo(match.ChildrenOfType().Single()); - InputManager.Click(MouseButton.Left); - }); - AddAssert("match has altered beatmap", () => match.Beatmap.Value.Beatmap.Difficulty.CircleSize == 1); - AddStep("re-import original beatmap", () => manager.Import(CreateBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo.BeatmapSet).Wait()); + importBeatmap(); AddAssert("match has original beatmap", () => match.Beatmap.Value.Beatmap.Difficulty.CircleSize != 1); } - private void setupRoom(Action room) + private void setupAndCreateRoom(Action room) { AddStep("setup room", () => { @@ -159,6 +153,11 @@ namespace osu.Game.Tests.Visual.Playlists }); } + private void importBeatmap() + { + AddStep("import beatmap", () => importedBeatmap = manager.Import(CreateBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo.BeatmapSet).Result); + } + private class TestPlaylistsRoomSubScreen : PlaylistsRoomSubScreen { public new Bindable SelectedItem => base.SelectedItem; From 85b21174dd26cb55bc7598bf3d903bf6fd54d297 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 27 Oct 2021 14:49:50 +0900 Subject: [PATCH 125/260] Fix online play test request handling --- .../Visual/OnlinePlay/OnlinePlayTestScene.cs | 20 +++++++++++++++++-- .../TestRequestHandlingRoomManager.cs | 10 ---------- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestScene.cs b/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestScene.cs index 8716646074..d782160ee5 100644 --- a/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestScene.cs +++ b/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestScene.cs @@ -7,6 +7,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Game.Online.API; using osu.Game.Online.Rooms; using osu.Game.Screens.OnlinePlay; @@ -27,11 +28,15 @@ namespace osu.Game.Tests.Visual.OnlinePlay /// protected OnlinePlayTestSceneDependencies OnlinePlayDependencies => dependencies?.OnlinePlayDependencies; - private DelegatedDependencyContainer dependencies; - protected override Container Content => content; + + [Resolved] + private OsuGameBase game { get; set; } + private readonly Container content; private readonly Container drawableDependenciesContainer; + private DelegatedDependencyContainer dependencies; + private TestRoomRequestsHandler requestsHandler; protected OnlinePlayTestScene() { @@ -57,6 +62,17 @@ namespace osu.Game.Tests.Visual.OnlinePlay drawableDependenciesContainer.AddRange(OnlinePlayDependencies.DrawableComponents); }); + public override void SetUpSteps() + { + base.SetUpSteps(); + + AddStep("setup API", () => + { + requestsHandler = new TestRoomRequestsHandler(); + ((DummyAPIAccess)API).HandleRequest = request => requestsHandler.HandleRequest(request, API.LocalUser.Value, game); + }); + } + /// /// Creates the room dependencies. Called every . /// diff --git a/osu.Game/Tests/Visual/OnlinePlay/TestRequestHandlingRoomManager.cs b/osu.Game/Tests/Visual/OnlinePlay/TestRequestHandlingRoomManager.cs index d88fd68b20..ef0ceafd02 100644 --- a/osu.Game/Tests/Visual/OnlinePlay/TestRequestHandlingRoomManager.cs +++ b/osu.Game/Tests/Visual/OnlinePlay/TestRequestHandlingRoomManager.cs @@ -2,9 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; -using osu.Framework.Allocation; using osu.Game.Beatmaps; -using osu.Game.Online.API; using osu.Game.Online.Rooms; using osu.Game.Rulesets; using osu.Game.Screens.OnlinePlay.Components; @@ -21,14 +19,6 @@ namespace osu.Game.Tests.Visual.OnlinePlay private int currentRoomId; - private readonly TestRoomRequestsHandler handler = new TestRoomRequestsHandler(); - - [BackgroundDependencyLoader] - private void load(IAPIProvider api, OsuGameBase game) - { - ((DummyAPIAccess)api).HandleRequest = request => handler.HandleRequest(request, api.LocalUser.Value, game); - } - public override void JoinRoom(Room room, string password = null, Action onSuccess = null, Action onError = null) { JoinRoomRequested?.Invoke(room, password); From 676070946c38f1bfa79ce77b902a747f332c5bc6 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 27 Oct 2021 15:25:26 +0900 Subject: [PATCH 126/260] Fix missed base.SetUpSteps() --- .../Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs index eff107faee..10633f4a4f 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs @@ -43,6 +43,8 @@ namespace osu.Game.Tests.Visual.Multiplayer [SetUpSteps] public override void SetUpSteps() { + base.SetUpSteps(); + AddStep("set local user", () => ((DummyAPIAccess)API).LocalUser.Value = LookupCache.GetUserAsync(1).Result); AddStep("create leaderboard", () => From f2683639242e18f474749df7cdaaf2182337bc9f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 Oct 2021 15:20:01 +0900 Subject: [PATCH 127/260] Update `UpdateableBeatmapBackgroundSprite` to accept `IBeatmapInfo` --- .../UpdateableBeatmapBackgroundSprite.cs | 19 ++++++++++--------- .../OnlinePlay/DrawableRoomPlaylistItem.cs | 9 +++++++-- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/osu.Game/Beatmaps/Drawables/UpdateableBeatmapBackgroundSprite.cs b/osu.Game/Beatmaps/Drawables/UpdateableBeatmapBackgroundSprite.cs index 8943ad350e..636c568bd0 100644 --- a/osu.Game/Beatmaps/Drawables/UpdateableBeatmapBackgroundSprite.cs +++ b/osu.Game/Beatmaps/Drawables/UpdateableBeatmapBackgroundSprite.cs @@ -12,9 +12,9 @@ namespace osu.Game.Beatmaps.Drawables /// /// Display a beatmap background from a local source, but fallback to online source if not available. /// - public class UpdateableBeatmapBackgroundSprite : ModelBackedDrawable + public class UpdateableBeatmapBackgroundSprite : ModelBackedDrawable { - public readonly Bindable Beatmap = new Bindable(); + public readonly Bindable Beatmap = new Bindable(); protected override double LoadDelay => 500; @@ -39,7 +39,7 @@ namespace osu.Game.Beatmaps.Drawables protected override double TransformDuration => 400; - protected override Drawable CreateDrawable(BeatmapInfo model) + protected override Drawable CreateDrawable(IBeatmapInfo model) { var drawable = getDrawableForModel(model); drawable.RelativeSizeAxes = Axes.Both; @@ -50,15 +50,16 @@ namespace osu.Game.Beatmaps.Drawables return drawable; } - private Drawable getDrawableForModel(BeatmapInfo model) + private Drawable getDrawableForModel(IBeatmapInfo model) { // prefer online cover where available. - if (model?.BeatmapSet?.OnlineInfo != null) - return new OnlineBeatmapSetCover(model.BeatmapSet, beatmapSetCoverType); + if (model?.BeatmapSet is IBeatmapSetOnlineInfo online) + return new OnlineBeatmapSetCover(online, beatmapSetCoverType); - return model?.ID > 0 - ? new BeatmapBackgroundSprite(beatmaps.GetWorkingBeatmap(model)) - : new BeatmapBackgroundSprite(beatmaps.DefaultBeatmap); + if (model is BeatmapInfo localModel) + return new BeatmapBackgroundSprite(beatmaps.GetWorkingBeatmap(localModel)); + + return new BeatmapBackgroundSprite(beatmaps.DefaultBeatmap); } } } diff --git a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs index 585b024623..264d49849c 100644 --- a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs +++ b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs @@ -333,13 +333,14 @@ namespace osu.Game.Screens.OnlinePlay public PanelBackground() { + UpdateableBeatmapBackgroundSprite backgroundSprite; + InternalChildren = new Drawable[] { - new UpdateableBeatmapBackgroundSprite + backgroundSprite = new UpdateableBeatmapBackgroundSprite { RelativeSizeAxes = Axes.Both, FillMode = FillMode.Fill, - Beatmap = { BindTarget = Beatmap } }, new FillFlowContainer { @@ -374,6 +375,10 @@ namespace osu.Game.Screens.OnlinePlay } } }; + + // manual binding required as playlists don't expose IBeatmapInfo currently. + // may be removed in the future if this changes. + Beatmap.BindValueChanged(beatmap => backgroundSprite.Beatmap.Value = beatmap.NewValue); } } } From d19580cf6023e336b26c354c64edc63c7326a8a6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 25 Oct 2021 13:47:12 +0900 Subject: [PATCH 128/260] Fix incorrectly changed difficulty count in recently updated test --- osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs index 7042f1e4fe..c7a065fdd7 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs @@ -90,7 +90,7 @@ namespace osu.Game.Tests.Visual.Online { AddAssert("is visible", () => overlay.State.Value == Visibility.Visible); - AddStep("show many results", () => fetchFor(Enumerable.Repeat(CreateAPIBeatmapSet(Ruleset.Value), 10).ToArray())); + AddStep("show many results", () => fetchFor(Enumerable.Repeat(CreateAPIBeatmapSet(Ruleset.Value), 100).ToArray())); AddUntilStep("placeholder hidden", () => !overlay.ChildrenOfType().Any(d => d.IsPresent)); From 40d963fc8a115631bab1c7f0d1ba3ced61a0b456 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 Oct 2021 12:44:14 +0900 Subject: [PATCH 129/260] Allow setting of `APIBeatmap.Length` (and don't serialise twice) --- osu.Game/Online/API/Requests/Responses/APIBeatmap.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs b/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs index 0945ad30b4..e65dca752b 100644 --- a/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs +++ b/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs @@ -54,10 +54,15 @@ namespace osu.Game.Online.API.Requests.Responses [JsonProperty(@"accuracy")] private float overallDifficulty { get; set; } - public double Length => TimeSpan.FromSeconds(lengthInSeconds).TotalMilliseconds; + [JsonIgnore] + public double Length { get; set; } [JsonProperty(@"total_length")] - private double lengthInSeconds { get; set; } + private double lengthInSeconds + { + get => TimeSpan.FromMilliseconds(Length).TotalSeconds; + set => Length = TimeSpan.FromSeconds(value).TotalMilliseconds; + } [JsonProperty(@"count_circles")] public int CircleCount { get; set; } From db6343c44caf3d3f46ba9acfd3dc05c9e913dd17 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 Oct 2021 17:09:51 +0900 Subject: [PATCH 130/260] Correctly redirect `BPM` from API to interface --- osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs b/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs index 83f04fb5f2..d8efa20b39 100644 --- a/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs +++ b/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs @@ -164,7 +164,7 @@ namespace osu.Game.Online.API.Requests.Responses IEnumerable IBeatmapSetInfo.Files => throw new NotImplementedException(); double IBeatmapSetInfo.MaxStarDifficulty => throw new NotImplementedException(); double IBeatmapSetInfo.MaxLength => throw new NotImplementedException(); - double IBeatmapSetInfo.MaxBPM => throw new NotImplementedException(); + double IBeatmapSetInfo.MaxBPM => BPM; #endregion } From 31306163003dc0ea62e9165a9987b9196af1407d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 Oct 2021 17:22:23 +0900 Subject: [PATCH 131/260] Update tournament components to use `APIBeatmap` directly --- .../TestSceneTournamentBeatmapPanel.cs | 6 ++--- .../TestSceneTournamentModDisplay.cs | 13 +++++------ .../NonVisual/LadderInfoSerialisationTest.cs | 4 ++-- .../Screens/TestSceneMapPoolScreen.cs | 2 +- .../TournamentTestScene.cs | 22 +++++++++---------- osu.Game.Tournament/Components/SongBar.cs | 22 +++++++++---------- .../Components/TournamentBeatmapPanel.cs | 7 +++--- osu.Game.Tournament/IPC/FileBasedIPC.cs | 6 ++--- osu.Game.Tournament/IPC/MatchIPCInfo.cs | 4 ++-- osu.Game.Tournament/Models/RoundBeatmap.cs | 4 ++-- osu.Game.Tournament/Models/SeedingBeatmap.cs | 4 ++-- .../Screens/BeatmapInfoScreen.cs | 4 ++-- .../Screens/Editors/RoundEditorScreen.cs | 6 ++--- .../Screens/Editors/SeedingEditorScreen.cs | 6 ++--- .../Screens/MapPool/MapPoolScreen.cs | 10 ++++----- osu.Game.Tournament/TournamentGameBase.cs | 4 ++-- 16 files changed, 59 insertions(+), 65 deletions(-) diff --git a/osu.Game.Tournament.Tests/Components/TestSceneTournamentBeatmapPanel.cs b/osu.Game.Tournament.Tests/Components/TestSceneTournamentBeatmapPanel.cs index f9c553cb3f..8139387a96 100644 --- a/osu.Game.Tournament.Tests/Components/TestSceneTournamentBeatmapPanel.cs +++ b/osu.Game.Tournament.Tests/Components/TestSceneTournamentBeatmapPanel.cs @@ -3,7 +3,6 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; -using osu.Game.Beatmaps; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; @@ -23,14 +22,13 @@ namespace osu.Game.Tournament.Tests.Components [BackgroundDependencyLoader] private void load() { - var req = new GetBeatmapRequest(new BeatmapInfo { OnlineBeatmapID = 1091460 }); + var req = new GetBeatmapRequest(new APIBeatmap { OnlineID = 1091460 }); req.Success += success; api.Queue(req); } - private void success(APIBeatmap apiBeatmap) + private void success(APIBeatmap beatmap) { - var beatmap = apiBeatmap.ToBeatmapInfo(rulesets); Add(new TournamentBeatmapPanel(beatmap) { Anchor = Anchor.Centre, diff --git a/osu.Game.Tournament.Tests/Components/TestSceneTournamentModDisplay.cs b/osu.Game.Tournament.Tests/Components/TestSceneTournamentModDisplay.cs index 27eb55a9fb..3c22bdca03 100644 --- a/osu.Game.Tournament.Tests/Components/TestSceneTournamentModDisplay.cs +++ b/osu.Game.Tournament.Tests/Components/TestSceneTournamentModDisplay.cs @@ -4,12 +4,12 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Game.Beatmaps; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; using osu.Game.Rulesets; using osu.Game.Tournament.Components; +using osuTK; namespace osu.Game.Tournament.Tests.Components { @@ -23,12 +23,10 @@ namespace osu.Game.Tournament.Tests.Components private FillFlowContainer fillFlow; - private BeatmapInfo beatmapInfo; - [BackgroundDependencyLoader] private void load() { - var req = new GetBeatmapRequest(new BeatmapInfo { OnlineBeatmapID = 490154 }); + var req = new GetBeatmapRequest(new APIBeatmap { OnlineID = 490154 }); req.Success += success; api.Queue(req); @@ -38,18 +36,17 @@ namespace osu.Game.Tournament.Tests.Components Anchor = Anchor.Centre, Origin = Anchor.Centre, Direction = FillDirection.Full, - Spacing = new osuTK.Vector2(10) + Spacing = new Vector2(10) }); } - private void success(APIBeatmap apiBeatmap) + private void success(APIBeatmap beatmap) { - beatmapInfo = apiBeatmap.ToBeatmapInfo(rulesets); var mods = rulesets.GetRuleset(Ladder.Ruleset.Value.ID ?? 0).CreateInstance().AllMods; foreach (var mod in mods) { - fillFlow.Add(new TournamentBeatmapPanel(beatmapInfo, mod.Acronym) + fillFlow.Add(new TournamentBeatmapPanel(beatmap, mod.Acronym) { Anchor = Anchor.Centre, Origin = Anchor.Centre diff --git a/osu.Game.Tournament.Tests/NonVisual/LadderInfoSerialisationTest.cs b/osu.Game.Tournament.Tests/NonVisual/LadderInfoSerialisationTest.cs index 13cbcd3caf..2071512f60 100644 --- a/osu.Game.Tournament.Tests/NonVisual/LadderInfoSerialisationTest.cs +++ b/osu.Game.Tournament.Tests/NonVisual/LadderInfoSerialisationTest.cs @@ -44,8 +44,8 @@ namespace osu.Game.Tournament.Tests.NonVisual { Beatmaps = { - new RoundBeatmap { BeatmapInfo = TournamentTestScene.CreateSampleBeatmapInfo() }, - new RoundBeatmap { BeatmapInfo = TournamentTestScene.CreateSampleBeatmapInfo() }, + new RoundBeatmap { BeatmapInfo = TournamentTestScene.CreateSampleBeatmap() }, + new RoundBeatmap { BeatmapInfo = TournamentTestScene.CreateSampleBeatmap() }, } } }, diff --git a/osu.Game.Tournament.Tests/Screens/TestSceneMapPoolScreen.cs b/osu.Game.Tournament.Tests/Screens/TestSceneMapPoolScreen.cs index f4032fdd54..41fbf44179 100644 --- a/osu.Game.Tournament.Tests/Screens/TestSceneMapPoolScreen.cs +++ b/osu.Game.Tournament.Tests/Screens/TestSceneMapPoolScreen.cs @@ -132,7 +132,7 @@ namespace osu.Game.Tournament.Tests.Screens { Ladder.CurrentMatch.Value.Round.Value.Beatmaps.Add(new RoundBeatmap { - BeatmapInfo = CreateSampleBeatmapInfo(), + BeatmapInfo = CreateSampleBeatmap(), Mods = mods }); } diff --git a/osu.Game.Tournament.Tests/TournamentTestScene.cs b/osu.Game.Tournament.Tests/TournamentTestScene.cs index ce9fd91ff1..4f057286b6 100644 --- a/osu.Game.Tournament.Tests/TournamentTestScene.cs +++ b/osu.Game.Tournament.Tests/TournamentTestScene.cs @@ -7,7 +7,6 @@ using osu.Framework.Allocation; using osu.Framework.Platform; using osu.Framework.Testing; using osu.Framework.Utils; -using osu.Game.Beatmaps; using osu.Game.Online.API.Requests.Responses; using osu.Game.Rulesets; using osu.Game.Tests.Visual; @@ -74,19 +73,19 @@ namespace osu.Game.Tournament.Tests { new SeedingBeatmap { - BeatmapInfo = CreateSampleBeatmapInfo(), + BeatmapInfo = CreateSampleBeatmap(), Score = 12345672, Seed = { Value = 24 }, }, new SeedingBeatmap { - BeatmapInfo = CreateSampleBeatmapInfo(), + BeatmapInfo = CreateSampleBeatmap(), Score = 1234567, Seed = { Value = 12 }, }, new SeedingBeatmap { - BeatmapInfo = CreateSampleBeatmapInfo(), + BeatmapInfo = CreateSampleBeatmap(), Score = 1234567, Seed = { Value = 16 }, } @@ -100,19 +99,19 @@ namespace osu.Game.Tournament.Tests { new SeedingBeatmap { - BeatmapInfo = CreateSampleBeatmapInfo(), + BeatmapInfo = CreateSampleBeatmap(), Score = 234567, Seed = { Value = 3 }, }, new SeedingBeatmap { - BeatmapInfo = CreateSampleBeatmapInfo(), + BeatmapInfo = CreateSampleBeatmap(), Score = 234567, Seed = { Value = 6 }, }, new SeedingBeatmap { - BeatmapInfo = CreateSampleBeatmapInfo(), + BeatmapInfo = CreateSampleBeatmap(), Score = 234567, Seed = { Value = 12 }, } @@ -152,16 +151,15 @@ namespace osu.Game.Tournament.Tests } }; - public static BeatmapInfo CreateSampleBeatmapInfo() => - new BeatmapInfo + public static APIBeatmap CreateSampleBeatmap() => + new APIBeatmap { - Metadata = new BeatmapMetadata + BeatmapSet = new APIBeatmapSet { Title = "Test Title", Artist = "Test Artist", - ID = RNG.Next(0, 1000000) }, - OnlineInfo = new APIBeatmap(), + OnlineID = RNG.Next(0, 1000000), }; protected override ITestSceneTestRunner CreateRunner() => new TournamentTestSceneTestRunner(); diff --git a/osu.Game.Tournament/Components/SongBar.cs b/osu.Game.Tournament/Components/SongBar.cs index 60be7dec91..bdcb6f8ded 100644 --- a/osu.Game.Tournament/Components/SongBar.cs +++ b/osu.Game.Tournament/Components/SongBar.cs @@ -12,6 +12,7 @@ using osu.Game.Beatmaps; using osu.Game.Beatmaps.Legacy; using osu.Game.Extensions; using osu.Game.Graphics; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Rulesets; using osu.Game.Screens.Menu; using osuTK; @@ -21,16 +22,15 @@ namespace osu.Game.Tournament.Components { public class SongBar : CompositeDrawable { - private BeatmapInfo beatmapInfo; + private APIBeatmap beatmapInfo; public const float HEIGHT = 145 / 2f; [Resolved] private IBindable ruleset { get; set; } - public BeatmapInfo BeatmapInfo + public APIBeatmap BeatmapInfo { - get => beatmapInfo; set { if (beatmapInfo == value) @@ -101,12 +101,12 @@ namespace osu.Game.Tournament.Components return; } - double bpm = beatmapInfo.BeatmapSet.OnlineInfo.BPM; + double bpm = beatmapInfo.BPM; // TODO: check this works. double length = beatmapInfo.Length; string hardRockExtra = ""; string srExtra = ""; - float ar = beatmapInfo.BaseDifficulty.ApproachRate; + float ar = beatmapInfo.Difficulty.ApproachRate; if ((mods & LegacyMods.HardRock) > 0) { @@ -132,9 +132,9 @@ namespace osu.Game.Tournament.Components default: stats = new (string heading, string content)[] { - ("CS", $"{beatmapInfo.BaseDifficulty.CircleSize:0.#}{hardRockExtra}"), + ("CS", $"{beatmapInfo.Difficulty.CircleSize:0.#}{hardRockExtra}"), ("AR", $"{ar:0.#}{hardRockExtra}"), - ("OD", $"{beatmapInfo.BaseDifficulty.OverallDifficulty:0.#}{hardRockExtra}"), + ("OD", $"{beatmapInfo.Difficulty.OverallDifficulty:0.#}{hardRockExtra}"), }; break; @@ -142,15 +142,15 @@ namespace osu.Game.Tournament.Components case 3: stats = new (string heading, string content)[] { - ("OD", $"{beatmapInfo.BaseDifficulty.OverallDifficulty:0.#}{hardRockExtra}"), - ("HP", $"{beatmapInfo.BaseDifficulty.DrainRate:0.#}{hardRockExtra}") + ("OD", $"{beatmapInfo.Difficulty.OverallDifficulty:0.#}{hardRockExtra}"), + ("HP", $"{beatmapInfo.Difficulty.DrainRate:0.#}{hardRockExtra}") }; break; case 2: stats = new (string heading, string content)[] { - ("CS", $"{beatmapInfo.BaseDifficulty.CircleSize:0.#}{hardRockExtra}"), + ("CS", $"{beatmapInfo.Difficulty.CircleSize:0.#}{hardRockExtra}"), ("AR", $"{ar:0.#}"), }; break; @@ -186,7 +186,7 @@ namespace osu.Game.Tournament.Components Children = new Drawable[] { new DiffPiece(stats), - new DiffPiece(("Star Rating", $"{beatmapInfo.StarDifficulty:0.#}{srExtra}")) + new DiffPiece(("Star Rating", $"{beatmapInfo.StarRating:0.#}{srExtra}")) } }, new FillFlowContainer diff --git a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs index be29566e07..39fc1d0c0a 100644 --- a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs +++ b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs @@ -13,6 +13,7 @@ using osu.Framework.Graphics.Textures; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Tournament.Models; using osuTK.Graphics; @@ -20,7 +21,7 @@ namespace osu.Game.Tournament.Components { public class TournamentBeatmapPanel : CompositeDrawable { - public readonly IBeatmapInfo BeatmapInfo; + public readonly APIBeatmap BeatmapInfo; private readonly string mod; @@ -32,7 +33,7 @@ namespace osu.Game.Tournament.Components private readonly Bindable currentMatch = new Bindable(); private Box flash; - public TournamentBeatmapPanel(IBeatmapInfo beatmapInfo, string mod = null) + public TournamentBeatmapPanel(APIBeatmap beatmapInfo, string mod = null) { if (beatmapInfo == null) throw new ArgumentNullException(nameof(beatmapInfo)); @@ -62,7 +63,7 @@ namespace osu.Game.Tournament.Components { RelativeSizeAxes = Axes.Both, Colour = OsuColour.Gray(0.5f), - BeatmapSet = BeatmapInfo.BeatmapSet as IBeatmapSetOnlineInfo, + BeatmapSet = BeatmapInfo.BeatmapSet, }, new FillFlowContainer { diff --git a/osu.Game.Tournament/IPC/FileBasedIPC.cs b/osu.Game.Tournament/IPC/FileBasedIPC.cs index 6dde265bd6..15aa9dd588 100644 --- a/osu.Game.Tournament/IPC/FileBasedIPC.cs +++ b/osu.Game.Tournament/IPC/FileBasedIPC.cs @@ -11,10 +11,10 @@ using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Logging; using osu.Framework.Platform; using osu.Framework.Threading; -using osu.Game.Beatmaps; using osu.Game.Beatmaps.Legacy; using osu.Game.Online.API; using osu.Game.Online.API.Requests; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Rulesets; using osu.Game.Tournament.Models; @@ -93,8 +93,8 @@ namespace osu.Game.Tournament.IPC Beatmap.Value = existing.BeatmapInfo; else { - beatmapLookupRequest = new GetBeatmapRequest(new BeatmapInfo { OnlineBeatmapID = beatmapId }); - beatmapLookupRequest.Success += b => Beatmap.Value = b.ToBeatmapInfo(Rulesets); + beatmapLookupRequest = new GetBeatmapRequest(new APIBeatmap { OnlineID = beatmapId }); + beatmapLookupRequest.Success += b => Beatmap.Value = b; API.Queue(beatmapLookupRequest); } } diff --git a/osu.Game.Tournament/IPC/MatchIPCInfo.cs b/osu.Game.Tournament/IPC/MatchIPCInfo.cs index 701258c6c7..fa7079b824 100644 --- a/osu.Game.Tournament/IPC/MatchIPCInfo.cs +++ b/osu.Game.Tournament/IPC/MatchIPCInfo.cs @@ -3,14 +3,14 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; -using osu.Game.Beatmaps; using osu.Game.Beatmaps.Legacy; +using osu.Game.Online.API.Requests.Responses; namespace osu.Game.Tournament.IPC { public class MatchIPCInfo : Component { - public Bindable Beatmap { get; } = new Bindable(); + public Bindable Beatmap { get; } = new Bindable(); public Bindable Mods { get; } = new Bindable(); public Bindable State { get; } = new Bindable(); public Bindable ChatChannel { get; } = new Bindable(); diff --git a/osu.Game.Tournament/Models/RoundBeatmap.cs b/osu.Game.Tournament/Models/RoundBeatmap.cs index 5d43d0ca66..8f1968236e 100644 --- a/osu.Game.Tournament/Models/RoundBeatmap.cs +++ b/osu.Game.Tournament/Models/RoundBeatmap.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. -using osu.Game.Beatmaps; +using osu.Game.Online.API.Requests.Responses; namespace osu.Game.Tournament.Models { @@ -10,6 +10,6 @@ namespace osu.Game.Tournament.Models public int ID; public string Mods; - public BeatmapInfo BeatmapInfo; + public APIBeatmap BeatmapInfo; } } diff --git a/osu.Game.Tournament/Models/SeedingBeatmap.cs b/osu.Game.Tournament/Models/SeedingBeatmap.cs index 2cd6fa7188..37a8a92546 100644 --- a/osu.Game.Tournament/Models/SeedingBeatmap.cs +++ b/osu.Game.Tournament/Models/SeedingBeatmap.cs @@ -2,7 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Bindables; -using osu.Game.Beatmaps; +using osu.Game.Online.API.Requests.Responses; namespace osu.Game.Tournament.Models { @@ -10,7 +10,7 @@ namespace osu.Game.Tournament.Models { public int ID; - public BeatmapInfo BeatmapInfo; + public APIBeatmap BeatmapInfo; public long Score; diff --git a/osu.Game.Tournament/Screens/BeatmapInfoScreen.cs b/osu.Game.Tournament/Screens/BeatmapInfoScreen.cs index b94b164116..50324d9c83 100644 --- a/osu.Game.Tournament/Screens/BeatmapInfoScreen.cs +++ b/osu.Game.Tournament/Screens/BeatmapInfoScreen.cs @@ -4,8 +4,8 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; -using osu.Game.Beatmaps; using osu.Game.Beatmaps.Legacy; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Tournament.Components; using osu.Game.Tournament.IPC; @@ -37,7 +37,7 @@ namespace osu.Game.Tournament.Screens SongBar.Mods = mods.NewValue; } - private void beatmapChanged(ValueChangedEvent beatmap) + private void beatmapChanged(ValueChangedEvent beatmap) { SongBar.FadeInFromZero(300, Easing.OutQuint); SongBar.BeatmapInfo = beatmap.NewValue; diff --git a/osu.Game.Tournament/Screens/Editors/RoundEditorScreen.cs b/osu.Game.Tournament/Screens/Editors/RoundEditorScreen.cs index 1d8c4e7476..2f77209d57 100644 --- a/osu.Game.Tournament/Screens/Editors/RoundEditorScreen.cs +++ b/osu.Game.Tournament/Screens/Editors/RoundEditorScreen.cs @@ -7,10 +7,10 @@ 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.Online.API; using osu.Game.Online.API.Requests; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays.Settings; using osu.Game.Rulesets; using osu.Game.Tournament.Components; @@ -234,11 +234,11 @@ namespace osu.Game.Tournament.Screens.Editors return; } - var req = new GetBeatmapRequest(new BeatmapInfo { OnlineBeatmapID = Model.ID }); + var req = new GetBeatmapRequest(new APIBeatmap { OnlineID = Model.ID }); req.Success += res => { - Model.BeatmapInfo = res.ToBeatmapInfo(rulesets); + Model.BeatmapInfo = res; updatePanel(); }; diff --git a/osu.Game.Tournament/Screens/Editors/SeedingEditorScreen.cs b/osu.Game.Tournament/Screens/Editors/SeedingEditorScreen.cs index d5b55823a5..c17939e040 100644 --- a/osu.Game.Tournament/Screens/Editors/SeedingEditorScreen.cs +++ b/osu.Game.Tournament/Screens/Editors/SeedingEditorScreen.cs @@ -7,10 +7,10 @@ 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.Online.API; using osu.Game.Online.API.Requests; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays.Settings; using osu.Game.Rulesets; using osu.Game.Tournament.Components; @@ -242,11 +242,11 @@ namespace osu.Game.Tournament.Screens.Editors return; } - var req = new GetBeatmapRequest(new BeatmapInfo { OnlineBeatmapID = Model.ID }); + var req = new GetBeatmapRequest(new APIBeatmap { OnlineID = Model.ID }); req.Success += res => { - Model.BeatmapInfo = res.ToBeatmapInfo(rulesets); + Model.BeatmapInfo = res; updatePanel(); }; diff --git a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs index 5f6546c303..a2432d7b5e 100644 --- a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs +++ b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs @@ -8,8 +8,8 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input.Events; using osu.Framework.Threading; -using osu.Game.Beatmaps; using osu.Game.Graphics.UserInterface; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Tournament.Components; using osu.Game.Tournament.IPC; using osu.Game.Tournament.Models; @@ -105,14 +105,14 @@ namespace osu.Game.Tournament.Screens.MapPool ipc.Beatmap.BindValueChanged(beatmapChanged); } - private void beatmapChanged(ValueChangedEvent beatmap) + private void beatmapChanged(ValueChangedEvent beatmap) { if (CurrentMatch.Value == null || CurrentMatch.Value.PicksBans.Count(p => p.Type == ChoiceType.Ban) < 2) return; // if bans have already been placed, beatmap changes result in a selection being made autoamtically - if (beatmap.NewValue.OnlineBeatmapID != null) - addForBeatmap(beatmap.NewValue.OnlineBeatmapID.Value); + if (beatmap.NewValue.OnlineID > 0) + addForBeatmap(beatmap.NewValue.OnlineID); } private void setMode(TeamColour colour, ChoiceType choiceType) @@ -179,7 +179,7 @@ namespace osu.Game.Tournament.Screens.MapPool if (CurrentMatch.Value == null) return; - if (CurrentMatch.Value.Round.Value.Beatmaps.All(b => b.BeatmapInfo.OnlineBeatmapID != beatmapId)) + if (CurrentMatch.Value.Round.Value.Beatmaps.All(b => b.BeatmapInfo.OnlineID != beatmapId)) // don't attempt to add if the beatmap isn't in our pool return; diff --git a/osu.Game.Tournament/TournamentGameBase.cs b/osu.Game.Tournament/TournamentGameBase.cs index 978be720df..5f7c75f9a7 100644 --- a/osu.Game.Tournament/TournamentGameBase.cs +++ b/osu.Game.Tournament/TournamentGameBase.cs @@ -190,7 +190,7 @@ namespace osu.Game.Tournament { var req = new GetBeatmapRequest(new BeatmapInfo { OnlineBeatmapID = b.ID }); API.Perform(req); - b.BeatmapInfo = req.Response?.ToBeatmapInfo(RulesetStore); + b.BeatmapInfo = req.Response; addedInfo = true; } @@ -211,7 +211,7 @@ namespace osu.Game.Tournament { var req = new GetBeatmapRequest(new BeatmapInfo { OnlineBeatmapID = b.ID }); req.Perform(API); - b.BeatmapInfo = req.Response?.ToBeatmapInfo(RulesetStore); + b.BeatmapInfo = req.Response; addedInfo = true; } From 35168948fe871b2e32813e50a72119f36fd5f2a8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 Oct 2021 17:45:46 +0900 Subject: [PATCH 132/260] Fix `PopulateUser` not populating immediately when `immediate` is specified (due to scheduling) --- osu.Game.Tournament/TournamentGameBase.cs | 37 +++++++++++++++-------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/osu.Game.Tournament/TournamentGameBase.cs b/osu.Game.Tournament/TournamentGameBase.cs index 5f7c75f9a7..fd31c325b4 100644 --- a/osu.Game.Tournament/TournamentGameBase.cs +++ b/osu.Game.Tournament/TournamentGameBase.cs @@ -226,8 +226,30 @@ namespace osu.Game.Tournament { var req = new GetUserRequest(user.Id, Ruleset.Value); - req.Success += res => + if (immediate) { + API.Perform(req); + populate(); + } + else + { + req.Success += res => { populate(); }; + req.Failure += _ => + { + user.Id = 1; + failure?.Invoke(); + }; + + API.Queue(req); + } + + void populate() + { + var res = req.Response; + + if (res == null) + return; + user.Id = res.Id; user.Username = res.Username; @@ -236,18 +258,7 @@ namespace osu.Game.Tournament user.Cover = res.Cover; success?.Invoke(); - }; - - req.Failure += _ => - { - user.Id = 1; - failure?.Invoke(); - }; - - if (immediate) - API.Perform(req); - else - API.Queue(req); + } } protected override void LoadComplete() From 5448b94039b024884fec0a3efbfbab32cdc11217 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 Oct 2021 18:20:10 +0900 Subject: [PATCH 133/260] Change repopulation rules to cover the fact that the serisalisation model has changed This also adds a progress display to show how repopulation is going. It can take a while on a large bracket. --- osu.Game.Tournament/TournamentGameBase.cs | 125 +++++++++++++--------- 1 file changed, 72 insertions(+), 53 deletions(-) diff --git a/osu.Game.Tournament/TournamentGameBase.cs b/osu.Game.Tournament/TournamentGameBase.cs index fd31c325b4..5748315418 100644 --- a/osu.Game.Tournament/TournamentGameBase.cs +++ b/osu.Game.Tournament/TournamentGameBase.cs @@ -7,12 +7,14 @@ using System.Linq; using System.Threading.Tasks; using Newtonsoft.Json; using osu.Framework.Allocation; +using osu.Framework.Graphics; using osu.Framework.Graphics.Textures; using osu.Framework.Input; using osu.Framework.IO.Stores; using osu.Framework.Platform; -using osu.Game.Beatmaps; +using osu.Game.Graphics; using osu.Game.Online.API.Requests; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Tournament.IO; using osu.Game.Tournament.IPC; using osu.Game.Tournament.Models; @@ -39,9 +41,18 @@ namespace osu.Game.Tournament return dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); } + private TournamentSpriteText initialisationText; + [BackgroundDependencyLoader] private void load(Storage baseStorage) { + AddInternal(initialisationText = new TournamentSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Font = OsuFont.Torus.With(size: 32), + }); + Resources.AddStore(new DllResourceStore(typeof(TournamentGameBase).Assembly)); dependencies.CacheAs(storage = new TournamentStorage(baseStorage)); @@ -123,7 +134,8 @@ namespace osu.Game.Tournament } addedInfo |= addPlayers(); - addedInfo |= addBeatmaps(); + addedInfo |= addRoundBeatmaps(); + addedInfo |= addSeedingBeatmaps(); if (addedInfo) SaveChanges(); @@ -145,6 +157,8 @@ namespace osu.Game.Tournament Add(ipc); taskCompletionSource.SetResult(true); + + initialisationText.Expire(); }); } @@ -153,75 +167,80 @@ namespace osu.Game.Tournament /// private bool addPlayers() { - bool addedInfo = false; + var playersRequiringPopulation = ladder.Teams + .SelectMany(t => t.Players) + .Where(p => string.IsNullOrEmpty(p.Username) + || p.Statistics?.GlobalRank == null + || p.Statistics?.CountryRank == null).ToList(); - foreach (var t in ladder.Teams) + if (playersRequiringPopulation.Count == 0) + return false; + + for (int i = 0; i < playersRequiringPopulation.Count; i++) { - foreach (var p in t.Players) - { - if (string.IsNullOrEmpty(p.Username) - || p.Statistics?.GlobalRank == null - || p.Statistics?.CountryRank == null) - { - PopulateUser(p, immediate: true); - addedInfo = true; - } - } + var p = playersRequiringPopulation[i]; + PopulateUser(p, immediate: true); + updateLoadProgressMessage($"Populating user stats ({i} / {playersRequiringPopulation.Count})"); } - return addedInfo; + return true; } /// /// Add missing beatmap info based on beatmap IDs /// - private bool addBeatmaps() + private bool addRoundBeatmaps() { - bool addedInfo = false; + var beatmapsRequiringPopulation = ladder.Rounds + .SelectMany(r => r.Beatmaps) + .Where(b => string.IsNullOrEmpty(b.BeatmapInfo?.BeatmapSet?.Title) && b.ID > 0).ToList(); - foreach (var r in ladder.Rounds) + if (beatmapsRequiringPopulation.Count == 0) + return false; + + for (int i = 0; i < beatmapsRequiringPopulation.Count; i++) { - foreach (var b in r.Beatmaps.ToList()) - { - if (b.BeatmapInfo != null) - continue; + var b = beatmapsRequiringPopulation[i]; - if (b.ID > 0) - { - var req = new GetBeatmapRequest(new BeatmapInfo { OnlineBeatmapID = b.ID }); - API.Perform(req); - b.BeatmapInfo = req.Response; + var req = new GetBeatmapRequest(new APIBeatmap { OnlineID = b.ID }); + API.Perform(req); + b.BeatmapInfo = req.Response ?? new APIBeatmap(); - addedInfo = true; - } - - if (b.BeatmapInfo == null) - // if online population couldn't be performed, ensure we don't leave a null value behind - r.Beatmaps.Remove(b); - } + updateLoadProgressMessage($"Populating round beatmaps ({i} / {beatmapsRequiringPopulation.Count})"); } - foreach (var t in ladder.Teams) - { - foreach (var s in t.SeedingResults) - { - foreach (var b in s.Beatmaps) - { - if (b.BeatmapInfo == null && b.ID > 0) - { - var req = new GetBeatmapRequest(new BeatmapInfo { OnlineBeatmapID = b.ID }); - req.Perform(API); - b.BeatmapInfo = req.Response; - - addedInfo = true; - } - } - } - } - - return addedInfo; + return true; } + /// + /// Add missing beatmap info based on beatmap IDs + /// + private bool addSeedingBeatmaps() + { + var beatmapsRequiringPopulation = ladder.Teams + .SelectMany(r => r.SeedingResults) + .SelectMany(r => r.Beatmaps) + .Where(b => string.IsNullOrEmpty(b.BeatmapInfo?.BeatmapSet?.Title) && b.ID > 0).ToList(); + + if (beatmapsRequiringPopulation.Count == 0) + return false; + + for (int i = 0; i < beatmapsRequiringPopulation.Count; i++) + { + var b = beatmapsRequiringPopulation[i]; + + var req = new GetBeatmapRequest(new APIBeatmap { OnlineID = b.ID }); + API.Perform(req); + b.BeatmapInfo = req.Response ?? new APIBeatmap(); + + updateLoadProgressMessage($"Populating seeding beatmaps ({i} / {beatmapsRequiringPopulation.Count})"); + } + + return true; + } + + private void updateLoadProgressMessage(string s) => Schedule(() => initialisationText.Text = s); + public void PopulateUser(User user, Action success = null, Action failure = null, bool immediate = false) { var req = new GetUserRequest(user.Id, Ruleset.Value); From f1499641f3d12789dfaf185b4aa2ad29352052e8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 Oct 2021 18:25:23 +0900 Subject: [PATCH 134/260] Rename all usages of `APIBeatmap` to `beatmap` in tournament namespace --- .../NonVisual/LadderInfoSerialisationTest.cs | 4 +-- .../Screens/TestSceneMapPoolScreen.cs | 2 +- .../TournamentTestScene.cs | 12 ++++---- osu.Game.Tournament/Components/SongBar.cs | 30 +++++++++---------- .../Components/TournamentBeatmapPanel.cs | 18 +++++------ osu.Game.Tournament/IPC/FileBasedIPC.cs | 4 +-- osu.Game.Tournament/Models/RoundBeatmap.cs | 4 ++- osu.Game.Tournament/Models/SeedingBeatmap.cs | 4 ++- .../Screens/BeatmapInfoScreen.cs | 2 +- .../Screens/Editors/RoundEditorScreen.cs | 12 ++++---- .../Screens/Editors/SeedingEditorScreen.cs | 12 ++++---- .../Screens/MapPool/MapPoolScreen.cs | 10 +++---- .../Screens/TeamIntro/SeedingScreen.cs | 4 +-- osu.Game.Tournament/TournamentGameBase.cs | 8 ++--- 14 files changed, 65 insertions(+), 61 deletions(-) diff --git a/osu.Game.Tournament.Tests/NonVisual/LadderInfoSerialisationTest.cs b/osu.Game.Tournament.Tests/NonVisual/LadderInfoSerialisationTest.cs index 2071512f60..8bdf909af3 100644 --- a/osu.Game.Tournament.Tests/NonVisual/LadderInfoSerialisationTest.cs +++ b/osu.Game.Tournament.Tests/NonVisual/LadderInfoSerialisationTest.cs @@ -44,8 +44,8 @@ namespace osu.Game.Tournament.Tests.NonVisual { Beatmaps = { - new RoundBeatmap { BeatmapInfo = TournamentTestScene.CreateSampleBeatmap() }, - new RoundBeatmap { BeatmapInfo = TournamentTestScene.CreateSampleBeatmap() }, + new RoundBeatmap { Beatmap = TournamentTestScene.CreateSampleBeatmap() }, + new RoundBeatmap { Beatmap = TournamentTestScene.CreateSampleBeatmap() }, } } }, diff --git a/osu.Game.Tournament.Tests/Screens/TestSceneMapPoolScreen.cs b/osu.Game.Tournament.Tests/Screens/TestSceneMapPoolScreen.cs index 41fbf44179..f732c5582b 100644 --- a/osu.Game.Tournament.Tests/Screens/TestSceneMapPoolScreen.cs +++ b/osu.Game.Tournament.Tests/Screens/TestSceneMapPoolScreen.cs @@ -132,7 +132,7 @@ namespace osu.Game.Tournament.Tests.Screens { Ladder.CurrentMatch.Value.Round.Value.Beatmaps.Add(new RoundBeatmap { - BeatmapInfo = CreateSampleBeatmap(), + Beatmap = CreateSampleBeatmap(), Mods = mods }); } diff --git a/osu.Game.Tournament.Tests/TournamentTestScene.cs b/osu.Game.Tournament.Tests/TournamentTestScene.cs index 4f057286b6..81741a43a9 100644 --- a/osu.Game.Tournament.Tests/TournamentTestScene.cs +++ b/osu.Game.Tournament.Tests/TournamentTestScene.cs @@ -73,19 +73,19 @@ namespace osu.Game.Tournament.Tests { new SeedingBeatmap { - BeatmapInfo = CreateSampleBeatmap(), + Beatmap = CreateSampleBeatmap(), Score = 12345672, Seed = { Value = 24 }, }, new SeedingBeatmap { - BeatmapInfo = CreateSampleBeatmap(), + Beatmap = CreateSampleBeatmap(), Score = 1234567, Seed = { Value = 12 }, }, new SeedingBeatmap { - BeatmapInfo = CreateSampleBeatmap(), + Beatmap = CreateSampleBeatmap(), Score = 1234567, Seed = { Value = 16 }, } @@ -99,19 +99,19 @@ namespace osu.Game.Tournament.Tests { new SeedingBeatmap { - BeatmapInfo = CreateSampleBeatmap(), + Beatmap = CreateSampleBeatmap(), Score = 234567, Seed = { Value = 3 }, }, new SeedingBeatmap { - BeatmapInfo = CreateSampleBeatmap(), + Beatmap = CreateSampleBeatmap(), Score = 234567, Seed = { Value = 6 }, }, new SeedingBeatmap { - BeatmapInfo = CreateSampleBeatmap(), + Beatmap = CreateSampleBeatmap(), Score = 234567, Seed = { Value = 12 }, } diff --git a/osu.Game.Tournament/Components/SongBar.cs b/osu.Game.Tournament/Components/SongBar.cs index bdcb6f8ded..9af73a2abb 100644 --- a/osu.Game.Tournament/Components/SongBar.cs +++ b/osu.Game.Tournament/Components/SongBar.cs @@ -22,21 +22,21 @@ namespace osu.Game.Tournament.Components { public class SongBar : CompositeDrawable { - private APIBeatmap beatmapInfo; + private APIBeatmap beatmap; public const float HEIGHT = 145 / 2f; [Resolved] private IBindable ruleset { get; set; } - public APIBeatmap BeatmapInfo + public APIBeatmap Beatmap { set { - if (beatmapInfo == value) + if (beatmap == value) return; - beatmapInfo = value; + beatmap = value; update(); } } @@ -95,18 +95,18 @@ namespace osu.Game.Tournament.Components private void update() { - if (beatmapInfo == null) + if (beatmap == null) { flow.Clear(); return; } - double bpm = beatmapInfo.BPM; // TODO: check this works. - double length = beatmapInfo.Length; + double bpm = beatmap.BPM; // TODO: check this works. + double length = beatmap.Length; string hardRockExtra = ""; string srExtra = ""; - float ar = beatmapInfo.Difficulty.ApproachRate; + float ar = beatmap.Difficulty.ApproachRate; if ((mods & LegacyMods.HardRock) > 0) { @@ -132,9 +132,9 @@ namespace osu.Game.Tournament.Components default: stats = new (string heading, string content)[] { - ("CS", $"{beatmapInfo.Difficulty.CircleSize:0.#}{hardRockExtra}"), + ("CS", $"{beatmap.Difficulty.CircleSize:0.#}{hardRockExtra}"), ("AR", $"{ar:0.#}{hardRockExtra}"), - ("OD", $"{beatmapInfo.Difficulty.OverallDifficulty:0.#}{hardRockExtra}"), + ("OD", $"{beatmap.Difficulty.OverallDifficulty:0.#}{hardRockExtra}"), }; break; @@ -142,15 +142,15 @@ namespace osu.Game.Tournament.Components case 3: stats = new (string heading, string content)[] { - ("OD", $"{beatmapInfo.Difficulty.OverallDifficulty:0.#}{hardRockExtra}"), - ("HP", $"{beatmapInfo.Difficulty.DrainRate:0.#}{hardRockExtra}") + ("OD", $"{beatmap.Difficulty.OverallDifficulty:0.#}{hardRockExtra}"), + ("HP", $"{beatmap.Difficulty.DrainRate:0.#}{hardRockExtra}") }; break; case 2: stats = new (string heading, string content)[] { - ("CS", $"{beatmapInfo.Difficulty.CircleSize:0.#}{hardRockExtra}"), + ("CS", $"{beatmap.Difficulty.CircleSize:0.#}{hardRockExtra}"), ("AR", $"{ar:0.#}"), }; break; @@ -186,7 +186,7 @@ namespace osu.Game.Tournament.Components Children = new Drawable[] { new DiffPiece(stats), - new DiffPiece(("Star Rating", $"{beatmapInfo.StarRating:0.#}{srExtra}")) + new DiffPiece(("Star Rating", $"{beatmap.StarRating:0.#}{srExtra}")) } }, new FillFlowContainer @@ -229,7 +229,7 @@ namespace osu.Game.Tournament.Components } } }, - new TournamentBeatmapPanel(beatmapInfo) + new TournamentBeatmapPanel(beatmap) { RelativeSizeAxes = Axes.X, Width = 0.5f, diff --git a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs index 39fc1d0c0a..328b9278d5 100644 --- a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs +++ b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs @@ -21,7 +21,7 @@ namespace osu.Game.Tournament.Components { public class TournamentBeatmapPanel : CompositeDrawable { - public readonly APIBeatmap BeatmapInfo; + public readonly APIBeatmap Beatmap; private readonly string mod; @@ -33,11 +33,11 @@ namespace osu.Game.Tournament.Components private readonly Bindable currentMatch = new Bindable(); private Box flash; - public TournamentBeatmapPanel(APIBeatmap beatmapInfo, string mod = null) + public TournamentBeatmapPanel(APIBeatmap beatmap, string mod = null) { - if (beatmapInfo == null) throw new ArgumentNullException(nameof(beatmapInfo)); + if (beatmap == null) throw new ArgumentNullException(nameof(beatmap)); - BeatmapInfo = beatmapInfo; + Beatmap = beatmap; this.mod = mod; Width = 400; @@ -63,7 +63,7 @@ namespace osu.Game.Tournament.Components { RelativeSizeAxes = Axes.Both, Colour = OsuColour.Gray(0.5f), - BeatmapSet = BeatmapInfo.BeatmapSet, + BeatmapSet = Beatmap.BeatmapSet, }, new FillFlowContainer { @@ -76,7 +76,7 @@ namespace osu.Game.Tournament.Components { new TournamentSpriteText { - Text = BeatmapInfo.GetDisplayTitleRomanisable(false), + Text = Beatmap.GetDisplayTitleRomanisable(false), Font = OsuFont.Torus.With(weight: FontWeight.Bold), }, new FillFlowContainer @@ -93,7 +93,7 @@ namespace osu.Game.Tournament.Components }, new TournamentSpriteText { - Text = BeatmapInfo.Metadata?.Author, + Text = Beatmap.Metadata?.Author, Padding = new MarginPadding { Right = 20 }, Font = OsuFont.Torus.With(weight: FontWeight.Bold, size: 14) }, @@ -105,7 +105,7 @@ namespace osu.Game.Tournament.Components }, new TournamentSpriteText { - Text = BeatmapInfo.DifficultyName, + Text = Beatmap.DifficultyName, Font = OsuFont.Torus.With(weight: FontWeight.Bold, size: 14) }, } @@ -149,7 +149,7 @@ namespace osu.Game.Tournament.Components private void updateState() { - var found = currentMatch.Value.PicksBans.FirstOrDefault(p => p.BeatmapID == BeatmapInfo.OnlineID); + var found = currentMatch.Value.PicksBans.FirstOrDefault(p => p.BeatmapID == Beatmap.OnlineID); bool doFlash = found != choice; choice = found; diff --git a/osu.Game.Tournament/IPC/FileBasedIPC.cs b/osu.Game.Tournament/IPC/FileBasedIPC.cs index 15aa9dd588..a57f9fd691 100644 --- a/osu.Game.Tournament/IPC/FileBasedIPC.cs +++ b/osu.Game.Tournament/IPC/FileBasedIPC.cs @@ -87,10 +87,10 @@ namespace osu.Game.Tournament.IPC lastBeatmapId = beatmapId; - var existing = ladder.CurrentMatch.Value?.Round.Value?.Beatmaps.FirstOrDefault(b => b.ID == beatmapId && b.BeatmapInfo != null); + var existing = ladder.CurrentMatch.Value?.Round.Value?.Beatmaps.FirstOrDefault(b => b.ID == beatmapId && b.Beatmap != null); if (existing != null) - Beatmap.Value = existing.BeatmapInfo; + Beatmap.Value = existing.Beatmap; else { beatmapLookupRequest = new GetBeatmapRequest(new APIBeatmap { OnlineID = beatmapId }); diff --git a/osu.Game.Tournament/Models/RoundBeatmap.cs b/osu.Game.Tournament/Models/RoundBeatmap.cs index 8f1968236e..2fd79546f1 100644 --- a/osu.Game.Tournament/Models/RoundBeatmap.cs +++ b/osu.Game.Tournament/Models/RoundBeatmap.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 Newtonsoft.Json; using osu.Game.Online.API.Requests.Responses; namespace osu.Game.Tournament.Models @@ -10,6 +11,7 @@ namespace osu.Game.Tournament.Models public int ID; public string Mods; - public APIBeatmap BeatmapInfo; + [JsonProperty("BeatmapInfo")] + public APIBeatmap Beatmap; } } diff --git a/osu.Game.Tournament/Models/SeedingBeatmap.cs b/osu.Game.Tournament/Models/SeedingBeatmap.cs index 37a8a92546..26f3016ac8 100644 --- a/osu.Game.Tournament/Models/SeedingBeatmap.cs +++ b/osu.Game.Tournament/Models/SeedingBeatmap.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 Newtonsoft.Json; using osu.Framework.Bindables; using osu.Game.Online.API.Requests.Responses; @@ -10,7 +11,8 @@ namespace osu.Game.Tournament.Models { public int ID; - public APIBeatmap BeatmapInfo; + [JsonProperty("BeatmapInfo")] + public APIBeatmap Beatmap; public long Score; diff --git a/osu.Game.Tournament/Screens/BeatmapInfoScreen.cs b/osu.Game.Tournament/Screens/BeatmapInfoScreen.cs index 50324d9c83..77b816d24c 100644 --- a/osu.Game.Tournament/Screens/BeatmapInfoScreen.cs +++ b/osu.Game.Tournament/Screens/BeatmapInfoScreen.cs @@ -40,7 +40,7 @@ namespace osu.Game.Tournament.Screens private void beatmapChanged(ValueChangedEvent beatmap) { SongBar.FadeInFromZero(300, Easing.OutQuint); - SongBar.BeatmapInfo = beatmap.NewValue; + SongBar.Beatmap = beatmap.NewValue; } } } diff --git a/osu.Game.Tournament/Screens/Editors/RoundEditorScreen.cs b/osu.Game.Tournament/Screens/Editors/RoundEditorScreen.cs index 2f77209d57..f6bc607447 100644 --- a/osu.Game.Tournament/Screens/Editors/RoundEditorScreen.cs +++ b/osu.Game.Tournament/Screens/Editors/RoundEditorScreen.cs @@ -226,9 +226,9 @@ namespace osu.Game.Tournament.Screens.Editors Model.ID = id.NewValue ?? 0; if (id.NewValue != id.OldValue) - Model.BeatmapInfo = null; + Model.Beatmap = null; - if (Model.BeatmapInfo != null) + if (Model.Beatmap != null) { updatePanel(); return; @@ -238,13 +238,13 @@ namespace osu.Game.Tournament.Screens.Editors req.Success += res => { - Model.BeatmapInfo = res; + Model.Beatmap = res; updatePanel(); }; req.Failure += _ => { - Model.BeatmapInfo = null; + Model.Beatmap = null; updatePanel(); }; @@ -259,9 +259,9 @@ namespace osu.Game.Tournament.Screens.Editors { drawableContainer.Clear(); - if (Model.BeatmapInfo != null) + if (Model.Beatmap != null) { - drawableContainer.Child = new TournamentBeatmapPanel(Model.BeatmapInfo, Model.Mods) + drawableContainer.Child = new TournamentBeatmapPanel(Model.Beatmap, Model.Mods) { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, diff --git a/osu.Game.Tournament/Screens/Editors/SeedingEditorScreen.cs b/osu.Game.Tournament/Screens/Editors/SeedingEditorScreen.cs index c17939e040..9abf1d3adb 100644 --- a/osu.Game.Tournament/Screens/Editors/SeedingEditorScreen.cs +++ b/osu.Game.Tournament/Screens/Editors/SeedingEditorScreen.cs @@ -234,9 +234,9 @@ namespace osu.Game.Tournament.Screens.Editors Model.ID = id.NewValue ?? 0; if (id.NewValue != id.OldValue) - Model.BeatmapInfo = null; + Model.Beatmap = null; - if (Model.BeatmapInfo != null) + if (Model.Beatmap != null) { updatePanel(); return; @@ -246,13 +246,13 @@ namespace osu.Game.Tournament.Screens.Editors req.Success += res => { - Model.BeatmapInfo = res; + Model.Beatmap = res; updatePanel(); }; req.Failure += _ => { - Model.BeatmapInfo = null; + Model.Beatmap = null; updatePanel(); }; @@ -267,9 +267,9 @@ namespace osu.Game.Tournament.Screens.Editors { drawableContainer.Clear(); - if (Model.BeatmapInfo != null) + if (Model.Beatmap != null) { - drawableContainer.Child = new TournamentBeatmapPanel(Model.BeatmapInfo, result.Mod.Value) + drawableContainer.Child = new TournamentBeatmapPanel(Model.Beatmap, result.Mod.Value) { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, diff --git a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs index a2432d7b5e..3ae007f955 100644 --- a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs +++ b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs @@ -147,11 +147,11 @@ namespace osu.Game.Tournament.Screens.MapPool if (map != null) { - if (e.Button == MouseButton.Left && map.BeatmapInfo.OnlineID > 0) - addForBeatmap(map.BeatmapInfo.OnlineID); + if (e.Button == MouseButton.Left && map.Beatmap.OnlineID > 0) + addForBeatmap(map.Beatmap.OnlineID); else { - var existing = CurrentMatch.Value.PicksBans.FirstOrDefault(p => p.BeatmapID == map.BeatmapInfo.OnlineID); + var existing = CurrentMatch.Value.PicksBans.FirstOrDefault(p => p.BeatmapID == map.Beatmap.OnlineID); if (existing != null) { @@ -179,7 +179,7 @@ namespace osu.Game.Tournament.Screens.MapPool if (CurrentMatch.Value == null) return; - if (CurrentMatch.Value.Round.Value.Beatmaps.All(b => b.BeatmapInfo.OnlineID != beatmapId)) + if (CurrentMatch.Value.Round.Value.Beatmaps.All(b => b.Beatmap.OnlineID != beatmapId)) // don't attempt to add if the beatmap isn't in our pool return; @@ -245,7 +245,7 @@ namespace osu.Game.Tournament.Screens.MapPool flowCount = 1; } - currentFlow.Add(new TournamentBeatmapPanel(b.BeatmapInfo, b.Mods) + currentFlow.Add(new TournamentBeatmapPanel(b.Beatmap, b.Mods) { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, diff --git a/osu.Game.Tournament/Screens/TeamIntro/SeedingScreen.cs b/osu.Game.Tournament/Screens/TeamIntro/SeedingScreen.cs index 3a0bd232b0..d34a8583b9 100644 --- a/osu.Game.Tournament/Screens/TeamIntro/SeedingScreen.cs +++ b/osu.Game.Tournament/Screens/TeamIntro/SeedingScreen.cs @@ -141,9 +141,9 @@ namespace osu.Game.Tournament.Screens.TeamIntro Spacing = new Vector2(5), Children = new Drawable[] { - new TournamentSpriteText { Text = beatmap.BeatmapInfo.Metadata.Title, Colour = TournamentGame.TEXT_COLOUR, }, + new TournamentSpriteText { Text = beatmap.Beatmap.Metadata.Title, Colour = TournamentGame.TEXT_COLOUR, }, new TournamentSpriteText { Text = "by", Colour = TournamentGame.TEXT_COLOUR, Font = OsuFont.Torus.With(weight: FontWeight.Regular) }, - new TournamentSpriteText { Text = beatmap.BeatmapInfo.Metadata.Artist, Colour = TournamentGame.TEXT_COLOUR, Font = OsuFont.Torus.With(weight: FontWeight.Regular) }, + new TournamentSpriteText { Text = beatmap.Beatmap.Metadata.Artist, Colour = TournamentGame.TEXT_COLOUR, Font = OsuFont.Torus.With(weight: FontWeight.Regular) }, } }, new FillFlowContainer diff --git a/osu.Game.Tournament/TournamentGameBase.cs b/osu.Game.Tournament/TournamentGameBase.cs index 5748315418..4551192583 100644 --- a/osu.Game.Tournament/TournamentGameBase.cs +++ b/osu.Game.Tournament/TournamentGameBase.cs @@ -193,7 +193,7 @@ namespace osu.Game.Tournament { var beatmapsRequiringPopulation = ladder.Rounds .SelectMany(r => r.Beatmaps) - .Where(b => string.IsNullOrEmpty(b.BeatmapInfo?.BeatmapSet?.Title) && b.ID > 0).ToList(); + .Where(b => string.IsNullOrEmpty(b.Beatmap?.BeatmapSet?.Title) && b.ID > 0).ToList(); if (beatmapsRequiringPopulation.Count == 0) return false; @@ -204,7 +204,7 @@ namespace osu.Game.Tournament var req = new GetBeatmapRequest(new APIBeatmap { OnlineID = b.ID }); API.Perform(req); - b.BeatmapInfo = req.Response ?? new APIBeatmap(); + b.Beatmap = req.Response ?? new APIBeatmap(); updateLoadProgressMessage($"Populating round beatmaps ({i} / {beatmapsRequiringPopulation.Count})"); } @@ -220,7 +220,7 @@ namespace osu.Game.Tournament var beatmapsRequiringPopulation = ladder.Teams .SelectMany(r => r.SeedingResults) .SelectMany(r => r.Beatmaps) - .Where(b => string.IsNullOrEmpty(b.BeatmapInfo?.BeatmapSet?.Title) && b.ID > 0).ToList(); + .Where(b => string.IsNullOrEmpty(b.Beatmap?.BeatmapSet?.Title) && b.ID > 0).ToList(); if (beatmapsRequiringPopulation.Count == 0) return false; @@ -231,7 +231,7 @@ namespace osu.Game.Tournament var req = new GetBeatmapRequest(new APIBeatmap { OnlineID = b.ID }); API.Perform(req); - b.BeatmapInfo = req.Response ?? new APIBeatmap(); + b.Beatmap = req.Response ?? new APIBeatmap(); updateLoadProgressMessage($"Populating seeding beatmaps ({i} / {beatmapsRequiringPopulation.Count})"); } From f3dba49aae9794e22336c18020c16f8c1d3b0b0d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 27 Oct 2021 16:10:22 +0900 Subject: [PATCH 135/260] Rename room managers --- .../TestSceneLoungeRoomsContainer.cs | 2 +- .../Visual/Multiplayer/TestSceneMultiplayer.cs | 7 ++++--- .../TestSceneMultiplayerLoungeSubScreen.cs | 2 +- .../Visual/Multiplayer/TestSceneTeamVersus.cs | 4 ++-- .../Navigation/TestSceneScreenNavigation.cs | 4 ++-- .../TestScenePlaylistsLoungeSubScreen.cs | 2 +- .../IMultiplayerTestSceneDependencies.cs | 2 +- .../Visual/Multiplayer/MultiplayerTestScene.cs | 12 +++++------- .../MultiplayerTestSceneDependencies.cs | 4 ++-- .../Multiplayer/TestMultiplayerClient.cs | 4 ++-- ...anager.cs => TestMultiplayerRoomManager.cs} | 18 ++++++------------ .../Visual/OnlinePlay/OnlinePlayTestScene.cs | 8 +------- .../OnlinePlayTestSceneDependencies.cs | 5 ++++- ...ndlingRoomManager.cs => TestRoomManager.cs} | 2 +- 14 files changed, 33 insertions(+), 43 deletions(-) rename osu.Game/Tests/Visual/Multiplayer/{TestRequestHandlingMultiplayerRoomManager.cs => TestMultiplayerRoomManager.cs} (56%) rename osu.Game/Tests/Visual/OnlinePlay/{TestRequestHandlingRoomManager.cs => TestRoomManager.cs} (97%) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs index 1bdf3c2750..c3d5f7ec23 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs @@ -19,7 +19,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { public class TestSceneLoungeRoomsContainer : OnlinePlayTestScene { - protected new TestRequestHandlingRoomManager RoomManager => (TestRequestHandlingRoomManager)base.RoomManager; + protected new TestRoomManager RoomManager => (TestRoomManager)base.RoomManager; private RoomsContainer container; diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs index f4a72dd7e7..bb4603da69 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs @@ -4,6 +4,7 @@ using System; using System.Diagnostics; using System.Linq; +using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; @@ -48,7 +49,7 @@ namespace osu.Game.Tests.Visual.Multiplayer private TestMultiplayer multiplayerScreen; private TestMultiplayerClient client; - private TestRequestHandlingMultiplayerRoomManager roomManager => multiplayerScreen.RoomManager; + private TestMultiplayerRoomManager roomManager => multiplayerScreen.RoomManager; [Cached(typeof(UserLookupCache))] private UserLookupCache lookupCache = new TestUserLookupCache(); @@ -624,9 +625,9 @@ namespace osu.Game.Tests.Visual.Multiplayer private class TestMultiplayer : Screens.OnlinePlay.Multiplayer.Multiplayer { - public new TestRequestHandlingMultiplayerRoomManager RoomManager { get; private set; } + public new TestMultiplayerRoomManager RoomManager { get; private set; } - protected override RoomManager CreateRoomManager() => RoomManager = new TestRequestHandlingMultiplayerRoomManager(); + protected override RoomManager CreateRoomManager() => RoomManager = new TestMultiplayerRoomManager(); } } } diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs index b7da31a2b5..de3df754a2 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs @@ -18,7 +18,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { public class TestSceneMultiplayerLoungeSubScreen : OnlinePlayTestScene { - protected new TestRequestHandlingRoomManager RoomManager => (TestRequestHandlingRoomManager)base.RoomManager; + protected new TestRoomManager RoomManager => (TestRoomManager)base.RoomManager; private LoungeSubScreen loungeScreen; diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs index 80217a7726..7b34150610 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs @@ -184,9 +184,9 @@ namespace osu.Game.Tests.Visual.Multiplayer private class TestMultiplayer : Screens.OnlinePlay.Multiplayer.Multiplayer { - public new TestRequestHandlingMultiplayerRoomManager RoomManager { get; private set; } + public new TestMultiplayerRoomManager RoomManager { get; private set; } - protected override RoomManager CreateRoomManager() => RoomManager = new TestRequestHandlingMultiplayerRoomManager(); + protected override RoomManager CreateRoomManager() => RoomManager = new TestMultiplayerRoomManager(); } } } diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs index ce437e7299..87223a3eca 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs @@ -461,10 +461,10 @@ namespace osu.Game.Tests.Visual.Navigation public TestMultiplayer() { - Client = new TestMultiplayerClient((TestRequestHandlingMultiplayerRoomManager)RoomManager); + Client = new TestMultiplayerClient((TestMultiplayerRoomManager)RoomManager); } - protected override RoomManager CreateRoomManager() => new TestRequestHandlingMultiplayerRoomManager(); + protected override RoomManager CreateRoomManager() => new TestMultiplayerRoomManager(); } } } diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs index 5c248163d7..9ba0da1911 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsLoungeSubScreen.cs @@ -17,7 +17,7 @@ namespace osu.Game.Tests.Visual.Playlists { public class TestScenePlaylistsLoungeSubScreen : OnlinePlayTestScene { - protected new TestRequestHandlingRoomManager RoomManager => (TestRequestHandlingRoomManager)base.RoomManager; + protected new TestRoomManager RoomManager => (TestRoomManager)base.RoomManager; private TestLoungeSubScreen loungeScreen; diff --git a/osu.Game/Tests/Visual/Multiplayer/IMultiplayerTestSceneDependencies.cs b/osu.Game/Tests/Visual/Multiplayer/IMultiplayerTestSceneDependencies.cs index 3362ebbbd6..204c189591 100644 --- a/osu.Game/Tests/Visual/Multiplayer/IMultiplayerTestSceneDependencies.cs +++ b/osu.Game/Tests/Visual/Multiplayer/IMultiplayerTestSceneDependencies.cs @@ -22,7 +22,7 @@ namespace osu.Game.Tests.Visual.Multiplayer /// /// The cached . /// - new TestRequestHandlingMultiplayerRoomManager RoomManager { get; } + new TestMultiplayerRoomManager RoomManager { get; } /// /// The cached . diff --git a/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs b/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs index f259784170..c628541825 100644 --- a/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs +++ b/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestScene.cs @@ -18,7 +18,7 @@ namespace osu.Game.Tests.Visual.Multiplayer public const int PLAYER_2_ID = 56; public TestMultiplayerClient Client => OnlinePlayDependencies.Client; - public new TestRequestHandlingMultiplayerRoomManager RoomManager => OnlinePlayDependencies.RoomManager; + public new TestMultiplayerRoomManager RoomManager => OnlinePlayDependencies.RoomManager; public TestUserLookupCache LookupCache => OnlinePlayDependencies?.LookupCache; public TestSpectatorClient SpectatorClient => OnlinePlayDependencies?.SpectatorClient; @@ -35,12 +35,7 @@ namespace osu.Game.Tests.Visual.Multiplayer public new void Setup() => Schedule(() => { if (joinRoom) - { - var room = CreateRoom(); - - RoomManager.CreateRoom(room); - SelectedRoom.Value = room; - } + SelectedRoom.Value = CreateRoom(); }); protected virtual Room CreateRoom() @@ -64,7 +59,10 @@ namespace osu.Game.Tests.Visual.Multiplayer base.SetUpSteps(); if (joinRoom) + { + AddStep("join room", () => RoomManager.CreateRoom(SelectedRoom.Value)); AddUntilStep("wait for room join", () => Client.Room != null); + } } protected override OnlinePlayTestSceneDependencies CreateOnlinePlayDependencies() => new MultiplayerTestSceneDependencies(); diff --git a/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestSceneDependencies.cs b/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestSceneDependencies.cs index 2e13fb6a56..a2b0b066a7 100644 --- a/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestSceneDependencies.cs +++ b/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestSceneDependencies.cs @@ -18,7 +18,7 @@ namespace osu.Game.Tests.Visual.Multiplayer public TestMultiplayerClient Client { get; } public TestUserLookupCache LookupCache { get; } public TestSpectatorClient SpectatorClient { get; } - public new TestRequestHandlingMultiplayerRoomManager RoomManager => (TestRequestHandlingMultiplayerRoomManager)base.RoomManager; + public new TestMultiplayerRoomManager RoomManager => (TestMultiplayerRoomManager)base.RoomManager; public MultiplayerTestSceneDependencies() { @@ -31,7 +31,7 @@ namespace osu.Game.Tests.Visual.Multiplayer CacheAs(SpectatorClient); } - protected override IRoomManager CreateRoomManager() => new TestRequestHandlingMultiplayerRoomManager(); + protected override IRoomManager CreateRoomManager() => new TestMultiplayerRoomManager(); protected virtual TestSpectatorClient CreateSpectatorClient() => new TestSpectatorClient(); } diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs index 5e4e5942d9..cd0f070d73 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs @@ -39,9 +39,9 @@ namespace osu.Game.Tests.Visual.Multiplayer [Resolved] private BeatmapManager beatmaps { get; set; } = null!; - private readonly TestRequestHandlingMultiplayerRoomManager roomManager; + private readonly TestMultiplayerRoomManager roomManager; - public TestMultiplayerClient(TestRequestHandlingMultiplayerRoomManager roomManager) + public TestMultiplayerClient(TestMultiplayerRoomManager roomManager) { this.roomManager = roomManager; } diff --git a/osu.Game/Tests/Visual/Multiplayer/TestRequestHandlingMultiplayerRoomManager.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomManager.cs similarity index 56% rename from osu.Game/Tests/Visual/Multiplayer/TestRequestHandlingMultiplayerRoomManager.cs rename to osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomManager.cs index 5de518990a..fef2a0a16d 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestRequestHandlingMultiplayerRoomManager.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomManager.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using osu.Framework.Allocation; -using osu.Game.Online.API; using osu.Game.Online.Rooms; using osu.Game.Screens.OnlinePlay.Components; using osu.Game.Screens.OnlinePlay.Multiplayer; @@ -12,25 +11,20 @@ using osu.Game.Tests.Visual.OnlinePlay; namespace osu.Game.Tests.Visual.Multiplayer { /// - /// A for use in multiplayer test scenes, backed by a . + /// A for use in multiplayer test scenes. /// Should generally not be used by itself outside of a . /// - public class TestRequestHandlingMultiplayerRoomManager : MultiplayerRoomManager + public class TestMultiplayerRoomManager : MultiplayerRoomManager { - public IReadOnlyList ServerSideRooms => handler.ServerSideRooms; + [Resolved] + private TestRoomRequestsHandler requestsHandler { get; set; } - private readonly TestRoomRequestsHandler handler = new TestRoomRequestsHandler(); - - [BackgroundDependencyLoader] - private void load(IAPIProvider api, OsuGameBase game) - { - ((DummyAPIAccess)api).HandleRequest = request => handler.HandleRequest(request, api.LocalUser.Value, game); - } + public IReadOnlyList ServerSideRooms => requestsHandler.ServerSideRooms; /// /// Adds a room to a local "server-side" list that's returned when a is fired. /// /// The room. - public void AddServerSideRoom(Room room) => handler.AddServerSideRoom(room); + public void AddServerSideRoom(Room room) => requestsHandler.AddServerSideRoom(room); } } diff --git a/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestScene.cs b/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestScene.cs index d782160ee5..4771ace1c4 100644 --- a/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestScene.cs +++ b/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestScene.cs @@ -36,7 +36,6 @@ namespace osu.Game.Tests.Visual.OnlinePlay private readonly Container content; private readonly Container drawableDependenciesContainer; private DelegatedDependencyContainer dependencies; - private TestRoomRequestsHandler requestsHandler; protected OnlinePlayTestScene() { @@ -65,12 +64,7 @@ namespace osu.Game.Tests.Visual.OnlinePlay public override void SetUpSteps() { base.SetUpSteps(); - - AddStep("setup API", () => - { - requestsHandler = new TestRoomRequestsHandler(); - ((DummyAPIAccess)API).HandleRequest = request => requestsHandler.HandleRequest(request, API.LocalUser.Value, game); - }); + AddStep("setup API", () => ((DummyAPIAccess)API).HandleRequest = request => OnlinePlayDependencies.RequestsHandler.HandleRequest(request, API.LocalUser.Value, game)); } /// diff --git a/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestSceneDependencies.cs b/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestSceneDependencies.cs index defc971eef..9e5264fb12 100644 --- a/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestSceneDependencies.cs +++ b/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestSceneDependencies.cs @@ -21,6 +21,7 @@ namespace osu.Game.Tests.Visual.OnlinePlay public IRoomManager RoomManager { get; } public OngoingOperationTracker OngoingOperationTracker { get; } public OnlinePlayBeatmapAvailabilityTracker AvailabilityTracker { get; } + public TestRoomRequestsHandler RequestsHandler { get; } /// /// All cached dependencies which are also components. @@ -36,9 +37,11 @@ namespace osu.Game.Tests.Visual.OnlinePlay RoomManager = CreateRoomManager(); OngoingOperationTracker = new OngoingOperationTracker(); AvailabilityTracker = new OnlinePlayBeatmapAvailabilityTracker(); + RequestsHandler = new TestRoomRequestsHandler(); dependencies = new DependencyContainer(new CachedModelDependencyContainer(null) { Model = { BindTarget = SelectedRoom } }); + CacheAs(RequestsHandler); CacheAs(SelectedRoom); CacheAs(RoomManager); CacheAs(OngoingOperationTracker); @@ -71,6 +74,6 @@ namespace osu.Game.Tests.Visual.OnlinePlay drawableComponents.Add(drawable); } - protected virtual IRoomManager CreateRoomManager() => new TestRequestHandlingRoomManager(); + protected virtual IRoomManager CreateRoomManager() => new TestRoomManager(); } } diff --git a/osu.Game/Tests/Visual/OnlinePlay/TestRequestHandlingRoomManager.cs b/osu.Game/Tests/Visual/OnlinePlay/TestRoomManager.cs similarity index 97% rename from osu.Game/Tests/Visual/OnlinePlay/TestRequestHandlingRoomManager.cs rename to osu.Game/Tests/Visual/OnlinePlay/TestRoomManager.cs index ef0ceafd02..5fe3dc8406 100644 --- a/osu.Game/Tests/Visual/OnlinePlay/TestRequestHandlingRoomManager.cs +++ b/osu.Game/Tests/Visual/OnlinePlay/TestRoomManager.cs @@ -13,7 +13,7 @@ namespace osu.Game.Tests.Visual.OnlinePlay /// /// A very simple for use in online play test scenes. /// - public class TestRequestHandlingRoomManager : RoomManager + public class TestRoomManager : RoomManager { public Action JoinRoomRequested; From 48f280440c696925653ed36a6794e83f404cf637 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 27 Oct 2021 19:36:43 +0900 Subject: [PATCH 136/260] Fix incorrect clearing of room --- .../Visual/Multiplayer/TestSceneMultiplayerMatchFooter.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchFooter.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchFooter.cs index 44a8d7b439..6536ef2ca1 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchFooter.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchFooter.cs @@ -4,7 +4,6 @@ using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Game.Online.Rooms; using osu.Game.Screens.OnlinePlay.Multiplayer.Match; namespace osu.Game.Tests.Visual.Multiplayer @@ -14,8 +13,6 @@ namespace osu.Game.Tests.Visual.Multiplayer [SetUp] public new void Setup() => Schedule(() => { - SelectedRoom.Value = new Room(); - Child = new Container { Anchor = Anchor.Centre, From a87d8d0359af262408e097c6e3da406436be2cc8 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 27 Oct 2021 20:47:39 +0900 Subject: [PATCH 137/260] Fix dependency missing in a few TestScenes Hopefully these are rather temporary cases until a better solution is found for these dependency-loading screens. --- .../Visual/Multiplayer/TestSceneMultiplayer.cs | 11 +++++++++++ .../Visual/Multiplayer/TestSceneTeamVersus.cs | 11 +++++++++++ 2 files changed, 22 insertions(+) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs index bb4603da69..a106f3ea95 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs @@ -18,6 +18,7 @@ using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Database; using osu.Game.Graphics.UserInterface; +using osu.Game.Online.API; using osu.Game.Online.Multiplayer; using osu.Game.Online.Rooms; using osu.Game.Overlays.Mods; @@ -34,6 +35,7 @@ using osu.Game.Screens.OnlinePlay.Multiplayer.Match; using osu.Game.Screens.Play; using osu.Game.Screens.Ranking; using osu.Game.Tests.Resources; +using osu.Game.Tests.Visual.OnlinePlay; using osu.Game.Users; using osuTK.Input; @@ -617,10 +619,19 @@ namespace osu.Game.Tests.Visual.Multiplayer [Cached(typeof(MultiplayerClient))] public readonly TestMultiplayerClient Client; + [Cached] + public readonly TestRoomRequestsHandler RequestsHandler = new TestRoomRequestsHandler(); + public DependenciesScreen(TestMultiplayerClient client) { Client = client; } + + [BackgroundDependencyLoader] + private void load(IAPIProvider api, OsuGameBase game) + { + ((DummyAPIAccess)api).HandleRequest = request => RequestsHandler.HandleRequest(request, api.LocalUser.Value, game); + } } private class TestMultiplayer : Screens.OnlinePlay.Multiplayer.Multiplayer diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs index 7b34150610..9bb19d4286 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs @@ -11,6 +11,7 @@ using osu.Framework.Platform; using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Database; +using osu.Game.Online.API; using osu.Game.Online.Multiplayer; using osu.Game.Online.Multiplayer.MatchTypes.TeamVersus; using osu.Game.Online.Rooms; @@ -23,6 +24,7 @@ using osu.Game.Screens.OnlinePlay.Multiplayer; using osu.Game.Screens.OnlinePlay.Multiplayer.Match; using osu.Game.Screens.OnlinePlay.Multiplayer.Participants; using osu.Game.Tests.Resources; +using osu.Game.Tests.Visual.OnlinePlay; using osuTK.Input; namespace osu.Game.Tests.Visual.Multiplayer @@ -176,10 +178,19 @@ namespace osu.Game.Tests.Visual.Multiplayer [Cached(typeof(MultiplayerClient))] public readonly TestMultiplayerClient Client; + [Cached] + public readonly TestRoomRequestsHandler RequestsHandler = new TestRoomRequestsHandler(); + public DependenciesScreen(TestMultiplayerClient client) { Client = client; } + + [BackgroundDependencyLoader] + private void load(IAPIProvider api, OsuGameBase game) + { + ((DummyAPIAccess)api).HandleRequest = request => RequestsHandler.HandleRequest(request, api.LocalUser.Value, game); + } } private class TestMultiplayer : Screens.OnlinePlay.Multiplayer.Multiplayer From 72bb72a559dda6edfb923a1d89fcc8a83e9a9621 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 27 Oct 2021 20:56:03 +0900 Subject: [PATCH 138/260] Fix a case of missed base.SetUpSteps() --- .../TestSceneMultiplayerGameplayLeaderboardTeams.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs index 32114fa500..3d48ddc7ca 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboardTeams.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using System.Linq; using NUnit.Framework; using osu.Framework.Graphics; -using osu.Framework.Testing; using osu.Framework.Utils; using osu.Game.Online.API; using osu.Game.Online.Multiplayer; @@ -44,9 +43,10 @@ namespace osu.Game.Tests.Visual.Multiplayer return room; } - [SetUpSteps] public override void SetUpSteps() { + base.SetUpSteps(); + AddStep("set local user", () => ((DummyAPIAccess)API).LocalUser.Value = LookupCache.GetUserAsync(1).Result); AddStep("create leaderboard", () => From 9015ac6ba8d9fd25ceb23321c9a0e9d3dc551221 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 Oct 2021 19:52:41 +0900 Subject: [PATCH 139/260] Implement new version of download tracker --- osu.Game/Database/ModelDownloader.cs | 4 +- osu.Game/Online/BeatmapDownloadTracker.cs | 172 ++++++++++++++++++++++ osu.Game/Online/DownloadTracker.cs | 39 +++++ osu.Game/Online/ScoreDownloadTracker.cs | 172 ++++++++++++++++++++++ osu.Game/Scoring/ScoreInfo.cs | 2 +- 5 files changed, 386 insertions(+), 3 deletions(-) create mode 100644 osu.Game/Online/BeatmapDownloadTracker.cs create mode 100644 osu.Game/Online/DownloadTracker.cs create mode 100644 osu.Game/Online/ScoreDownloadTracker.cs diff --git a/osu.Game/Database/ModelDownloader.cs b/osu.Game/Database/ModelDownloader.cs index e613b39b6b..b9677c1b2b 100644 --- a/osu.Game/Database/ModelDownloader.cs +++ b/osu.Game/Database/ModelDownloader.cs @@ -15,7 +15,7 @@ using osu.Game.Overlays.Notifications; namespace osu.Game.Database { public abstract class ModelDownloader : IModelDownloader - where TModel : class, IHasPrimaryKey, ISoftDelete, IEquatable + where TModel : class, IHasPrimaryKey, ISoftDelete, IEquatable, IHasOnlineID { public Action PostNotification { protected get; set; } @@ -107,7 +107,7 @@ namespace osu.Game.Database } } - public ArchiveDownloadRequest GetExistingDownload(TModel model) => currentDownloads.Find(r => r.Model.Equals(model)); + public ArchiveDownloadRequest GetExistingDownload(TModel model) => currentDownloads.Find(r => r.Model.OnlineID == model.OnlineID); private bool canDownload(TModel model) => GetExistingDownload(model) == null && api != null; diff --git a/osu.Game/Online/BeatmapDownloadTracker.cs b/osu.Game/Online/BeatmapDownloadTracker.cs new file mode 100644 index 0000000000..969cb9af36 --- /dev/null +++ b/osu.Game/Online/BeatmapDownloadTracker.cs @@ -0,0 +1,172 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Game.Beatmaps; +using osu.Game.Online.API; + +#nullable enable + +namespace osu.Game.Online +{ + public class BeatmapDownloadTracker : DownloadTracker + { + [Resolved(CanBeNull = true)] + protected BeatmapManager? Manager { get; private set; } + + private ArchiveDownloadRequest? attachedRequest; + + public BeatmapDownloadTracker(IBeatmapSetInfo trackedItem) + : base(trackedItem) + { + } + + private IBindable>? managerUpdated; + private IBindable>? managerRemoved; + private IBindable>>? managerDownloadBegan; + private IBindable>>? managerDownloadFailed; + + [BackgroundDependencyLoader(true)] + private void load() + { + // Used to interact with manager classes that don't support interface types. Will eventually be replaced. + var beatmapSetInfo = new BeatmapSetInfo { OnlineBeatmapSetID = TrackedItem.OnlineID }; + + if ((TrackedItem as BeatmapSetInfo)?.ID > 0 || Manager?.IsAvailableLocally(beatmapSetInfo) == true) + UpdateState(DownloadState.LocallyAvailable); + else if (Manager != null) + attachDownload(Manager.GetExistingDownload(beatmapSetInfo)); + + if (Manager != null) + { + managerDownloadBegan = Manager.DownloadBegan.GetBoundCopy(); + managerDownloadBegan.BindValueChanged(downloadBegan); + managerDownloadFailed = Manager.DownloadFailed.GetBoundCopy(); + managerDownloadFailed.BindValueChanged(downloadFailed); + managerUpdated = Manager.ItemUpdated.GetBoundCopy(); + managerUpdated.BindValueChanged(itemUpdated); + managerRemoved = Manager.ItemRemoved.GetBoundCopy(); + managerRemoved.BindValueChanged(itemRemoved); + } + } + + /// + /// Checks that a database model matches the one expected to be downloaded. + /// + /// + /// For online play, this could be used to check that the databased model matches the online beatmap. + /// + /// The model in database. + protected virtual bool VerifyDatabasedModel(BeatmapSetInfo databasedModel) => true; // TODO: do we still need this? + + private void downloadBegan(ValueChangedEvent>> weakRequest) + { + if (weakRequest.NewValue.TryGetTarget(out var request)) + { + Schedule(() => + { + if (checkEquality(request.Model, TrackedItem)) + attachDownload(request); + }); + } + } + + private void downloadFailed(ValueChangedEvent>> weakRequest) + { + if (weakRequest.NewValue.TryGetTarget(out var request)) + { + Schedule(() => + { + if (checkEquality(request.Model, TrackedItem)) + attachDownload(null); + }); + } + } + + private void attachDownload(ArchiveDownloadRequest? request) + { + if (attachedRequest != null) + { + attachedRequest.Failure -= onRequestFailure; + attachedRequest.DownloadProgressed -= onRequestProgress; + attachedRequest.Success -= onRequestSuccess; + } + + attachedRequest = request; + + if (attachedRequest != null) + { + if (attachedRequest.Progress == 1) + { + UpdateProgress(1); + UpdateState(DownloadState.Importing); + } + else + { + UpdateProgress(attachedRequest.Progress); + UpdateState(DownloadState.Downloading); + + attachedRequest.Failure += onRequestFailure; + attachedRequest.DownloadProgressed += onRequestProgress; + attachedRequest.Success += onRequestSuccess; + } + } + else + { + UpdateState(DownloadState.NotDownloaded); + } + } + + private void onRequestSuccess(string _) => Schedule(() => UpdateState(DownloadState.Importing)); + + private void onRequestProgress(float progress) => Schedule(() => UpdateProgress(progress)); + + private void onRequestFailure(Exception e) => Schedule(() => attachDownload(null)); + + private void itemUpdated(ValueChangedEvent> weakItem) + { + if (weakItem.NewValue.TryGetTarget(out var item)) + { + Schedule(() => + { + if (!checkEquality(item, TrackedItem)) + return; + + if (!VerifyDatabasedModel(item)) + { + UpdateState(DownloadState.NotDownloaded); + return; + } + + UpdateState(DownloadState.LocallyAvailable); + }); + } + } + + private void itemRemoved(ValueChangedEvent> weakItem) + { + if (weakItem.NewValue.TryGetTarget(out var item)) + { + Schedule(() => + { + if (checkEquality(item, TrackedItem)) + UpdateState(DownloadState.NotDownloaded); + }); + } + } + + private bool checkEquality(IBeatmapSetInfo x, IBeatmapSetInfo y) => x.OnlineID == y.OnlineID; + + #region Disposal + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + attachDownload(null); + } + + #endregion + } +} diff --git a/osu.Game/Online/DownloadTracker.cs b/osu.Game/Online/DownloadTracker.cs new file mode 100644 index 0000000000..357c64b6a3 --- /dev/null +++ b/osu.Game/Online/DownloadTracker.cs @@ -0,0 +1,39 @@ +// 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.Framework.Graphics; + +#nullable enable + +namespace osu.Game.Online +{ + public abstract class DownloadTracker : Component + where T : class + { + public readonly T TrackedItem; + + /// + /// Holds the current download state of the download - whether is has already been downloaded, is in progress, or is not downloaded. + /// + public IBindable State => state; + + private readonly Bindable state = new Bindable(); + + /// + /// The progress of an active download. + /// + public IBindableNumber Progress => progress; + + private readonly BindableNumber progress = new BindableNumber { MinValue = 0, MaxValue = 1 }; + + protected DownloadTracker(T trackedItem) + { + TrackedItem = trackedItem; + } + + protected void UpdateState(DownloadState newState) => state.Value = newState; + + protected void UpdateProgress(double newProgress) => progress.Value = newProgress; + } +} diff --git a/osu.Game/Online/ScoreDownloadTracker.cs b/osu.Game/Online/ScoreDownloadTracker.cs new file mode 100644 index 0000000000..47cbabbee8 --- /dev/null +++ b/osu.Game/Online/ScoreDownloadTracker.cs @@ -0,0 +1,172 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Game.Online.API; +using osu.Game.Scoring; + +#nullable enable + +namespace osu.Game.Online +{ + public class ScoreDownloadTracker : DownloadTracker + { + [Resolved(CanBeNull = true)] + protected ScoreManager? Manager { get; private set; } + + private ArchiveDownloadRequest? attachedRequest; + + public ScoreDownloadTracker(ScoreInfo trackedItem) + : base(trackedItem) + { + } + + private IBindable>? managerUpdated; + private IBindable>? managerRemoved; + private IBindable>>? managerDownloadBegan; + private IBindable>>? managerDownloadFailed; + + [BackgroundDependencyLoader(true)] + private void load() + { + // Used to interact with manager classes that don't support interface types. Will eventually be replaced. + var beatmapSetInfo = new ScoreInfo { OnlineScoreID = TrackedItem.OnlineScoreID }; + + if (TrackedItem.ID > 0 || Manager?.IsAvailableLocally(beatmapSetInfo) == true) + UpdateState(DownloadState.LocallyAvailable); + else if (Manager != null) + attachDownload(Manager.GetExistingDownload(beatmapSetInfo)); + + if (Manager != null) + { + managerDownloadBegan = Manager.DownloadBegan.GetBoundCopy(); + managerDownloadBegan.BindValueChanged(downloadBegan); + managerDownloadFailed = Manager.DownloadFailed.GetBoundCopy(); + managerDownloadFailed.BindValueChanged(downloadFailed); + managerUpdated = Manager.ItemUpdated.GetBoundCopy(); + managerUpdated.BindValueChanged(itemUpdated); + managerRemoved = Manager.ItemRemoved.GetBoundCopy(); + managerRemoved.BindValueChanged(itemRemoved); + } + } + + /// + /// Checks that a database model matches the one expected to be downloaded. + /// + /// + /// For online play, this could be used to check that the databased model matches the online beatmap. + /// + /// The model in database. + protected virtual bool VerifyDatabasedModel(ScoreInfo databasedModel) => true; // TODO: do we still need this? + + private void downloadBegan(ValueChangedEvent>> weakRequest) + { + if (weakRequest.NewValue.TryGetTarget(out var request)) + { + Schedule(() => + { + if (checkEquality(request.Model, TrackedItem)) + attachDownload(request); + }); + } + } + + private void downloadFailed(ValueChangedEvent>> weakRequest) + { + if (weakRequest.NewValue.TryGetTarget(out var request)) + { + Schedule(() => + { + if (checkEquality(request.Model, TrackedItem)) + attachDownload(null); + }); + } + } + + private void attachDownload(ArchiveDownloadRequest? request) + { + if (attachedRequest != null) + { + attachedRequest.Failure -= onRequestFailure; + attachedRequest.DownloadProgressed -= onRequestProgress; + attachedRequest.Success -= onRequestSuccess; + } + + attachedRequest = request; + + if (attachedRequest != null) + { + if (attachedRequest.Progress == 1) + { + UpdateProgress(1); + UpdateState(DownloadState.Importing); + } + else + { + UpdateProgress(attachedRequest.Progress); + UpdateState(DownloadState.Downloading); + + attachedRequest.Failure += onRequestFailure; + attachedRequest.DownloadProgressed += onRequestProgress; + attachedRequest.Success += onRequestSuccess; + } + } + else + { + UpdateState(DownloadState.NotDownloaded); + } + } + + private void onRequestSuccess(string _) => Schedule(() => UpdateState(DownloadState.Importing)); + + private void onRequestProgress(float progress) => Schedule(() => UpdateProgress(progress)); + + private void onRequestFailure(Exception e) => Schedule(() => attachDownload(null)); + + private void itemUpdated(ValueChangedEvent> weakItem) + { + if (weakItem.NewValue.TryGetTarget(out var item)) + { + Schedule(() => + { + if (!checkEquality(item, TrackedItem)) + return; + + if (!VerifyDatabasedModel(item)) + { + UpdateState(DownloadState.NotDownloaded); + return; + } + + UpdateState(DownloadState.LocallyAvailable); + }); + } + } + + private void itemRemoved(ValueChangedEvent> weakItem) + { + if (weakItem.NewValue.TryGetTarget(out var item)) + { + Schedule(() => + { + if (checkEquality(item, TrackedItem)) + UpdateState(DownloadState.NotDownloaded); + }); + } + } + + private bool checkEquality(ScoreInfo x, ScoreInfo y) => x.OnlineScoreID == y.OnlineScoreID; + + #region Disposal + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + attachDownload(null); + } + + #endregion + } +} diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index 5cf22f7945..1bc04cd21e 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -19,7 +19,7 @@ using osu.Game.Utils; namespace osu.Game.Scoring { - public class ScoreInfo : IHasFiles, IHasPrimaryKey, ISoftDelete, IEquatable, IDeepCloneable + public class ScoreInfo : IHasFiles, IHasPrimaryKey, ISoftDelete, IEquatable, IDeepCloneable, IHasOnlineID { public int ID { get; set; } From 617e6febb6bb81c61fbf126089a836edf7860817 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 Oct 2021 20:02:51 +0900 Subject: [PATCH 140/260] Refactor `ModelDownloader` to allow for different `OnlineID matching` --- osu.Game/Beatmaps/BeatmapModelDownloader.cs | 3 +++ osu.Game/Database/ModelDownloader.cs | 12 ++++++------ osu.Game/Scoring/ScoreInfo.cs | 2 +- osu.Game/Scoring/ScoreModelDownloader.cs | 3 +++ 4 files changed, 13 insertions(+), 7 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapModelDownloader.cs b/osu.Game/Beatmaps/BeatmapModelDownloader.cs index 30dc95a966..001726e741 100644 --- a/osu.Game/Beatmaps/BeatmapModelDownloader.cs +++ b/osu.Game/Beatmaps/BeatmapModelDownloader.cs @@ -13,6 +13,9 @@ namespace osu.Game.Beatmaps protected override ArchiveDownloadRequest CreateDownloadRequest(BeatmapSetInfo set, bool minimiseDownloadSize) => new DownloadBeatmapSetRequest(set, minimiseDownloadSize); + public override ArchiveDownloadRequest GetExistingDownload(BeatmapSetInfo model) + => CurrentDownloads.Find(r => r.Model.OnlineID == model.OnlineID); + public BeatmapModelDownloader(IBeatmapModelManager beatmapModelManager, IAPIProvider api, GameHost host = null) : base(beatmapModelManager, api, host) { diff --git a/osu.Game/Database/ModelDownloader.cs b/osu.Game/Database/ModelDownloader.cs index b9677c1b2b..12bf5e9ce7 100644 --- a/osu.Game/Database/ModelDownloader.cs +++ b/osu.Game/Database/ModelDownloader.cs @@ -15,7 +15,7 @@ using osu.Game.Overlays.Notifications; namespace osu.Game.Database { public abstract class ModelDownloader : IModelDownloader - where TModel : class, IHasPrimaryKey, ISoftDelete, IEquatable, IHasOnlineID + where TModel : class, IHasPrimaryKey, ISoftDelete, IEquatable { public Action PostNotification { protected get; set; } @@ -30,7 +30,7 @@ namespace osu.Game.Database private readonly IModelManager modelManager; private readonly IAPIProvider api; - private readonly List> currentDownloads = new List>(); + protected readonly List> CurrentDownloads = new List>(); protected ModelDownloader(IModelManager modelManager, IAPIProvider api, IIpcHost importHost = null) { @@ -74,7 +74,7 @@ namespace osu.Game.Database if (!imported.Any()) downloadFailed.Value = new WeakReference>(request); - currentDownloads.Remove(request); + CurrentDownloads.Remove(request); }, TaskCreationOptions.LongRunning); }; @@ -86,7 +86,7 @@ namespace osu.Game.Database return true; }; - currentDownloads.Add(request); + CurrentDownloads.Add(request); PostNotification?.Invoke(notification); api.PerformAsync(request); @@ -96,7 +96,7 @@ namespace osu.Game.Database void triggerFailure(Exception error) { - currentDownloads.Remove(request); + CurrentDownloads.Remove(request); downloadFailed.Value = new WeakReference>(request); @@ -107,7 +107,7 @@ namespace osu.Game.Database } } - public ArchiveDownloadRequest GetExistingDownload(TModel model) => currentDownloads.Find(r => r.Model.OnlineID == model.OnlineID); + public abstract ArchiveDownloadRequest GetExistingDownload(TModel model); private bool canDownload(TModel model) => GetExistingDownload(model) == null && api != null; diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index 1bc04cd21e..5cf22f7945 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -19,7 +19,7 @@ using osu.Game.Utils; namespace osu.Game.Scoring { - public class ScoreInfo : IHasFiles, IHasPrimaryKey, ISoftDelete, IEquatable, IDeepCloneable, IHasOnlineID + public class ScoreInfo : IHasFiles, IHasPrimaryKey, ISoftDelete, IEquatable, IDeepCloneable { public int ID { get; set; } diff --git a/osu.Game/Scoring/ScoreModelDownloader.cs b/osu.Game/Scoring/ScoreModelDownloader.cs index b3c1e2928a..52355585a9 100644 --- a/osu.Game/Scoring/ScoreModelDownloader.cs +++ b/osu.Game/Scoring/ScoreModelDownloader.cs @@ -16,5 +16,8 @@ namespace osu.Game.Scoring } protected override ArchiveDownloadRequest CreateDownloadRequest(ScoreInfo score, bool minimiseDownload) => new DownloadReplayRequest(score); + + public override ArchiveDownloadRequest GetExistingDownload(ScoreInfo model) + => CurrentDownloads.Find(r => r.Model.OnlineScoreID == model.OnlineScoreID); } } From 692db9a9ec2f424153e0374923247edeee0eb98e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 Oct 2021 20:21:00 +0900 Subject: [PATCH 141/260] Remove unnecessary verification flow from score download tracking --- osu.Game/Online/BeatmapDownloadTracker.cs | 15 --------------- osu.Game/Online/ScoreDownloadTracker.cs | 17 +---------------- 2 files changed, 1 insertion(+), 31 deletions(-) diff --git a/osu.Game/Online/BeatmapDownloadTracker.cs b/osu.Game/Online/BeatmapDownloadTracker.cs index 969cb9af36..6df74efe9c 100644 --- a/osu.Game/Online/BeatmapDownloadTracker.cs +++ b/osu.Game/Online/BeatmapDownloadTracker.cs @@ -52,15 +52,6 @@ namespace osu.Game.Online } } - /// - /// Checks that a database model matches the one expected to be downloaded. - /// - /// - /// For online play, this could be used to check that the databased model matches the online beatmap. - /// - /// The model in database. - protected virtual bool VerifyDatabasedModel(BeatmapSetInfo databasedModel) => true; // TODO: do we still need this? - private void downloadBegan(ValueChangedEvent>> weakRequest) { if (weakRequest.NewValue.TryGetTarget(out var request)) @@ -134,12 +125,6 @@ namespace osu.Game.Online if (!checkEquality(item, TrackedItem)) return; - if (!VerifyDatabasedModel(item)) - { - UpdateState(DownloadState.NotDownloaded); - return; - } - UpdateState(DownloadState.LocallyAvailable); }); } diff --git a/osu.Game/Online/ScoreDownloadTracker.cs b/osu.Game/Online/ScoreDownloadTracker.cs index 47cbabbee8..7a714a7a0e 100644 --- a/osu.Game/Online/ScoreDownloadTracker.cs +++ b/osu.Game/Online/ScoreDownloadTracker.cs @@ -52,15 +52,6 @@ namespace osu.Game.Online } } - /// - /// Checks that a database model matches the one expected to be downloaded. - /// - /// - /// For online play, this could be used to check that the databased model matches the online beatmap. - /// - /// The model in database. - protected virtual bool VerifyDatabasedModel(ScoreInfo databasedModel) => true; // TODO: do we still need this? - private void downloadBegan(ValueChangedEvent>> weakRequest) { if (weakRequest.NewValue.TryGetTarget(out var request)) @@ -134,13 +125,7 @@ namespace osu.Game.Online if (!checkEquality(item, TrackedItem)) return; - if (!VerifyDatabasedModel(item)) - { - UpdateState(DownloadState.NotDownloaded); - return; - } - - UpdateState(DownloadState.LocallyAvailable); + UpdateState(DownloadState.NotDownloaded); }); } } From 6339064dbd152ac4ebf121e8911037b32c9756b9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 Oct 2021 20:07:00 +0900 Subject: [PATCH 142/260] Remove old versions of `DownloadTrackingComposite` --- osu.Game/Online/DownloadTrackingComposite.cs | 196 ------------------ .../BeatmapDownloadTrackingComposite.cs | 19 -- 2 files changed, 215 deletions(-) delete mode 100644 osu.Game/Online/DownloadTrackingComposite.cs delete mode 100644 osu.Game/Overlays/BeatmapDownloadTrackingComposite.cs diff --git a/osu.Game/Online/DownloadTrackingComposite.cs b/osu.Game/Online/DownloadTrackingComposite.cs deleted file mode 100644 index 2a96051427..0000000000 --- a/osu.Game/Online/DownloadTrackingComposite.cs +++ /dev/null @@ -1,196 +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 JetBrains.Annotations; -using osu.Framework.Allocation; -using osu.Framework.Bindables; -using osu.Framework.Graphics.Containers; -using osu.Game.Database; -using osu.Game.Online.API; - -namespace osu.Game.Online -{ - /// - /// A component which tracks a through potential download/import/deletion. - /// - public abstract class DownloadTrackingComposite : CompositeDrawable - where TModel : class, IEquatable - where TModelManager : class, IModelDownloader, IModelManager - { - protected readonly Bindable Model = new Bindable(); - - [Resolved(CanBeNull = true)] - protected TModelManager Manager { get; private set; } - - /// - /// Holds the current download state of the , whether is has already been downloaded, is in progress, or is not downloaded. - /// - protected readonly Bindable State = new Bindable(); - - protected readonly BindableNumber Progress = new BindableNumber { MinValue = 0, MaxValue = 1 }; - - protected DownloadTrackingComposite(TModel model = null) - { - Model.Value = model; - } - - private IBindable> managerUpdated; - private IBindable> managerRemoved; - private IBindable>> managerDownloadBegan; - private IBindable>> managerDownloadFailed; - - [BackgroundDependencyLoader(true)] - private void load() - { - Model.BindValueChanged(modelInfo => - { - if (modelInfo.NewValue == null) - attachDownload(null); - else if (IsModelAvailableLocally()) - State.Value = DownloadState.LocallyAvailable; - else - attachDownload(Manager?.GetExistingDownload(modelInfo.NewValue)); - }, true); - - if (Manager == null) - return; - - managerDownloadBegan = Manager.DownloadBegan.GetBoundCopy(); - managerDownloadBegan.BindValueChanged(downloadBegan); - managerDownloadFailed = Manager.DownloadFailed.GetBoundCopy(); - managerDownloadFailed.BindValueChanged(downloadFailed); - managerUpdated = Manager.ItemUpdated.GetBoundCopy(); - managerUpdated.BindValueChanged(itemUpdated); - managerRemoved = Manager.ItemRemoved.GetBoundCopy(); - managerRemoved.BindValueChanged(itemRemoved); - } - - /// - /// Checks that a database model matches the one expected to be downloaded. - /// - /// - /// For online play, this could be used to check that the databased model matches the online beatmap. - /// - /// The model in database. - protected virtual bool VerifyDatabasedModel([NotNull] TModel databasedModel) => true; - - /// - /// Whether the given model is available in the database. - /// By default, this calls , - /// but can be overriden to add additional checks for verifying the model in database. - /// - protected virtual bool IsModelAvailableLocally() => Manager?.IsAvailableLocally(Model.Value) == true; - - private void downloadBegan(ValueChangedEvent>> weakRequest) - { - if (weakRequest.NewValue.TryGetTarget(out var request)) - { - Schedule(() => - { - if (request.Model.Equals(Model.Value)) - attachDownload(request); - }); - } - } - - private void downloadFailed(ValueChangedEvent>> weakRequest) - { - if (weakRequest.NewValue.TryGetTarget(out var request)) - { - Schedule(() => - { - if (request.Model.Equals(Model.Value)) - attachDownload(null); - }); - } - } - - private ArchiveDownloadRequest attachedRequest; - - private void attachDownload(ArchiveDownloadRequest request) - { - if (attachedRequest != null) - { - attachedRequest.Failure -= onRequestFailure; - attachedRequest.DownloadProgressed -= onRequestProgress; - attachedRequest.Success -= onRequestSuccess; - } - - attachedRequest = request; - - if (attachedRequest != null) - { - if (attachedRequest.Progress == 1) - { - Progress.Value = 1; - State.Value = DownloadState.Importing; - } - else - { - Progress.Value = attachedRequest.Progress; - State.Value = DownloadState.Downloading; - - attachedRequest.Failure += onRequestFailure; - attachedRequest.DownloadProgressed += onRequestProgress; - attachedRequest.Success += onRequestSuccess; - } - } - else - { - State.Value = DownloadState.NotDownloaded; - } - } - - private void onRequestSuccess(string _) => Schedule(() => State.Value = DownloadState.Importing); - - private void onRequestProgress(float progress) => Schedule(() => Progress.Value = progress); - - private void onRequestFailure(Exception e) => Schedule(() => attachDownload(null)); - - private void itemUpdated(ValueChangedEvent> weakItem) - { - if (weakItem.NewValue.TryGetTarget(out var item)) - { - Schedule(() => - { - if (!item.Equals(Model.Value)) - return; - - if (!VerifyDatabasedModel(item)) - { - State.Value = DownloadState.NotDownloaded; - return; - } - - State.Value = DownloadState.LocallyAvailable; - }); - } - } - - private void itemRemoved(ValueChangedEvent> weakItem) - { - if (weakItem.NewValue.TryGetTarget(out var item)) - { - Schedule(() => - { - if (item.Equals(Model.Value)) - State.Value = DownloadState.NotDownloaded; - }); - } - } - - #region Disposal - - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - - State.UnbindAll(); - - attachDownload(null); - } - - #endregion - } -} diff --git a/osu.Game/Overlays/BeatmapDownloadTrackingComposite.cs b/osu.Game/Overlays/BeatmapDownloadTrackingComposite.cs deleted file mode 100644 index f6b5b181c3..0000000000 --- a/osu.Game/Overlays/BeatmapDownloadTrackingComposite.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 osu.Framework.Bindables; -using osu.Game.Beatmaps; -using osu.Game.Online; - -namespace osu.Game.Overlays -{ - public abstract class BeatmapDownloadTrackingComposite : DownloadTrackingComposite - { - public Bindable BeatmapSet => Model; - - protected BeatmapDownloadTrackingComposite(BeatmapSetInfo set = null) - : base(set) - { - } - } -} From dd06c617b149f2459b209640808b6a88d9bee440 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 Oct 2021 20:19:34 +0900 Subject: [PATCH 143/260] Update `OnlinePlayBeatmapAvailabilityTracker` in line with new download tracker The structure here has changed a bit - rather than deriving from the donwload tracker I moved the additional logic into the callbacks. I think this feels better? --- .../OnlinePlayBeatmapAvailabilityTracker.cs | 93 ++++++++++--------- 1 file changed, 48 insertions(+), 45 deletions(-) diff --git a/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs b/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs index 52aa115083..6dd1204555 100644 --- a/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs +++ b/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs @@ -2,8 +2,9 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Linq; +using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Graphics.Containers; using osu.Framework.Logging; using osu.Framework.Threading; using osu.Game.Beatmaps; @@ -16,10 +17,13 @@ namespace osu.Game.Online.Rooms /// This differs from a regular download tracking composite as this accounts for the /// databased beatmap set's checksum, to disallow from playing with an altered version of the beatmap. /// - public class OnlinePlayBeatmapAvailabilityTracker : DownloadTrackingComposite + public class OnlinePlayBeatmapAvailabilityTracker : CompositeDrawable { public readonly IBindable SelectedItem = new Bindable(); + [Resolved] + private BeatmapManager beatmapManager { get; set; } + /// /// The availability state of the currently selected playlist item. /// @@ -29,6 +33,8 @@ namespace osu.Game.Online.Rooms private ScheduledDelegate progressUpdate; + private BeatmapDownloadTracker downloadTracker; + protected override void LoadComplete() { base.LoadComplete(); @@ -40,58 +46,39 @@ namespace osu.Game.Online.Rooms if (item.NewValue == null) return; - Model.Value = item.NewValue.Beatmap.Value.BeatmapSet; + downloadTracker?.Expire(); + downloadTracker = new BeatmapDownloadTracker(item.NewValue.Beatmap.Value.BeatmapSet); + + downloadTracker.Progress.BindValueChanged(_ => + { + if (downloadTracker.State.Value != DownloadState.Downloading) + return; + + // incoming progress changes are going to be at a very high rate. + // we don't want to flood the network with this, so rate limit how often we send progress updates. + if (progressUpdate?.Completed != false) + progressUpdate = Scheduler.AddDelayed(updateAvailability, progressUpdate == null ? 0 : 500); + }); + + downloadTracker.State.BindValueChanged(_ => updateAvailability(), true); + + AddInternal(downloadTracker); }, true); - - Progress.BindValueChanged(_ => - { - if (State.Value != DownloadState.Downloading) - return; - - // incoming progress changes are going to be at a very high rate. - // we don't want to flood the network with this, so rate limit how often we send progress updates. - if (progressUpdate?.Completed != false) - progressUpdate = Scheduler.AddDelayed(updateAvailability, progressUpdate == null ? 0 : 500); - }); - - State.BindValueChanged(_ => updateAvailability(), true); - } - - protected override bool VerifyDatabasedModel(BeatmapSetInfo databasedSet) - { - int beatmapId = SelectedItem.Value?.Beatmap.Value.OnlineID ?? -1; - string checksum = SelectedItem.Value?.Beatmap.Value.MD5Hash; - - var matchingBeatmap = databasedSet.Beatmaps.FirstOrDefault(b => b.OnlineBeatmapID == beatmapId && b.MD5Hash == checksum); - - if (matchingBeatmap == null) - { - Logger.Log("The imported beatmap set does not match the online version.", LoggingTarget.Runtime, LogLevel.Important); - return false; - } - - return true; - } - - protected override bool IsModelAvailableLocally() - { - int onlineId = SelectedItem.Value.Beatmap.Value.OnlineID; - string checksum = SelectedItem.Value.Beatmap.Value.MD5Hash; - - var beatmap = Manager.QueryBeatmap(b => b.OnlineBeatmapID == onlineId && b.MD5Hash == checksum); - return beatmap?.BeatmapSet.DeletePending == false; } private void updateAvailability() { - switch (State.Value) + if (downloadTracker == null) + return; + + switch (downloadTracker.State.Value) { case DownloadState.NotDownloaded: availability.Value = BeatmapAvailability.NotDownloaded(); break; case DownloadState.Downloading: - availability.Value = BeatmapAvailability.Downloading((float)Progress.Value); + availability.Value = BeatmapAvailability.Downloading((float)downloadTracker.Progress.Value); break; case DownloadState.Importing: @@ -99,12 +86,28 @@ namespace osu.Game.Online.Rooms break; case DownloadState.LocallyAvailable: - availability.Value = BeatmapAvailability.LocallyAvailable(); + bool hashMatches = checkHashValidity(); + + availability.Value = hashMatches ? BeatmapAvailability.LocallyAvailable() : BeatmapAvailability.NotDownloaded(); + + // only display a message to the user if a download seems to have just completed. + if (!hashMatches && downloadTracker.Progress.Value == 1) + Logger.Log("The imported beatmap set does not match the online version.", LoggingTarget.Runtime, LogLevel.Important); + break; default: - throw new ArgumentOutOfRangeException(nameof(State)); + throw new ArgumentOutOfRangeException(); } } + + private bool checkHashValidity() + { + int onlineId = SelectedItem.Value.Beatmap.Value.OnlineID; + string checksum = SelectedItem.Value.Beatmap.Value.MD5Hash; + + var beatmap = beatmapManager.QueryBeatmap(b => b.OnlineBeatmapID == onlineId && b.MD5Hash == checksum); + return beatmap?.BeatmapSet.DeletePending == false; + } } } From f014ceaead3556b47c8e0ea510cb3382206455a8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 Oct 2021 20:49:15 +0900 Subject: [PATCH 144/260] Update remaining usages of download tracking --- .../Online/TestSceneDirectDownloadButton.cs | 3 +- .../Panels/BeatmapPanelDownloadButton.cs | 36 +++++++++++++------ .../Panels/DownloadProgressBar.cs | 18 +++++++--- .../BeatmapSet/BeatmapSetHeaderContent.cs | 19 +++++++--- .../Buttons/HeaderDownloadButton.cs | 33 ++++++++++------- .../OnlinePlay/DrawableRoomPlaylistItem.cs | 2 +- .../Screens/Ranking/ReplayDownloadButton.cs | 35 ++++++++++++------ 7 files changed, 100 insertions(+), 46 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneDirectDownloadButton.cs b/osu.Game.Tests/Visual/Online/TestSceneDirectDownloadButton.cs index bb7fcc2fce..879e35057b 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneDirectDownloadButton.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneDirectDownloadButton.cs @@ -5,6 +5,7 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Online; using osu.Game.Online.API.Requests.Responses; @@ -144,7 +145,7 @@ namespace osu.Game.Tests.Visual.Online { public new bool DownloadEnabled => base.DownloadEnabled; - public DownloadState DownloadState => State.Value; + public DownloadState DownloadState => this.ChildrenOfType().First().State.Value; public TestDownloadButton(BeatmapSetInfo beatmapSet) : base(beatmapSet) diff --git a/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelDownloadButton.cs b/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelDownloadButton.cs index a8c4334ffb..5c6472cd1b 100644 --- a/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelDownloadButton.cs +++ b/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelDownloadButton.cs @@ -5,6 +5,7 @@ using System; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.Graphics.Containers; @@ -13,7 +14,7 @@ using osu.Game.Online; namespace osu.Game.Overlays.BeatmapListing.Panels { - public class BeatmapPanelDownloadButton : BeatmapDownloadTrackingComposite + public class BeatmapPanelDownloadButton : CompositeDrawable { protected bool DownloadEnabled => button.Enabled.Value; @@ -26,16 +27,29 @@ namespace osu.Game.Overlays.BeatmapListing.Panels private readonly DownloadButton button; private Bindable noVideoSetting; + protected readonly BeatmapDownloadTracker DownloadTracker; + + protected readonly Bindable State = new Bindable(); + + private readonly BeatmapSetInfo beatmapSet; + public BeatmapPanelDownloadButton(BeatmapSetInfo beatmapSet) - : base(beatmapSet) { - InternalChild = shakeContainer = new ShakeContainer + this.beatmapSet = beatmapSet; + InternalChildren = new Drawable[] { - RelativeSizeAxes = Axes.Both, - Child = button = new DownloadButton + shakeContainer = new ShakeContainer { RelativeSizeAxes = Axes.Both, + Child = button = new DownloadButton + { + RelativeSizeAxes = Axes.Both, + }, }, + DownloadTracker = new BeatmapDownloadTracker(beatmapSet) + { + State = { BindTarget = State } + } }; button.Add(new DownloadProgressBar(beatmapSet) @@ -50,7 +64,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels { base.LoadComplete(); - button.State.BindTo(State); + ((IBindable)button.State).BindTo(DownloadTracker.State); FinishTransforms(true); } @@ -61,7 +75,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels button.Action = () => { - switch (State.Value) + switch (DownloadTracker.State.Value) { case DownloadState.Downloading: case DownloadState.Importing: @@ -73,16 +87,16 @@ namespace osu.Game.Overlays.BeatmapListing.Panels if (SelectedBeatmap.Value != null) findPredicate = b => b.OnlineBeatmapID == SelectedBeatmap.Value.OnlineBeatmapID; - game?.PresentBeatmap(BeatmapSet.Value, findPredicate); + game?.PresentBeatmap(beatmapSet, findPredicate); break; default: - beatmaps.Download(BeatmapSet.Value, noVideoSetting.Value); + beatmaps.Download(beatmapSet, noVideoSetting.Value); break; } }; - State.BindValueChanged(state => + DownloadTracker.State.BindValueChanged(state => { switch (state.NewValue) { @@ -92,7 +106,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels break; default: - if (BeatmapSet.Value?.OnlineInfo?.Availability.DownloadDisabled ?? false) + if (beatmapSet.OnlineInfo?.Availability.DownloadDisabled ?? false) { button.Enabled.Value = false; button.TooltipText = "this beatmap is currently not available for download."; diff --git a/osu.Game/Overlays/BeatmapListing/Panels/DownloadProgressBar.cs b/osu.Game/Overlays/BeatmapListing/Panels/DownloadProgressBar.cs index ca94078401..24f929f55e 100644 --- a/osu.Game/Overlays/BeatmapListing/Panels/DownloadProgressBar.cs +++ b/osu.Game/Overlays/BeatmapListing/Panels/DownloadProgressBar.cs @@ -4,6 +4,7 @@ using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; @@ -12,13 +13,22 @@ using osuTK.Graphics; namespace osu.Game.Overlays.BeatmapListing.Panels { - public class DownloadProgressBar : BeatmapDownloadTrackingComposite + public class DownloadProgressBar : CompositeDrawable { private readonly ProgressBar progressBar; + private readonly BeatmapDownloadTracker downloadTracker; public DownloadProgressBar(BeatmapSetInfo beatmapSet) - : base(beatmapSet) { + InternalChildren = new Drawable[] + { + progressBar = new ProgressBar(false) + { + Height = 0, + Alpha = 0, + }, + downloadTracker = new BeatmapDownloadTracker(beatmapSet), + }; AddInternal(progressBar = new ProgressBar(false) { Height = 0, @@ -34,9 +44,9 @@ namespace osu.Game.Overlays.BeatmapListing.Panels { progressBar.FillColour = colours.Blue; progressBar.BackgroundColour = Color4.Black.Opacity(0.7f); - progressBar.Current = Progress; + progressBar.Current.BindTarget = downloadTracker.Progress; - State.BindValueChanged(state => + downloadTracker.State.BindValueChanged(state => { switch (state.NewValue) { diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs b/osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs index 6f85846720..e0a09ed520 100644 --- a/osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs +++ b/osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs @@ -3,12 +3,14 @@ using System.Linq; 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.Framework.Localisation; +using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; @@ -21,8 +23,10 @@ using osuTK; namespace osu.Game.Overlays.BeatmapSet { - public class BeatmapSetHeaderContent : BeatmapDownloadTrackingComposite + public class BeatmapSetHeaderContent : CompositeDrawable { + public readonly Bindable BeatmapSet = new Bindable(); + private const float transition_duration = 200; private const float buttons_height = 45; private const float buttons_spacing = 5; @@ -45,6 +49,8 @@ namespace osu.Game.Overlays.BeatmapSet private readonly FillFlowContainer fadeContent; private readonly LoadingSpinner loading; + private BeatmapDownloadTracker downloadTracker; + [Resolved] private IAPIProvider api { get; set; } @@ -222,13 +228,13 @@ namespace osu.Game.Overlays.BeatmapSet { coverGradient.Colour = ColourInfo.GradientVertical(colourProvider.Background6.Opacity(0.3f), colourProvider.Background6.Opacity(0.8f)); - State.BindValueChanged(_ => updateDownloadButtons()); - BeatmapSet.BindValueChanged(setInfo => { Picker.BeatmapSet = rulesetSelector.BeatmapSet = author.BeatmapSet = beatmapAvailability.BeatmapSet = Details.BeatmapSet = setInfo.NewValue; cover.BeatmapSet = setInfo.NewValue; + downloadTracker?.Expire(); + if (setInfo.NewValue == null) { onlineStatusPill.FadeTo(0.5f, 500, Easing.OutQuint); @@ -241,6 +247,9 @@ namespace osu.Game.Overlays.BeatmapSet } else { + downloadTracker = new BeatmapDownloadTracker(setInfo.NewValue); + downloadTracker.State.BindValueChanged(_ => updateDownloadButtons()); + fadeContent.FadeIn(500, Easing.OutQuint); loading.Hide(); @@ -266,13 +275,13 @@ namespace osu.Game.Overlays.BeatmapSet { if (BeatmapSet.Value == null) return; - if (BeatmapSet.Value.OnlineInfo.Availability.DownloadDisabled && State.Value != DownloadState.LocallyAvailable) + if (BeatmapSet.Value.OnlineInfo.Availability.DownloadDisabled && downloadTracker.State.Value != DownloadState.LocallyAvailable) { downloadButtonsContainer.Clear(); return; } - switch (State.Value) + switch (downloadTracker.State.Value) { case DownloadState.LocallyAvailable: // temporary for UX until new design is implemented. diff --git a/osu.Game/Overlays/BeatmapSet/Buttons/HeaderDownloadButton.cs b/osu.Game/Overlays/BeatmapSet/Buttons/HeaderDownloadButton.cs index e7a55079ec..88d0778ae4 100644 --- a/osu.Game/Overlays/BeatmapSet/Buttons/HeaderDownloadButton.cs +++ b/osu.Game/Overlays/BeatmapSet/Buttons/HeaderDownloadButton.cs @@ -22,7 +22,7 @@ using osuTK.Graphics; namespace osu.Game.Overlays.BeatmapSet.Buttons { - public class HeaderDownloadButton : BeatmapDownloadTrackingComposite, IHasTooltip + public class HeaderDownloadButton : CompositeDrawable, IHasTooltip { private const int text_size = 12; @@ -35,9 +35,12 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons private ShakeContainer shakeContainer; private HeaderButton button; + private BeatmapDownloadTracker downloadTracker; + private readonly BeatmapSetInfo beatmapSet; + public HeaderDownloadButton(BeatmapSetInfo beatmapSet, bool noVideo = false) - : base(beatmapSet) { + this.beatmapSet = beatmapSet; this.noVideo = noVideo; Width = 120; @@ -49,13 +52,17 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons { FillFlowContainer textSprites; - AddInternal(shakeContainer = new ShakeContainer + InternalChildren = new Drawable[] { - RelativeSizeAxes = Axes.Both, - Masking = true, - CornerRadius = 5, - Child = button = new HeaderButton { RelativeSizeAxes = Axes.Both }, - }); + shakeContainer = new ShakeContainer + { + RelativeSizeAxes = Axes.Both, + Masking = true, + CornerRadius = 5, + Child = button = new HeaderButton { RelativeSizeAxes = Axes.Both }, + }, + downloadTracker = new BeatmapDownloadTracker(beatmapSet), + }; button.AddRange(new Drawable[] { @@ -83,7 +90,7 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons }, } }, - new DownloadProgressBar(BeatmapSet.Value) + new DownloadProgressBar(beatmapSet) { Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, @@ -92,20 +99,20 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons button.Action = () => { - if (State.Value != DownloadState.NotDownloaded) + if (downloadTracker.State.Value != DownloadState.NotDownloaded) { shakeContainer.Shake(); return; } - beatmaps.Download(BeatmapSet.Value, noVideo); + beatmaps.Download(beatmapSet, noVideo); }; localUser.BindTo(api.LocalUser); localUser.BindValueChanged(userChanged, true); button.Enabled.BindValueChanged(enabledChanged, true); - State.BindValueChanged(state => + downloadTracker.State.BindValueChanged(state => { switch (state.NewValue) { @@ -161,7 +168,7 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons private LocalisableString getVideoSuffixText() { - if (!BeatmapSet.Value.OnlineInfo.HasVideo) + if (!beatmapSet.OnlineInfo.HasVideo) return string.Empty; return noVideo ? BeatmapsetsStrings.ShowDetailsDownloadNoVideo : BeatmapsetsStrings.ShowDetailsDownloadVideo; diff --git a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs index 585b024623..16dd7b498b 100644 --- a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs +++ b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs @@ -302,7 +302,7 @@ namespace osu.Game.Screens.OnlinePlay { base.LoadComplete(); - State.BindValueChanged(stateChanged, true); + DownloadTracker.State.BindValueChanged(stateChanged, true); FinishTransforms(true); } diff --git a/osu.Game/Screens/Ranking/ReplayDownloadButton.cs b/osu.Game/Screens/Ranking/ReplayDownloadButton.cs index e644eb671a..f7325d2eb4 100644 --- a/osu.Game/Screens/Ranking/ReplayDownloadButton.cs +++ b/osu.Game/Screens/Ranking/ReplayDownloadButton.cs @@ -4,6 +4,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Online; @@ -12,13 +13,17 @@ using osuTK; namespace osu.Game.Screens.Ranking { - public class ReplayDownloadButton : DownloadTrackingComposite + public class ReplayDownloadButton : CompositeDrawable { - public Bindable Score => Model; + public readonly Bindable Score = new Bindable(); + + protected readonly Bindable State = new Bindable(); private DownloadButton button; private ShakeContainer shakeContainer; + private ScoreDownloadTracker downloadTracker; + private ReplayAvailability replayAvailability { get @@ -26,7 +31,7 @@ namespace osu.Game.Screens.Ranking if (State.Value == DownloadState.LocallyAvailable) return ReplayAvailability.Local; - if (!string.IsNullOrEmpty(Model.Value?.Hash)) + if (!string.IsNullOrEmpty(Score.Value?.Hash)) return ReplayAvailability.Online; return ReplayAvailability.NotAvailable; @@ -34,8 +39,8 @@ namespace osu.Game.Screens.Ranking } public ReplayDownloadButton(ScoreInfo score) - : base(score) { + Score.Value = score; Size = new Vector2(50, 30); } @@ -56,11 +61,11 @@ namespace osu.Game.Screens.Ranking switch (State.Value) { case DownloadState.LocallyAvailable: - game?.PresentScore(Model.Value, ScorePresentType.Gameplay); + game?.PresentScore(Score.Value, ScorePresentType.Gameplay); break; case DownloadState.NotDownloaded: - scores.Download(Model.Value, false); + scores.Download(Score.Value, false); break; case DownloadState.Importing: @@ -70,17 +75,25 @@ namespace osu.Game.Screens.Ranking } }; - State.BindValueChanged(state => + Score.BindValueChanged(score => { - button.State.Value = state.NewValue; + downloadTracker?.Expire(); + if (score.NewValue != null) + { + AddInternal(downloadTracker = new ScoreDownloadTracker(score.NewValue) + { + State = { BindTarget = State } + }); + } + + button.Enabled.Value = replayAvailability != ReplayAvailability.NotAvailable; updateTooltip(); }, true); - Model.BindValueChanged(_ => + State.BindValueChanged(state => { - button.Enabled.Value = replayAvailability != ReplayAvailability.NotAvailable; - + button.State.Value = state.NewValue; updateTooltip(); }, true); } From 746d6a4c166f274e456b36a7e7f40fcd25ae39f4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 Oct 2021 21:26:26 +0900 Subject: [PATCH 145/260] Fix some oversights and test failures --- .../Online/TestSceneDirectDownloadButton.cs | 3 +-- osu.Game/Online/BeatmapDownloadTracker.cs | 8 +++----- .../OnlinePlayBeatmapAvailabilityTracker.cs | 12 +++++++----- .../Panels/BeatmapPanelDownloadButton.cs | 18 +++++++++--------- .../BeatmapSet/BeatmapSetHeaderContent.cs | 2 +- .../OnlinePlay/DrawableRoomPlaylistItem.cs | 7 +++---- .../Screens/OnlinePlay/Match/RoomSubScreen.cs | 2 -- .../Multiplayer/MultiplayerMatchSubScreen.cs | 4 ++-- .../Screens/Ranking/ReplayDownloadButton.cs | 2 +- 9 files changed, 27 insertions(+), 31 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneDirectDownloadButton.cs b/osu.Game.Tests/Visual/Online/TestSceneDirectDownloadButton.cs index 879e35057b..bb7fcc2fce 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneDirectDownloadButton.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneDirectDownloadButton.cs @@ -5,7 +5,6 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; -using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Online; using osu.Game.Online.API.Requests.Responses; @@ -145,7 +144,7 @@ namespace osu.Game.Tests.Visual.Online { public new bool DownloadEnabled => base.DownloadEnabled; - public DownloadState DownloadState => this.ChildrenOfType().First().State.Value; + public DownloadState DownloadState => State.Value; public TestDownloadButton(BeatmapSetInfo beatmapSet) : base(beatmapSet) diff --git a/osu.Game/Online/BeatmapDownloadTracker.cs b/osu.Game/Online/BeatmapDownloadTracker.cs index 6df74efe9c..181968c02f 100644 --- a/osu.Game/Online/BeatmapDownloadTracker.cs +++ b/osu.Game/Online/BeatmapDownloadTracker.cs @@ -34,7 +34,7 @@ namespace osu.Game.Online // Used to interact with manager classes that don't support interface types. Will eventually be replaced. var beatmapSetInfo = new BeatmapSetInfo { OnlineBeatmapSetID = TrackedItem.OnlineID }; - if ((TrackedItem as BeatmapSetInfo)?.ID > 0 || Manager?.IsAvailableLocally(beatmapSetInfo) == true) + if (Manager?.IsAvailableLocally(beatmapSetInfo) == true) UpdateState(DownloadState.LocallyAvailable); else if (Manager != null) attachDownload(Manager.GetExistingDownload(beatmapSetInfo)); @@ -122,10 +122,8 @@ namespace osu.Game.Online { Schedule(() => { - if (!checkEquality(item, TrackedItem)) - return; - - UpdateState(DownloadState.LocallyAvailable); + if (checkEquality(item, TrackedItem)) + UpdateState(DownloadState.LocallyAvailable); }); } } diff --git a/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs b/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs index 6dd1204555..5e09f42aa8 100644 --- a/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs +++ b/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs @@ -4,6 +4,7 @@ using System; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Logging; using osu.Framework.Threading; @@ -17,10 +18,12 @@ namespace osu.Game.Online.Rooms /// This differs from a regular download tracking composite as this accounts for the /// databased beatmap set's checksum, to disallow from playing with an altered version of the beatmap. /// - public class OnlinePlayBeatmapAvailabilityTracker : CompositeDrawable + public sealed class OnlinePlayBeatmapAvailabilityTracker : CompositeDrawable { public readonly IBindable SelectedItem = new Bindable(); + protected override bool RequiresChildrenUpdate => true; + [Resolved] private BeatmapManager beatmapManager { get; set; } @@ -46,9 +49,10 @@ namespace osu.Game.Online.Rooms if (item.NewValue == null) return; - downloadTracker?.Expire(); - downloadTracker = new BeatmapDownloadTracker(item.NewValue.Beatmap.Value.BeatmapSet); + downloadTracker?.RemoveAndDisposeImmediately(); + downloadTracker = new BeatmapDownloadTracker(item.NewValue.Beatmap.Value.BeatmapSet); + downloadTracker.State.BindValueChanged(_ => updateAvailability()); downloadTracker.Progress.BindValueChanged(_ => { if (downloadTracker.State.Value != DownloadState.Downloading) @@ -60,8 +64,6 @@ namespace osu.Game.Online.Rooms progressUpdate = Scheduler.AddDelayed(updateAvailability, progressUpdate == null ? 0 : 500); }); - downloadTracker.State.BindValueChanged(_ => updateAvailability(), true); - AddInternal(downloadTracker); }, true); } diff --git a/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelDownloadButton.cs b/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelDownloadButton.cs index 5c6472cd1b..dd12e8e467 100644 --- a/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelDownloadButton.cs +++ b/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelDownloadButton.cs @@ -36,6 +36,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels public BeatmapPanelDownloadButton(BeatmapSetInfo beatmapSet) { this.beatmapSet = beatmapSet; + InternalChildren = new Drawable[] { shakeContainer = new ShakeContainer @@ -44,6 +45,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels Child = button = new DownloadButton { RelativeSizeAxes = Axes.Both, + State = { BindTarget = State } }, }, DownloadTracker = new BeatmapDownloadTracker(beatmapSet) @@ -60,14 +62,6 @@ namespace osu.Game.Overlays.BeatmapListing.Panels }); } - protected override void LoadComplete() - { - base.LoadComplete(); - - ((IBindable)button.State).BindTo(DownloadTracker.State); - FinishTransforms(true); - } - [BackgroundDependencyLoader(true)] private void load(OsuGame game, BeatmapManager beatmaps, OsuConfigManager osuConfig) { @@ -96,7 +90,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels } }; - DownloadTracker.State.BindValueChanged(state => + State.BindValueChanged(state => { switch (state.NewValue) { @@ -116,5 +110,11 @@ namespace osu.Game.Overlays.BeatmapListing.Panels } }, true); } + + protected override void LoadComplete() + { + base.LoadComplete(); + FinishTransforms(true); + } } } diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs b/osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs index e0a09ed520..624d07fb0f 100644 --- a/osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs +++ b/osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs @@ -233,7 +233,7 @@ namespace osu.Game.Overlays.BeatmapSet Picker.BeatmapSet = rulesetSelector.BeatmapSet = author.BeatmapSet = beatmapAvailability.BeatmapSet = Details.BeatmapSet = setInfo.NewValue; cover.BeatmapSet = setInfo.NewValue; - downloadTracker?.Expire(); + downloadTracker?.RemoveAndDisposeImmediately(); if (setInfo.NewValue == null) { diff --git a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs index 16dd7b498b..5639265617 100644 --- a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs +++ b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs @@ -289,7 +289,8 @@ namespace osu.Game.Screens.OnlinePlay [Resolved] private BeatmapManager beatmapManager { get; set; } - public override bool IsPresent => base.IsPresent || Scheduler.HasPendingTasks; + // required for download tracking, as this button hides itself. can probably be removed with a bit of consideration. + public override bool IsPresent => true; public PlaylistDownloadButton(PlaylistItem playlistItem) : base(playlistItem.Beatmap.Value.BeatmapSet) @@ -300,10 +301,8 @@ namespace osu.Game.Screens.OnlinePlay protected override void LoadComplete() { + State.BindValueChanged(stateChanged, true); base.LoadComplete(); - - DownloadTracker.State.BindValueChanged(stateChanged, true); - FinishTransforms(true); } private void stateChanged(ValueChangedEvent state) diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs index bcb793062b..270d12ec97 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -67,8 +67,6 @@ namespace osu.Game.Screens.OnlinePlay.Match [Cached] protected OnlinePlayBeatmapAvailabilityTracker BeatmapAvailabilityTracker { get; private set; } - protected IBindable BeatmapAvailability => BeatmapAvailabilityTracker.Availability; - public readonly Room Room; private readonly bool allowEdit; diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index 544eac4127..0af41c977b 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -66,7 +66,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer SelectedItem.BindTo(client.CurrentMatchPlayingItem); - BeatmapAvailability.BindValueChanged(updateBeatmapAvailability, true); + BeatmapAvailabilityTracker.Availability.BindValueChanged(updateBeatmapAvailability, true); UserMods.BindValueChanged(onUserModsChanged); client.LoadRequested += onLoadRequested; @@ -362,7 +362,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer private void onLoadRequested() { - if (BeatmapAvailability.Value.State != DownloadState.LocallyAvailable) + if (BeatmapAvailabilityTracker.Availability.Value.State != DownloadState.LocallyAvailable) return; // In the case of spectating, IMultiplayerClient.LoadRequested can be fired while the game is still spectating a previous session. diff --git a/osu.Game/Screens/Ranking/ReplayDownloadButton.cs b/osu.Game/Screens/Ranking/ReplayDownloadButton.cs index f7325d2eb4..66b3c973f5 100644 --- a/osu.Game/Screens/Ranking/ReplayDownloadButton.cs +++ b/osu.Game/Screens/Ranking/ReplayDownloadButton.cs @@ -77,7 +77,7 @@ namespace osu.Game.Screens.Ranking Score.BindValueChanged(score => { - downloadTracker?.Expire(); + downloadTracker?.RemoveAndDisposeImmediately(); if (score.NewValue != null) { From 347f41f367c67e2e7c2d79f76e1bdcc66ea907f3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 Oct 2021 23:54:02 +0900 Subject: [PATCH 146/260] Change test to allow for potentially single-frame delayed events firing --- .../Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs b/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs index 558b874234..5e7ce3abf5 100644 --- a/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs +++ b/osu.Game.Tests/Online/TestSceneOnlinePlayBeatmapAvailabilityTracker.cs @@ -128,7 +128,7 @@ namespace osu.Game.Tests.Online private void addAvailabilityCheckStep(string description, Func expected) { - AddAssert(description, () => availabilityTracker.Availability.Value.Equals(expected.Invoke())); + AddUntilStep(description, () => availabilityTracker.Availability.Value.Equals(expected.Invoke())); } private static BeatmapInfo getTestBeatmapInfo(string archiveFile) From 7245baba691b96dd2c86c9abdebb2b3b7139c61d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Oct 2021 00:13:25 +0900 Subject: [PATCH 147/260] Remove `IBeatmapSetOnlineInfo` implementation from `BeatmapSetInfo` to avoid null cases --- .../TestSceneUpdateableBeatmapSetCover.cs | 12 ++++++------ .../Components/TournamentBeatmapPanel.cs | 2 +- osu.Game/Beatmaps/BeatmapSetInfo.cs | 2 +- .../Drawables/UpdateableBeatmapBackgroundSprite.cs | 5 +++++ .../Drawables/UpdateableOnlineBeatmapSetCover.cs | 2 +- .../BeatmapListing/BeatmapListingSearchControl.cs | 2 +- .../Overlays/BeatmapListing/Panels/BeatmapPanel.cs | 2 +- .../Overlays/BeatmapSet/BeatmapSetHeaderContent.cs | 2 +- .../Overlays/Dashboard/Home/DashboardBeatmapPanel.cs | 2 +- .../Sections/Historical/DrawableMostPlayedBeatmap.cs | 2 +- 10 files changed, 19 insertions(+), 14 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapSetCover.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapSetCover.cs index 3ac3002713..f67f6258cc 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapSetCover.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapSetCover.cs @@ -25,7 +25,7 @@ namespace osu.Game.Tests.Visual.UserInterface { AddStep("setup cover", () => Child = new UpdateableOnlineBeatmapSetCover(coverType) { - BeatmapSet = CreateBeatmap(Ruleset.Value).BeatmapInfo.BeatmapSet, + OnlineInfo = CreateBeatmap(Ruleset.Value).BeatmapInfo.BeatmapSet.OnlineInfo, RelativeSizeAxes = Axes.Both, Masking = true, }); @@ -68,7 +68,7 @@ namespace osu.Game.Tests.Visual.UserInterface var cover = new UpdateableOnlineBeatmapSetCover(coverType) { - BeatmapSet = setInfo, + OnlineInfo = setInfo.OnlineInfo, Height = 100, Masking = true, }; @@ -99,12 +99,12 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("setup cover", () => Child = updateableCover = new TestUpdateableOnlineBeatmapSetCover { - BeatmapSet = CreateBeatmap(Ruleset.Value).BeatmapInfo.BeatmapSet, + OnlineInfo = CreateBeatmap(Ruleset.Value).BeatmapInfo.BeatmapSet.OnlineInfo, RelativeSizeAxes = Axes.Both, Masking = true, }); - AddStep("change model", () => updateableCover.BeatmapSet = null); + AddStep("change model", () => updateableCover.OnlineInfo = null); AddWaitStep("wait some", 5); AddAssert("no cover added", () => !updateableCover.ChildrenOfType().Any()); } @@ -117,7 +117,7 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("setup cover", () => Child = updateableCover = new TestUpdateableOnlineBeatmapSetCover(0) { - BeatmapSet = createBeatmapWithCover("https://assets.ppy.sh/beatmaps/1189904/covers/cover.jpg"), + OnlineInfo = createBeatmapWithCover("https://assets.ppy.sh/beatmaps/1189904/covers/cover.jpg").OnlineInfo, RelativeSizeAxes = Axes.Both, Masking = true, Alpha = 0.4f @@ -128,7 +128,7 @@ namespace osu.Game.Tests.Visual.UserInterface AddUntilStep("wait for fade complete", () => initialCover.Alpha == 1); AddStep("switch beatmap", - () => updateableCover.BeatmapSet = createBeatmapWithCover("https://assets.ppy.sh/beatmaps/1079428/covers/cover.jpg")); + () => updateableCover.OnlineInfo = createBeatmapWithCover("https://assets.ppy.sh/beatmaps/1079428/covers/cover.jpg").OnlineInfo); AddUntilStep("new cover loaded", () => updateableCover.ChildrenOfType().Except(new[] { initialCover }).Any()); } diff --git a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs index be29566e07..70544f3a3f 100644 --- a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs +++ b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs @@ -62,7 +62,7 @@ namespace osu.Game.Tournament.Components { RelativeSizeAxes = Axes.Both, Colour = OsuColour.Gray(0.5f), - BeatmapSet = BeatmapInfo.BeatmapSet as IBeatmapSetOnlineInfo, + OnlineInfo = BeatmapInfo.BeatmapSet as IBeatmapSetOnlineInfo, }, new FillFlowContainer { diff --git a/osu.Game/Beatmaps/BeatmapSetInfo.cs b/osu.Game/Beatmaps/BeatmapSetInfo.cs index ae32ad000e..6ed617898f 100644 --- a/osu.Game/Beatmaps/BeatmapSetInfo.cs +++ b/osu.Game/Beatmaps/BeatmapSetInfo.cs @@ -14,7 +14,7 @@ using osu.Game.Online.API.Requests.Responses; namespace osu.Game.Beatmaps { [ExcludeFromDynamicCompile] - public class BeatmapSetInfo : IHasPrimaryKey, IHasFiles, ISoftDelete, IEquatable, IBeatmapSetInfo, IBeatmapSetOnlineInfo + public class BeatmapSetInfo : IHasPrimaryKey, IHasFiles, ISoftDelete, IEquatable, IBeatmapSetInfo { public int ID { get; set; } diff --git a/osu.Game/Beatmaps/Drawables/UpdateableBeatmapBackgroundSprite.cs b/osu.Game/Beatmaps/Drawables/UpdateableBeatmapBackgroundSprite.cs index 636c568bd0..4100fe9586 100644 --- a/osu.Game/Beatmaps/Drawables/UpdateableBeatmapBackgroundSprite.cs +++ b/osu.Game/Beatmaps/Drawables/UpdateableBeatmapBackgroundSprite.cs @@ -57,7 +57,12 @@ namespace osu.Game.Beatmaps.Drawables return new OnlineBeatmapSetCover(online, beatmapSetCoverType); if (model is BeatmapInfo localModel) + { + if (localModel.BeatmapSet?.OnlineInfo != null) + return new OnlineBeatmapSetCover(localModel.BeatmapSet.OnlineInfo, beatmapSetCoverType); + return new BeatmapBackgroundSprite(beatmaps.GetWorkingBeatmap(localModel)); + } return new BeatmapBackgroundSprite(beatmaps.DefaultBeatmap); } diff --git a/osu.Game/Beatmaps/Drawables/UpdateableOnlineBeatmapSetCover.cs b/osu.Game/Beatmaps/Drawables/UpdateableOnlineBeatmapSetCover.cs index 73f87beb58..4a6a1b888e 100644 --- a/osu.Game/Beatmaps/Drawables/UpdateableOnlineBeatmapSetCover.cs +++ b/osu.Game/Beatmaps/Drawables/UpdateableOnlineBeatmapSetCover.cs @@ -13,7 +13,7 @@ namespace osu.Game.Beatmaps.Drawables { private readonly BeatmapSetCoverType coverType; - public IBeatmapSetOnlineInfo BeatmapSet + public IBeatmapSetOnlineInfo OnlineInfo { get => Model; set => Model = value; diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchControl.cs b/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchControl.cs index da2dcfebdf..776a8e73b0 100644 --- a/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchControl.cs +++ b/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchControl.cs @@ -59,7 +59,7 @@ namespace osu.Game.Overlays.BeatmapListing return; } - beatmapCover.BeatmapSet = value; + beatmapCover.OnlineInfo = value.OnlineInfo; beatmapCover.FadeTo(0.1f, 200, Easing.OutQuint); } } diff --git a/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanel.cs b/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanel.cs index 779f3860f2..c7fa98f159 100644 --- a/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanel.cs +++ b/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanel.cs @@ -163,7 +163,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels protected Drawable CreateBackground() => new UpdateableOnlineBeatmapSetCover { RelativeSizeAxes = Axes.Both, - BeatmapSet = SetInfo, + OnlineInfo = SetInfo.OnlineInfo, }; public class Statistic : FillFlowContainer diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs b/osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs index 6f85846720..4c94e95383 100644 --- a/osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs +++ b/osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs @@ -227,7 +227,7 @@ namespace osu.Game.Overlays.BeatmapSet BeatmapSet.BindValueChanged(setInfo => { Picker.BeatmapSet = rulesetSelector.BeatmapSet = author.BeatmapSet = beatmapAvailability.BeatmapSet = Details.BeatmapSet = setInfo.NewValue; - cover.BeatmapSet = setInfo.NewValue; + cover.OnlineInfo = setInfo.NewValue?.OnlineInfo; if (setInfo.NewValue == null) { diff --git a/osu.Game/Overlays/Dashboard/Home/DashboardBeatmapPanel.cs b/osu.Game/Overlays/Dashboard/Home/DashboardBeatmapPanel.cs index edc737d8fe..50186def37 100644 --- a/osu.Game/Overlays/Dashboard/Home/DashboardBeatmapPanel.cs +++ b/osu.Game/Overlays/Dashboard/Home/DashboardBeatmapPanel.cs @@ -82,7 +82,7 @@ namespace osu.Game.Overlays.Dashboard.Home RelativeSizeAxes = Axes.Both, Anchor = Anchor.Centre, Origin = Anchor.Centre, - BeatmapSet = SetInfo + OnlineInfo = SetInfo.OnlineInfo } }, new Container diff --git a/osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedBeatmap.cs b/osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedBeatmap.cs index c4c8bfb84f..ac4299ae49 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedBeatmap.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedBeatmap.cs @@ -46,7 +46,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical { RelativeSizeAxes = Axes.Y, Width = cover_width, - BeatmapSet = mostPlayed.BeatmapSet, + OnlineInfo = mostPlayed.BeatmapSet, }, new Container { From 290f583ae0d19aa35549d00e101598d96de7500a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Oct 2021 00:59:48 +0900 Subject: [PATCH 148/260] Fix incorrect initial state --- osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs b/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs index 5e09f42aa8..87348fbd1c 100644 --- a/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs +++ b/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs @@ -32,7 +32,7 @@ namespace osu.Game.Online.Rooms /// public IBindable Availability => availability; - private readonly Bindable availability = new Bindable(BeatmapAvailability.LocallyAvailable()); + private readonly Bindable availability = new Bindable(BeatmapAvailability.NotDownloaded()); private ScheduledDelegate progressUpdate; From cd04ca12400e641b08c3e06f99832dd39c3c21d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 27 Oct 2021 20:35:19 +0200 Subject: [PATCH 149/260] Remove unused using statement --- osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs index a106f3ea95..ebb348c3a2 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs @@ -4,7 +4,6 @@ using System; using System.Diagnostics; using System.Linq; -using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; From 798349243fabb373d290303d618809ab779607da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 27 Oct 2021 20:38:52 +0200 Subject: [PATCH 150/260] Cache test request handler in screen navigation test --- .../Visual/Navigation/TestSceneScreenNavigation.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs index 87223a3eca..1df0680b69 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs @@ -9,6 +9,7 @@ using osu.Framework.Screens; using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Graphics.UserInterface; +using osu.Game.Online.API; using osu.Game.Online.Multiplayer; using osu.Game.Overlays; using osu.Game.Overlays.Mods; @@ -24,6 +25,7 @@ using osu.Game.Screens.Select; using osu.Game.Screens.Select.Options; using osu.Game.Tests.Beatmaps.IO; using osu.Game.Tests.Visual.Multiplayer; +using osu.Game.Tests.Visual.OnlinePlay; using osuTK; using osuTK.Input; @@ -459,9 +461,19 @@ namespace osu.Game.Tests.Visual.Navigation [Cached(typeof(MultiplayerClient))] public readonly TestMultiplayerClient Client; + [Cached] + public readonly TestRoomRequestsHandler RequestsHandler; + public TestMultiplayer() { Client = new TestMultiplayerClient((TestMultiplayerRoomManager)RoomManager); + RequestsHandler = new TestRoomRequestsHandler(); + } + + [BackgroundDependencyLoader] + private void load(IAPIProvider api, OsuGameBase game) + { + ((DummyAPIAccess)api).HandleRequest = request => RequestsHandler.HandleRequest(request, api.LocalUser.Value, game); } protected override RoomManager CreateRoomManager() => new TestMultiplayerRoomManager(); From 65a7b6776e72000e8784c60216d207f5a5c774e2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Oct 2021 05:50:22 +0900 Subject: [PATCH 151/260] Add note regarding `OnlineInfo` being temporary --- osu.Game/Beatmaps/BeatmapSetInfo.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Beatmaps/BeatmapSetInfo.cs b/osu.Game/Beatmaps/BeatmapSetInfo.cs index 6ed617898f..79cc8b70fb 100644 --- a/osu.Game/Beatmaps/BeatmapSetInfo.cs +++ b/osu.Game/Beatmaps/BeatmapSetInfo.cs @@ -35,6 +35,7 @@ namespace osu.Game.Beatmaps [NotNull] public List Files { get; set; } = new List(); + // This field is temporary and only used by `APIBeatmapSet.ToBeatmapSet` (soon to be removed) and tests (to be updated to provide APIBeatmapSet instead). [NotMapped] public APIBeatmapSet OnlineInfo { get; set; } From 3905ecd18b37abc9b988359f0891742d7fed6ca4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Oct 2021 13:09:03 +0900 Subject: [PATCH 152/260] Use retrieval of bindable and tidy up code slightly --- osu.Game/OsuGame.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 25ce62b787..f9e080a93c 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -158,6 +158,8 @@ namespace osu.Game private Bindable configRuleset; + private Bindable uiScale; + private Bindable configSkin; private readonly string[] args; @@ -219,6 +221,7 @@ namespace osu.Game // bind config int to database RulesetInfo configRuleset = LocalConfig.GetBindable(OsuSetting.Ruleset); + uiScale = LocalConfig.GetBindable(OsuSetting.UIScale); var preferredRuleset = RulesetStore.GetRuleset(configRuleset.Value); @@ -1022,26 +1025,23 @@ namespace osu.Game public override bool OnPressed(KeyBindingPressEvent e) { + const float adjustment_increment = 0.05f; + switch (e.Action) { case PlatformAction.ZoomIn: - adjustUIScaling(1); + uiScale.Value += adjustment_increment; return true; case PlatformAction.ZoomOut: - adjustUIScaling(-1); + uiScale.Value -= adjustment_increment; return true; case PlatformAction.ZoomDefault: - LocalConfig.GetBindable(OsuSetting.UIScale).SetDefault(); + uiScale.SetDefault(); return true; } - void adjustUIScaling(int amount) - { - LocalConfig.SetValue(OsuSetting.UIScale, LocalConfig.Get(OsuSetting.UIScale) + (0.05f * amount)); - } - return base.OnPressed(e); } From 1213c5553a64944df87eeae67e2b2a20a6dcc02c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 28 Oct 2021 13:16:50 +0900 Subject: [PATCH 153/260] Remove forgotten attribute --- .../Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs index 10633f4a4f..832998d5d3 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerGameplayLeaderboard.cs @@ -8,7 +8,6 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; -using osu.Framework.Testing; using osu.Framework.Utils; using osu.Game.Configuration; using osu.Game.Online.API; @@ -40,7 +39,6 @@ namespace osu.Game.Tests.Visual.Multiplayer Dependencies.Cache(config = new OsuConfigManager(LocalStorage)); } - [SetUpSteps] public override void SetUpSteps() { base.SetUpSteps(); From 0e293471c2b7ee4fb49b6baa4b19697291c67a59 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 28 Oct 2021 14:29:49 +0900 Subject: [PATCH 154/260] Clean up multiplayer-screen tests by removing intermediate screen --- .../Multiplayer/TestSceneMultiplayer.cs | 78 ++++------------- .../Visual/Multiplayer/TestSceneTeamVersus.cs | 66 ++------------- .../Navigation/TestSceneScreenNavigation.cs | 36 +------- .../Visual/TestMultiplayerScreenStack.cs | 84 +++++++++++++++++++ .../MultiplayerTestSceneDependencies.cs | 2 +- .../Multiplayer/TestMultiplayerRoomManager.cs | 9 +- .../OnlinePlayTestSceneDependencies.cs | 4 +- 7 files changed, 119 insertions(+), 160 deletions(-) create mode 100644 osu.Game.Tests/Visual/TestMultiplayerScreenStack.cs diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs index ebb348c3a2..11caf9f498 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs @@ -17,15 +17,12 @@ using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Database; using osu.Game.Graphics.UserInterface; -using osu.Game.Online.API; using osu.Game.Online.Multiplayer; using osu.Game.Online.Rooms; using osu.Game.Overlays.Mods; using osu.Game.Rulesets; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Mods; -using osu.Game.Screens; -using osu.Game.Screens.OnlinePlay.Components; using osu.Game.Screens.OnlinePlay.Lounge; using osu.Game.Screens.OnlinePlay.Lounge.Components; using osu.Game.Screens.OnlinePlay.Match; @@ -33,8 +30,8 @@ using osu.Game.Screens.OnlinePlay.Multiplayer; using osu.Game.Screens.OnlinePlay.Multiplayer.Match; using osu.Game.Screens.Play; using osu.Game.Screens.Ranking; +using osu.Game.Screens.Spectate; using osu.Game.Tests.Resources; -using osu.Game.Tests.Visual.OnlinePlay; using osu.Game.Users; using osuTK.Input; @@ -46,11 +43,10 @@ namespace osu.Game.Tests.Visual.Multiplayer private RulesetStore rulesets; private BeatmapSetInfo importedSet; - private DependenciesScreen dependenciesScreen; - private TestMultiplayer multiplayerScreen; - private TestMultiplayerClient client; + private TestMultiplayerScreenStack multiplayerScreenStack; - private TestMultiplayerRoomManager roomManager => multiplayerScreen.RoomManager; + private TestMultiplayerClient client => multiplayerScreenStack.Client; + private TestMultiplayerRoomManager roomManager => multiplayerScreenStack.RoomManager; [Cached(typeof(UserLookupCache))] private UserLookupCache lookupCache = new TestUserLookupCache(); @@ -72,22 +68,8 @@ namespace osu.Game.Tests.Visual.Multiplayer importedSet = beatmaps.GetAllUsableBeatmapSetsEnumerable(IncludedDetails.All).First(); }); - AddStep("create multiplayer screen", () => multiplayerScreen = new TestMultiplayer()); - - AddStep("load dependencies", () => - { - client = new TestMultiplayerClient(roomManager); - - // The screen gets suspended so it stops receiving updates. - Child = client; - - LoadScreen(dependenciesScreen = new DependenciesScreen(client)); - }); - - AddUntilStep("wait for dependencies to load", () => dependenciesScreen.IsLoaded); - - AddStep("load multiplayer", () => LoadScreen(multiplayerScreen)); - AddUntilStep("wait for multiplayer to load", () => multiplayerScreen.IsLoaded); + AddStep("load multiplayer", () => LoadScreen(multiplayerScreenStack = new TestMultiplayerScreenStack())); + AddUntilStep("wait for multiplayer to load", () => multiplayerScreenStack.IsLoaded); AddUntilStep("wait for lounge to load", () => this.ChildrenOfType().FirstOrDefault()?.IsLoaded == true); } @@ -443,7 +425,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("start match externally", () => client.StartMatch()); - AddAssert("play not started", () => multiplayerScreen.IsCurrentScreen()); + AddAssert("play not started", () => multiplayerScreenStack.IsCurrentScreen()); } [Test] @@ -487,7 +469,7 @@ namespace osu.Game.Tests.Visual.Multiplayer importedSet = beatmaps.GetAllUsableBeatmapSetsEnumerable(IncludedDetails.All).First(); }); - AddUntilStep("play started", () => !multiplayerScreen.IsCurrentScreen()); + AddUntilStep("play started", () => multiplayerScreenStack.CurrentScreen is SpectatorScreen); } [Test] @@ -529,16 +511,16 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("open mod overlay", () => this.ChildrenOfType().Single().TriggerClick()); - AddStep("invoke on back button", () => multiplayerScreen.OnBackButton()); + AddStep("invoke on back button", () => multiplayerScreenStack.OnBackButton()); AddAssert("mod overlay is hidden", () => this.ChildrenOfType().Single().State.Value == Visibility.Hidden); AddAssert("dialog overlay is hidden", () => DialogOverlay.State.Value == Visibility.Hidden); - testLeave("back button", () => multiplayerScreen.OnBackButton()); + testLeave("back button", () => multiplayerScreenStack.OnBackButton()); // mimics home button and OS window close - testLeave("forced exit", () => multiplayerScreen.Exit()); + testLeave("forced exit", () => multiplayerScreenStack.Exit()); void testLeave(string actionName, Action action) { @@ -579,7 +561,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("click start button", () => InputManager.Click(MouseButton.Left)); - AddUntilStep("wait for player", () => Stack.CurrentScreen is Player); + AddUntilStep("wait for player", () => multiplayerScreenStack.CurrentScreen is Player); // Gameplay runs in real-time, so we need to incrementally check if gameplay has finished in order to not time out. for (double i = 1000; i < TestResources.QUICK_BEATMAP_LENGTH; i += 1000) @@ -588,15 +570,15 @@ namespace osu.Game.Tests.Visual.Multiplayer AddUntilStep($"wait for time > {i}", () => this.ChildrenOfType().SingleOrDefault()?.GameplayClock.CurrentTime > time); } - AddUntilStep("wait for results", () => Stack.CurrentScreen is ResultsScreen); + AddUntilStep("wait for results", () => multiplayerScreenStack.CurrentScreen is ResultsScreen); } private MultiplayerReadyButton readyButton => this.ChildrenOfType().Single(); private void createRoom(Func room) { - AddUntilStep("wait for lounge", () => multiplayerScreen.ChildrenOfType().SingleOrDefault()?.IsLoaded == true); - AddStep("open room", () => multiplayerScreen.ChildrenOfType().Single().Open(room())); + AddUntilStep("wait for lounge", () => multiplayerScreenStack.ChildrenOfType().SingleOrDefault()?.IsLoaded == true); + AddStep("open room", () => multiplayerScreenStack.ChildrenOfType().Single().Open(room())); AddUntilStep("wait for room open", () => this.ChildrenOfType().FirstOrDefault()?.IsLoaded == true); AddWaitStep("wait for transition", 2); @@ -609,35 +591,5 @@ namespace osu.Game.Tests.Visual.Multiplayer AddUntilStep("wait for join", () => client.Room != null); } - - /// - /// Used for the sole purpose of adding as a resolvable dependency. - /// - private class DependenciesScreen : OsuScreen - { - [Cached(typeof(MultiplayerClient))] - public readonly TestMultiplayerClient Client; - - [Cached] - public readonly TestRoomRequestsHandler RequestsHandler = new TestRoomRequestsHandler(); - - public DependenciesScreen(TestMultiplayerClient client) - { - Client = client; - } - - [BackgroundDependencyLoader] - private void load(IAPIProvider api, OsuGameBase game) - { - ((DummyAPIAccess)api).HandleRequest = request => RequestsHandler.HandleRequest(request, api.LocalUser.Value, game); - } - } - - private class TestMultiplayer : Screens.OnlinePlay.Multiplayer.Multiplayer - { - public new TestMultiplayerRoomManager RoomManager { get; private set; } - - protected override RoomManager CreateRoomManager() => RoomManager = new TestMultiplayerRoomManager(); - } } } diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs index 9bb19d4286..ad92886bab 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneTeamVersus.cs @@ -6,25 +6,19 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; -using osu.Framework.Graphics; using osu.Framework.Platform; using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Database; -using osu.Game.Online.API; -using osu.Game.Online.Multiplayer; using osu.Game.Online.Multiplayer.MatchTypes.TeamVersus; using osu.Game.Online.Rooms; using osu.Game.Rulesets; using osu.Game.Rulesets.Osu; -using osu.Game.Screens; -using osu.Game.Screens.OnlinePlay.Components; using osu.Game.Screens.OnlinePlay.Lounge; using osu.Game.Screens.OnlinePlay.Multiplayer; using osu.Game.Screens.OnlinePlay.Multiplayer.Match; using osu.Game.Screens.OnlinePlay.Multiplayer.Participants; using osu.Game.Tests.Resources; -using osu.Game.Tests.Visual.OnlinePlay; using osuTK.Input; namespace osu.Game.Tests.Visual.Multiplayer @@ -35,9 +29,9 @@ namespace osu.Game.Tests.Visual.Multiplayer private RulesetStore rulesets; private BeatmapSetInfo importedSet; - private DependenciesScreen dependenciesScreen; - private TestMultiplayer multiplayerScreen; - private TestMultiplayerClient client; + private TestMultiplayerScreenStack multiplayerScreenStack; + + private TestMultiplayerClient client => multiplayerScreenStack.Client; [Cached(typeof(UserLookupCache))] private UserLookupCache lookupCache = new TestUserLookupCache(); @@ -59,24 +53,8 @@ namespace osu.Game.Tests.Visual.Multiplayer importedSet = beatmaps.GetAllUsableBeatmapSetsEnumerable(IncludedDetails.All).First(); }); - AddStep("create multiplayer screen", () => multiplayerScreen = new TestMultiplayer()); - - AddStep("load dependencies", () => - { - client = new TestMultiplayerClient(multiplayerScreen.RoomManager); - - // The screen gets suspended so it stops receiving updates. - Child = client; - - LoadScreen(dependenciesScreen = new DependenciesScreen(client)); - }); - - AddUntilStep("wait for dependencies screen", () => Stack.CurrentScreen is DependenciesScreen); - AddUntilStep("wait for dependencies to start load", () => dependenciesScreen.LoadState > LoadState.NotLoaded); - AddUntilStep("wait for dependencies to load", () => dependenciesScreen.IsLoaded); - - AddStep("load multiplayer", () => LoadScreen(multiplayerScreen)); - AddUntilStep("wait for multiplayer to load", () => multiplayerScreen.IsLoaded); + AddStep("load multiplayer", () => LoadScreen(multiplayerScreenStack = new TestMultiplayerScreenStack())); + AddUntilStep("wait for multiplayer to load", () => multiplayerScreenStack.IsLoaded); AddUntilStep("wait for lounge to load", () => this.ChildrenOfType().FirstOrDefault()?.IsLoaded == true); } @@ -122,7 +100,7 @@ namespace osu.Game.Tests.Visual.Multiplayer AddStep("press button", () => { - InputManager.MoveMouseTo(multiplayerScreen.ChildrenOfType().First()); + InputManager.MoveMouseTo(multiplayerScreenStack.ChildrenOfType().First()); InputManager.Click(MouseButton.Left); }); AddAssert("user on team 1", () => (client.Room?.Users.FirstOrDefault()?.MatchState as TeamVersusUserState)?.TeamID == 1); @@ -156,7 +134,7 @@ namespace osu.Game.Tests.Visual.Multiplayer private void createRoom(Func room) { - AddStep("open room", () => multiplayerScreen.ChildrenOfType().Single().Open(room())); + AddStep("open room", () => multiplayerScreenStack.ChildrenOfType().Single().Open(room())); AddUntilStep("wait for room open", () => this.ChildrenOfType().FirstOrDefault()?.IsLoaded == true); AddWaitStep("wait for transition", 2); @@ -169,35 +147,5 @@ namespace osu.Game.Tests.Visual.Multiplayer AddUntilStep("wait for join", () => client.Room != null); } - - /// - /// Used for the sole purpose of adding as a resolvable dependency. - /// - private class DependenciesScreen : OsuScreen - { - [Cached(typeof(MultiplayerClient))] - public readonly TestMultiplayerClient Client; - - [Cached] - public readonly TestRoomRequestsHandler RequestsHandler = new TestRoomRequestsHandler(); - - public DependenciesScreen(TestMultiplayerClient client) - { - Client = client; - } - - [BackgroundDependencyLoader] - private void load(IAPIProvider api, OsuGameBase game) - { - ((DummyAPIAccess)api).HandleRequest = request => RequestsHandler.HandleRequest(request, api.LocalUser.Value, game); - } - } - - private class TestMultiplayer : Screens.OnlinePlay.Multiplayer.Multiplayer - { - public new TestMultiplayerRoomManager RoomManager { get; private set; } - - protected override RoomManager CreateRoomManager() => RoomManager = new TestMultiplayerRoomManager(); - } } } diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs index 1df0680b69..5d4594c415 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs @@ -9,23 +9,18 @@ using osu.Framework.Screens; using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Graphics.UserInterface; -using osu.Game.Online.API; -using osu.Game.Online.Multiplayer; using osu.Game.Overlays; using osu.Game.Overlays.Mods; using osu.Game.Overlays.Toolbar; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Mods; using osu.Game.Screens.Menu; -using osu.Game.Screens.OnlinePlay.Components; using osu.Game.Screens.OnlinePlay.Lounge; using osu.Game.Screens.Play; using osu.Game.Screens.Ranking; using osu.Game.Screens.Select; using osu.Game.Screens.Select.Options; using osu.Game.Tests.Beatmaps.IO; -using osu.Game.Tests.Visual.Multiplayer; -using osu.Game.Tests.Visual.OnlinePlay; using osuTK; using osuTK.Input; @@ -335,12 +330,12 @@ namespace osu.Game.Tests.Visual.Navigation [Test] public void TestPushMatchSubScreenAndPressBackButtonImmediately() { - TestMultiplayer multiplayer = null; + TestMultiplayerScreenStack multiplayerScreenStack = null; - PushAndConfirm(() => multiplayer = new TestMultiplayer()); + PushAndConfirm(() => multiplayerScreenStack = new TestMultiplayerScreenStack()); - AddUntilStep("wait for lounge", () => multiplayer.ChildrenOfType().SingleOrDefault()?.IsLoaded == true); - AddStep("open room", () => multiplayer.ChildrenOfType().Single().Open()); + AddUntilStep("wait for lounge", () => multiplayerScreenStack.ChildrenOfType().SingleOrDefault()?.IsLoaded == true); + AddStep("open room", () => multiplayerScreenStack.ChildrenOfType().Single().Open()); AddStep("press back button", () => Game.ChildrenOfType().First().Action()); AddWaitStep("wait two frames", 2); } @@ -455,28 +450,5 @@ namespace osu.Game.Tests.Visual.Navigation protected override bool DisplayStableImportPrompt => false; } - - private class TestMultiplayer : Screens.OnlinePlay.Multiplayer.Multiplayer - { - [Cached(typeof(MultiplayerClient))] - public readonly TestMultiplayerClient Client; - - [Cached] - public readonly TestRoomRequestsHandler RequestsHandler; - - public TestMultiplayer() - { - Client = new TestMultiplayerClient((TestMultiplayerRoomManager)RoomManager); - RequestsHandler = new TestRoomRequestsHandler(); - } - - [BackgroundDependencyLoader] - private void load(IAPIProvider api, OsuGameBase game) - { - ((DummyAPIAccess)api).HandleRequest = request => RequestsHandler.HandleRequest(request, api.LocalUser.Value, game); - } - - protected override RoomManager CreateRoomManager() => new TestMultiplayerRoomManager(); - } } } diff --git a/osu.Game.Tests/Visual/TestMultiplayerScreenStack.cs b/osu.Game.Tests/Visual/TestMultiplayerScreenStack.cs new file mode 100644 index 0000000000..6b3657df44 --- /dev/null +++ b/osu.Game.Tests/Visual/TestMultiplayerScreenStack.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 System.Threading; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Screens; +using osu.Game.Online.API; +using osu.Game.Online.Multiplayer; +using osu.Game.Screens; +using osu.Game.Screens.OnlinePlay.Components; +using osu.Game.Tests.Visual.Multiplayer; +using osu.Game.Tests.Visual.OnlinePlay; + +namespace osu.Game.Tests.Visual +{ + /// + /// An loadable into s via , + /// which provides dependencies for and loads an isolated screen. + ///

+ /// This screen: + /// + /// Provides a to be resolved as a dependency in the screen, + /// which is typically a part of . + /// Rebinds the to handle requests via a . + /// Provides a for the screen. + /// + ///

+ ///
+ public class TestMultiplayerScreenStack : OsuScreen + { + public Screens.OnlinePlay.Multiplayer.Multiplayer MultiplayerScreen => multiplayerScreen; + + public TestMultiplayerRoomManager RoomManager => multiplayerScreen.RoomManager; + + public IScreen CurrentScreen => screenStack.CurrentScreen; + + public new bool IsLoaded => base.IsLoaded && MultiplayerScreen.IsLoaded; + + [Cached(typeof(MultiplayerClient))] + public readonly TestMultiplayerClient Client; + + private readonly OsuScreenStack screenStack; + private readonly TestMultiplayer multiplayerScreen; + + public TestMultiplayerScreenStack() + { + multiplayerScreen = new TestMultiplayer(); + + InternalChildren = new Drawable[] + { + Client = new TestMultiplayerClient(RoomManager), + screenStack = new OsuScreenStack { RelativeSizeAxes = Axes.Both } + }; + + screenStack.Push(multiplayerScreen); + } + + [BackgroundDependencyLoader] + private void load(IAPIProvider api, OsuGameBase game) + { + ((DummyAPIAccess)api).HandleRequest = request => multiplayerScreen.RequestsHandler.HandleRequest(request, api.LocalUser.Value, game); + } + + public override bool OnBackButton() => multiplayerScreen.OnBackButton(); + + public override bool OnExiting(IScreen next) + { + if (screenStack.CurrentScreen == null) + return base.OnExiting(next); + + screenStack.Exit(); + return true; + } + + private class TestMultiplayer : Screens.OnlinePlay.Multiplayer.Multiplayer + { + public new TestMultiplayerRoomManager RoomManager { get; private set; } + public TestRoomRequestsHandler RequestsHandler { get; private set; } + + protected override RoomManager CreateRoomManager() => RoomManager = new TestMultiplayerRoomManager(RequestsHandler = new TestRoomRequestsHandler()); + } + } +} diff --git a/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestSceneDependencies.cs b/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestSceneDependencies.cs index a2b0b066a7..ed349a7103 100644 --- a/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestSceneDependencies.cs +++ b/osu.Game/Tests/Visual/Multiplayer/MultiplayerTestSceneDependencies.cs @@ -31,7 +31,7 @@ namespace osu.Game.Tests.Visual.Multiplayer CacheAs(SpectatorClient); } - protected override IRoomManager CreateRoomManager() => new TestMultiplayerRoomManager(); + protected override IRoomManager CreateRoomManager() => new TestMultiplayerRoomManager(RequestsHandler); protected virtual TestSpectatorClient CreateSpectatorClient() => new TestSpectatorClient(); } diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomManager.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomManager.cs index fef2a0a16d..4129d190be 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomManager.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerRoomManager.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; -using osu.Framework.Allocation; using osu.Game.Online.Rooms; using osu.Game.Screens.OnlinePlay.Components; using osu.Game.Screens.OnlinePlay.Multiplayer; @@ -16,8 +15,12 @@ namespace osu.Game.Tests.Visual.Multiplayer /// public class TestMultiplayerRoomManager : MultiplayerRoomManager { - [Resolved] - private TestRoomRequestsHandler requestsHandler { get; set; } + private readonly TestRoomRequestsHandler requestsHandler; + + public TestMultiplayerRoomManager(TestRoomRequestsHandler requestsHandler) + { + this.requestsHandler = requestsHandler; + } public IReadOnlyList ServerSideRooms => requestsHandler.ServerSideRooms; diff --git a/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestSceneDependencies.cs b/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestSceneDependencies.cs index 9e5264fb12..24c4ff79d4 100644 --- a/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestSceneDependencies.cs +++ b/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestSceneDependencies.cs @@ -34,10 +34,10 @@ namespace osu.Game.Tests.Visual.OnlinePlay public OnlinePlayTestSceneDependencies() { SelectedRoom = new Bindable(); - RoomManager = CreateRoomManager(); + RequestsHandler = new TestRoomRequestsHandler(); OngoingOperationTracker = new OngoingOperationTracker(); AvailabilityTracker = new OnlinePlayBeatmapAvailabilityTracker(); - RequestsHandler = new TestRoomRequestsHandler(); + RoomManager = CreateRoomManager(); dependencies = new DependencyContainer(new CachedModelDependencyContainer(null) { Model = { BindTarget = SelectedRoom } }); From 8bc414e7254781de0271389be14e73e19497f0fc Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 28 Oct 2021 14:34:27 +0900 Subject: [PATCH 155/260] Upgrade nvika to 1.0.3 --- .config/dotnet-tools.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 007e4341b8..1b06aa4d17 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -15,7 +15,7 @@ ] }, "smoogipoo.nvika": { - "version": "1.0.1", + "version": "1.0.3", "commands": [ "nvika" ] @@ -33,4 +33,4 @@ ] } } -} +} \ No newline at end of file From f3d208fecf32d42cb78d77417cc1123916a8d21b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Oct 2021 14:48:36 +0900 Subject: [PATCH 156/260] Remove TODO (tested to work correctly) --- osu.Game.Tournament/Components/SongBar.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tournament/Components/SongBar.cs b/osu.Game.Tournament/Components/SongBar.cs index 9af73a2abb..a74b88c592 100644 --- a/osu.Game.Tournament/Components/SongBar.cs +++ b/osu.Game.Tournament/Components/SongBar.cs @@ -101,7 +101,7 @@ namespace osu.Game.Tournament.Components return; } - double bpm = beatmap.BPM; // TODO: check this works. + double bpm = beatmap.BPM; double length = beatmap.Length; string hardRockExtra = ""; string srExtra = ""; From ef26b0ba8aba42209ddbae7c2de2ef8cd6f2334e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Oct 2021 15:00:30 +0900 Subject: [PATCH 157/260] Fix serlialisation failure during ladder saving causing all existing file content to be deleted --- osu.Game.Tournament/TournamentGameBase.cs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tournament/TournamentGameBase.cs b/osu.Game.Tournament/TournamentGameBase.cs index 978be720df..22cc07ad8d 100644 --- a/osu.Game.Tournament/TournamentGameBase.cs +++ b/osu.Game.Tournament/TournamentGameBase.cs @@ -269,17 +269,19 @@ namespace osu.Game.Tournament ladder.Matches.Where(p => p.LosersProgression.Value != null).Select(p => new TournamentProgression(p.ID, p.LosersProgression.Value.ID, true))) .ToList(); + string serialisedLadder = JsonConvert.SerializeObject(ladder, + new JsonSerializerSettings + { + Formatting = Formatting.Indented, + NullValueHandling = NullValueHandling.Ignore, + DefaultValueHandling = DefaultValueHandling.Ignore, + Converters = new JsonConverter[] { new JsonPointConverter() } + }); + using (var stream = storage.GetStream(bracket_filename, FileAccess.Write, FileMode.Create)) using (var sw = new StreamWriter(stream)) { - sw.Write(JsonConvert.SerializeObject(ladder, - new JsonSerializerSettings - { - Formatting = Formatting.Indented, - NullValueHandling = NullValueHandling.Ignore, - DefaultValueHandling = DefaultValueHandling.Ignore, - Converters = new JsonConverter[] { new JsonPointConverter() } - })); + sw.Write(serialisedLadder); } } From a6669a3892744152b7c4628303325263e45499e1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Oct 2021 15:04:09 +0900 Subject: [PATCH 158/260] Add mention of why the serialisation happens outside stream usage --- osu.Game.Tournament/TournamentGameBase.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game.Tournament/TournamentGameBase.cs b/osu.Game.Tournament/TournamentGameBase.cs index 22cc07ad8d..0e8646d15c 100644 --- a/osu.Game.Tournament/TournamentGameBase.cs +++ b/osu.Game.Tournament/TournamentGameBase.cs @@ -269,6 +269,7 @@ namespace osu.Game.Tournament ladder.Matches.Where(p => p.LosersProgression.Value != null).Select(p => new TournamentProgression(p.ID, p.LosersProgression.Value.ID, true))) .ToList(); + // Serialise before opening stream for writing, so if there's a failure it will leave the file in the previous state. string serialisedLadder = JsonConvert.SerializeObject(ladder, new JsonSerializerSettings { @@ -280,9 +281,7 @@ namespace osu.Game.Tournament using (var stream = storage.GetStream(bracket_filename, FileAccess.Write, FileMode.Create)) using (var sw = new StreamWriter(stream)) - { sw.Write(serialisedLadder); - } } protected override UserInputManager CreateUserInputManager() => new TournamentInputManager(); From 56e3a42abebff9d036810f08d71025de2547edf1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Oct 2021 15:14:29 +0900 Subject: [PATCH 159/260] Remove unused using statement --- osu.Game.Tests/Visual/TestMultiplayerScreenStack.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Tests/Visual/TestMultiplayerScreenStack.cs b/osu.Game.Tests/Visual/TestMultiplayerScreenStack.cs index 6b3657df44..7f1171db1f 100644 --- a/osu.Game.Tests/Visual/TestMultiplayerScreenStack.cs +++ b/osu.Game.Tests/Visual/TestMultiplayerScreenStack.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Threading; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Screens; From 313f78cbfbe1638dfd2827560c584282850e9e8f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 28 Oct 2021 15:24:44 +0900 Subject: [PATCH 160/260] Fix non-null inspection --- osu.Game.Tournament/Components/TournamentBeatmapPanel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs index 328b9278d5..b81a45c1d7 100644 --- a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs +++ b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs @@ -93,7 +93,7 @@ namespace osu.Game.Tournament.Components }, new TournamentSpriteText { - Text = Beatmap.Metadata?.Author, + Text = Beatmap.Metadata.Author, Padding = new MarginPadding { Right = 20 }, Font = OsuFont.Torus.With(weight: FontWeight.Bold, size: 14) }, From b0b27a47d5ba5bd2240cb0ff0175ebce04f00cec Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 28 Oct 2021 15:28:16 +0900 Subject: [PATCH 161/260] Rename test scene --- ...enePlaylistCreation.cs => TestScenePlaylistsRoomCreation.cs} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename osu.Game.Tests/Visual/Playlists/{TestScenePlaylistCreation.cs => TestScenePlaylistsRoomCreation.cs} (98%) diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistCreation.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs similarity index 98% rename from osu.Game.Tests/Visual/Playlists/TestScenePlaylistCreation.cs rename to osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs index c4e6f41f27..1f43d254b6 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistCreation.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs @@ -22,7 +22,7 @@ using osuTK.Input; namespace osu.Game.Tests.Visual.Playlists { - public class TestScenePlaylistCreation : OnlinePlayTestScene + public class TestScenePlaylistsRoomCreation : OnlinePlayTestScene { private BeatmapManager manager; private RulesetStore rulesets; From e2c08fb2e5443e40667253bbe7aee6db93ca4153 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 28 Oct 2021 15:41:59 +0900 Subject: [PATCH 162/260] Create room by clicking button --- .../TestScenePlaylistsRoomCreation.cs | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs index 1f43d254b6..21ab5c761c 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs @@ -54,7 +54,6 @@ namespace osu.Game.Tests.Visual.Playlists { setupAndCreateRoom(room => { - room.RoomID.Value = 1; // forces room creation. room.Name.Value = "my awesome room"; room.Host.Value = API.LocalUser.Value; room.RecentParticipants.Add(room.Host.Value); @@ -84,13 +83,6 @@ namespace osu.Game.Tests.Visual.Playlists }); }); - AddStep("move mouse to create button", () => - { - InputManager.MoveMouseTo(this.ChildrenOfType().Single()); - }); - - AddStep("click", () => InputManager.Click(MouseButton.Left)); - AddAssert("first playlist item selected", () => match.SelectedItem.Value == SelectedRoom.Value.Playlist[0]); } @@ -143,13 +135,12 @@ namespace osu.Game.Tests.Visual.Playlists private void setupAndCreateRoom(Action room) { - AddStep("setup room", () => - { - room(SelectedRoom.Value); + AddStep("setup room", () => room(SelectedRoom.Value)); - // if this isn't done the test will crash when a poll kicks in. - // probably not correct, but works for now. - OnlinePlayDependencies.RoomManager.CreateRoom(SelectedRoom.Value); + AddStep("click create button", () => + { + InputManager.MoveMouseTo(this.ChildrenOfType().Single()); + InputManager.Click(MouseButton.Left); }); } From dce19cf7e47bfaa66619523a8ed9e871c0fe49f8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Oct 2021 15:42:06 +0900 Subject: [PATCH 163/260] Add visual test for `SettingsNumberBox` usage in settings source context --- osu.Game.Tests/Visual/Settings/TestSceneSettingsSource.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/osu.Game.Tests/Visual/Settings/TestSceneSettingsSource.cs b/osu.Game.Tests/Visual/Settings/TestSceneSettingsSource.cs index d530e1f796..fc81d9792e 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneSettingsSource.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneSettingsSource.cs @@ -6,6 +6,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Configuration; +using osu.Game.Overlays.Settings; using osuTK; namespace osu.Game.Tests.Visual.Settings @@ -58,6 +59,13 @@ namespace osu.Game.Tests.Visual.Settings Default = string.Empty, Value = "Sample text" }; + + [SettingSource("Sample number textbox", "Textbox number entry", SettingControlType = typeof(SettingsNumberBox))] + public Bindable IntTextboxBindable { get; } = new Bindable + { + Default = null, + Value = null + }; } private enum TestEnum From 1e1f8c472adc57f9612b41d8cdcba47b55e2efa6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Oct 2021 16:34:12 +0900 Subject: [PATCH 164/260] Add icons to various progress notification states --- .../TestSceneNotificationOverlay.cs | 40 +++++++++++++- .../Notifications/ProgressNotification.cs | 54 +++++++++++++++++-- 2 files changed, 88 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs index d0f6f3fe47..a2aa0499d2 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneNotificationOverlay.cs @@ -41,6 +41,44 @@ namespace osu.Game.Tests.Visual.UserInterface notificationOverlay.UnreadCount.ValueChanged += count => { displayedCount.Text = $"displayed count: {count.NewValue}"; }; }); + [Test] + public void TestCompleteProgress() + { + ProgressNotification notification = null; + AddStep("add progress notification", () => + { + notification = new ProgressNotification + { + Text = @"Uploading to BSS...", + CompletionText = "Uploaded to BSS!", + }; + notificationOverlay.Post(notification); + progressingNotifications.Add(notification); + }); + + AddUntilStep("wait completion", () => notification.State == ProgressNotificationState.Completed); + } + + [Test] + public void TestCancelProgress() + { + ProgressNotification notification = null; + AddStep("add progress notification", () => + { + notification = new ProgressNotification + { + Text = @"Uploading to BSS...", + CompletionText = "Uploaded to BSS!", + }; + notificationOverlay.Post(notification); + progressingNotifications.Add(notification); + }); + + AddWaitStep("wait 3", 3); + + AddStep("cancel notification", () => notification.State = ProgressNotificationState.Cancelled); + } + [Test] public void TestBasicFlow() { @@ -138,7 +176,7 @@ namespace osu.Game.Tests.Visual.UserInterface foreach (var n in progressingNotifications.FindAll(n => n.State == ProgressNotificationState.Active)) { if (n.Progress < 1) - n.Progress += (float)(Time.Elapsed / 400) * RNG.NextSingle(); + n.Progress += (float)(Time.Elapsed / 2000); else n.State = ProgressNotificationState.Completed; } diff --git a/osu.Game/Overlays/Notifications/ProgressNotification.cs b/osu.Game/Overlays/Notifications/ProgressNotification.cs index f8cd31f193..b27e15dd2c 100644 --- a/osu.Game/Overlays/Notifications/ProgressNotification.cs +++ b/osu.Game/Overlays/Notifications/ProgressNotification.cs @@ -4,11 +4,15 @@ using System; using System.Threading; using osu.Framework.Allocation; +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.Framework.Graphics.Sprites; using osu.Game.Graphics; using osu.Game.Graphics.Containers; +using osu.Game.Graphics.UserInterface; using osuTK; using osuTK.Graphics; @@ -16,6 +20,8 @@ namespace osu.Game.Overlays.Notifications { public class ProgressNotification : Notification, IHasCompletionTarget { + private const float loading_spinner_size = 22; + public string Text { set => Schedule(() => textDrawable.Text = value); @@ -65,29 +71,53 @@ namespace osu.Game.Overlays.Notifications private void updateState() { + const double colour_fade_duration = 200; + switch (state) { case ProgressNotificationState.Queued: Light.Colour = colourQueued; Light.Pulsate = false; progressBar.Active = false; + + iconBackground.FadeColour(ColourInfo.GradientVertical(colourQueued, colourQueued.Lighten(0.5f)), colour_fade_duration); + loadingSpinner.Show(); break; case ProgressNotificationState.Active: Light.Colour = colourActive; Light.Pulsate = true; progressBar.Active = true; + + iconBackground.FadeColour(ColourInfo.GradientVertical(colourActive, colourActive.Lighten(0.5f)), colour_fade_duration); + loadingSpinner.Show(); break; case ProgressNotificationState.Cancelled: cancellationTokenSource.Cancel(); + iconBackground.FadeColour(ColourInfo.GradientVertical(Color4.Gray, Color4.Gray.Lighten(0.5f)), colour_fade_duration); + loadingSpinner.Hide(); + + var icon = new SpriteIcon + { + Icon = FontAwesome.Solid.Ban, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(loading_spinner_size), + }; + + IconContent.Add(icon); + + icon.FadeInFromZero(200, Easing.OutQuint); + Light.Colour = colourCancelled; Light.Pulsate = false; progressBar.Active = false; break; case ProgressNotificationState.Completed: + loadingSpinner.Hide(); NotificationContent.MoveToY(-DrawSize.Y / 2, 200, Easing.OutQuint); this.FadeOut(200).Finally(d => Completed()); break; @@ -115,15 +145,13 @@ namespace osu.Game.Overlays.Notifications private Color4 colourActive; private Color4 colourCancelled; + private Box iconBackground; + private LoadingSpinner loadingSpinner; + private readonly TextFlowContainer textDrawable; public ProgressNotification() { - IconContent.Add(new Box - { - RelativeSizeAxes = Axes.Both, - }); - Content.Add(textDrawable = new OsuTextFlowContainer { Colour = OsuColour.Gray(128), @@ -138,6 +166,9 @@ namespace osu.Game.Overlays.Notifications RelativeSizeAxes = Axes.X, }); + // make some extra space for the progress bar. + IconContent.Margin = new MarginPadding { Bottom = 5 }; + State = ProgressNotificationState.Queued; // don't close on click by default. @@ -150,6 +181,19 @@ namespace osu.Game.Overlays.Notifications colourQueued = colours.YellowDark; colourActive = colours.Blue; colourCancelled = colours.Red; + + IconContent.AddRange(new Drawable[] + { + iconBackground = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.White, + }, + loadingSpinner = new LoadingSpinner + { + Size = new Vector2(loading_spinner_size), + } + }); } public override void Close() From bacaa9d20152c77c978abf1e593165e1ade84120 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 28 Oct 2021 16:48:17 +0900 Subject: [PATCH 165/260] Fix playlists test crash --- osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestScene.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestScene.cs b/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestScene.cs index 4771ace1c4..430aae72f8 100644 --- a/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestScene.cs +++ b/osu.Game/Tests/Visual/OnlinePlay/OnlinePlayTestScene.cs @@ -64,7 +64,12 @@ namespace osu.Game.Tests.Visual.OnlinePlay public override void SetUpSteps() { base.SetUpSteps(); - AddStep("setup API", () => ((DummyAPIAccess)API).HandleRequest = request => OnlinePlayDependencies.RequestsHandler.HandleRequest(request, API.LocalUser.Value, game)); + + AddStep("setup API", () => + { + var handler = OnlinePlayDependencies.RequestsHandler; + ((DummyAPIAccess)API).HandleRequest = request => handler.HandleRequest(request, API.LocalUser.Value, game); + }); } /// From b096bf6cd1c8857964e7c884c88665646ea0441f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Oct 2021 16:56:29 +0900 Subject: [PATCH 166/260] Ensure all test beatmaps have unique OnlineIDs to avoid import conflicts The order of operation of imports means that the already-imported instances are considered most correct (they have usually been populated using an online source, although that is skipped in tests), so on two consecutive test imports in a test scene, the second will lose its online IDs completely. This aims to ensure that all test beatmaps will have online IDs, which is the general expected behaviour. --- .../Visual/Playlists/TestScenePlaylistsRoomCreation.cs | 5 +---- osu.Game/Tests/Beatmaps/TestBeatmap.cs | 6 ++++++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs index 21ab5c761c..e9825f25d5 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs @@ -144,10 +144,7 @@ namespace osu.Game.Tests.Visual.Playlists }); } - private void importBeatmap() - { - AddStep("import beatmap", () => importedBeatmap = manager.Import(CreateBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo.BeatmapSet).Result); - } + private void importBeatmap() => AddStep("import beatmap", () => importedBeatmap = manager.Import(CreateBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo.BeatmapSet).Result); private class TestPlaylistsRoomSubScreen : PlaylistsRoomSubScreen { diff --git a/osu.Game/Tests/Beatmaps/TestBeatmap.cs b/osu.Game/Tests/Beatmaps/TestBeatmap.cs index 15b72ce6e3..caf83973c4 100644 --- a/osu.Game/Tests/Beatmaps/TestBeatmap.cs +++ b/osu.Game/Tests/Beatmaps/TestBeatmap.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.IO; using System.Text; +using System.Threading; using osu.Framework.Extensions; using osu.Game.Beatmaps; using osu.Game.IO; @@ -16,6 +17,9 @@ namespace osu.Game.Tests.Beatmaps { public class TestBeatmap : Beatmap { + private static int onlineSetID; + private static int onlineBeatmapID; + public TestBeatmap(RulesetInfo ruleset, bool withHitObjects = true) { var baseBeatmap = CreateBeatmap(); @@ -31,8 +35,10 @@ namespace osu.Game.Tests.Beatmaps BeatmapInfo.RulesetID = ruleset.ID ?? 0; BeatmapInfo.BeatmapSet.Metadata = BeatmapInfo.Metadata; BeatmapInfo.BeatmapSet.Beatmaps = new List { BeatmapInfo }; + BeatmapInfo.BeatmapSet.OnlineBeatmapSetID = Interlocked.Increment(ref onlineSetID); BeatmapInfo.Length = 75000; BeatmapInfo.OnlineInfo = new APIBeatmap(); + BeatmapInfo.OnlineBeatmapID = Interlocked.Increment(ref onlineBeatmapID); BeatmapInfo.BeatmapSet.OnlineInfo = new APIBeatmapSet { Status = BeatmapSetOnlineStatus.Ranked, From 8076e22e70b7e498a1a9fdc1351ca1e71fbf05ef Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Oct 2021 17:10:04 +0900 Subject: [PATCH 167/260] Replace weird import handling --- .../TestSceneDrawableRoomPlaylist.cs | 8 +++++--- .../TestScenePlaylistsRoomCreation.cs | 19 ++++--------------- 2 files changed, 9 insertions(+), 18 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs index 93bdbb79f4..f44b0c9716 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs @@ -38,8 +38,6 @@ namespace osu.Game.Tests.Visual.Multiplayer { Dependencies.Cache(rulesets = new RulesetStore(ContextFactory)); Dependencies.Cache(manager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, Resources, host, Beatmap.Default)); - - manager.Import(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo.BeatmapSet).Wait(); } [Test] @@ -204,7 +202,11 @@ namespace osu.Game.Tests.Visual.Multiplayer [Test] public void TestDownloadButtonHiddenWhenBeatmapExists() { - createPlaylist(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo); + var beatmap = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo; + + AddStep("import beatmap", () => manager.Import(beatmap.BeatmapSet).Wait()); + + createPlaylist(beatmap); assertDownloadButtonVisible(false); diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs index e9825f25d5..cec86fa97e 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs @@ -91,27 +91,16 @@ namespace osu.Game.Tests.Visual.Playlists { BeatmapSetInfo importedSet = null; - // this step is required to make sure the further imports actually get online IDs. - // all the playlist logic relies on online ID matching. - AddStep("remove all matching online IDs", () => - { - var existing = manager.QueryBeatmapSets(s => s.OnlineBeatmapSetID == importedBeatmap.Value.OnlineBeatmapSetID).ToList(); - - foreach (var s in existing) - { - s.OnlineBeatmapSetID = null; - foreach (var b in s.Beatmaps) - b.OnlineBeatmapID = null; - manager.Update(s); - } - }); - AddStep("import altered beatmap", () => { IBeatmap beatmap = CreateBeatmap(new OsuRuleset().RulesetInfo); beatmap.BeatmapInfo.BaseDifficulty.CircleSize = 1; + // intentionally increment online IDs to clash with import below. + beatmap.BeatmapInfo.OnlineBeatmapID++; + beatmap.BeatmapInfo.BeatmapSet.OnlineBeatmapSetID++; + importedSet = manager.Import(beatmap.BeatmapInfo.BeatmapSet).Result.Value; }); From c0848486d69474513975c640052aaff464ca3b38 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Oct 2021 17:10:12 +0900 Subject: [PATCH 168/260] Add missing test coverage for attempt count display --- .../TestScenePlaylistsRoomCreation.cs | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs index cec86fa97e..3f7409c5ba 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs @@ -15,6 +15,7 @@ using osu.Game.Database; using osu.Game.Online.Rooms; using osu.Game.Rulesets; using osu.Game.Rulesets.Osu; +using osu.Game.Screens.OnlinePlay.Components; using osu.Game.Screens.OnlinePlay.Playlists; using osu.Game.Screens.Play; using osu.Game.Tests.Visual.OnlinePlay; @@ -65,10 +66,33 @@ namespace osu.Game.Tests.Visual.Playlists }); }); + AddUntilStep("Progress details are hidden", () => match.ChildrenOfType().FirstOrDefault()?.Parent.Alpha == 0); + AddStep("start match", () => match.ChildrenOfType().First().TriggerClick()); AddUntilStep("player loader loaded", () => Stack.CurrentScreen is PlayerLoader); } + [Test] + public void TestAttemptLimitedMatch() + { + setupAndCreateRoom(room => + { + room.RoomID.Value = 1; // forces room creation. + room.Name.Value = "my awesome room"; + room.MaxAttempts.Value = 5; + room.Host.Value = API.LocalUser.Value; + room.RecentParticipants.Add(room.Host.Value); + room.EndDate.Value = DateTimeOffset.Now.AddMinutes(5); + room.Playlist.Add(new PlaylistItem + { + Beatmap = { Value = importedBeatmap.Value.Beatmaps.First() }, + Ruleset = { Value = new OsuRuleset().RulesetInfo } + }); + }); + + AddUntilStep("Progress details are visible", () => match.ChildrenOfType().FirstOrDefault()?.Parent.Alpha == 1); + } + [Test] public void TestPlaylistItemSelectedOnCreate() { From c96b07c62528a7f44c6f7537f67692ee2b65d486 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 28 Oct 2021 18:24:02 +0900 Subject: [PATCH 169/260] Remove unnecessary/wrong code --- .../Visual/Playlists/TestScenePlaylistsRoomCreation.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs index 3f7409c5ba..cda7e95a46 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsRoomCreation.cs @@ -77,7 +77,6 @@ namespace osu.Game.Tests.Visual.Playlists { setupAndCreateRoom(room => { - room.RoomID.Value = 1; // forces room creation. room.Name.Value = "my awesome room"; room.MaxAttempts.Value = 5; room.Host.Value = API.LocalUser.Value; From 219880d719c8a34d7cd87f86ceb4ab4b01a160a7 Mon Sep 17 00:00:00 2001 From: Xexxar Date: Thu, 28 Oct 2021 14:45:45 +0000 Subject: [PATCH 170/260] add in stanrs review --- osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs index 7ccb5158db..f706646cdf 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs @@ -63,9 +63,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills double aimStrain = currVelocity; // Start strain with regular velocity. - if (Precision.AlmostEquals(osuCurrObj.StrainTime, osuPrevObj.StrainTime, 10)) // If rhythms are the same. + if (Math.Max(osuCurrObj.StrainTime, osuPrevObj.StrainTime) < 1.25 * Math.Min(osuCurrObj.StrainTime, osuPrevObj.StrainTime)) // If rhythms are the same. { - if (osuCurrObj.Angle != null && osuPrevObj.Angle != null) + if (osuCurrObj.Angle != null && osuPrevObj.Angle != null && osuLastObj.Angle != null) { double currAngle = osuCurrObj.Angle.Value; double prevAngle = osuPrevObj.Angle.Value; @@ -83,7 +83,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills acuteAngleBonus *= calcAcuteAngleBonus(prevAngle) // Multiply by previous angle, we don't want to buff unless this is a wiggle type pattern. * Math.Min(angleBonus, 125 / osuCurrObj.StrainTime) // The maximum velocity we buff is equal to 125 / strainTime * Math.Pow(Math.Sin(Math.PI / 2 * Math.Min(1, (100 - osuCurrObj.StrainTime) / 25)), 2) // scale buff from 150 bpm 1/4 to 200 bpm 1/4 - * Math.Pow(Math.Sin(Math.PI / 2 * (Math.Min(100, osuCurrObj.JumpDistance) - 50) / 50), 2); // Buff distance exceeding 50 (radius) up to 100 (diameter). + * Math.Pow(Math.Sin(Math.PI / 2 * (Math.Clamp(osuCurrObj.JumpDistance, 50, 100) - 50) / 50), 2); // Buff distance exceeding 50 (radius) up to 100 (diameter). wideAngleBonus *= angleBonus * (1 - Math.Min(wideAngleBonus, Math.Pow(calcWideAngleBonus(prevAngle), 3))); // Penalize wide angles if they're repeated, reducing the penalty as the prevAngle gets more acute. acuteAngleBonus *= 0.5 + 0.5 * (1 - Math.Min(acuteAngleBonus, Math.Pow(calcAcuteAngleBonus(lastAngle), 3))); // Penalize acute angles if they're repeated, reducing the penalty as the lastAngle gets more obtuse. From ce29d6fd81b9aef105bca0befc77a16ab3fab1e2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Oct 2021 11:13:06 +0900 Subject: [PATCH 171/260] Change scroll speed adjust defaults to F3/F4 Not the most obvious keys, but does match stable expectations so let's go with it for now. Rationale for not using +/- is that local audio offset was bound to those, and people will expect it to be when we get to implementing eventually. Also, adjust the scroll speed is a pretty rare thing to do since it's been constant across beatmaps for years now. --- osu.Game/Input/Bindings/GlobalActionContainer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index 9fd7caadd0..22446634c1 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -84,8 +84,8 @@ namespace osu.Game.Input.Bindings new KeyBinding(InputKey.ExtraMouseButton2, GlobalAction.SkipCutscene), new KeyBinding(InputKey.Tilde, GlobalAction.QuickRetry), new KeyBinding(new[] { InputKey.Control, InputKey.Tilde }, GlobalAction.QuickExit), - new KeyBinding(new[] { InputKey.Control, InputKey.Plus }, GlobalAction.IncreaseScrollSpeed), - new KeyBinding(new[] { InputKey.Control, InputKey.Minus }, GlobalAction.DecreaseScrollSpeed), + new KeyBinding(new[] { InputKey.F3 }, GlobalAction.DecreaseScrollSpeed), + new KeyBinding(new[] { InputKey.F4 }, GlobalAction.IncreaseScrollSpeed), new KeyBinding(new[] { InputKey.Shift, InputKey.Tab }, GlobalAction.ToggleInGameInterface), new KeyBinding(InputKey.MouseMiddle, GlobalAction.PauseGameplay), new KeyBinding(InputKey.Space, GlobalAction.TogglePauseReplay), From 8d21f0b04b430e9c0943213a64b59cdc71ea3e2e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Oct 2021 11:14:22 +0900 Subject: [PATCH 172/260] Add migration to reset conflicting scroll speed key bindings --- osu.Game/Database/RealmContextFactory.cs | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/osu.Game/Database/RealmContextFactory.cs b/osu.Game/Database/RealmContextFactory.cs index 013a2e9d64..40b67beed5 100644 --- a/osu.Game/Database/RealmContextFactory.cs +++ b/osu.Game/Database/RealmContextFactory.cs @@ -6,9 +6,11 @@ using System.Linq; using System.Threading; using osu.Framework.Allocation; using osu.Framework.Development; +using osu.Framework.Input.Bindings; using osu.Framework.Logging; using osu.Framework.Platform; using osu.Framework.Statistics; +using osu.Game.Input.Bindings; using osu.Game.Models; using Realms; @@ -32,8 +34,9 @@ namespace osu.Game.Database /// Version history: /// 6 First tracked version (~20211018) /// 7 Changed OnlineID fields to non-nullable to add indexing support (20211018) + /// 8 Rebind scroll adjust keys to not have control modifier (20211029) /// - private const int schema_version = 7; + private const int schema_version = 8; /// /// Lock object which is held during sections, blocking context creation during blocking periods. @@ -148,6 +151,21 @@ namespace osu.Game.Database private void onMigration(Migration migration, ulong lastSchemaVersion) { + if (lastSchemaVersion < 8) + { + // Ctrl -/+ now adjusts UI scale so let's clear any bindings which overlap these combinations. + // New defaults will be populated by the key store afterwards. + var keyBindings = migration.NewRealm.All(); + + var increaseSpeedBinding = keyBindings.FirstOrDefault(k => k.ActionInt == (int)GlobalAction.IncreaseScrollSpeed); + if (increaseSpeedBinding != null && increaseSpeedBinding.KeyCombination.Keys.SequenceEqual(new[] { InputKey.Control, InputKey.Plus })) + migration.NewRealm.Remove(increaseSpeedBinding); + + var descreaseSpeedBinding = keyBindings.FirstOrDefault(k => k.ActionInt == (int)GlobalAction.DecreaseScrollSpeed); + if (descreaseSpeedBinding != null && descreaseSpeedBinding.KeyCombination.Keys.SequenceEqual(new[] { InputKey.Control, InputKey.Minus })) + migration.NewRealm.Remove(descreaseSpeedBinding); + } + if (lastSchemaVersion < 7) { convertOnlineIDs(); From 3838fe5c6adc2d84964e9aebb934edbed40dd02b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Oct 2021 11:39:15 +0900 Subject: [PATCH 173/260] Fix typo in variable name --- osu.Game/Database/RealmContextFactory.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Database/RealmContextFactory.cs b/osu.Game/Database/RealmContextFactory.cs index 40b67beed5..85f7dd6b89 100644 --- a/osu.Game/Database/RealmContextFactory.cs +++ b/osu.Game/Database/RealmContextFactory.cs @@ -161,9 +161,9 @@ namespace osu.Game.Database if (increaseSpeedBinding != null && increaseSpeedBinding.KeyCombination.Keys.SequenceEqual(new[] { InputKey.Control, InputKey.Plus })) migration.NewRealm.Remove(increaseSpeedBinding); - var descreaseSpeedBinding = keyBindings.FirstOrDefault(k => k.ActionInt == (int)GlobalAction.DecreaseScrollSpeed); - if (descreaseSpeedBinding != null && descreaseSpeedBinding.KeyCombination.Keys.SequenceEqual(new[] { InputKey.Control, InputKey.Minus })) - migration.NewRealm.Remove(descreaseSpeedBinding); + var decreaseSpeedBinding = keyBindings.FirstOrDefault(k => k.ActionInt == (int)GlobalAction.DecreaseScrollSpeed); + if (decreaseSpeedBinding != null && decreaseSpeedBinding.KeyCombination.Keys.SequenceEqual(new[] { InputKey.Control, InputKey.Minus })) + migration.NewRealm.Remove(decreaseSpeedBinding); } if (lastSchemaVersion < 7) From 34d471522018be48cd9f0e0e4e9e947467c647b4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Oct 2021 11:48:36 +0900 Subject: [PATCH 174/260] Allow for `long` online IDs and implement in `ScoreInfo` --- osu.Game/Beatmaps/IBeatmapInfo.cs | 2 +- osu.Game/Beatmaps/IBeatmapSetInfo.cs | 2 +- osu.Game/Database/IHasOnlineID.cs | 4 ++-- osu.Game/Rulesets/IRulesetInfo.cs | 2 +- osu.Game/Scoring/ScoreInfo.cs | 4 +++- 5 files changed, 8 insertions(+), 6 deletions(-) diff --git a/osu.Game/Beatmaps/IBeatmapInfo.cs b/osu.Game/Beatmaps/IBeatmapInfo.cs index 3d51c5d4b6..d206cfaaed 100644 --- a/osu.Game/Beatmaps/IBeatmapInfo.cs +++ b/osu.Game/Beatmaps/IBeatmapInfo.cs @@ -11,7 +11,7 @@ namespace osu.Game.Beatmaps /// /// A single beatmap difficulty. /// - public interface IBeatmapInfo : IHasOnlineID + public interface IBeatmapInfo : IHasOnlineID { /// /// The user-specified name given to this beatmap. diff --git a/osu.Game/Beatmaps/IBeatmapSetInfo.cs b/osu.Game/Beatmaps/IBeatmapSetInfo.cs index 0cfb0c4242..20c46d9063 100644 --- a/osu.Game/Beatmaps/IBeatmapSetInfo.cs +++ b/osu.Game/Beatmaps/IBeatmapSetInfo.cs @@ -12,7 +12,7 @@ namespace osu.Game.Beatmaps /// /// A representation of a collection of beatmap difficulties, generally packaged as an ".osz" archive. /// - public interface IBeatmapSetInfo : IHasOnlineID + public interface IBeatmapSetInfo : IHasOnlineID { /// /// The date when this beatmap was imported. diff --git a/osu.Game/Database/IHasOnlineID.cs b/osu.Game/Database/IHasOnlineID.cs index 6e2be7e1f9..36ae4035c4 100644 --- a/osu.Game/Database/IHasOnlineID.cs +++ b/osu.Game/Database/IHasOnlineID.cs @@ -5,7 +5,7 @@ namespace osu.Game.Database { - public interface IHasOnlineID + public interface IHasOnlineID { /// /// The server-side ID representing this instance, if one exists. Any value 0 or less denotes a missing ID. @@ -14,6 +14,6 @@ namespace osu.Game.Database /// Generally we use -1 when specifying "missing" in code, but values of 0 are also considered missing as the online source /// is generally a MySQL autoincrement value, which can never be 0. /// - int OnlineID { get; } + T OnlineID { get; } } } diff --git a/osu.Game/Rulesets/IRulesetInfo.cs b/osu.Game/Rulesets/IRulesetInfo.cs index 779433dc81..c3bc6c1995 100644 --- a/osu.Game/Rulesets/IRulesetInfo.cs +++ b/osu.Game/Rulesets/IRulesetInfo.cs @@ -11,7 +11,7 @@ namespace osu.Game.Rulesets /// /// A representation of a ruleset's metadata. /// - public interface IRulesetInfo : IHasOnlineID + public interface IRulesetInfo : IHasOnlineID { /// /// The user-exposed name of this ruleset. diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index 5cf22f7945..8eaec0a477 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -19,7 +19,7 @@ using osu.Game.Utils; namespace osu.Game.Scoring { - public class ScoreInfo : IHasFiles, IHasPrimaryKey, ISoftDelete, IEquatable, IDeepCloneable + public class ScoreInfo : IHasFiles, IHasPrimaryKey, ISoftDelete, IEquatable, IDeepCloneable, IHasOnlineID { public int ID { get; set; } @@ -271,5 +271,7 @@ namespace osu.Game.Scoring return ReferenceEquals(this, other); } + + public long OnlineID => OnlineScoreID ?? -1; } } From 748003b016ce84bf4d0f13f98a70ed494cbb8915 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Oct 2021 11:54:19 +0900 Subject: [PATCH 175/260] Simplify `Manager` null check to a single pre-check --- osu.Game/Online/BeatmapDownloadTracker.cs | 26 +++++++++++------------ 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/osu.Game/Online/BeatmapDownloadTracker.cs b/osu.Game/Online/BeatmapDownloadTracker.cs index 181968c02f..4a7d0b660a 100644 --- a/osu.Game/Online/BeatmapDownloadTracker.cs +++ b/osu.Game/Online/BeatmapDownloadTracker.cs @@ -31,25 +31,25 @@ namespace osu.Game.Online [BackgroundDependencyLoader(true)] private void load() { + if (Manager == null) + return; + // Used to interact with manager classes that don't support interface types. Will eventually be replaced. var beatmapSetInfo = new BeatmapSetInfo { OnlineBeatmapSetID = TrackedItem.OnlineID }; - if (Manager?.IsAvailableLocally(beatmapSetInfo) == true) + if (Manager.IsAvailableLocally(beatmapSetInfo)) UpdateState(DownloadState.LocallyAvailable); - else if (Manager != null) + else attachDownload(Manager.GetExistingDownload(beatmapSetInfo)); - if (Manager != null) - { - managerDownloadBegan = Manager.DownloadBegan.GetBoundCopy(); - managerDownloadBegan.BindValueChanged(downloadBegan); - managerDownloadFailed = Manager.DownloadFailed.GetBoundCopy(); - managerDownloadFailed.BindValueChanged(downloadFailed); - managerUpdated = Manager.ItemUpdated.GetBoundCopy(); - managerUpdated.BindValueChanged(itemUpdated); - managerRemoved = Manager.ItemRemoved.GetBoundCopy(); - managerRemoved.BindValueChanged(itemRemoved); - } + managerDownloadBegan = Manager.DownloadBegan.GetBoundCopy(); + managerDownloadBegan.BindValueChanged(downloadBegan); + managerDownloadFailed = Manager.DownloadFailed.GetBoundCopy(); + managerDownloadFailed.BindValueChanged(downloadFailed); + managerUpdated = Manager.ItemUpdated.GetBoundCopy(); + managerUpdated.BindValueChanged(itemUpdated); + managerRemoved = Manager.ItemRemoved.GetBoundCopy(); + managerRemoved.BindValueChanged(itemRemoved); } private void downloadBegan(ValueChangedEvent>> weakRequest) From 75a088d4f4f8082986a418c901e7def8b7eae859 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Oct 2021 11:54:42 +0900 Subject: [PATCH 176/260] Fix variable name mismatch --- osu.Game/Online/ScoreDownloadTracker.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Online/ScoreDownloadTracker.cs b/osu.Game/Online/ScoreDownloadTracker.cs index 7a714a7a0e..ac700fc587 100644 --- a/osu.Game/Online/ScoreDownloadTracker.cs +++ b/osu.Game/Online/ScoreDownloadTracker.cs @@ -32,12 +32,12 @@ namespace osu.Game.Online private void load() { // Used to interact with manager classes that don't support interface types. Will eventually be replaced. - var beatmapSetInfo = new ScoreInfo { OnlineScoreID = TrackedItem.OnlineScoreID }; + var scoreInfo = new ScoreInfo { OnlineScoreID = TrackedItem.OnlineScoreID }; - if (TrackedItem.ID > 0 || Manager?.IsAvailableLocally(beatmapSetInfo) == true) + if (TrackedItem.ID > 0 || Manager?.IsAvailableLocally(scoreInfo) == true) UpdateState(DownloadState.LocallyAvailable); else if (Manager != null) - attachDownload(Manager.GetExistingDownload(beatmapSetInfo)); + attachDownload(Manager.GetExistingDownload(scoreInfo)); if (Manager != null) { From dc44734db20e25c4a39b52faa4df0ff62740ee21 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Oct 2021 11:56:17 +0900 Subject: [PATCH 177/260] Simplify null check in `ScoreDownloadTracker` and remove unnecessary ID check --- osu.Game/Online/ScoreDownloadTracker.cs | 26 ++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/osu.Game/Online/ScoreDownloadTracker.cs b/osu.Game/Online/ScoreDownloadTracker.cs index ac700fc587..8222a5382c 100644 --- a/osu.Game/Online/ScoreDownloadTracker.cs +++ b/osu.Game/Online/ScoreDownloadTracker.cs @@ -31,25 +31,25 @@ namespace osu.Game.Online [BackgroundDependencyLoader(true)] private void load() { + if (Manager == null) + return; + // Used to interact with manager classes that don't support interface types. Will eventually be replaced. var scoreInfo = new ScoreInfo { OnlineScoreID = TrackedItem.OnlineScoreID }; - if (TrackedItem.ID > 0 || Manager?.IsAvailableLocally(scoreInfo) == true) + if (Manager.IsAvailableLocally(scoreInfo)) UpdateState(DownloadState.LocallyAvailable); - else if (Manager != null) + else attachDownload(Manager.GetExistingDownload(scoreInfo)); - if (Manager != null) - { - managerDownloadBegan = Manager.DownloadBegan.GetBoundCopy(); - managerDownloadBegan.BindValueChanged(downloadBegan); - managerDownloadFailed = Manager.DownloadFailed.GetBoundCopy(); - managerDownloadFailed.BindValueChanged(downloadFailed); - managerUpdated = Manager.ItemUpdated.GetBoundCopy(); - managerUpdated.BindValueChanged(itemUpdated); - managerRemoved = Manager.ItemRemoved.GetBoundCopy(); - managerRemoved.BindValueChanged(itemRemoved); - } + managerDownloadBegan = Manager.DownloadBegan.GetBoundCopy(); + managerDownloadBegan.BindValueChanged(downloadBegan); + managerDownloadFailed = Manager.DownloadFailed.GetBoundCopy(); + managerDownloadFailed.BindValueChanged(downloadFailed); + managerUpdated = Manager.ItemUpdated.GetBoundCopy(); + managerUpdated.BindValueChanged(itemUpdated); + managerRemoved = Manager.ItemRemoved.GetBoundCopy(); + managerRemoved.BindValueChanged(itemRemoved); } private void downloadBegan(ValueChangedEvent>> weakRequest) From f3811edb0b83377ceee067571f4d553bb8bb5b1a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Oct 2021 11:57:54 +0900 Subject: [PATCH 178/260] Add inline comment explaining usage of `RequiresChildrenUpdate` override --- osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs b/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs index 87348fbd1c..88793c7cfc 100644 --- a/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs +++ b/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs @@ -22,6 +22,7 @@ namespace osu.Game.Online.Rooms { public readonly IBindable SelectedItem = new Bindable(); + // Required to allow child components to update. Can potentially be replaced with a `CompositeComponent` class if or when we make one. protected override bool RequiresChildrenUpdate => true; [Resolved] From b252b176d45770bbf9e8e1fbd089fa37408b5a51 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Oct 2021 11:59:07 +0900 Subject: [PATCH 179/260] Seal implementation and add comment about `LoadComplete` execution order --- osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs index 1a59f36c11..c946bf12fd 100644 --- a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs +++ b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs @@ -282,7 +282,7 @@ namespace osu.Game.Screens.OnlinePlay return true; } - private class PlaylistDownloadButton : BeatmapPanelDownloadButton + private sealed class PlaylistDownloadButton : BeatmapPanelDownloadButton { private readonly PlaylistItem playlistItem; @@ -302,6 +302,8 @@ namespace osu.Game.Screens.OnlinePlay protected override void LoadComplete() { State.BindValueChanged(stateChanged, true); + + // base implementation calls FinishTransforms, so should be run after the above state update. base.LoadComplete(); } From 414d920ca25caf0f06e619a0c810414212d8b5a9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Oct 2021 12:02:10 +0900 Subject: [PATCH 180/260] Revert to previous exposure of `RoomSubScreen.BeatmapAvailability` --- osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs | 8 +++++--- .../OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs | 4 ++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs index 270d12ec97..2015a050bb 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -65,7 +65,9 @@ namespace osu.Game.Screens.OnlinePlay.Match private IBindable> managerUpdated; [Cached] - protected OnlinePlayBeatmapAvailabilityTracker BeatmapAvailabilityTracker { get; private set; } + private OnlinePlayBeatmapAvailabilityTracker beatmapAvailabilityTracker { get; set; } + + protected IBindable BeatmapAvailability => beatmapAvailabilityTracker.Availability; public readonly Room Room; private readonly bool allowEdit; @@ -86,7 +88,7 @@ namespace osu.Game.Screens.OnlinePlay.Match Padding = new MarginPadding { Top = Header.HEIGHT }; - BeatmapAvailabilityTracker = new OnlinePlayBeatmapAvailabilityTracker + beatmapAvailabilityTracker = new OnlinePlayBeatmapAvailabilityTracker { SelectedItem = { BindTarget = SelectedItem } }; @@ -101,7 +103,7 @@ namespace osu.Game.Screens.OnlinePlay.Match InternalChildren = new Drawable[] { - BeatmapAvailabilityTracker, + beatmapAvailabilityTracker, new GridContainer { RelativeSizeAxes = Axes.Both, diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index 0af41c977b..544eac4127 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -66,7 +66,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer SelectedItem.BindTo(client.CurrentMatchPlayingItem); - BeatmapAvailabilityTracker.Availability.BindValueChanged(updateBeatmapAvailability, true); + BeatmapAvailability.BindValueChanged(updateBeatmapAvailability, true); UserMods.BindValueChanged(onUserModsChanged); client.LoadRequested += onLoadRequested; @@ -362,7 +362,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer private void onLoadRequested() { - if (BeatmapAvailabilityTracker.Availability.Value.State != DownloadState.LocallyAvailable) + if (BeatmapAvailability.Value.State != DownloadState.LocallyAvailable) return; // In the case of spectating, IMultiplayerClient.LoadRequested can be fired while the game is still spectating a previous session. From 0a56e22fa0da644e69c3a692960928b7dd9cc5e3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Oct 2021 12:14:14 +0900 Subject: [PATCH 181/260] Remove unused using statement --- osu.Game.Rulesets.Osu.Tests/TestSceneNoSpinnerStacking.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneNoSpinnerStacking.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneNoSpinnerStacking.cs index 52597385bb..ef05bcd320 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneNoSpinnerStacking.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneNoSpinnerStacking.cs @@ -1,7 +1,6 @@ // 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 NUnit.Framework; using osu.Game.Beatmaps; using osu.Game.Rulesets.Osu.Objects; From 6f863ca204cd9488a4e9a141ce8feadbbabef26d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 1 Aug 2021 17:12:04 +0200 Subject: [PATCH 182/260] Adjust game-side text flow containers to part-based model --- .../Graphics/Containers/LinkFlowContainer.cs | 50 ++++++++++++++----- .../Containers/OsuTextFlowContainer.cs | 6 +-- osu.Game/Graphics/ErrorTextFlowContainer.cs | 9 ++-- osu.Game/Online/Chat/DrawableLinkCompiler.cs | 7 +++ .../Overlays/AccountCreation/ScreenEntry.cs | 5 +- .../Changelog/ChangelogSupporterPromo.cs | 7 ++- osu.Game/Overlays/Music/PlaylistItem.cs | 16 +++--- osu.Game/Screens/Menu/Disclaimer.cs | 10 ++-- 8 files changed, 73 insertions(+), 37 deletions(-) diff --git a/osu.Game/Graphics/Containers/LinkFlowContainer.cs b/osu.Game/Graphics/Containers/LinkFlowContainer.cs index 21c1d70d45..7569200c53 100644 --- a/osu.Game/Graphics/Containers/LinkFlowContainer.cs +++ b/osu.Game/Graphics/Containers/LinkFlowContainer.cs @@ -9,6 +9,7 @@ using osu.Framework.Graphics.Sprites; using System.Collections.Generic; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Localisation; using osu.Framework.Platform; using osu.Game.Graphics.Sprites; @@ -71,26 +72,20 @@ namespace osu.Game.Graphics.Containers var spriteText = new OsuSpriteText { Text = text }; AddText(spriteText, creationParameters); - createLink(spriteText.Yield(), new LinkDetails(action, argument), tooltipText); + createLink(new TextPartManual(spriteText.Yield()), new LinkDetails(action, argument), tooltipText); } public void AddLink(IEnumerable text, LinkAction action, string linkArgument, string tooltipText = null) { - foreach (var t in text) - AddArbitraryDrawable(t); - - createLink(text, new LinkDetails(action, linkArgument), tooltipText); + createLink(new TextPartManual(text), new LinkDetails(action, linkArgument), tooltipText); } public void AddUserLink(User user, Action creationParameters = null) => createLink(AddText(user.Username, creationParameters), new LinkDetails(LinkAction.OpenUserProfile, user.Id.ToString()), "view profile"); - private void createLink(IEnumerable drawables, LinkDetails link, string tooltipText, Action action = null) + private void createLink(ITextPart textPart, LinkDetails link, LocalisableString tooltipText, Action action = null) { - var linkCompiler = CreateLinkCompiler(drawables.OfType()); - linkCompiler.RelativeSizeAxes = Axes.Both; - linkCompiler.TooltipText = tooltipText; - linkCompiler.Action = () => + Action onClickAction = () => { if (action != null) action(); @@ -101,10 +96,41 @@ namespace osu.Game.Graphics.Containers host.OpenUrlExternally(link.Argument); }; - AddInternal(linkCompiler); + AddPart(new TextLink(textPart, tooltipText, onClickAction)); } - protected virtual DrawableLinkCompiler CreateLinkCompiler(IEnumerable parts) => new DrawableLinkCompiler(parts); + private class TextLink : TextPart + { + private readonly ITextPart innerPart; + private readonly LocalisableString tooltipText; + private readonly Action action; + + public TextLink(ITextPart innerPart, LocalisableString tooltipText, Action action) + { + this.innerPart = innerPart; + this.tooltipText = tooltipText; + this.action = action; + } + + protected override IEnumerable CreateDrawablesFor(TextFlowContainer textFlowContainer) + { + var linkFlowContainer = (LinkFlowContainer)textFlowContainer; + + innerPart.RecreateDrawablesFor(linkFlowContainer); + var drawables = innerPart.Drawables.ToList(); + + drawables.Add(linkFlowContainer.CreateLinkCompiler(innerPart).With(c => + { + c.RelativeSizeAxes = Axes.Both; + c.TooltipText = tooltipText; + c.Action = action; + })); + + return drawables; + } + } + + protected virtual DrawableLinkCompiler CreateLinkCompiler(ITextPart textPart) => new DrawableLinkCompiler(textPart); // We want the compilers to always be visible no matter where they are, so RelativeSizeAxes is used. // However due to https://github.com/ppy/osu-framework/issues/2073, it's possible for the compilers to be relative size in the flow's auto-size axes - an unsupported operation. diff --git a/osu.Game/Graphics/Containers/OsuTextFlowContainer.cs b/osu.Game/Graphics/Containers/OsuTextFlowContainer.cs index 6a87a4b8b9..b8237832a3 100644 --- a/osu.Game/Graphics/Containers/OsuTextFlowContainer.cs +++ b/osu.Game/Graphics/Containers/OsuTextFlowContainer.cs @@ -2,7 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Collections.Generic; +using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; @@ -19,8 +19,8 @@ namespace osu.Game.Graphics.Containers protected override SpriteText CreateSpriteText() => new OsuSpriteText(); - public void AddArbitraryDrawable(Drawable drawable) => AddInternal(drawable); + public ITextPart AddArbitraryDrawable(Drawable drawable) => AddPart(new TextPartManual(drawable.Yield())); - public IEnumerable AddIcon(IconUsage icon, Action creationParameters = null) => AddText(icon.Icon.ToString(), creationParameters); + public ITextPart AddIcon(IconUsage icon, Action creationParameters = null) => AddText(icon.Icon.ToString(), creationParameters); } } diff --git a/osu.Game/Graphics/ErrorTextFlowContainer.cs b/osu.Game/Graphics/ErrorTextFlowContainer.cs index 486382bf33..dafc363973 100644 --- a/osu.Game/Graphics/ErrorTextFlowContainer.cs +++ b/osu.Game/Graphics/ErrorTextFlowContainer.cs @@ -2,7 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; -using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Game.Graphics.Containers; using osuTK.Graphics; @@ -10,7 +10,7 @@ namespace osu.Game.Graphics { public class ErrorTextFlowContainer : OsuTextFlowContainer { - private readonly List errorDrawables = new List(); + private readonly List errorTextParts = new List(); public ErrorTextFlowContainer() : base(cp => cp.Font = cp.Font.With(size: 12)) @@ -19,7 +19,8 @@ namespace osu.Game.Graphics public void ClearErrors() { - errorDrawables.ForEach(d => d.Expire()); + foreach (var textPart in errorTextParts) + RemovePart(textPart); } public void AddErrors(string[] errors) @@ -29,7 +30,7 @@ namespace osu.Game.Graphics if (errors == null) return; foreach (string error in errors) - errorDrawables.AddRange(AddParagraph(error, cp => cp.Colour = Color4.Red)); + errorTextParts.Add(AddParagraph(error, cp => cp.Colour = Color4.Red)); } } } diff --git a/osu.Game/Online/Chat/DrawableLinkCompiler.cs b/osu.Game/Online/Chat/DrawableLinkCompiler.cs index 4df60eba69..8356b36667 100644 --- a/osu.Game/Online/Chat/DrawableLinkCompiler.cs +++ b/osu.Game/Online/Chat/DrawableLinkCompiler.cs @@ -5,6 +5,8 @@ using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; @@ -30,6 +32,11 @@ namespace osu.Game.Online.Chat protected override HoverSounds CreateHoverSounds(HoverSampleSet sampleSet) => new LinkHoverSounds(sampleSet, Parts); + public DrawableLinkCompiler(ITextPart part) + : this(part.Drawables.OfType()) + { + } + public DrawableLinkCompiler(IEnumerable parts) : base(HoverSampleSet.Submit) { diff --git a/osu.Game/Overlays/AccountCreation/ScreenEntry.cs b/osu.Game/Overlays/AccountCreation/ScreenEntry.cs index bcb3d4b635..8ee3b1cb2e 100644 --- a/osu.Game/Overlays/AccountCreation/ScreenEntry.cs +++ b/osu.Game/Overlays/AccountCreation/ScreenEntry.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using osu.Framework.Allocation; @@ -37,7 +36,7 @@ namespace osu.Game.Overlays.AccountCreation private IAPIProvider api { get; set; } private ShakeContainer registerShake; - private IEnumerable characterCheckText; + private ITextPart characterCheckText; private OsuTextBox[] textboxes; private LoadingLayer loadingLayer; @@ -136,7 +135,7 @@ namespace osu.Game.Overlays.AccountCreation characterCheckText = passwordDescription.AddText("8 characters long"); passwordDescription.AddText(". Choose something long but also something you will remember, like a line from your favourite song."); - passwordTextBox.Current.ValueChanged += password => { characterCheckText.ForEach(s => s.Colour = password.NewValue.Length == 0 ? Color4.White : Interpolation.ValueAt(password.NewValue.Length, Color4.OrangeRed, Color4.YellowGreen, 0, 8, Easing.In)); }; + passwordTextBox.Current.ValueChanged += password => { characterCheckText.Drawables.ForEach(s => s.Colour = password.NewValue.Length == 0 ? Color4.White : Interpolation.ValueAt(password.NewValue.Length, Color4.OrangeRed, Color4.YellowGreen, 0, 8, Easing.In)); }; } public override void OnEntering(IScreen last) diff --git a/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs b/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs index 1b0a62dc4a..5cc598ae70 100644 --- a/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs +++ b/osu.Game/Overlays/Changelog/ChangelogSupporterPromo.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; @@ -166,12 +165,12 @@ namespace osu.Game.Overlays.Changelog { } - protected override DrawableLinkCompiler CreateLinkCompiler(IEnumerable parts) => new SupporterPromoLinkCompiler(parts); + protected override DrawableLinkCompiler CreateLinkCompiler(ITextPart textPart) => new SupporterPromoLinkCompiler(textPart); private class SupporterPromoLinkCompiler : DrawableLinkCompiler { - public SupporterPromoLinkCompiler(IEnumerable parts) - : base(parts) + public SupporterPromoLinkCompiler(ITextPart part) + : base(part) { } diff --git a/osu.Game/Overlays/Music/PlaylistItem.cs b/osu.Game/Overlays/Music/PlaylistItem.cs index ef25de77c6..eea2a9dc7e 100644 --- a/osu.Game/Overlays/Music/PlaylistItem.cs +++ b/osu.Game/Overlays/Music/PlaylistItem.cs @@ -3,12 +3,10 @@ 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.Graphics.Sprites; using osu.Framework.Input.Events; using osu.Framework.Localisation; using osu.Game.Beatmaps; @@ -25,7 +23,7 @@ namespace osu.Game.Overlays.Music public Action RequestSelection; private TextFlowContainer text; - private IEnumerable titleSprites; + private ITextPart titlePart; private ILocalisedBindableString title; private ILocalisedBindableString artist; @@ -63,11 +61,16 @@ namespace osu.Game.Overlays.Music if (set.OldValue?.Equals(Model) != true && set.NewValue?.Equals(Model) != true) return; - foreach (Drawable s in titleSprites) - s.FadeColour(set.NewValue.Equals(Model) ? selectedColour : Color4.White, FADE_DURATION); + updateSelectionState(false); }, true); } + private void updateSelectionState(bool instant) + { + foreach (Drawable s in titlePart.Drawables) + s.FadeColour(SelectedSet.Value?.Equals(Model) == true ? selectedColour : Color4.White, instant ? 0 : FADE_DURATION); + } + protected override Drawable CreateContent() => text = new OsuTextFlowContainer { RelativeSizeAxes = Axes.X, @@ -79,7 +82,8 @@ namespace osu.Game.Overlays.Music text.Clear(); // space after the title to put a space between the title and artist - titleSprites = text.AddText(title.Value + @" ", sprite => sprite.Font = OsuFont.GetFont(weight: FontWeight.Regular)).OfType(); + titlePart = text.AddText(title.Value + @" ", sprite => sprite.Font = OsuFont.GetFont(weight: FontWeight.Regular)); + updateSelectionState(true); text.AddText(artist.Value, sprite => { diff --git a/osu.Game/Screens/Menu/Disclaimer.cs b/osu.Game/Screens/Menu/Disclaimer.cs index 7f34e1e395..b8abc131fd 100644 --- a/osu.Game/Screens/Menu/Disclaimer.cs +++ b/osu.Game/Screens/Menu/Disclaimer.cs @@ -37,7 +37,7 @@ namespace osu.Game.Screens.Menu private readonly Bindable currentUser = new Bindable(); private FillFlowContainer fill; - private readonly List expendableText = new List(); + private readonly List expendableText = new List(); public Disclaimer(OsuScreen nextScreen = null) { @@ -97,7 +97,7 @@ namespace osu.Game.Screens.Menu textFlow.AddText("this is osu!", t => t.Font = t.Font.With(Typeface.Torus, 30, FontWeight.Regular)); - expendableText.AddRange(textFlow.AddText("lazer", t => + expendableText.Add(textFlow.AddText("lazer", t => { t.Font = t.Font.With(Typeface.Torus, 30, FontWeight.Regular); t.Colour = colours.PinkLight; @@ -114,7 +114,7 @@ namespace osu.Game.Screens.Menu t.Font = t.Font.With(Typeface.Torus, 20, FontWeight.SemiBold); t.Colour = colours.Pink; }); - expendableText.AddRange(textFlow.AddText(" coming to osu!", formatRegular)); + expendableText.Add(textFlow.AddText(" coming to osu!", formatRegular)); textFlow.AddText(".", formatRegular); textFlow.NewParagraph(); @@ -152,7 +152,7 @@ namespace osu.Game.Screens.Menu t.Font = t.Font.With(size: 20); t.Origin = Anchor.Centre; t.Colour = colours.Pink; - }).First(); + }).Drawables.First(); if (IsLoaded) animateHeart(); @@ -193,7 +193,7 @@ namespace osu.Game.Screens.Menu using (BeginDelayedSequence(520 + 160)) { fill.MoveToOffset(new Vector2(0, 15), 160, Easing.OutQuart); - Schedule(() => expendableText.ForEach(t => + Schedule(() => expendableText.SelectMany(t => t.Drawables).ForEach(t => { t.FadeOut(100); t.ScaleTo(new Vector2(0, 1), 100, Easing.OutQuart); From 61f0339c1d43bac24a1b1addf9d66859fba62401 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 7 Aug 2021 20:52:12 +0200 Subject: [PATCH 183/260] Avoid double-adding chunks in link flow container --- osu.Game/Graphics/Containers/LinkFlowContainer.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/osu.Game/Graphics/Containers/LinkFlowContainer.cs b/osu.Game/Graphics/Containers/LinkFlowContainer.cs index 7569200c53..0b43c16ebe 100644 --- a/osu.Game/Graphics/Containers/LinkFlowContainer.cs +++ b/osu.Game/Graphics/Containers/LinkFlowContainer.cs @@ -59,19 +59,20 @@ namespace osu.Game.Graphics.Containers } public void AddLink(string text, string url, Action creationParameters = null) => - createLink(AddText(text, creationParameters), new LinkDetails(LinkAction.External, url), url); + createLink(CreateChunkFor(text, true, CreateSpriteText, creationParameters), new LinkDetails(LinkAction.External, url), url); public void AddLink(string text, Action action, string tooltipText = null, Action creationParameters = null) - => createLink(AddText(text, creationParameters), new LinkDetails(LinkAction.Custom, string.Empty), tooltipText, action); + => createLink(CreateChunkFor(text, true, CreateSpriteText, creationParameters), new LinkDetails(LinkAction.Custom, string.Empty), tooltipText, action); public void AddLink(string text, LinkAction action, string argument, string tooltipText = null, Action creationParameters = null) - => createLink(AddText(text, creationParameters), new LinkDetails(action, argument), tooltipText); + => createLink(CreateChunkFor(text, true, CreateSpriteText, creationParameters), new LinkDetails(action, argument), tooltipText); public void AddLink(LocalisableString text, LinkAction action, string argument, string tooltipText = null, Action creationParameters = null) { var spriteText = new OsuSpriteText { Text = text }; AddText(spriteText, creationParameters); + RemoveInternal(spriteText); // TODO: temporary, will go away when TextParts support localisation properly. createLink(new TextPartManual(spriteText.Yield()), new LinkDetails(action, argument), tooltipText); } @@ -81,7 +82,7 @@ namespace osu.Game.Graphics.Containers } public void AddUserLink(User user, Action creationParameters = null) - => createLink(AddText(user.Username, creationParameters), new LinkDetails(LinkAction.OpenUserProfile, user.Id.ToString()), "view profile"); + => createLink(CreateChunkFor(user.Username, true, CreateSpriteText, creationParameters), new LinkDetails(LinkAction.OpenUserProfile, user.Id.ToString()), "view profile"); private void createLink(ITextPart textPart, LinkDetails link, LocalisableString tooltipText, Action action = null) { From 817369903a71151096671b2017c7b54afe5802c0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Oct 2021 17:48:56 +0900 Subject: [PATCH 184/260] Rename API score classes --- .../Gameplay/TestSceneReplayDownloadButton.cs | 2 +- .../Visual/Online/TestSceneScoresContainer.cs | 30 +++++++++---------- .../Online/API/Requests/GetScoresRequest.cs | 6 ++-- .../API/Requests/GetUserScoresRequest.cs | 2 +- ...{APILegacyScoreInfo.cs => APIScoreInfo.cs} | 2 +- ...egacyScores.cs => APIScoreWithPosition.cs} | 16 ++-------- .../Requests/Responses/APIScoresCollection.cs | 17 +++++++++++ .../BeatmapSet/Scores/ScoresContainer.cs | 2 +- .../Sections/Ranks/PaginatedScoreContainer.cs | 8 ++--- 9 files changed, 46 insertions(+), 39 deletions(-) rename osu.Game/Online/API/Requests/Responses/{APILegacyScoreInfo.cs => APIScoreInfo.cs} (99%) rename osu.Game/Online/API/Requests/Responses/{APILegacyScores.cs => APIScoreWithPosition.cs} (55%) create mode 100644 osu.Game/Online/API/Requests/Responses/APIScoresCollection.cs diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs index 5e2374cbcb..20dec46101 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs @@ -132,7 +132,7 @@ namespace osu.Game.Tests.Visual.Gameplay private ScoreInfo getScoreInfo(bool replayAvailable) { - return new APILegacyScoreInfo + return new APIScoreInfo { OnlineScoreID = 2553163309, OnlineRulesetID = 0, diff --git a/osu.Game.Tests/Visual/Online/TestSceneScoresContainer.cs b/osu.Game.Tests/Visual/Online/TestSceneScoresContainer.cs index 0cb8cc22ec..991be33917 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneScoresContainer.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneScoresContainer.cs @@ -43,11 +43,11 @@ namespace osu.Game.Tests.Visual.Online } }; - var allScores = new APILegacyScores + var allScores = new APIScoresCollection { - Scores = new List + Scores = new List { - new APILegacyScoreInfo + new APIScoreInfo { User = new User { @@ -72,7 +72,7 @@ namespace osu.Game.Tests.Visual.Online TotalScore = 1234567890, Accuracy = 1, }, - new APILegacyScoreInfo + new APIScoreInfo { User = new User { @@ -96,7 +96,7 @@ namespace osu.Game.Tests.Visual.Online TotalScore = 1234789, Accuracy = 0.9997, }, - new APILegacyScoreInfo + new APIScoreInfo { User = new User { @@ -119,7 +119,7 @@ namespace osu.Game.Tests.Visual.Online TotalScore = 12345678, Accuracy = 0.9854, }, - new APILegacyScoreInfo + new APIScoreInfo { User = new User { @@ -141,7 +141,7 @@ namespace osu.Game.Tests.Visual.Online TotalScore = 1234567, Accuracy = 0.8765, }, - new APILegacyScoreInfo + new APIScoreInfo { User = new User { @@ -162,9 +162,9 @@ namespace osu.Game.Tests.Visual.Online } }; - var myBestScore = new APILegacyUserTopScoreInfo + var myBestScore = new APIScoreWithPosition { - Score = new APILegacyScoreInfo + Score = new APIScoreInfo { User = new User { @@ -185,9 +185,9 @@ namespace osu.Game.Tests.Visual.Online Position = 1337, }; - var myBestScoreWithNullPosition = new APILegacyUserTopScoreInfo + var myBestScoreWithNullPosition = new APIScoreWithPosition { - Score = new APILegacyScoreInfo + Score = new APIScoreInfo { User = new User { @@ -208,11 +208,11 @@ namespace osu.Game.Tests.Visual.Online Position = null, }; - var oneScore = new APILegacyScores + var oneScore = new APIScoresCollection { - Scores = new List + Scores = new List { - new APILegacyScoreInfo + new APIScoreInfo { User = new User { @@ -273,7 +273,7 @@ namespace osu.Game.Tests.Visual.Online private class TestScoresContainer : ScoresContainer { - public new APILegacyScores Scores + public new APIScoresCollection Scores { set => base.Scores = value; } diff --git a/osu.Game/Online/API/Requests/GetScoresRequest.cs b/osu.Game/Online/API/Requests/GetScoresRequest.cs index f3bf690ed5..11b0cdddd8 100644 --- a/osu.Game/Online/API/Requests/GetScoresRequest.cs +++ b/osu.Game/Online/API/Requests/GetScoresRequest.cs @@ -13,7 +13,7 @@ using System.Diagnostics; namespace osu.Game.Online.API.Requests { - public class GetScoresRequest : APIRequest + public class GetScoresRequest : APIRequest { private readonly BeatmapInfo beatmapInfo; private readonly BeatmapLeaderboardScope scope; @@ -36,11 +36,11 @@ namespace osu.Game.Online.API.Requests Success += onSuccess; } - private void onSuccess(APILegacyScores r) + private void onSuccess(APIScoresCollection r) { Debug.Assert(ruleset.ID != null, "ruleset.ID != null"); - foreach (APILegacyScoreInfo score in r.Scores) + foreach (APIScoreInfo score in r.Scores) { score.BeatmapInfo = beatmapInfo; score.OnlineRulesetID = ruleset.ID.Value; diff --git a/osu.Game/Online/API/Requests/GetUserScoresRequest.cs b/osu.Game/Online/API/Requests/GetUserScoresRequest.cs index 7b4d66e7b2..e13ac8e539 100644 --- a/osu.Game/Online/API/Requests/GetUserScoresRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserScoresRequest.cs @@ -8,7 +8,7 @@ using osu.Game.Rulesets; namespace osu.Game.Online.API.Requests { - public class GetUserScoresRequest : PaginatedAPIRequest> + public class GetUserScoresRequest : PaginatedAPIRequest> { private readonly long userId; private readonly ScoreType type; diff --git a/osu.Game/Online/API/Requests/Responses/APILegacyScoreInfo.cs b/osu.Game/Online/API/Requests/Responses/APIScoreInfo.cs similarity index 99% rename from osu.Game/Online/API/Requests/Responses/APILegacyScoreInfo.cs rename to osu.Game/Online/API/Requests/Responses/APIScoreInfo.cs index aaf2dccc82..c977d27a68 100644 --- a/osu.Game/Online/API/Requests/Responses/APILegacyScoreInfo.cs +++ b/osu.Game/Online/API/Requests/Responses/APIScoreInfo.cs @@ -15,7 +15,7 @@ using osu.Game.Users; namespace osu.Game.Online.API.Requests.Responses { - public class APILegacyScoreInfo + public class APIScoreInfo { public ScoreInfo CreateScoreInfo(RulesetStore rulesets) { diff --git a/osu.Game/Online/API/Requests/Responses/APILegacyScores.cs b/osu.Game/Online/API/Requests/Responses/APIScoreWithPosition.cs similarity index 55% rename from osu.Game/Online/API/Requests/Responses/APILegacyScores.cs rename to osu.Game/Online/API/Requests/Responses/APIScoreWithPosition.cs index 009639c1dc..9c97ad4f05 100644 --- a/osu.Game/Online/API/Requests/Responses/APILegacyScores.cs +++ b/osu.Game/Online/API/Requests/Responses/APIScoreWithPosition.cs @@ -1,29 +1,19 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// 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 Newtonsoft.Json; using osu.Game.Rulesets; using osu.Game.Scoring; namespace osu.Game.Online.API.Requests.Responses { - public class APILegacyScores - { - [JsonProperty(@"scores")] - public List Scores; - - [JsonProperty(@"userScore")] - public APILegacyUserTopScoreInfo UserScore; - } - - public class APILegacyUserTopScoreInfo + public class APIScoreWithPosition { [JsonProperty(@"position")] public int? Position; [JsonProperty(@"score")] - public APILegacyScoreInfo Score; + public APIScoreInfo Score; public ScoreInfo CreateScoreInfo(RulesetStore rulesets) { diff --git a/osu.Game/Online/API/Requests/Responses/APIScoresCollection.cs b/osu.Game/Online/API/Requests/Responses/APIScoresCollection.cs new file mode 100644 index 0000000000..5304664bf8 --- /dev/null +++ b/osu.Game/Online/API/Requests/Responses/APIScoresCollection.cs @@ -0,0 +1,17 @@ +// 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 Newtonsoft.Json; + +namespace osu.Game.Online.API.Requests.Responses +{ + public class APIScoresCollection + { + [JsonProperty(@"scores")] + public List Scores; + + [JsonProperty(@"userScore")] + public APIScoreWithPosition UserScore; + } +} diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs index 82657afc86..3a3566aad9 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs @@ -52,7 +52,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores private CancellationTokenSource loadCancellationSource; - protected APILegacyScores Scores + protected APIScoresCollection Scores { set => Schedule(() => { diff --git a/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs b/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs index 7c04b331c2..1fb6100a28 100644 --- a/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs @@ -15,7 +15,7 @@ using osu.Framework.Localisation; namespace osu.Game.Overlays.Profile.Sections.Ranks { - public class PaginatedScoreContainer : PaginatedProfileSubsection + public class PaginatedScoreContainer : PaginatedProfileSubsection { private readonly ScoreType type; @@ -51,7 +51,7 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks } } - protected override void OnItemsReceived(List items) + protected override void OnItemsReceived(List items) { if (VisiblePages == 0) drawableItemIndex = 0; @@ -59,12 +59,12 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks base.OnItemsReceived(items); } - protected override APIRequest> CreateRequest() => + protected override APIRequest> CreateRequest() => new GetUserScoresRequest(User.Value.Id, type, VisiblePages++, ItemsPerPage); private int drawableItemIndex; - protected override Drawable CreateDrawableItem(APILegacyScoreInfo model) + protected override Drawable CreateDrawableItem(APIScoreInfo model) { switch (type) { From 49b5de64befad30c60a1d2fae2adc760e8d8fbab Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Oct 2021 17:57:17 +0900 Subject: [PATCH 185/260] Extract interface --- osu.Game/Scoring/IScoreInfo.cs | 42 ++++++++++++++++++++++++++++++++++ osu.Game/Scoring/ScoreInfo.cs | 4 ++-- 2 files changed, 44 insertions(+), 2 deletions(-) create mode 100644 osu.Game/Scoring/IScoreInfo.cs diff --git a/osu.Game/Scoring/IScoreInfo.cs b/osu.Game/Scoring/IScoreInfo.cs new file mode 100644 index 0000000000..c783a77203 --- /dev/null +++ b/osu.Game/Scoring/IScoreInfo.cs @@ -0,0 +1,42 @@ +// 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 osu.Game.Beatmaps; +using osu.Game.Database; +using osu.Game.Users; + +namespace osu.Game.Scoring +{ + public interface IScoreInfo : IHasOnlineID + { + int TotalScore { get; set; } + + int MaxCombo { get; set; } + + User User { get; set; } + + long OnlineScoreID { get; set; } + + bool Replay { get; set; } + + DateTimeOffset Date { get; set; } + + BeatmapInfo BeatmapInfo { get; set; } + + double Accuracy { get; set; } + + double? PP { get; set; } + + BeatmapMetadata Metadata { get; set; } + + Dictionary Statistics { get; set; } + + int OnlineRulesetID { get; set; } + + string[] Mods { get; set; } + + public ScoreRank Rank { get; set; } + } +} diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index 8eaec0a477..0afec89450 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -19,7 +19,7 @@ using osu.Game.Utils; namespace osu.Game.Scoring { - public class ScoreInfo : IHasFiles, IHasPrimaryKey, ISoftDelete, IEquatable, IDeepCloneable, IHasOnlineID + public class ScoreInfo : IScoreInfo, IHasFiles, IHasPrimaryKey, ISoftDelete, IEquatable, IDeepCloneable { public int ID { get; set; } @@ -54,7 +54,7 @@ namespace osu.Game.Scoring public bool Passed { get; set; } = true; [JsonIgnore] - public virtual RulesetInfo Ruleset { get; set; } + public RulesetInfo Ruleset { get; set; } private APIMod[] localAPIMods; private Mod[] mods; From 1944c255a78575a0e075642f66e1ca3655fd2d80 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Oct 2021 18:23:52 +0900 Subject: [PATCH 186/260] Implement score interfaces --- .../Gameplay/TestSceneReplayDownloadButton.cs | 2 +- .../Online/API/Requests/GetScoresRequest.cs | 10 +- .../API/Requests/Responses/APIScoreInfo.cs | 113 ++++++++++-------- .../Responses/APIScoreWithPosition.cs | 1 + osu.Game/Scoring/IScoreInfo.cs | 31 +++-- osu.Game/Scoring/ScoreInfo.cs | 7 +- osu.Game/Screens/Ranking/SoloResultsScreen.cs | 2 +- 7 files changed, 95 insertions(+), 71 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs index 20dec46101..b84ac6c2f1 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs @@ -134,7 +134,7 @@ namespace osu.Game.Tests.Visual.Gameplay { return new APIScoreInfo { - OnlineScoreID = 2553163309, + OnlineID = 2553163309, OnlineRulesetID = 0, Replay = replayAvailable, User = new User diff --git a/osu.Game/Online/API/Requests/GetScoresRequest.cs b/osu.Game/Online/API/Requests/GetScoresRequest.cs index 11b0cdddd8..9037869c77 100644 --- a/osu.Game/Online/API/Requests/GetScoresRequest.cs +++ b/osu.Game/Online/API/Requests/GetScoresRequest.cs @@ -38,20 +38,20 @@ namespace osu.Game.Online.API.Requests private void onSuccess(APIScoresCollection r) { - Debug.Assert(ruleset.ID != null, "ruleset.ID != null"); + Debug.Assert(ruleset.OnlineID >= 0); foreach (APIScoreInfo score in r.Scores) { - score.BeatmapInfo = beatmapInfo; - score.OnlineRulesetID = ruleset.ID.Value; + score.Beatmap = beatmapInfo; + score.OnlineRulesetID = ruleset.OnlineID; } var userScore = r.UserScore; if (userScore != null) { - userScore.Score.BeatmapInfo = beatmapInfo; - userScore.Score.OnlineRulesetID = ruleset.ID.Value; + userScore.Score.Beatmap = beatmapInfo; + userScore.Score.OnlineRulesetID = ruleset.OnlineID; } } diff --git a/osu.Game/Online/API/Requests/Responses/APIScoreInfo.cs b/osu.Game/Online/API/Requests/Responses/APIScoreInfo.cs index c977d27a68..568c6bea7f 100644 --- a/osu.Game/Online/API/Requests/Responses/APIScoreInfo.cs +++ b/osu.Game/Online/API/Requests/Responses/APIScoreInfo.cs @@ -9,14 +9,71 @@ using Newtonsoft.Json.Converters; using osu.Game.Beatmaps; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; using osu.Game.Scoring.Legacy; using osu.Game.Users; namespace osu.Game.Online.API.Requests.Responses { - public class APIScoreInfo + public class APIScoreInfo : IScoreInfo { + [JsonProperty(@"score")] + public long TotalScore { get; set; } + + [JsonProperty(@"max_combo")] + public int MaxCombo { get; set; } + + [JsonProperty(@"user")] + public User User { get; set; } + + public bool HasReplay { get; set; } + + [JsonProperty(@"id")] + public long OnlineID { get; set; } + + [JsonProperty(@"replay")] + public bool Replay { get; set; } + + [JsonProperty(@"created_at")] + public DateTimeOffset Date { get; set; } + + [JsonProperty(@"beatmap")] + public IBeatmapInfo Beatmap { get; set; } + + [JsonProperty("accuracy")] + public double Accuracy { get; set; } + + [JsonProperty(@"pp")] + public double? PP { get; set; } + + [JsonProperty(@"beatmapset")] + public APIBeatmapSet BeatmapSet + { + set + { + // in the deserialisation case we need to ferry this data across. + if (Beatmap is APIBeatmap apiBeatmap) + apiBeatmap.BeatmapSet = value; + } + } + + [JsonProperty("statistics")] + public Dictionary Statistics { get; set; } + + [JsonProperty(@"mode_int")] + public int OnlineRulesetID { get; set; } + + [JsonProperty(@"mods")] + public string[] Mods { get; set; } + + [JsonProperty("rank")] + [JsonConverter(typeof(StringEnumConverter))] + public ScoreRank Rank { get; set; } + + IBeatmapInfo IScoreInfo.Beatmap => Beatmap; + + // TODO: nuke public ScoreInfo CreateScoreInfo(RulesetStore rulesets) { var ruleset = rulesets.GetRuleset(OnlineRulesetID); @@ -34,10 +91,10 @@ namespace osu.Game.Online.API.Requests.Responses MaxCombo = MaxCombo, User = User, Accuracy = Accuracy, - OnlineScoreID = OnlineScoreID, + OnlineScoreID = OnlineID, Date = Date, PP = PP, - BeatmapInfo = BeatmapInfo, + BeatmapInfo = Beatmap.ToBeatmapInfo(rulesets), RulesetID = OnlineRulesetID, Hash = Replay ? "online" : string.Empty, // todo: temporary? Rank = Rank, @@ -81,57 +138,19 @@ namespace osu.Game.Online.API.Requests.Responses return scoreInfo; } - [JsonProperty(@"score")] - public int TotalScore { get; set; } - - [JsonProperty(@"max_combo")] - public int MaxCombo { get; set; } - - [JsonProperty(@"user")] - public User User { get; set; } - - [JsonProperty(@"id")] - public long OnlineScoreID { get; set; } - - [JsonProperty(@"replay")] - public bool Replay { get; set; } - - [JsonProperty(@"created_at")] - public DateTimeOffset Date { get; set; } - - [JsonProperty(@"beatmap")] - public BeatmapInfo BeatmapInfo { get; set; } - - [JsonProperty("accuracy")] - public double Accuracy { get; set; } - - [JsonProperty(@"pp")] - public double? PP { get; set; } - [JsonProperty(@"beatmapset")] - public BeatmapMetadata Metadata + public APIBeatmapSet Metadata { set { - // extract the set ID to its correct place. - BeatmapInfo.BeatmapSet = new BeatmapSetInfo { OnlineBeatmapSetID = value.ID }; - value.ID = 0; - - BeatmapInfo.Metadata = value; + // in the deserialisation case we need to ferry this data across. + if (Beatmap is APIBeatmap apiBeatmap) + apiBeatmap.BeatmapSet = value; } } - [JsonProperty(@"statistics")] - public Dictionary Statistics { get; set; } + public IRulesetInfo Ruleset => new RulesetInfo { ID = OnlineRulesetID }; - [JsonProperty(@"mode_int")] - public int OnlineRulesetID { get; set; } - - [JsonProperty(@"mods")] - public string[] Mods { get; set; } - - [JsonProperty("rank")] - [JsonConverter(typeof(StringEnumConverter))] - public ScoreRank Rank { get; set; } + Dictionary IScoreInfo.Statistics => new Dictionary(); // TODO: implement... maybe. hitresults have weird mappings per ruleset it would seem. } } diff --git a/osu.Game/Online/API/Requests/Responses/APIScoreWithPosition.cs b/osu.Game/Online/API/Requests/Responses/APIScoreWithPosition.cs index 9c97ad4f05..89bb416393 100644 --- a/osu.Game/Online/API/Requests/Responses/APIScoreWithPosition.cs +++ b/osu.Game/Online/API/Requests/Responses/APIScoreWithPosition.cs @@ -16,6 +16,7 @@ namespace osu.Game.Online.API.Requests.Responses public APIScoreInfo Score; public ScoreInfo CreateScoreInfo(RulesetStore rulesets) + { var score = Score.CreateScoreInfo(rulesets); score.Position = Position; diff --git a/osu.Game/Scoring/IScoreInfo.cs b/osu.Game/Scoring/IScoreInfo.cs index c783a77203..b230babf3c 100644 --- a/osu.Game/Scoring/IScoreInfo.cs +++ b/osu.Game/Scoring/IScoreInfo.cs @@ -5,38 +5,37 @@ using System; using System.Collections.Generic; using osu.Game.Beatmaps; using osu.Game.Database; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Scoring; using osu.Game.Users; namespace osu.Game.Scoring { public interface IScoreInfo : IHasOnlineID { - int TotalScore { get; set; } + User User { get; } - int MaxCombo { get; set; } + long TotalScore { get; } - User User { get; set; } + int MaxCombo { get; } - long OnlineScoreID { get; set; } + double Accuracy { get; } - bool Replay { get; set; } + bool HasReplay { get; } - DateTimeOffset Date { get; set; } + DateTimeOffset Date { get; } - BeatmapInfo BeatmapInfo { get; set; } + double? PP { get; } - double Accuracy { get; set; } + IBeatmapInfo Beatmap { get; } - double? PP { get; set; } + Dictionary Statistics { get; } - BeatmapMetadata Metadata { get; set; } + IRulesetInfo Ruleset { get; } - Dictionary Statistics { get; set; } + public ScoreRank Rank { get; } - int OnlineRulesetID { get; set; } - - string[] Mods { get; set; } - - public ScoreRank Rank { get; set; } + // Mods is currently missing from this interface as the `IMod` class has properties which can't be fulfilled by `APIMod`, + // but also doesn't expose `Settings`. We can consider how to implement this in the future if required. } } diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index 0afec89450..3b09669926 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -57,6 +57,7 @@ namespace osu.Game.Scoring public RulesetInfo Ruleset { get; set; } private APIMod[] localAPIMods; + private Mod[] mods; [JsonIgnore] @@ -160,7 +161,7 @@ namespace osu.Game.Scoring public DateTimeOffset Date { get; set; } [JsonProperty("statistics")] - public Dictionary Statistics = new Dictionary(); + public Dictionary Statistics { get; set; } = new Dictionary(); [JsonIgnore] [Column("Statistics")] @@ -273,5 +274,9 @@ namespace osu.Game.Scoring } public long OnlineID => OnlineScoreID ?? -1; + + IBeatmapInfo IScoreInfo.Beatmap => BeatmapInfo; + IRulesetInfo IScoreInfo.Ruleset => Ruleset; + bool IScoreInfo.HasReplay => Files.Any(); } } diff --git a/osu.Game/Screens/Ranking/SoloResultsScreen.cs b/osu.Game/Screens/Ranking/SoloResultsScreen.cs index 5e582a8dcb..684c7574cb 100644 --- a/osu.Game/Screens/Ranking/SoloResultsScreen.cs +++ b/osu.Game/Screens/Ranking/SoloResultsScreen.cs @@ -31,7 +31,7 @@ namespace osu.Game.Screens.Ranking return null; getScoreRequest = new GetScoresRequest(Score.BeatmapInfo, Score.Ruleset); - getScoreRequest.Success += r => scoresCallback?.Invoke(r.Scores.Where(s => s.OnlineScoreID != Score.OnlineScoreID).Select(s => s.CreateScoreInfo(rulesets))); + getScoreRequest.Success += r => scoresCallback?.Invoke(r.Scores.Where(s => s.OnlineID != Score.OnlineScoreID).Select(s => s.CreateScoreInfo(rulesets))); return getScoreRequest; } From 54073d8a1e4b6f9294590c37b54c0eb87bd89b9c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Oct 2021 13:02:19 +0900 Subject: [PATCH 187/260] Isolate score submissions model and remove serialisation from `ScoreInfo` --- .../Online/Solo/SubmitSoloScoreRequest.cs | 64 ++++++++++++++++++- osu.Game/Scoring/ScoreInfo.cs | 43 ++----------- 2 files changed, 68 insertions(+), 39 deletions(-) diff --git a/osu.Game/Online/Solo/SubmitSoloScoreRequest.cs b/osu.Game/Online/Solo/SubmitSoloScoreRequest.cs index 85fa3eeb34..184dbc911d 100644 --- a/osu.Game/Online/Solo/SubmitSoloScoreRequest.cs +++ b/osu.Game/Online/Solo/SubmitSoloScoreRequest.cs @@ -1,12 +1,17 @@ // 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.Net.Http; using Newtonsoft.Json; +using Newtonsoft.Json.Converters; using osu.Framework.IO.Network; using osu.Game.Online.API; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.Rooms; +using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; +using osu.Game.Users; namespace osu.Game.Online.Solo { @@ -16,13 +21,13 @@ namespace osu.Game.Online.Solo private readonly int beatmapId; - private readonly ScoreInfo scoreInfo; + private readonly SubmittableScore score; public SubmitSoloScoreRequest(int beatmapId, long scoreId, ScoreInfo scoreInfo) { this.beatmapId = beatmapId; this.scoreId = scoreId; - this.scoreInfo = scoreInfo; + score = new SubmittableScore(scoreInfo); } protected override WebRequest CreateWebRequest() @@ -32,7 +37,7 @@ namespace osu.Game.Online.Solo req.ContentType = "application/json"; req.Method = HttpMethod.Put; - req.AddRaw(JsonConvert.SerializeObject(scoreInfo, new JsonSerializerSettings + req.AddRaw(JsonConvert.SerializeObject(score, new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore })); @@ -42,4 +47,57 @@ namespace osu.Game.Online.Solo protected override string Target => $@"beatmaps/{beatmapId}/solo/scores/{scoreId}"; } + + /// + /// A class specifically for sending scores to the API during score submission. + /// This is used instead of due to marginally different serialisation naming requirements. + /// + public class SubmittableScore + { + [JsonProperty("rank")] + [JsonConverter(typeof(StringEnumConverter))] + public ScoreRank Rank { get; } + + [JsonProperty("total_score")] + public long TotalScore { get; } + + [JsonProperty("accuracy")] + public double Accuracy { get; } + + [JsonProperty(@"pp")] + public double? PP { get; } + + [JsonProperty("max_combo")] + public int MaxCombo { get; } + + [JsonProperty("ruleset_id")] + public int RulesetID { get; } + + [JsonProperty("passed")] + public bool Passed { get; } + + // Used for API serialisation/deserialisation. + [JsonProperty("mods")] + public APIMod[] Mods { get; } + + [JsonProperty("user")] + public User User { get; } + + [JsonProperty("statistics")] + public Dictionary Statistics { get; } + + public SubmittableScore(ScoreInfo score) + { + Rank = score.Rank; + TotalScore = score.TotalScore; + Accuracy = score.Accuracy; + PP = score.PP; + MaxCombo = score.MaxCombo; + RulesetID = score.RulesetID; + Passed = score.Passed; + Mods = score.APIMods; + User = score.User; + Statistics = score.Statistics; + } + } } diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index 3b09669926..2747cd48c0 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -6,7 +6,6 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; using System.Linq; using Newtonsoft.Json; -using Newtonsoft.Json.Converters; using osu.Framework.Localisation; using osu.Game.Beatmaps; using osu.Game.Database; @@ -23,44 +22,32 @@ namespace osu.Game.Scoring { public int ID { get; set; } - [JsonProperty("rank")] - [JsonConverter(typeof(StringEnumConverter))] public ScoreRank Rank { get; set; } - [JsonProperty("total_score")] public long TotalScore { get; set; } - [JsonProperty("accuracy")] [Column(TypeName = "DECIMAL(1,4)")] // TODO: This data type is wrong (should contain more precision). But at the same time, we probably don't need to be storing this in the database. public double Accuracy { get; set; } - [JsonIgnore] public LocalisableString DisplayAccuracy => Accuracy.FormatAccuracy(); - [JsonProperty(@"pp")] public double? PP { get; set; } - [JsonProperty("max_combo")] public int MaxCombo { get; set; } - [JsonIgnore] public int Combo { get; set; } // Todo: Shouldn't exist in here - [JsonProperty("ruleset_id")] public int RulesetID { get; set; } - [JsonProperty("passed")] [NotMapped] public bool Passed { get; set; } = true; - [JsonIgnore] public RulesetInfo Ruleset { get; set; } private APIMod[] localAPIMods; private Mod[] mods; - [JsonIgnore] [NotMapped] public Mod[] Mods { @@ -75,7 +62,7 @@ namespace osu.Game.Scoring if (mods != null) scoreMods = mods; else if (localAPIMods != null) - scoreMods = apiMods.Select(m => m.ToMod(rulesetInstance)).ToArray(); + scoreMods = APIMods.Select(m => m.ToMod(rulesetInstance)).ToArray(); return scoreMods; } @@ -87,9 +74,8 @@ namespace osu.Game.Scoring } // Used for API serialisation/deserialisation. - [JsonProperty("mods")] [NotMapped] - private APIMod[] apiMods + public APIMod[] APIMods { get { @@ -111,19 +97,16 @@ namespace osu.Game.Scoring } // Used for database serialisation/deserialisation. - [JsonIgnore] [Column("Mods")] public string ModsJson { - get => JsonConvert.SerializeObject(apiMods); - set => apiMods = JsonConvert.DeserializeObject(value); + get => JsonConvert.SerializeObject(APIMods); + set => APIMods = JsonConvert.DeserializeObject(value); } [NotMapped] - [JsonProperty("user")] public User User { get; set; } - [JsonIgnore] [Column("User")] public string UserString { @@ -135,7 +118,6 @@ namespace osu.Game.Scoring } } - [JsonIgnore] [Column("UserID")] public int? UserID { @@ -147,23 +129,18 @@ namespace osu.Game.Scoring } } - [JsonIgnore] public int BeatmapInfoID { get; set; } - [JsonIgnore] [Column("Beatmap")] - public virtual BeatmapInfo BeatmapInfo { get; set; } + public BeatmapInfo BeatmapInfo { get; set; } - [JsonIgnore] public long? OnlineScoreID { get; set; } - [JsonIgnore] public DateTimeOffset Date { get; set; } - [JsonProperty("statistics")] + [NotMapped] public Dictionary Statistics { get; set; } = new Dictionary(); - [JsonIgnore] [Column("Statistics")] public string StatisticsJson { @@ -181,29 +158,23 @@ namespace osu.Game.Scoring } [NotMapped] - [JsonIgnore] public List HitEvents { get; set; } - [JsonIgnore] public List Files { get; set; } - [JsonIgnore] public string Hash { get; set; } - [JsonIgnore] public bool DeletePending { get; set; } /// /// The position of this score, starting at 1. /// [NotMapped] - [JsonProperty("position")] - public int? Position { get; set; } + public int? Position { get; set; } // TODO: remove after all calls to `CreateScoreInfo` are gone. /// /// Whether this represents a legacy (osu!stable) score. /// - [JsonIgnore] [NotMapped] public bool IsLegacyScore => Mods.OfType().Any(); From 3f030cebf4b2e36ff4d375a2a182d87e9c12eeb7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Oct 2021 14:14:25 +0900 Subject: [PATCH 188/260] Remove local score copying in `GetScoresRequest` to allow `APIScoreInfo.Beatmap` to be `APIBeatmap` --- .../Online/API/Requests/GetScoresRequest.cs | 22 ---------------- .../API/Requests/Responses/APIScoreInfo.cs | 25 ++++++------------- .../Responses/APIScoreWithPosition.cs | 5 ++-- .../BeatmapSet/Scores/ScoresContainer.cs | 4 +-- osu.Game/Screens/Ranking/SoloResultsScreen.cs | 2 +- .../Select/Leaderboards/BeatmapLeaderboard.cs | 4 +-- 6 files changed, 16 insertions(+), 46 deletions(-) diff --git a/osu.Game/Online/API/Requests/GetScoresRequest.cs b/osu.Game/Online/API/Requests/GetScoresRequest.cs index 9037869c77..790a247ccb 100644 --- a/osu.Game/Online/API/Requests/GetScoresRequest.cs +++ b/osu.Game/Online/API/Requests/GetScoresRequest.cs @@ -9,7 +9,6 @@ using osu.Game.Online.API.Requests.Responses; using osu.Game.Rulesets.Mods; using System.Text; using System.Collections.Generic; -using System.Diagnostics; namespace osu.Game.Online.API.Requests { @@ -32,27 +31,6 @@ namespace osu.Game.Online.API.Requests this.scope = scope; this.ruleset = ruleset ?? throw new ArgumentNullException(nameof(ruleset)); this.mods = mods ?? Array.Empty(); - - Success += onSuccess; - } - - private void onSuccess(APIScoresCollection r) - { - Debug.Assert(ruleset.OnlineID >= 0); - - foreach (APIScoreInfo score in r.Scores) - { - score.Beatmap = beatmapInfo; - score.OnlineRulesetID = ruleset.OnlineID; - } - - var userScore = r.UserScore; - - if (userScore != null) - { - userScore.Score.Beatmap = beatmapInfo; - userScore.Score.OnlineRulesetID = ruleset.OnlineID; - } } protected override string Target => $@"beatmaps/{beatmapInfo.OnlineBeatmapID}/scores{createQueryParameters()}"; diff --git a/osu.Game/Online/API/Requests/Responses/APIScoreInfo.cs b/osu.Game/Online/API/Requests/Responses/APIScoreInfo.cs index 568c6bea7f..957d5991c8 100644 --- a/osu.Game/Online/API/Requests/Responses/APIScoreInfo.cs +++ b/osu.Game/Online/API/Requests/Responses/APIScoreInfo.cs @@ -39,7 +39,7 @@ namespace osu.Game.Online.API.Requests.Responses public DateTimeOffset Date { get; set; } [JsonProperty(@"beatmap")] - public IBeatmapInfo Beatmap { get; set; } + public APIBeatmap Beatmap { get; set; } [JsonProperty("accuracy")] public double Accuracy { get; set; } @@ -71,10 +71,8 @@ namespace osu.Game.Online.API.Requests.Responses [JsonConverter(typeof(StringEnumConverter))] public ScoreRank Rank { get; set; } - IBeatmapInfo IScoreInfo.Beatmap => Beatmap; - - // TODO: nuke - public ScoreInfo CreateScoreInfo(RulesetStore rulesets) + // TODO: This function will eventually be going away. + public ScoreInfo CreateScoreInfo(RulesetStore rulesets, BeatmapInfo beatmap = null) { var ruleset = rulesets.GetRuleset(OnlineRulesetID); @@ -94,7 +92,6 @@ namespace osu.Game.Online.API.Requests.Responses OnlineScoreID = OnlineID, Date = Date, PP = PP, - BeatmapInfo = Beatmap.ToBeatmapInfo(rulesets), RulesetID = OnlineRulesetID, Hash = Replay ? "online" : string.Empty, // todo: temporary? Rank = Rank, @@ -102,6 +99,9 @@ namespace osu.Game.Online.API.Requests.Responses Mods = mods, }; + if (beatmap != null) + scoreInfo.BeatmapInfo = beatmap; + if (Statistics != null) { foreach (var kvp in Statistics) @@ -138,19 +138,10 @@ namespace osu.Game.Online.API.Requests.Responses return scoreInfo; } - [JsonProperty(@"beatmapset")] - public APIBeatmapSet Metadata - { - set - { - // in the deserialisation case we need to ferry this data across. - if (Beatmap is APIBeatmap apiBeatmap) - apiBeatmap.BeatmapSet = value; - } - } - public IRulesetInfo Ruleset => new RulesetInfo { ID = OnlineRulesetID }; + IBeatmapInfo IScoreInfo.Beatmap => Beatmap; + Dictionary IScoreInfo.Statistics => new Dictionary(); // TODO: implement... maybe. hitresults have weird mappings per ruleset it would seem. } } diff --git a/osu.Game/Online/API/Requests/Responses/APIScoreWithPosition.cs b/osu.Game/Online/API/Requests/Responses/APIScoreWithPosition.cs index 89bb416393..a0fc549d98 100644 --- a/osu.Game/Online/API/Requests/Responses/APIScoreWithPosition.cs +++ b/osu.Game/Online/API/Requests/Responses/APIScoreWithPosition.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using Newtonsoft.Json; +using osu.Game.Beatmaps; using osu.Game.Rulesets; using osu.Game.Scoring; @@ -15,10 +16,10 @@ namespace osu.Game.Online.API.Requests.Responses [JsonProperty(@"score")] public APIScoreInfo Score; - public ScoreInfo CreateScoreInfo(RulesetStore rulesets) + public ScoreInfo CreateScoreInfo(RulesetStore rulesets, BeatmapInfo beatmap = null) { - var score = Score.CreateScoreInfo(rulesets); + var score = Score.CreateScoreInfo(rulesets, beatmap); score.Position = Position; return score; } diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs index 3a3566aad9..c6eb516c7c 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs @@ -66,7 +66,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores if (value?.Scores.Any() != true) return; - scoreManager.OrderByTotalScoreAsync(value.Scores.Select(s => s.CreateScoreInfo(rulesets)).ToArray(), loadCancellationSource.Token) + scoreManager.OrderByTotalScoreAsync(value.Scores.Select(s => s.CreateScoreInfo(rulesets, Beatmap.Value)).ToArray(), loadCancellationSource.Token) .ContinueWith(ordered => Schedule(() => { if (loadCancellationSource.IsCancellationRequested) @@ -78,7 +78,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores scoreTable.Show(); var userScore = value.UserScore; - var userScoreInfo = userScore?.Score.CreateScoreInfo(rulesets); + var userScoreInfo = userScore?.Score.CreateScoreInfo(rulesets, Beatmap.Value); topScoresContainer.Add(new DrawableTopScore(topScore)); diff --git a/osu.Game/Screens/Ranking/SoloResultsScreen.cs b/osu.Game/Screens/Ranking/SoloResultsScreen.cs index 684c7574cb..4f4dfa4909 100644 --- a/osu.Game/Screens/Ranking/SoloResultsScreen.cs +++ b/osu.Game/Screens/Ranking/SoloResultsScreen.cs @@ -31,7 +31,7 @@ namespace osu.Game.Screens.Ranking return null; getScoreRequest = new GetScoresRequest(Score.BeatmapInfo, Score.Ruleset); - getScoreRequest.Success += r => scoresCallback?.Invoke(r.Scores.Where(s => s.OnlineID != Score.OnlineScoreID).Select(s => s.CreateScoreInfo(rulesets))); + getScoreRequest.Success += r => scoresCallback?.Invoke(r.Scores.Where(s => s.OnlineID != Score.OnlineScoreID).Select(s => s.CreateScoreInfo(rulesets, Beatmap.Value.BeatmapInfo))); return getScoreRequest; } diff --git a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs index 07300635aa..de1ba5cf2e 100644 --- a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs @@ -192,14 +192,14 @@ namespace osu.Game.Screens.Select.Leaderboards req.Success += r => { - scoreManager.OrderByTotalScoreAsync(r.Scores.Select(s => s.CreateScoreInfo(rulesets)).ToArray(), loadCancellationSource.Token) + scoreManager.OrderByTotalScoreAsync(r.Scores.Select(s => s.CreateScoreInfo(rulesets, BeatmapInfo)).ToArray(), loadCancellationSource.Token) .ContinueWith(ordered => Schedule(() => { if (loadCancellationSource.IsCancellationRequested) return; scoresCallback?.Invoke(ordered.Result); - TopScore = r.UserScore?.CreateScoreInfo(rulesets); + TopScore = r.UserScore?.CreateScoreInfo(rulesets, BeatmapInfo); }), TaskContinuationOptions.OnlyOnRanToCompletion); }; From cd7bd935f6e4c4512a42f8a256d774f50bb1fff6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Oct 2021 14:18:10 +0900 Subject: [PATCH 189/260] Remove `Statistics` from interface until we figure how to properly deserialise --- osu.Game/Online/API/Requests/Responses/APIScoreInfo.cs | 3 --- osu.Game/Scoring/IScoreInfo.cs | 6 ++---- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/osu.Game/Online/API/Requests/Responses/APIScoreInfo.cs b/osu.Game/Online/API/Requests/Responses/APIScoreInfo.cs index 957d5991c8..b996d21ad3 100644 --- a/osu.Game/Online/API/Requests/Responses/APIScoreInfo.cs +++ b/osu.Game/Online/API/Requests/Responses/APIScoreInfo.cs @@ -9,7 +9,6 @@ using Newtonsoft.Json.Converters; using osu.Game.Beatmaps; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; using osu.Game.Scoring.Legacy; using osu.Game.Users; @@ -141,7 +140,5 @@ namespace osu.Game.Online.API.Requests.Responses public IRulesetInfo Ruleset => new RulesetInfo { ID = OnlineRulesetID }; IBeatmapInfo IScoreInfo.Beatmap => Beatmap; - - Dictionary IScoreInfo.Statistics => new Dictionary(); // TODO: implement... maybe. hitresults have weird mappings per ruleset it would seem. } } diff --git a/osu.Game/Scoring/IScoreInfo.cs b/osu.Game/Scoring/IScoreInfo.cs index b230babf3c..171964206d 100644 --- a/osu.Game/Scoring/IScoreInfo.cs +++ b/osu.Game/Scoring/IScoreInfo.cs @@ -2,11 +2,9 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Collections.Generic; using osu.Game.Beatmaps; using osu.Game.Database; using osu.Game.Rulesets; -using osu.Game.Rulesets.Scoring; using osu.Game.Users; namespace osu.Game.Scoring @@ -29,13 +27,13 @@ namespace osu.Game.Scoring IBeatmapInfo Beatmap { get; } - Dictionary Statistics { get; } - IRulesetInfo Ruleset { get; } public ScoreRank Rank { get; } // Mods is currently missing from this interface as the `IMod` class has properties which can't be fulfilled by `APIMod`, // but also doesn't expose `Settings`. We can consider how to implement this in the future if required. + + // Statistics is also missing. This can be reconsidered once changes in serialisation have been completed. } } From f68d6dbc8f3c0665468dce17ccf1f8f7ee6d4224 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Oct 2021 14:32:31 +0900 Subject: [PATCH 190/260] Update score submission serialisation tests to use `SubmittableScore` --- .../Online/TestAPIModJsonSerialization.cs | 23 +++--- .../Online/Solo/SubmitSoloScoreRequest.cs | 58 -------------- osu.Game/Online/Solo/SubmittableScore.cs | 75 +++++++++++++++++++ 3 files changed, 84 insertions(+), 72 deletions(-) create mode 100644 osu.Game/Online/Solo/SubmittableScore.cs diff --git a/osu.Game.Tests/Online/TestAPIModJsonSerialization.cs b/osu.Game.Tests/Online/TestAPIModJsonSerialization.cs index ad2007f202..c8848ab7d8 100644 --- a/osu.Game.Tests/Online/TestAPIModJsonSerialization.cs +++ b/osu.Game.Tests/Online/TestAPIModJsonSerialization.cs @@ -8,6 +8,7 @@ using osu.Framework.Bindables; using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.Online.API; +using osu.Game.Online.Solo; using osu.Game.Rulesets; using osu.Game.Rulesets.Difficulty; using osu.Game.Rulesets.Mods; @@ -88,33 +89,27 @@ namespace osu.Game.Tests.Online } [Test] - public void TestDeserialiseScoreInfoWithEmptyMods() + public void TestDeserialiseSubmittableScoreWithEmptyMods() { - var score = new ScoreInfo { Ruleset = new OsuRuleset().RulesetInfo }; + var score = new SubmittableScore(new ScoreInfo { Ruleset = new OsuRuleset().RulesetInfo }); - var deserialised = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(score)); - - if (deserialised != null) - deserialised.Ruleset = new OsuRuleset().RulesetInfo; + var deserialised = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(score)); Assert.That(deserialised?.Mods.Length, Is.Zero); } [Test] - public void TestDeserialiseScoreInfoWithCustomModSetting() + public void TestDeserialiseSubmittableScoreWithCustomModSetting() { - var score = new ScoreInfo + var score = new SubmittableScore(new ScoreInfo { Ruleset = new OsuRuleset().RulesetInfo, Mods = new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 2 } } } - }; + }); - var deserialised = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(score)); + 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)); + Assert.That((deserialised?.Mods[0])?.Settings["speed_change"], Is.EqualTo(2)); } private class TestRuleset : Ruleset diff --git a/osu.Game/Online/Solo/SubmitSoloScoreRequest.cs b/osu.Game/Online/Solo/SubmitSoloScoreRequest.cs index 184dbc911d..25c2e5a61f 100644 --- a/osu.Game/Online/Solo/SubmitSoloScoreRequest.cs +++ b/osu.Game/Online/Solo/SubmitSoloScoreRequest.cs @@ -1,17 +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.Collections.Generic; using System.Net.Http; using Newtonsoft.Json; -using Newtonsoft.Json.Converters; using osu.Framework.IO.Network; using osu.Game.Online.API; -using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.Rooms; -using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; -using osu.Game.Users; namespace osu.Game.Online.Solo { @@ -47,57 +42,4 @@ namespace osu.Game.Online.Solo protected override string Target => $@"beatmaps/{beatmapId}/solo/scores/{scoreId}"; } - - /// - /// A class specifically for sending scores to the API during score submission. - /// This is used instead of due to marginally different serialisation naming requirements. - /// - public class SubmittableScore - { - [JsonProperty("rank")] - [JsonConverter(typeof(StringEnumConverter))] - public ScoreRank Rank { get; } - - [JsonProperty("total_score")] - public long TotalScore { get; } - - [JsonProperty("accuracy")] - public double Accuracy { get; } - - [JsonProperty(@"pp")] - public double? PP { get; } - - [JsonProperty("max_combo")] - public int MaxCombo { get; } - - [JsonProperty("ruleset_id")] - public int RulesetID { get; } - - [JsonProperty("passed")] - public bool Passed { get; } - - // Used for API serialisation/deserialisation. - [JsonProperty("mods")] - public APIMod[] Mods { get; } - - [JsonProperty("user")] - public User User { get; } - - [JsonProperty("statistics")] - public Dictionary Statistics { get; } - - public SubmittableScore(ScoreInfo score) - { - Rank = score.Rank; - TotalScore = score.TotalScore; - Accuracy = score.Accuracy; - PP = score.PP; - MaxCombo = score.MaxCombo; - RulesetID = score.RulesetID; - Passed = score.Passed; - Mods = score.APIMods; - User = score.User; - Statistics = score.Statistics; - } - } } diff --git a/osu.Game/Online/Solo/SubmittableScore.cs b/osu.Game/Online/Solo/SubmittableScore.cs new file mode 100644 index 0000000000..bafb308a13 --- /dev/null +++ b/osu.Game/Online/Solo/SubmittableScore.cs @@ -0,0 +1,75 @@ +// 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 JetBrains.Annotations; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests.Responses; +using osu.Game.Rulesets.Scoring; +using osu.Game.Scoring; +using osu.Game.Users; + +namespace osu.Game.Online.Solo +{ + /// + /// A class specifically for sending scores to the API during score submission. + /// This is used instead of due to marginally different serialisation naming requirements. + /// + [Serializable] + public class SubmittableScore + { + [JsonProperty("rank")] + [JsonConverter(typeof(StringEnumConverter))] + public ScoreRank Rank { get; set; } + + [JsonProperty("total_score")] + public long TotalScore { get; set; } + + [JsonProperty("accuracy")] + public double Accuracy { get; set; } + + [JsonProperty(@"pp")] + public double? PP { get; set; } + + [JsonProperty("max_combo")] + public int MaxCombo { get; set; } + + [JsonProperty("ruleset_id")] + public int RulesetID { get; set; } + + [JsonProperty("passed")] + public bool Passed { get; set; } + + // Used for API serialisation/deserialisation. + [JsonProperty("mods")] + public APIMod[] Mods { get; set; } + + [JsonProperty("user")] + public User User { get; set; } + + [JsonProperty("statistics")] + public Dictionary Statistics { get; set; } + + [UsedImplicitly] + public SubmittableScore() + { + } + + public SubmittableScore(ScoreInfo score) + { + Rank = score.Rank; + TotalScore = score.TotalScore; + Accuracy = score.Accuracy; + PP = score.PP; + MaxCombo = score.MaxCombo; + RulesetID = score.RulesetID; + Passed = score.Passed; + Mods = score.APIMods; + User = score.User; + Statistics = score.Statistics; + } + } +} From 06b6bcfd29a355b8a7ca89d2f2febafc74ca98d2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Oct 2021 14:51:06 +0900 Subject: [PATCH 191/260] Add xmldoc for `CreateScoreInfo` function for now I don't actually know how temporary this one is going to be. The usages are quite deep - ie. converting to a `ScoreInfo` to get a calculated total score for ordering purposes. --- osu.Game/Online/API/Requests/Responses/APIScoreInfo.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game/Online/API/Requests/Responses/APIScoreInfo.cs b/osu.Game/Online/API/Requests/Responses/APIScoreInfo.cs index b996d21ad3..4abb227414 100644 --- a/osu.Game/Online/API/Requests/Responses/APIScoreInfo.cs +++ b/osu.Game/Online/API/Requests/Responses/APIScoreInfo.cs @@ -70,7 +70,12 @@ namespace osu.Game.Online.API.Requests.Responses [JsonConverter(typeof(StringEnumConverter))] public ScoreRank Rank { get; set; } - // TODO: This function will eventually be going away. + /// + /// Create a from an API score instance. + /// + /// A ruleset store, used to populate a ruleset instance in the returned score. + /// An optional beatmap, copied into the returned score (for cases where the API does not populate the beatmap). + /// public ScoreInfo CreateScoreInfo(RulesetStore rulesets, BeatmapInfo beatmap = null) { var ruleset = rulesets.GetRuleset(OnlineRulesetID); From 17a83f701a0e021f048cdf7567a316f2486591a4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Oct 2021 15:13:33 +0900 Subject: [PATCH 192/260] Store mods as `APIMods` for the time being --- .../Gameplay/TestSceneReplayDownloadButton.cs | 2 +- .../API/Requests/Responses/APIScoreInfo.cs | 18 ++++++++++-------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs index b84ac6c2f1..2f9b03e86e 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs @@ -135,7 +135,7 @@ namespace osu.Game.Tests.Visual.Gameplay return new APIScoreInfo { OnlineID = 2553163309, - OnlineRulesetID = 0, + RulesetID = 0, Replay = replayAvailable, User = new User { diff --git a/osu.Game/Online/API/Requests/Responses/APIScoreInfo.cs b/osu.Game/Online/API/Requests/Responses/APIScoreInfo.cs index 4abb227414..448156b63a 100644 --- a/osu.Game/Online/API/Requests/Responses/APIScoreInfo.cs +++ b/osu.Game/Online/API/Requests/Responses/APIScoreInfo.cs @@ -61,10 +61,12 @@ namespace osu.Game.Online.API.Requests.Responses public Dictionary Statistics { get; set; } [JsonProperty(@"mode_int")] - public int OnlineRulesetID { get; set; } + public int RulesetID { get; set; } [JsonProperty(@"mods")] - public string[] Mods { get; set; } + private string[] mods { set => Mods = value.Select(acronym => new APIMod { Acronym = acronym }); } + + public IEnumerable Mods { get; set; } [JsonProperty("rank")] [JsonConverter(typeof(StringEnumConverter))] @@ -78,14 +80,14 @@ namespace osu.Game.Online.API.Requests.Responses /// public ScoreInfo CreateScoreInfo(RulesetStore rulesets, BeatmapInfo beatmap = null) { - var ruleset = rulesets.GetRuleset(OnlineRulesetID); + var ruleset = rulesets.GetRuleset(RulesetID); var rulesetInstance = ruleset.CreateInstance(); - var mods = Mods != null ? Mods.Select(acronym => rulesetInstance.CreateModFromAcronym(acronym)).Where(m => m != null).ToArray() : Array.Empty(); + var modInstances = Mods != null ? Mods.Select(apiMod => rulesetInstance.CreateModFromAcronym(apiMod.Acronym)).Where(m => m != null).ToArray() : Array.Empty(); // all API scores provided by this class are considered to be legacy. - mods = mods.Append(rulesetInstance.CreateMod()).ToArray(); + modInstances = modInstances.Append(rulesetInstance.CreateMod()).ToArray(); var scoreInfo = new ScoreInfo { @@ -96,11 +98,11 @@ namespace osu.Game.Online.API.Requests.Responses OnlineScoreID = OnlineID, Date = Date, PP = PP, - RulesetID = OnlineRulesetID, + RulesetID = RulesetID, Hash = Replay ? "online" : string.Empty, // todo: temporary? Rank = Rank, Ruleset = ruleset, - Mods = mods, + Mods = modInstances, }; if (beatmap != null) @@ -142,7 +144,7 @@ namespace osu.Game.Online.API.Requests.Responses return scoreInfo; } - public IRulesetInfo Ruleset => new RulesetInfo { ID = OnlineRulesetID }; + public IRulesetInfo Ruleset => new RulesetInfo { ID = RulesetID }; IBeatmapInfo IScoreInfo.Beatmap => Beatmap; } From 31403daa2078b5023a91958d919d273b9dbef1a9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Oct 2021 15:14:52 +0900 Subject: [PATCH 193/260] Change `DrawableProfileScore` to use `APIScoreInfo` --- .../Visual/Online/TestSceneScoresContainer.cs | 29 ++++---- .../Online/TestSceneUserProfileScores.cs | 72 +++++++++---------- .../Sections/Ranks/DrawableProfileScore.cs | 18 ++--- .../Ranks/DrawableProfileWeightedScore.cs | 4 +- .../Sections/Ranks/PaginatedScoreContainer.cs | 4 +- 5 files changed, 65 insertions(+), 62 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneScoresContainer.cs b/osu.Game.Tests/Visual/Online/TestSceneScoresContainer.cs index 991be33917..23899154c4 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneScoresContainer.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneScoresContainer.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Utils; +using osu.Game.Online.API; using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays; using osu.Game.Overlays.BeatmapSet.Scores; @@ -61,10 +62,10 @@ namespace osu.Game.Tests.Visual.Online }, Mods = new[] { - new OsuModDoubleTime().Acronym, - new OsuModHidden().Acronym, - new OsuModFlashlight().Acronym, - new OsuModHardRock().Acronym, + new APIMod { Acronym = new OsuModDoubleTime().Acronym }, + new APIMod { Acronym = new OsuModHidden().Acronym }, + new APIMod { Acronym = new OsuModFlashlight().Acronym }, + new APIMod { Acronym = new OsuModHardRock().Acronym }, }, Rank = ScoreRank.XH, PP = 200, @@ -86,9 +87,9 @@ namespace osu.Game.Tests.Visual.Online }, Mods = new[] { - new OsuModDoubleTime().Acronym, - new OsuModHidden().Acronym, - new OsuModFlashlight().Acronym, + new APIMod { Acronym = new OsuModDoubleTime().Acronym }, + new APIMod { Acronym = new OsuModHidden().Acronym }, + new APIMod { Acronym = new OsuModFlashlight().Acronym }, }, Rank = ScoreRank.S, PP = 190, @@ -110,8 +111,8 @@ namespace osu.Game.Tests.Visual.Online }, Mods = new[] { - new OsuModDoubleTime().Acronym, - new OsuModHidden().Acronym, + new APIMod { Acronym = new OsuModDoubleTime().Acronym }, + new APIMod { Acronym = new OsuModHidden().Acronym }, }, Rank = ScoreRank.B, PP = 180, @@ -133,7 +134,7 @@ namespace osu.Game.Tests.Visual.Online }, Mods = new[] { - new OsuModDoubleTime().Acronym, + new APIMod { Acronym = new OsuModDoubleTime().Acronym }, }, Rank = ScoreRank.C, PP = 170, @@ -226,10 +227,10 @@ namespace osu.Game.Tests.Visual.Online }, Mods = new[] { - new OsuModDoubleTime().Acronym, - new OsuModHidden().Acronym, - new OsuModFlashlight().Acronym, - new OsuModHardRock().Acronym, + new APIMod { Acronym = new OsuModDoubleTime().Acronym }, + new APIMod { Acronym = new OsuModHidden().Acronym }, + new APIMod { Acronym = new OsuModFlashlight().Acronym }, + new APIMod { Acronym = new OsuModHardRock().Acronym }, }, Rank = ScoreRank.XH, PP = 200, diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserProfileScores.cs b/osu.Game.Tests/Visual/Online/TestSceneUserProfileScores.cs index 513631a221..fac350ea57 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneUserProfileScores.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneUserProfileScores.cs @@ -2,16 +2,16 @@ // See the LICENCE file in the repository root for full licence text. using System; -using osu.Game.Overlays.Profile.Sections.Ranks; -using osu.Framework.Graphics; -using osu.Game.Scoring; -using osu.Framework.Graphics.Containers; -using osuTK; -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Osu.Mods; -using osu.Game.Overlays; using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests.Responses; +using osu.Game.Overlays; +using osu.Game.Overlays.Profile.Sections.Ranks; +using osu.Game.Rulesets.Osu.Mods; +using osu.Game.Scoring; +using osuTK; namespace osu.Game.Tests.Visual.Online { @@ -19,79 +19,79 @@ namespace osu.Game.Tests.Visual.Online { public TestSceneUserProfileScores() { - var firstScore = new ScoreInfo + var firstScore = new APIScoreInfo { PP = 1047.21, Rank = ScoreRank.SH, - BeatmapInfo = new BeatmapInfo + Beatmap = new APIBeatmap { - Metadata = new BeatmapMetadata + BeatmapSet = { Title = "JUSTadICE (TV Size)", - Artist = "Oomori Seiko" + Artist = "Oomori Seiko", }, - Version = "Extreme" + DifficultyName = "Extreme" }, Date = DateTimeOffset.Now, - Mods = new Mod[] + Mods = new[] { - new OsuModHidden(), - new OsuModHardRock(), - new OsuModDoubleTime() + new APIMod { Acronym = new OsuModHidden().Acronym }, + new APIMod { Acronym = new OsuModHardRock().Acronym }, + new APIMod { Acronym = new OsuModDoubleTime().Acronym }, }, Accuracy = 0.9813 }; - var secondScore = new ScoreInfo + var secondScore = new APIScoreInfo { PP = 134.32, Rank = ScoreRank.A, - BeatmapInfo = new BeatmapInfo + Beatmap = new APIBeatmap { - Metadata = new BeatmapMetadata + BeatmapSet = { Title = "Triumph & Regret", - Artist = "typeMARS" + Artist = "typeMARS", }, - Version = "[4K] Regret" + DifficultyName = "[4K] Regret" }, Date = DateTimeOffset.Now, - Mods = new Mod[] + Mods = new[] { - new OsuModHardRock(), - new OsuModDoubleTime(), + new APIMod { Acronym = new OsuModHardRock().Acronym }, + new APIMod { Acronym = new OsuModDoubleTime().Acronym }, }, Accuracy = 0.998546 }; - var thirdScore = new ScoreInfo + var thirdScore = new APIScoreInfo { PP = 96.83, Rank = ScoreRank.S, - BeatmapInfo = new BeatmapInfo + Beatmap = new APIBeatmap { - Metadata = new BeatmapMetadata + BeatmapSet = { Title = "Idolize", - Artist = "Creo" + Artist = "Creo", }, - Version = "Insane" + DifficultyName = "Insane" }, Date = DateTimeOffset.Now, Accuracy = 0.9726 }; - var noPPScore = new ScoreInfo + var noPPScore = new APIScoreInfo { Rank = ScoreRank.B, - BeatmapInfo = new BeatmapInfo + Beatmap = new APIBeatmap { - Metadata = new BeatmapMetadata + BeatmapSet = { Title = "C18H27NO3(extend)", - Artist = "Team Grimoire" + Artist = "Team Grimoire", }, - Version = "[4K] Cataclysmic Hypernova" + DifficultyName = "[4K] Cataclysmic Hypernova" }, Date = DateTimeOffset.Now, Accuracy = 0.55879 diff --git a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs index 7bfa2ee51e..ca5534dbc2 100644 --- a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs +++ b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs @@ -12,9 +12,11 @@ using osu.Framework.Localisation; using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.Leaderboards; +using osu.Game.Rulesets; using osu.Game.Rulesets.UI; -using osu.Game.Scoring; +using osu.Game.Utils; using osuTK; namespace osu.Game.Overlays.Profile.Sections.Ranks @@ -26,7 +28,7 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks private const float performance_background_shear = 0.45f; - protected readonly ScoreInfo Score; + protected readonly APIScoreInfo Score; [Resolved] private OsuColour colours { get; set; } @@ -34,7 +36,7 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks [Resolved] private OverlayColourProvider colourProvider { get; set; } - public DrawableProfileScore(ScoreInfo score) + public DrawableProfileScore(APIScoreInfo score) { Score = score; @@ -43,7 +45,7 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks } [BackgroundDependencyLoader] - private void load() + private void load(RulesetStore rulesets) { AddInternal(new ProfileItemContainer { @@ -79,7 +81,7 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks Spacing = new Vector2(0, 2), Children = new Drawable[] { - new ScoreBeatmapMetadataContainer(Score.BeatmapInfo), + new ScoreBeatmapMetadataContainer(Score.Beatmap), new FillFlowContainer { AutoSizeAxes = Axes.Both, @@ -89,7 +91,7 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks { new OsuSpriteText { - Text = $"{Score.BeatmapInfo.Version}", + Text = $"{Score.Beatmap.DifficultyName}", Font = OsuFont.GetFont(size: 12, weight: FontWeight.Regular), Colour = colours.Yellow }, @@ -129,7 +131,7 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks Origin = Anchor.CentreRight, Direction = FillDirection.Horizontal, Spacing = new Vector2(2), - Children = Score.Mods.Select(mod => new ModIcon(mod) + Children = Score.Mods.Select(mod => new ModIcon(rulesets.GetRuleset(Score.RulesetID).CreateInstance().CreateModFromAcronym(mod.Acronym)) { Scale = new Vector2(0.35f) }).ToList(), @@ -198,7 +200,7 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks RelativeSizeAxes = Axes.Y, Child = new OsuSpriteText { - Text = Score.DisplayAccuracy, + Text = Score.Accuracy.FormatAccuracy(), Font = OsuFont.GetFont(size: 14, weight: FontWeight.Bold, italics: true), Colour = colours.Yellow, Anchor = Anchor.CentreLeft, diff --git a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileWeightedScore.cs b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileWeightedScore.cs index f77464ecb9..e653be5cfa 100644 --- a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileWeightedScore.cs +++ b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileWeightedScore.cs @@ -6,8 +6,8 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Resources.Localisation.Web; -using osu.Game.Scoring; using osuTK; namespace osu.Game.Overlays.Profile.Sections.Ranks @@ -16,7 +16,7 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks { private readonly double weight; - public DrawableProfileWeightedScore(ScoreInfo score, double weight) + public DrawableProfileWeightedScore(APIScoreInfo score, double weight) : base(score) { this.weight = weight; diff --git a/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs b/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs index 1fb6100a28..cde386bc7b 100644 --- a/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs @@ -69,10 +69,10 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks switch (type) { default: - return new DrawableProfileScore(model.CreateScoreInfo(Rulesets)); + return new DrawableProfileScore(model); case ScoreType.Best: - return new DrawableProfileWeightedScore(model.CreateScoreInfo(Rulesets), Math.Pow(0.95, drawableItemIndex++)); + return new DrawableProfileWeightedScore(model, Math.Pow(0.95, drawableItemIndex++)); } } } From 7583435901388816aaf9cf6000977b627a429e6e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Oct 2021 16:45:10 +0900 Subject: [PATCH 194/260] Refactor `BeatmapDifficultyCache` to work with `IBeatmapInfo` --- osu.Game/Beatmaps/BeatmapDifficultyCache.cs | 47 +++++++++++---------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapDifficultyCache.cs b/osu.Game/Beatmaps/BeatmapDifficultyCache.cs index 3777365088..9a0cdb387d 100644 --- a/osu.Game/Beatmaps/BeatmapDifficultyCache.cs +++ b/osu.Game/Beatmaps/BeatmapDifficultyCache.cs @@ -88,7 +88,7 @@ namespace osu.Game.Beatmaps /// The to get the difficulty of. /// An optional which stops updating the star difficulty for the given . /// A bindable that is updated to contain the star difficulty when it becomes available. Will be null while in an initial calculating state (but not during updates to ruleset and mods if a stale value is already propagated). - public IBindable GetBindableDifficulty([NotNull] BeatmapInfo beatmapInfo, CancellationToken cancellationToken = default) + public IBindable GetBindableDifficulty([NotNull] IBeatmapInfo beatmapInfo, CancellationToken cancellationToken = default) { var bindable = createBindable(beatmapInfo, currentRuleset.Value, currentMods.Value, cancellationToken); @@ -99,42 +99,45 @@ namespace osu.Game.Beatmaps } /// - /// Retrieves a bindable containing the star difficulty of a with a given and combination. + /// Retrieves a bindable containing the star difficulty of a with a given and combination. /// /// /// The bindable will not update to follow the currently-selected ruleset and mods or its settings. /// - /// The to get the difficulty of. - /// The to get the difficulty with. If null, the 's ruleset is used. + /// The to get the difficulty of. + /// The to get the difficulty with. If null, the 's ruleset is used. /// The s to get the difficulty with. If null, no mods will be assumed. - /// An optional which stops updating the star difficulty for the given . + /// An optional which stops updating the star difficulty for the given . /// A bindable that is updated to contain the star difficulty when it becomes available. Will be null while in an initial calculating state. - public IBindable GetBindableDifficulty([NotNull] BeatmapInfo beatmapInfo, [CanBeNull] RulesetInfo rulesetInfo, [CanBeNull] IEnumerable mods, + public IBindable GetBindableDifficulty([NotNull] IBeatmapInfo beatmapInfo, [CanBeNull] IRulesetInfo rulesetInfo, [CanBeNull] IEnumerable mods, CancellationToken cancellationToken = default) => createBindable(beatmapInfo, rulesetInfo, mods, cancellationToken); /// - /// Retrieves the difficulty of a . + /// Retrieves the difficulty of a . /// - /// The to get the difficulty of. - /// The to get the difficulty with. + /// The to get the difficulty of. + /// The to get the difficulty with. /// The s to get the difficulty with. /// An optional which stops computing the star difficulty. /// The . - public virtual Task GetDifficultyAsync([NotNull] BeatmapInfo beatmapInfo, [CanBeNull] RulesetInfo rulesetInfo = null, + public virtual Task GetDifficultyAsync([NotNull] IBeatmapInfo beatmapInfo, [CanBeNull] IRulesetInfo rulesetInfo = null, [CanBeNull] IEnumerable mods = null, CancellationToken cancellationToken = default) { // In the case that the user hasn't given us a ruleset, use the beatmap's default ruleset. rulesetInfo ??= beatmapInfo.Ruleset; + var localBeatmapInfo = beatmapInfo as BeatmapInfo; + var localRulesetInfo = rulesetInfo as RulesetInfo; + // Difficulty can only be computed if the beatmap and ruleset are locally available. - if (beatmapInfo.ID == 0 || rulesetInfo.ID == null) + if (localBeatmapInfo == null || localRulesetInfo == null) { // If not, fall back to the existing star difficulty (e.g. from an online source). - return Task.FromResult(new StarDifficulty(beatmapInfo.StarDifficulty, beatmapInfo.MaxCombo ?? 0)); + return Task.FromResult(new StarDifficulty(beatmapInfo.StarRating, (beatmapInfo as IBeatmapOnlineInfo)?.MaxCombo ?? 0)); } - return GetAsync(new DifficultyCacheLookup(beatmapInfo, rulesetInfo, mods), cancellationToken); + return GetAsync(new DifficultyCacheLookup(localBeatmapInfo, localRulesetInfo, mods), cancellationToken); } protected override Task ComputeValueAsync(DifficultyCacheLookup lookup, CancellationToken token = default) @@ -227,12 +230,12 @@ namespace osu.Game.Beatmaps /// /// Creates a new and triggers an initial value update. /// - /// The that star difficulty should correspond to. - /// The initial to get the difficulty with. + /// The that star difficulty should correspond to. + /// The initial to get the difficulty with. /// The initial s to get the difficulty with. - /// An optional which stops updating the star difficulty for the given . + /// An optional which stops updating the star difficulty for the given . /// The . - private BindableStarDifficulty createBindable([NotNull] BeatmapInfo beatmapInfo, [CanBeNull] RulesetInfo initialRulesetInfo, [CanBeNull] IEnumerable initialMods, + private BindableStarDifficulty createBindable([NotNull] IBeatmapInfo beatmapInfo, [CanBeNull] IRulesetInfo initialRulesetInfo, [CanBeNull] IEnumerable initialMods, CancellationToken cancellationToken) { var bindable = new BindableStarDifficulty(beatmapInfo, cancellationToken); @@ -244,12 +247,12 @@ namespace osu.Game.Beatmaps /// Updates the value of a with a given ruleset + mods. /// /// The to update. - /// The to update with. + /// The to update with. /// The s to update with. /// A token that may be used to cancel this update. - private void updateBindable([NotNull] BindableStarDifficulty bindable, [CanBeNull] RulesetInfo rulesetInfo, [CanBeNull] IEnumerable mods, CancellationToken cancellationToken = default) + private void updateBindable([NotNull] BindableStarDifficulty bindable, [CanBeNull] IRulesetInfo rulesetInfo, [CanBeNull] IEnumerable mods, CancellationToken cancellationToken = default) { - // GetDifficultyAsync will fall back to existing data from BeatmapInfo if not locally available + // GetDifficultyAsync will fall back to existing data from IBeatmapInfo if not locally available // (contrary to GetAsync) GetDifficultyAsync(bindable.BeatmapInfo, rulesetInfo, mods, cancellationToken) .ContinueWith(t => @@ -343,10 +346,10 @@ namespace osu.Game.Beatmaps private class BindableStarDifficulty : Bindable { - public readonly BeatmapInfo BeatmapInfo; + public readonly IBeatmapInfo BeatmapInfo; public readonly CancellationToken CancellationToken; - public BindableStarDifficulty(BeatmapInfo beatmapInfo, CancellationToken cancellationToken) + public BindableStarDifficulty(IBeatmapInfo beatmapInfo, CancellationToken cancellationToken) { BeatmapInfo = beatmapInfo; CancellationToken = cancellationToken; From aff216840fcf94c98a609c3f5cc9fbd4bbdc628c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Oct 2021 16:53:54 +0900 Subject: [PATCH 195/260] Add a note about `OnlineID` potentially being zero in non-autoincrement cases --- osu.Game/Database/IHasOnlineID.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Database/IHasOnlineID.cs b/osu.Game/Database/IHasOnlineID.cs index 6e2be7e1f9..bb05bf75cb 100644 --- a/osu.Game/Database/IHasOnlineID.cs +++ b/osu.Game/Database/IHasOnlineID.cs @@ -8,7 +8,7 @@ namespace osu.Game.Database public interface IHasOnlineID { /// - /// The server-side ID representing this instance, if one exists. Any value 0 or less denotes a missing ID. + /// The server-side ID representing this instance, if one exists. Any value 0 or less denotes a missing ID (except in special cases where autoincrement is not used, like rulesets). /// /// /// Generally we use -1 when specifying "missing" in code, but values of 0 are also considered missing as the online source From 3598adb3449e395bc15ce29d2e895a5421c84705 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Oct 2021 17:04:56 +0900 Subject: [PATCH 196/260] Fix test implementing old version of class --- .../Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs index f91d3f595b..50ae673c06 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapMetadataDisplay.cs @@ -140,7 +140,7 @@ namespace osu.Game.Tests.Visual.SongSelect } } - public override async Task GetDifficultyAsync(BeatmapInfo beatmapInfo, RulesetInfo rulesetInfo = null, IEnumerable mods = null, CancellationToken cancellationToken = default) + public override async Task GetDifficultyAsync(IBeatmapInfo beatmapInfo, IRulesetInfo rulesetInfo = null, IEnumerable mods = null, CancellationToken cancellationToken = default) { if (blockCalculation) await calculationBlocker.Task.ConfigureAwait(false); From be0564f7326960959970c398203bac1e8ed4280f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Oct 2021 17:03:50 +0900 Subject: [PATCH 197/260] Update `DifficultyIcon` classes to use `IBeatmapInfo` --- osu.Game/Beatmaps/Drawables/DifficultyIcon.cs | 23 +++++++++++-------- .../Drawables/DifficultyIconTooltip.cs | 6 ++--- .../Drawables/GroupedDifficultyIcon.cs | 6 ++--- 3 files changed, 19 insertions(+), 16 deletions(-) diff --git a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs index 880d70aec2..3a55b9261f 100644 --- a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs +++ b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs @@ -37,10 +37,10 @@ namespace osu.Game.Beatmaps.Drawables } [NotNull] - private readonly BeatmapInfo beatmapInfo; + private readonly IBeatmapInfo beatmapInfo; [CanBeNull] - private readonly RulesetInfo ruleset; + private readonly IRulesetInfo ruleset; [CanBeNull] private readonly IReadOnlyList mods; @@ -60,7 +60,7 @@ namespace osu.Game.Beatmaps.Drawables /// The ruleset to show the difficulty with. /// The mods to show the difficulty with. /// Whether to display a tooltip when hovered. - public DifficultyIcon([NotNull] BeatmapInfo beatmapInfo, [CanBeNull] RulesetInfo ruleset, [CanBeNull] IReadOnlyList mods, bool shouldShowTooltip = true) + public DifficultyIcon([NotNull] IBeatmapInfo beatmapInfo, [CanBeNull] IRulesetInfo ruleset, [CanBeNull] IReadOnlyList mods, bool shouldShowTooltip = true) : this(beatmapInfo, shouldShowTooltip) { this.ruleset = ruleset ?? beatmapInfo.Ruleset; @@ -73,7 +73,7 @@ namespace osu.Game.Beatmaps.Drawables /// The beatmap to show the difficulty of. /// Whether to display a tooltip when hovered. /// Whether to perform difficulty lookup (including calculation if necessary). - public DifficultyIcon([NotNull] BeatmapInfo beatmapInfo, bool shouldShowTooltip = true, bool performBackgroundDifficultyLookup = true) + public DifficultyIcon([NotNull] IBeatmapInfo beatmapInfo, bool shouldShowTooltip = true, bool performBackgroundDifficultyLookup = true) { this.beatmapInfo = beatmapInfo ?? throw new ArgumentNullException(nameof(beatmapInfo)); this.shouldShowTooltip = shouldShowTooltip; @@ -84,6 +84,9 @@ namespace osu.Game.Beatmaps.Drawables InternalChild = iconContainer = new Container { Size = new Vector2(20f) }; } + [Resolved] + private RulesetStore rulesets { get; set; } + [BackgroundDependencyLoader] private void load(OsuColour colours) { @@ -105,7 +108,7 @@ namespace osu.Game.Beatmaps.Drawables Child = background = new Box { RelativeSizeAxes = Axes.Both, - Colour = colours.ForStarDifficulty(beatmapInfo.StarDifficulty) // Default value that will be re-populated once difficulty calculation completes + Colour = colours.ForStarDifficulty(beatmapInfo.StarRating) // Default value that will be re-populated once difficulty calculation completes }, }, new ConstrainedIconContainer @@ -114,14 +117,14 @@ namespace osu.Game.Beatmaps.Drawables Origin = Anchor.Centre, RelativeSizeAxes = Axes.Both, // the null coalesce here is only present to make unit tests work (ruleset dlls aren't copied correctly for testing at the moment) - Icon = (ruleset ?? beatmapInfo.Ruleset)?.CreateInstance()?.CreateIcon() ?? new SpriteIcon { Icon = FontAwesome.Regular.QuestionCircle } + Icon = rulesets.GetRuleset((ruleset ?? beatmapInfo.Ruleset).OnlineID)?.CreateInstance()?.CreateIcon() ?? new SpriteIcon { Icon = FontAwesome.Regular.QuestionCircle } }, }; if (performBackgroundDifficultyLookup) iconContainer.Add(new DelayedLoadUnloadWrapper(() => new DifficultyRetriever(beatmapInfo, ruleset, mods) { StarDifficulty = { BindTarget = difficultyBindable } }, 0)); else - difficultyBindable.Value = new StarDifficulty(beatmapInfo.StarDifficulty, 0); + difficultyBindable.Value = new StarDifficulty(beatmapInfo.StarRating, 0); difficultyBindable.BindValueChanged(difficulty => background.Colour = colours.ForStarDifficulty(difficulty.NewValue.Stars)); } @@ -134,8 +137,8 @@ namespace osu.Game.Beatmaps.Drawables { public readonly Bindable StarDifficulty = new Bindable(); - private readonly BeatmapInfo beatmapInfo; - private readonly RulesetInfo ruleset; + private readonly IBeatmapInfo beatmapInfo; + private readonly IRulesetInfo ruleset; private readonly IReadOnlyList mods; private CancellationTokenSource difficultyCancellation; @@ -143,7 +146,7 @@ namespace osu.Game.Beatmaps.Drawables [Resolved] private BeatmapDifficultyCache difficultyCache { get; set; } - public DifficultyRetriever(BeatmapInfo beatmapInfo, RulesetInfo ruleset, IReadOnlyList mods) + public DifficultyRetriever(IBeatmapInfo beatmapInfo, IRulesetInfo ruleset, IReadOnlyList mods) { this.beatmapInfo = beatmapInfo; this.ruleset = ruleset; diff --git a/osu.Game/Beatmaps/Drawables/DifficultyIconTooltip.cs b/osu.Game/Beatmaps/Drawables/DifficultyIconTooltip.cs index d4c9f83a0a..ec4bcbd65f 100644 --- a/osu.Game/Beatmaps/Drawables/DifficultyIconTooltip.cs +++ b/osu.Game/Beatmaps/Drawables/DifficultyIconTooltip.cs @@ -89,7 +89,7 @@ namespace osu.Game.Beatmaps.Drawables public void SetContent(DifficultyIconTooltipContent content) { - difficultyName.Text = content.BeatmapInfo.Version; + difficultyName.Text = content.BeatmapInfo.DifficultyName; starDifficulty.UnbindAll(); starDifficulty.BindTo(content.Difficulty); @@ -109,10 +109,10 @@ namespace osu.Game.Beatmaps.Drawables internal class DifficultyIconTooltipContent { - public readonly BeatmapInfo BeatmapInfo; + public readonly IBeatmapInfo BeatmapInfo; public readonly IBindable Difficulty; - public DifficultyIconTooltipContent(BeatmapInfo beatmapInfo, IBindable difficulty) + public DifficultyIconTooltipContent(IBeatmapInfo beatmapInfo, IBindable difficulty) { BeatmapInfo = beatmapInfo; Difficulty = difficulty; diff --git a/osu.Game/Beatmaps/Drawables/GroupedDifficultyIcon.cs b/osu.Game/Beatmaps/Drawables/GroupedDifficultyIcon.cs index fcee4c2f1a..799a02579e 100644 --- a/osu.Game/Beatmaps/Drawables/GroupedDifficultyIcon.cs +++ b/osu.Game/Beatmaps/Drawables/GroupedDifficultyIcon.cs @@ -19,8 +19,8 @@ namespace osu.Game.Beatmaps.Drawables /// public class GroupedDifficultyIcon : DifficultyIcon { - public GroupedDifficultyIcon(List beatmaps, RulesetInfo ruleset, Color4 counterColour) - : base(beatmaps.OrderBy(b => b.StarDifficulty).Last(), ruleset, null, false) + public GroupedDifficultyIcon(IEnumerable beatmaps, IRulesetInfo ruleset, Color4 counterColour) + : base(beatmaps.OrderBy(b => b.StarRating).Last(), ruleset, null, false) { AddInternal(new OsuSpriteText { @@ -29,7 +29,7 @@ namespace osu.Game.Beatmaps.Drawables Padding = new MarginPadding { Left = Size.X }, Margin = new MarginPadding { Left = 2, Right = 5 }, Font = OsuFont.GetFont(size: 14, weight: FontWeight.SemiBold), - Text = beatmaps.Count.ToString(), + Text = beatmaps.Count().ToString(), Colour = counterColour, }); } From b5834044e0d405ec4acbfa9629271d4dc77fa729 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Oct 2021 17:40:02 +0900 Subject: [PATCH 198/260] Update `GetScoreRequest` to support requests with interface types --- osu.Game/Online/API/Requests/GetScoresRequest.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Online/API/Requests/GetScoresRequest.cs b/osu.Game/Online/API/Requests/GetScoresRequest.cs index 790a247ccb..8e447390e6 100644 --- a/osu.Game/Online/API/Requests/GetScoresRequest.cs +++ b/osu.Game/Online/API/Requests/GetScoresRequest.cs @@ -14,15 +14,15 @@ namespace osu.Game.Online.API.Requests { public class GetScoresRequest : APIRequest { - private readonly BeatmapInfo beatmapInfo; + private readonly IBeatmapInfo beatmapInfo; private readonly BeatmapLeaderboardScope scope; - private readonly RulesetInfo ruleset; + private readonly IRulesetInfo ruleset; private readonly IEnumerable mods; - public GetScoresRequest(BeatmapInfo beatmapInfo, RulesetInfo ruleset, BeatmapLeaderboardScope scope = BeatmapLeaderboardScope.Global, IEnumerable mods = null) + public GetScoresRequest(IBeatmapInfo beatmapInfo, IRulesetInfo ruleset, BeatmapLeaderboardScope scope = BeatmapLeaderboardScope.Global, IEnumerable mods = null) { - if (!beatmapInfo.OnlineBeatmapID.HasValue) - throw new InvalidOperationException($"Cannot lookup a beatmap's scores without having a populated {nameof(BeatmapInfo.OnlineBeatmapID)}."); + if (beatmapInfo.OnlineID <= 0) + throw new InvalidOperationException($"Cannot lookup a beatmap's scores without having a populated {nameof(IBeatmapInfo.OnlineID)}."); if (scope == BeatmapLeaderboardScope.Local) throw new InvalidOperationException("Should not attempt to request online scores for a local scoped leaderboard"); @@ -33,7 +33,7 @@ namespace osu.Game.Online.API.Requests this.mods = mods ?? Array.Empty(); } - protected override string Target => $@"beatmaps/{beatmapInfo.OnlineBeatmapID}/scores{createQueryParameters()}"; + protected override string Target => $@"beatmaps/{beatmapInfo.OnlineID}/scores{createQueryParameters()}"; private string createQueryParameters() { From 90503f72cae121f8856cd428eed3ccb0d09a446a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Oct 2021 17:40:12 +0900 Subject: [PATCH 199/260] Update `PresentBeatmap` to support interface types --- osu.Game/OsuGame.cs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index f9e080a93c..a7ed7fedf5 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -436,11 +436,15 @@ namespace osu.Game /// first beatmap from any ruleset. /// /// - public void PresentBeatmap(BeatmapSetInfo beatmap, Predicate difficultyCriteria = null) + public void PresentBeatmap(IBeatmapSetInfo beatmap, Predicate difficultyCriteria = null) { - var databasedSet = beatmap.OnlineBeatmapSetID != null - ? BeatmapManager.QueryBeatmapSet(s => s.OnlineBeatmapSetID == beatmap.OnlineBeatmapSetID) - : BeatmapManager.QueryBeatmapSet(s => s.Hash == beatmap.Hash); + BeatmapSetInfo databasedSet = null; + + if (beatmap.OnlineID > 0) + databasedSet = BeatmapManager.QueryBeatmapSet(s => s.OnlineBeatmapSetID == beatmap.OnlineID); + + if (beatmap is BeatmapSetInfo localBeatmap) + databasedSet ??= BeatmapManager.QueryBeatmapSet(s => s.Hash == localBeatmap.Hash); if (databasedSet == null) { From 7db8bdfb7c01db278088b8aa45c963eef6fad5ca Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Oct 2021 18:16:53 +0900 Subject: [PATCH 200/260] Fix fallback logic not considering case where ruleset is not available Occurs only in tests. --- osu.Game/Beatmaps/Drawables/DifficultyIcon.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs index 3a55b9261f..6d1984db5b 100644 --- a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs +++ b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs @@ -117,7 +117,7 @@ namespace osu.Game.Beatmaps.Drawables Origin = Anchor.Centre, RelativeSizeAxes = Axes.Both, // the null coalesce here is only present to make unit tests work (ruleset dlls aren't copied correctly for testing at the moment) - Icon = rulesets.GetRuleset((ruleset ?? beatmapInfo.Ruleset).OnlineID)?.CreateInstance()?.CreateIcon() ?? new SpriteIcon { Icon = FontAwesome.Regular.QuestionCircle } + Icon = getRulesetIcon() }, }; @@ -129,6 +129,16 @@ namespace osu.Game.Beatmaps.Drawables difficultyBindable.BindValueChanged(difficulty => background.Colour = colours.ForStarDifficulty(difficulty.NewValue.Stars)); } + private Drawable getRulesetIcon() + { + int? onlineID = (ruleset ?? beatmapInfo.Ruleset)?.OnlineID; + + if (onlineID >= 0 && rulesets.GetRuleset(onlineID.Value)?.CreateInstance() is Ruleset rulesetInstance) + return rulesetInstance.CreateIcon(); + + return new SpriteIcon { Icon = FontAwesome.Regular.QuestionCircle }; + } + ITooltip IHasCustomTooltip.GetCustomTooltip() => new DifficultyIconTooltip(); DifficultyIconTooltipContent IHasCustomTooltip.TooltipContent => shouldShowTooltip ? new DifficultyIconTooltipContent(beatmapInfo, difficultyBindable) : null; From 07e3ced315fe457eb97e32879c0c061541e201f4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Oct 2021 18:22:23 +0900 Subject: [PATCH 201/260] Fix test scene and remove "impossible" nullable coalesce --- osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs | 2 ++ osu.Game/Beatmaps/Drawables/DifficultyIcon.cs | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs index 66f15670f5..1eda3be039 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs @@ -15,6 +15,7 @@ using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.Rulesets; +using osu.Game.Rulesets.Osu; using osu.Game.Screens.Select; using osu.Game.Screens.Select.Carousel; using osu.Game.Screens.Select.Filter; @@ -868,6 +869,7 @@ namespace osu.Game.Tests.Visual.SongSelect OnlineBeatmapID = id++ * 10, Version = version, StarDifficulty = diff, + Ruleset = new OsuRuleset().RulesetInfo, BaseDifficulty = new BeatmapDifficulty { OverallDifficulty = diff, diff --git a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs index 6d1984db5b..64412675bb 100644 --- a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs +++ b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs @@ -131,7 +131,7 @@ namespace osu.Game.Beatmaps.Drawables private Drawable getRulesetIcon() { - int? onlineID = (ruleset ?? beatmapInfo.Ruleset)?.OnlineID; + int? onlineID = (ruleset ?? beatmapInfo.Ruleset).OnlineID; if (onlineID >= 0 && rulesets.GetRuleset(onlineID.Value)?.CreateInstance() is Ruleset rulesetInstance) return rulesetInstance.CreateIcon(); From 04acc7601c2a96906fb6e462391df4502ee61015 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Oct 2021 18:35:15 +0900 Subject: [PATCH 202/260] Fix one more missed case --- osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs index 1eda3be039..7a38d213d9 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs @@ -685,6 +685,7 @@ namespace osu.Game.Tests.Visual.SongSelect set.Beatmaps.Add(new BeatmapInfo { Version = $"Stars: {i}", + Ruleset = new OsuRuleset().RulesetInfo, StarDifficulty = i, }); } From b8ee7e05f497aedc1b4eeabd40ef856eddd25a46 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Oct 2021 17:49:31 +0900 Subject: [PATCH 203/260] Expose more pieces of `APIBeatmap` for testing purposes --- .../Online/API/Requests/Responses/APIBeatmap.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs b/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs index e65dca752b..061e034648 100644 --- a/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs +++ b/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs @@ -43,16 +43,16 @@ namespace osu.Game.Online.API.Requests.Responses public double StarRating { get; set; } [JsonProperty(@"drain")] - private float drainRate { get; set; } + public float DrainRate { get; set; } [JsonProperty(@"cs")] - private float circleSize { get; set; } + public float CircleSize { get; set; } [JsonProperty(@"ar")] - private float approachRate { get; set; } + public float ApproachRate { get; set; } [JsonProperty(@"accuracy")] - private float overallDifficulty { get; set; } + public float OverallDifficulty { get; set; } [JsonIgnore] public double Length { get; set; } @@ -115,10 +115,10 @@ namespace osu.Game.Online.API.Requests.Responses public IBeatmapDifficultyInfo Difficulty => new BeatmapDifficulty { - DrainRate = drainRate, - CircleSize = circleSize, - ApproachRate = approachRate, - OverallDifficulty = overallDifficulty, + DrainRate = DrainRate, + CircleSize = CircleSize, + ApproachRate = ApproachRate, + OverallDifficulty = OverallDifficulty, }; IBeatmapSetInfo? IBeatmapInfo.BeatmapSet => BeatmapSet; From a16c8f1ebc97255d9dc51030155e22d37beb67e5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Oct 2021 17:58:46 +0900 Subject: [PATCH 204/260] Update all beatmap overlay views to use `APIBeatmap`/`APIBeatmapSet` --- .../Online/TestSceneBeatmapAvailability.cs | 40 +-- .../Online/TestSceneBeatmapRulesetSelector.cs | 22 +- .../Online/TestSceneBeatmapSetOverlay.cs | 268 ++++++------------ .../TestSceneBeatmapSetOverlayDetails.cs | 24 +- .../TestSceneBeatmapSetOverlaySuccessRate.cs | 31 +- .../Online/TestSceneDirectDownloadButton.cs | 66 +++-- .../Visual/Online/TestSceneDirectPanel.cs | 160 +++++------ .../Visual/Online/TestSceneFavouriteButton.cs | 8 +- .../Online/TestSceneLeaderboardModSelector.cs | 3 +- .../SongSelect/TestSceneAdvancedStats.cs | 4 +- .../TestSceneBeatmapListingSearchControl.cs | 18 +- ...tSceneUpdateableBeatmapBackgroundSprite.cs | 5 +- .../API/Requests/Responses/APIBeatmap.cs | 8 +- .../BeatmapListingFilterControl.cs | 8 +- .../BeatmapListingSearchControl.cs | 8 +- .../BeatmapListing/Panels/BeatmapPanel.cs | 19 +- .../Panels/BeatmapPanelDownloadButton.cs | 13 +- .../Panels/DownloadProgressBar.cs | 2 +- .../BeatmapListing/Panels/GridBeatmapPanel.cs | 28 +- .../BeatmapListing/Panels/ListBeatmapPanel.cs | 28 +- .../BeatmapListing/Panels/PlayButton.cs | 8 +- osu.Game/Overlays/BeatmapListingOverlay.cs | 4 +- osu.Game/Overlays/BeatmapSet/AuthorInfo.cs | 22 +- osu.Game/Overlays/BeatmapSet/BasicStats.cs | 17 +- .../BeatmapSet/BeatmapAvailability.cs | 12 +- osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs | 58 ++-- .../BeatmapSet/BeatmapRulesetSelector.cs | 6 +- .../BeatmapSet/BeatmapRulesetTabItem.cs | 8 +- .../Overlays/BeatmapSet/BeatmapSetHeader.cs | 4 +- .../BeatmapSet/BeatmapSetHeaderContent.cs | 22 +- .../BeatmapSet/Buttons/FavouriteButton.cs | 12 +- .../Buttons/HeaderDownloadButton.cs | 15 +- .../BeatmapSet/Buttons/PreviewButton.cs | 4 +- osu.Game/Overlays/BeatmapSet/Details.cs | 11 +- osu.Game/Overlays/BeatmapSet/Info.cs | 20 +- .../BeatmapSet/LeaderboardModSelector.cs | 10 +- .../BeatmapSet/Scores/ScoresContainer.cs | 30 +- osu.Game/Overlays/BeatmapSet/SuccessRate.cs | 18 +- osu.Game/Overlays/BeatmapSetOverlay.cs | 18 +- .../Beatmaps/PaginatedBeatmapContainer.cs | 2 +- .../Historical/DrawableMostPlayedBeatmap.cs | 4 +- .../Overlays/Rankings/SpotlightsLayout.cs | 2 +- osu.Game/Screens/Play/SoloSpectator.cs | 2 +- osu.Game/Screens/Select/BeatmapDetails.cs | 4 +- .../Screens/Select/Details/AdvancedStats.cs | 8 +- 45 files changed, 471 insertions(+), 613 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapAvailability.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapAvailability.cs index 6f9744ca73..176e0592ef 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapAvailability.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapAvailability.cs @@ -21,15 +21,12 @@ namespace osu.Game.Tests.Visual.Online [Test] public void TestUndownloadableWithLink() { - AddStep("set undownloadable beatmapset with link", () => container.BeatmapSet = new BeatmapSetInfo + AddStep("set undownloadable beatmapset with link", () => container.BeatmapSet = new APIBeatmapSet { - OnlineInfo = new APIBeatmapSet + Availability = new BeatmapSetOnlineAvailability { - Availability = new BeatmapSetOnlineAvailability - { - DownloadDisabled = true, - ExternalLink = @"https://osu.ppy.sh", - }, + DownloadDisabled = true, + ExternalLink = @"https://osu.ppy.sh", }, }); @@ -39,14 +36,11 @@ namespace osu.Game.Tests.Visual.Online [Test] public void TestUndownloadableNoLink() { - AddStep("set undownloadable beatmapset without link", () => container.BeatmapSet = new BeatmapSetInfo + AddStep("set undownloadable beatmapset without link", () => container.BeatmapSet = new APIBeatmapSet { - OnlineInfo = new APIBeatmapSet + Availability = new BeatmapSetOnlineAvailability { - Availability = new BeatmapSetOnlineAvailability - { - DownloadDisabled = true, - }, + DownloadDisabled = true, }, }); @@ -56,15 +50,12 @@ namespace osu.Game.Tests.Visual.Online [Test] public void TestPartsRemovedWithLink() { - AddStep("set parts-removed beatmapset with link", () => container.BeatmapSet = new BeatmapSetInfo + AddStep("set parts-removed beatmapset with link", () => container.BeatmapSet = new APIBeatmapSet { - OnlineInfo = new APIBeatmapSet + Availability = new BeatmapSetOnlineAvailability { - Availability = new BeatmapSetOnlineAvailability - { - DownloadDisabled = false, - ExternalLink = @"https://osu.ppy.sh", - }, + DownloadDisabled = false, + ExternalLink = @"https://osu.ppy.sh", }, }); @@ -74,14 +65,11 @@ namespace osu.Game.Tests.Visual.Online [Test] public void TestNormal() { - AddStep("set normal beatmapset", () => container.BeatmapSet = new BeatmapSetInfo + AddStep("set normal beatmapset", () => container.BeatmapSet = new APIBeatmapSet { - OnlineInfo = new APIBeatmapSet + Availability = new BeatmapSetOnlineAvailability { - Availability = new BeatmapSetOnlineAvailability - { - DownloadDisabled = false, - }, + DownloadDisabled = false, }, }); diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapRulesetSelector.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapRulesetSelector.cs index eb34187cd6..b880633559 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapRulesetSelector.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapRulesetSelector.cs @@ -1,15 +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 System.Collections.Generic; +using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics.UserInterface; -using osu.Game.Beatmaps; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays; using osu.Game.Overlays.BeatmapSet; using osu.Game.Rulesets; -using System.Collections.Generic; -using System.Linq; namespace osu.Game.Tests.Visual.Online { @@ -35,9 +35,9 @@ namespace osu.Game.Tests.Visual.Online AddStep("load multiple rulesets beatmapset", () => { - selector.BeatmapSet = new BeatmapSetInfo + selector.BeatmapSet = new APIBeatmapSet { - Beatmaps = enabledRulesets.Select(r => new BeatmapInfo { Ruleset = r }).ToList() + Beatmaps = enabledRulesets.Select(r => new APIBeatmap { RulesetID = r.OnlineID }).ToList() }; }); @@ -53,13 +53,13 @@ namespace osu.Game.Tests.Visual.Online AddStep("load single ruleset beatmapset", () => { - selector.BeatmapSet = new BeatmapSetInfo + selector.BeatmapSet = new APIBeatmapSet { - Beatmaps = new List + Beatmaps = new List { - new BeatmapInfo + new APIBeatmap { - Ruleset = enabledRuleset + RulesetID = enabledRuleset.OnlineID } } }; @@ -71,9 +71,9 @@ namespace osu.Game.Tests.Visual.Online [Test] public void TestEmptyBeatmapSet() { - AddStep("load empty beatmapset", () => selector.BeatmapSet = new BeatmapSetInfo + AddStep("load empty beatmapset", () => selector.BeatmapSet = new APIBeatmapSet { - Beatmaps = new List() + Beatmaps = new List() }); AddAssert("no ruleset selected", () => selector.SelectedTab == null); diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs index 7f9b56e873..bae9af1897 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs @@ -49,60 +49,48 @@ namespace osu.Game.Tests.Visual.Online { AddStep(@"show first", () => { - overlay.ShowBeatmapSet(new BeatmapSetInfo + overlay.ShowBeatmapSet(new APIBeatmapSet { - OnlineBeatmapSetID = 1235, - Metadata = new BeatmapMetadata + OnlineID = 1235, + Title = @"an awesome beatmap", + Artist = @"naru narusegawa", + Source = @"hinata sou", + Tags = @"test tag tag more tag", + Author = new User { - Title = @"an awesome beatmap", - Artist = @"naru narusegawa", - Source = @"hinata sou", - Tags = @"test tag tag more tag", - Author = new User - { - Username = @"BanchoBot", - Id = 3, - }, + Username = @"BanchoBot", + Id = 3, }, - OnlineInfo = new APIBeatmapSet + Preview = @"https://b.ppy.sh/preview/12345.mp3", + PlayCount = 123, + FavouriteCount = 456, + Submitted = DateTime.Now, + Ranked = DateTime.Now, + BPM = 111, + HasVideo = true, + Ratings = Enumerable.Range(0, 11).ToArray(), + HasStoryboard = true, + Covers = new BeatmapSetOnlineCovers(), + Beatmaps = new List { - Preview = @"https://b.ppy.sh/preview/12345.mp3", - PlayCount = 123, - FavouriteCount = 456, - Submitted = DateTime.Now, - Ranked = DateTime.Now, - BPM = 111, - HasVideo = true, - Ratings = Enumerable.Range(0, 11).ToArray(), - HasStoryboard = true, - Covers = new BeatmapSetOnlineCovers(), - }, - Beatmaps = new List - { - new BeatmapInfo + new APIBeatmap { - StarDifficulty = 9.99, - Version = @"TEST", + StarRating = 9.99, + DifficultyName = @"TEST", Length = 456000, - Ruleset = rulesets.GetRuleset(3), - BaseDifficulty = new BeatmapDifficulty + RulesetID = 3, + CircleSize = 1, + DrainRate = 2.3f, + OverallDifficulty = 4.5f, + ApproachRate = 6, + CircleCount = 111, + SliderCount = 12, + PlayCount = 222, + PassCount = 21, + FailTimes = new APIFailTimes { - CircleSize = 1, - DrainRate = 2.3f, - OverallDifficulty = 4.5f, - ApproachRate = 6, - }, - OnlineInfo = new APIBeatmap - { - CircleCount = 111, - SliderCount = 12, - PlayCount = 222, - PassCount = 21, - FailTimes = new APIFailTimes - { - Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(), - Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(), - }, + Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(), + Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(), }, }, }, @@ -120,71 +108,15 @@ namespace osu.Game.Tests.Visual.Online { AddStep(@"show undownloadable", () => { - overlay.ShowBeatmapSet(new BeatmapSetInfo + var set = getBeatmapSet(); + + set.Availability = new BeatmapSetOnlineAvailability { - OnlineBeatmapSetID = 1234, - Metadata = new BeatmapMetadata - { - Title = @"undownloadable beatmap", - Artist = @"no one", - Source = @"some source", - Tags = @"another test tag tag more test tags", - Author = new User - { - Username = @"BanchoBot", - Id = 3, - }, - }, - OnlineInfo = new APIBeatmapSet - { - Availability = new BeatmapSetOnlineAvailability - { - DownloadDisabled = true, - ExternalLink = "https://osu.ppy.sh", - }, - Preview = @"https://b.ppy.sh/preview/1234.mp3", - PlayCount = 123, - FavouriteCount = 456, - Submitted = DateTime.Now, - Ranked = DateTime.Now, - BPM = 111, - HasVideo = true, - HasStoryboard = true, - Covers = new BeatmapSetOnlineCovers(), - Language = new BeatmapSetOnlineLanguage { Id = 3, Name = "English" }, - Genre = new BeatmapSetOnlineGenre { Id = 4, Name = "Rock" }, - Ratings = Enumerable.Range(0, 11).ToArray(), - }, - Beatmaps = new List - { - new BeatmapInfo - { - StarDifficulty = 5.67, - Version = @"ANOTHER TEST", - Length = 123000, - Ruleset = rulesets.GetRuleset(1), - BaseDifficulty = new BeatmapDifficulty - { - CircleSize = 9, - DrainRate = 8, - OverallDifficulty = 7, - ApproachRate = 6, - }, - OnlineInfo = new APIBeatmap - { - CircleCount = 123, - SliderCount = 45, - PlayCount = 567, - PassCount = 89, - FailTimes = new APIFailTimes - { - Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(), - Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(), - }, - }, - }, - }, - }); + DownloadDisabled = true, + ExternalLink = "https://osu.ppy.sh", + }; + + overlay.ShowBeatmapSet(set); }); downloadAssert(false); @@ -195,48 +127,30 @@ namespace osu.Game.Tests.Visual.Online { AddStep("show multiple rulesets beatmap", () => { - var beatmaps = new List(); + var beatmaps = new List(); foreach (var ruleset in rulesets.AvailableRulesets.Skip(1)) { - beatmaps.Add(new BeatmapInfo + beatmaps.Add(new APIBeatmap { - Version = ruleset.Name, - Ruleset = ruleset, - BaseDifficulty = new BeatmapDifficulty(), - OnlineInfo = new APIBeatmap + DifficultyName = ruleset.Name, + RulesetID = ruleset.OnlineID, + FailTimes = new APIFailTimes { - FailTimes = new APIFailTimes - { - Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(), - Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(), - }, - } + Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(), + Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(), + }, }); } - overlay.ShowBeatmapSet(new BeatmapSetInfo - { - Metadata = new BeatmapMetadata - { - Title = @"multiple rulesets beatmap", - Artist = @"none", - Author = new User - { - Username = "BanchoBot", - Id = 3, - } - }, - OnlineInfo = new APIBeatmapSet - { - Covers = new BeatmapSetOnlineCovers(), - Ratings = Enumerable.Range(0, 11).ToArray(), - }, - Beatmaps = beatmaps - }); + var set = getBeatmapSet(); + + set.Beatmaps = beatmaps; + + overlay.ShowBeatmapSet(set); }); - AddAssert("shown beatmaps of current ruleset", () => overlay.Header.HeaderContent.Picker.Difficulties.All(b => b.BeatmapInfo.Ruleset.Equals(overlay.Header.RulesetSelector.Current.Value))); + AddAssert("shown beatmaps of current ruleset", () => overlay.Header.HeaderContent.Picker.Difficulties.All(b => b.Beatmap.Ruleset.OnlineID == overlay.Header.RulesetSelector.Current.Value.OnlineID)); AddAssert("left-most beatmap selected", () => overlay.Header.HeaderContent.Picker.Difficulties.First().State == BeatmapPicker.DifficultySelectorState.Selected); } @@ -246,7 +160,7 @@ namespace osu.Game.Tests.Visual.Online AddStep("show explicit map", () => { var beatmapSet = getBeatmapSet(); - beatmapSet.OnlineInfo.HasExplicitContent = true; + beatmapSet.HasExplicitContent = true; overlay.ShowBeatmapSet(beatmapSet); }); } @@ -257,7 +171,7 @@ namespace osu.Game.Tests.Visual.Online AddStep("show featured map", () => { var beatmapSet = getBeatmapSet(); - beatmapSet.OnlineInfo.TrackId = 1; + beatmapSet.TrackId = 1; overlay.ShowBeatmapSet(beatmapSet); }); } @@ -274,63 +188,41 @@ namespace osu.Game.Tests.Visual.Online AddStep(@"show without reload", overlay.Show); } - private BeatmapSetInfo createManyDifficultiesBeatmapSet() + private APIBeatmapSet createManyDifficultiesBeatmapSet() { - var beatmaps = new List(); + var set = getBeatmapSet(); + + var beatmaps = new List(); for (int i = 1; i < 41; i++) { - beatmaps.Add(new BeatmapInfo + beatmaps.Add(new APIBeatmap { - OnlineBeatmapID = i * 10, - Version = $"Test #{i}", - Ruleset = Ruleset.Value, - StarDifficulty = 2 + i * 0.1, - BaseDifficulty = new BeatmapDifficulty + OnlineID = i * 10, + DifficultyName = $"Test #{i}", + RulesetID = Ruleset.Value.ID ?? -1, + StarRating = 2 + i * 0.1, + OverallDifficulty = 3.5f, + FailTimes = new APIFailTimes { - OverallDifficulty = 3.5f, + Fails = Enumerable.Range(1, 100).Select(j => j % 12 - 6).ToArray(), + Retries = Enumerable.Range(-2, 100).Select(j => j % 12 - 6).ToArray(), }, - OnlineInfo = new APIBeatmap - { - FailTimes = new APIFailTimes - { - Fails = Enumerable.Range(1, 100).Select(j => j % 12 - 6).ToArray(), - Retries = Enumerable.Range(-2, 100).Select(j => j % 12 - 6).ToArray(), - }, - } }); } - return new BeatmapSetInfo - { - OnlineBeatmapSetID = 123, - Metadata = new BeatmapMetadata - { - Title = @"many difficulties beatmap", - Artist = @"none", - Author = new User - { - Username = @"BanchoBot", - Id = 3, - }, - }, - OnlineInfo = new APIBeatmapSet - { - Preview = @"https://b.ppy.sh/preview/123.mp3", - HasVideo = true, - HasStoryboard = true, - Covers = new BeatmapSetOnlineCovers(), - Ratings = Enumerable.Range(0, 11).ToArray(), - }, - Beatmaps = beatmaps, - }; + set.Beatmaps = beatmaps; + + return set; } - private BeatmapSetInfo getBeatmapSet() + private APIBeatmapSet getBeatmapSet() { - var beatmapSet = CreateBeatmap(Ruleset.Value).BeatmapInfo.BeatmapSet; + var beatmapSet = CreateAPIBeatmapSet(Ruleset.Value); + // Make sure the overlay is reloaded (see `BeatmapSetInfo.Equals`). - beatmapSet.OnlineBeatmapSetID = nextBeatmapSetId++; + beatmapSet.OnlineID = nextBeatmapSetId++; + return beatmapSet; } diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlayDetails.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlayDetails.cs index d14f9f47d1..491a2d5d90 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlayDetails.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlayDetails.cs @@ -44,27 +44,21 @@ namespace osu.Game.Tests.Visual.Online AddStep("set second set", () => details.BeatmapSet = secondSet); AddAssert("ratings set", () => details.Ratings.Ratings == secondSet.Ratings); - static BeatmapSetInfo createSet() => new BeatmapSetInfo + static APIBeatmapSet createSet() => new APIBeatmapSet { - Beatmaps = new List + Beatmaps = new List { - new BeatmapInfo + new APIBeatmap { - OnlineInfo = new APIBeatmap + FailTimes = new APIFailTimes { - FailTimes = new APIFailTimes - { - Fails = Enumerable.Range(1, 100).Select(_ => RNG.Next(10)).ToArray(), - Retries = Enumerable.Range(-2, 100).Select(_ => RNG.Next(10)).ToArray(), - }, - } + Fails = Enumerable.Range(1, 100).Select(_ => RNG.Next(10)).ToArray(), + Retries = Enumerable.Range(-2, 100).Select(_ => RNG.Next(10)).ToArray(), + }, } }, - OnlineInfo = new APIBeatmapSet - { - Ratings = Enumerable.Range(0, 11).Select(_ => RNG.Next(10)).ToArray(), - Status = BeatmapSetOnlineStatus.Ranked - } + Ratings = Enumerable.Range(0, 11).Select(_ => RNG.Next(10)).ToArray(), + Status = BeatmapSetOnlineStatus.Ranked }; } diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlaySuccessRate.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlaySuccessRate.cs index b3b67fcbca..be3fc7aff9 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlaySuccessRate.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlaySuccessRate.cs @@ -59,21 +59,18 @@ namespace osu.Game.Tests.Visual.Online var firstBeatmap = createBeatmap(); var secondBeatmap = createBeatmap(); - AddStep("set first set", () => successRate.BeatmapInfo = firstBeatmap); + AddStep("set first set", () => successRate.Beatmap = firstBeatmap); AddAssert("ratings set", () => successRate.Graph.FailTimes == firstBeatmap.FailTimes); - AddStep("set second set", () => successRate.BeatmapInfo = secondBeatmap); + AddStep("set second set", () => successRate.Beatmap = secondBeatmap); AddAssert("ratings set", () => successRate.Graph.FailTimes == secondBeatmap.FailTimes); - static BeatmapInfo createBeatmap() => new BeatmapInfo + static APIBeatmap createBeatmap() => new APIBeatmap { - OnlineInfo = new APIBeatmap + FailTimes = new APIFailTimes { - FailTimes = new APIFailTimes - { - Fails = Enumerable.Range(1, 100).Select(_ => RNG.Next(10)).ToArray(), - Retries = Enumerable.Range(-2, 100).Select(_ => RNG.Next(10)).ToArray(), - } + Fails = Enumerable.Range(1, 100).Select(_ => RNG.Next(10)).ToArray(), + Retries = Enumerable.Range(-2, 100).Select(_ => RNG.Next(10)).ToArray(), } }; } @@ -81,14 +78,11 @@ namespace osu.Game.Tests.Visual.Online [Test] public void TestOnlyFailMetrics() { - AddStep("set beatmap", () => successRate.BeatmapInfo = new BeatmapInfo + AddStep("set beatmap", () => successRate.Beatmap = new APIBeatmap { - OnlineInfo = new APIBeatmap + FailTimes = new APIFailTimes { - FailTimes = new APIFailTimes - { - Fails = Enumerable.Range(1, 100).ToArray(), - } + Fails = Enumerable.Range(1, 100).ToArray(), } }); @@ -98,12 +92,9 @@ namespace osu.Game.Tests.Visual.Online [Test] public void TestEmptyMetrics() { - AddStep("set beatmap", () => successRate.BeatmapInfo = new BeatmapInfo + AddStep("set beatmap", () => successRate.Beatmap = new APIBeatmap { - OnlineInfo = new APIBeatmap - { - FailTimes = new APIFailTimes(), - } + FailTimes = new APIFailTimes() }); AddAssert("graph max values correct", () => successRate.ChildrenOfType().All(graph => graph.MaxValue == 0)); diff --git a/osu.Game.Tests/Visual/Online/TestSceneDirectDownloadButton.cs b/osu.Game.Tests/Visual/Online/TestSceneDirectDownloadButton.cs index bb7fcc2fce..f0cf88840a 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneDirectDownloadButton.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneDirectDownloadButton.cs @@ -69,24 +69,7 @@ namespace osu.Game.Tests.Visual.Online AddAssert($"button {(enabled ? "enabled" : "disabled")}", () => downloadButton.DownloadEnabled == enabled); } - private BeatmapSetInfo createSoleily() - { - return new BeatmapSetInfo - { - ID = 1, - OnlineBeatmapSetID = 241526, - OnlineInfo = new APIBeatmapSet - { - Availability = new BeatmapSetOnlineAvailability - { - DownloadDisabled = false, - ExternalLink = string.Empty, - }, - }, - }; - } - - private void createButtonWithBeatmap(BeatmapSetInfo beatmap) + private void createButtonWithBeatmap(IBeatmapSetInfo beatmap) { AddStep("create button", () => { @@ -112,32 +95,47 @@ namespace osu.Game.Tests.Visual.Online }); } - private BeatmapSetInfo getDownloadableBeatmapSet() + private IBeatmapSetInfo createSoleily() { - var normal = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo).BeatmapSetInfo; - normal.OnlineInfo.HasVideo = true; - normal.OnlineInfo.HasStoryboard = true; - - return normal; + return new APIBeatmapSet + { + OnlineID = 241526, + Availability = new BeatmapSetOnlineAvailability + { + DownloadDisabled = false, + ExternalLink = string.Empty, + }, + }; } - private BeatmapSetInfo getUndownloadableBeatmapSet() + private IBeatmapSetInfo getDownloadableBeatmapSet() { - var beatmap = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo).BeatmapSetInfo; - beatmap.Metadata.Artist = "test"; - beatmap.Metadata.Title = "undownloadable"; - beatmap.Metadata.AuthorString = "test"; + var apiBeatmapSet = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo).BeatmapSetInfo.OnlineInfo; - beatmap.OnlineInfo.HasVideo = true; - beatmap.OnlineInfo.HasStoryboard = true; + apiBeatmapSet.HasVideo = true; + apiBeatmapSet.HasStoryboard = true; - beatmap.OnlineInfo.Availability = new BeatmapSetOnlineAvailability + return apiBeatmapSet; + } + + private IBeatmapSetInfo getUndownloadableBeatmapSet() + { + var apiBeatmapSet = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo).BeatmapSetInfo.OnlineInfo; + + apiBeatmapSet.Artist = "test"; + apiBeatmapSet.Title = "undownloadable"; + apiBeatmapSet.AuthorString = "test"; + + apiBeatmapSet.HasVideo = true; + apiBeatmapSet.HasStoryboard = true; + + apiBeatmapSet.Availability = new BeatmapSetOnlineAvailability { DownloadDisabled = true, ExternalLink = "http://osu.ppy.sh", }; - return beatmap; + return apiBeatmapSet; } private class TestDownloadButton : BeatmapPanelDownloadButton @@ -146,7 +144,7 @@ namespace osu.Game.Tests.Visual.Online public DownloadState DownloadState => State.Value; - public TestDownloadButton(BeatmapSetInfo beatmapSet) + public TestDownloadButton(IBeatmapSetInfo beatmapSet) : base(beatmapSet) { } diff --git a/osu.Game.Tests/Visual/Online/TestSceneDirectPanel.cs b/osu.Game.Tests/Visual/Online/TestSceneDirectPanel.cs index 6caca2a67c..9a3d998966 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneDirectPanel.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneDirectPanel.cs @@ -18,104 +18,25 @@ namespace osu.Game.Tests.Visual.Online [Cached(typeof(IPreviewTrackOwner))] public class TestSceneDirectPanel : OsuTestScene, IPreviewTrackOwner { - private BeatmapSetInfo getUndownloadableBeatmapSet() => new BeatmapSetInfo - { - OnlineBeatmapSetID = 123, - Metadata = new BeatmapMetadata - { - Title = "undownloadable beatmap", - Artist = "test", - Source = "more tests", - Author = new User - { - Username = "BanchoBot", - Id = 3, - }, - }, - OnlineInfo = new APIBeatmapSet - { - Availability = new BeatmapSetOnlineAvailability - { - DownloadDisabled = true, - }, - Preview = @"https://b.ppy.sh/preview/12345.mp3", - PlayCount = 123, - FavouriteCount = 456, - BPM = 111, - HasVideo = true, - HasStoryboard = true, - Covers = new BeatmapSetOnlineCovers(), - }, - Beatmaps = new List - { - new BeatmapInfo - { - Ruleset = Ruleset.Value, - Version = "Test", - StarDifficulty = 6.42, - } - } - }; - - private BeatmapSetInfo getManyDifficultiesBeatmapSet(RulesetStore rulesets) - { - var beatmaps = new List(); - - for (int i = 0; i < 100; i++) - { - beatmaps.Add(new BeatmapInfo - { - Ruleset = rulesets.GetRuleset(i % 4), - StarDifficulty = 2 + i % 4 * 2, - BaseDifficulty = new BeatmapDifficulty - { - OverallDifficulty = 3.5f, - } - }); - } - - return new BeatmapSetInfo - { - OnlineBeatmapSetID = 1, - Metadata = new BeatmapMetadata - { - Title = "many difficulties beatmap", - Artist = "test", - Author = new User - { - Username = "BanchoBot", - Id = 3, - } - }, - OnlineInfo = new APIBeatmapSet - { - HasVideo = true, - HasStoryboard = true, - Covers = new BeatmapSetOnlineCovers(), - }, - Beatmaps = beatmaps, - }; - } - [BackgroundDependencyLoader] private void load(RulesetStore rulesets) { var normal = getBeatmapSet(); - normal.OnlineInfo.HasVideo = true; - normal.OnlineInfo.HasStoryboard = true; + normal.HasVideo = true; + normal.HasStoryboard = true; var undownloadable = getUndownloadableBeatmapSet(); - var manyDifficulties = getManyDifficultiesBeatmapSet(rulesets); + var manyDifficulties = getManyDifficultiesBeatmapSet(); var explicitMap = getBeatmapSet(); - explicitMap.OnlineInfo.HasExplicitContent = true; + explicitMap.HasExplicitContent = true; var featuredMap = getBeatmapSet(); - featuredMap.OnlineInfo.TrackId = 1; + featuredMap.TrackId = 1; var explicitFeaturedMap = getBeatmapSet(); - explicitFeaturedMap.OnlineInfo.HasExplicitContent = true; - explicitFeaturedMap.OnlineInfo.TrackId = 2; + explicitFeaturedMap.HasExplicitContent = true; + explicitFeaturedMap.TrackId = 2; Child = new BasicScrollContainer { @@ -145,7 +66,72 @@ namespace osu.Game.Tests.Visual.Online }, }; - BeatmapSetInfo getBeatmapSet() => CreateBeatmap(Ruleset.Value).BeatmapInfo.BeatmapSet; + APIBeatmapSet getBeatmapSet() => CreateAPIBeatmapSet(Ruleset.Value); + + APIBeatmapSet getUndownloadableBeatmapSet() => new APIBeatmapSet + { + OnlineID = 123, + Title = "undownloadable beatmap", + Artist = "test", + Source = "more tests", + Author = new User + { + Username = "BanchoBot", + Id = 3, + }, + Availability = new BeatmapSetOnlineAvailability + { + DownloadDisabled = true, + }, + Preview = @"https://b.ppy.sh/preview/12345.mp3", + PlayCount = 123, + FavouriteCount = 456, + BPM = 111, + HasVideo = true, + HasStoryboard = true, + Covers = new BeatmapSetOnlineCovers(), + Beatmaps = new List + { + new APIBeatmap + { + RulesetID = Ruleset.Value.ID ?? 0, + DifficultyName = "Test", + StarRating = 6.42, + } + } + }; + + APIBeatmapSet getManyDifficultiesBeatmapSet() + { + var beatmaps = new List(); + + for (int i = 0; i < 100; i++) + { + beatmaps.Add(new APIBeatmap + { + RulesetID = i % 4, + StarRating = 2 + i % 4 * 2, + OverallDifficulty = 3.5f, + }); + } + + return new APIBeatmapSet + { + OnlineID = 1, + Title = "undownloadable beatmap", + Artist = "test", + Source = "more tests", + Author = new User + { + Username = "BanchoBot", + Id = 3, + }, + HasVideo = true, + HasStoryboard = true, + Covers = new BeatmapSetOnlineCovers(), + Beatmaps = beatmaps, + }; + } } } } diff --git a/osu.Game.Tests/Visual/Online/TestSceneFavouriteButton.cs b/osu.Game.Tests/Visual/Online/TestSceneFavouriteButton.cs index 8e2ee4e28d..87458da578 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneFavouriteButton.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneFavouriteButton.cs @@ -4,7 +4,7 @@ using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Testing; -using osu.Game.Beatmaps; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays.BeatmapSet.Buttons; using osuTK; @@ -29,7 +29,7 @@ namespace osu.Game.Tests.Visual.Online [Test] public void TestLoggedOutIn() { - AddStep("set valid beatmap", () => favourite.BeatmapSet.Value = new BeatmapSetInfo { OnlineBeatmapSetID = 88 }); + AddStep("set valid beatmap", () => favourite.BeatmapSet.Value = new APIBeatmapSet { OnlineID = 88 }); AddStep("log out", () => API.Logout()); checkEnabled(false); AddStep("log in", () => API.Login("test", "test")); @@ -40,9 +40,9 @@ namespace osu.Game.Tests.Visual.Online public void TestBeatmapChange() { AddStep("log in", () => API.Login("test", "test")); - AddStep("set valid beatmap", () => favourite.BeatmapSet.Value = new BeatmapSetInfo { OnlineBeatmapSetID = 88 }); + AddStep("set valid beatmap", () => favourite.BeatmapSet.Value = new APIBeatmapSet { OnlineID = 88 }); checkEnabled(true); - AddStep("set invalid beatmap", () => favourite.BeatmapSet.Value = new BeatmapSetInfo()); + AddStep("set invalid beatmap", () => favourite.BeatmapSet.Value = new APIBeatmapSet()); checkEnabled(false); } diff --git a/osu.Game.Tests/Visual/Online/TestSceneLeaderboardModSelector.cs b/osu.Game.Tests/Visual/Online/TestSceneLeaderboardModSelector.cs index fc438ce6dd..aa442ded02 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneLeaderboardModSelector.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneLeaderboardModSelector.cs @@ -26,7 +26,8 @@ namespace osu.Game.Tests.Visual.Online { LeaderboardModSelector modSelector; FillFlowContainer selectedMods; - var ruleset = new Bindable(); + + var ruleset = new Bindable(); Add(selectedMods = new FillFlowContainer { diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneAdvancedStats.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneAdvancedStats.cs index 4538e36c5e..6a2b577fd3 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneAdvancedStats.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneAdvancedStats.cs @@ -124,7 +124,7 @@ namespace osu.Game.Tests.Visual.SongSelect { var ruleset = advancedStats.BeatmapInfo.Ruleset.CreateInstance(); var difficultyAdjustMod = ruleset.CreateMod(); - difficultyAdjustMod.ReadFromDifficulty(advancedStats.BeatmapInfo.BaseDifficulty); + difficultyAdjustMod.ReadFromDifficulty(advancedStats.BeatmapInfo.Difficulty); SelectedMods.Value = new[] { difficultyAdjustMod }; }); @@ -143,7 +143,7 @@ namespace osu.Game.Tests.Visual.SongSelect { var ruleset = advancedStats.BeatmapInfo.Ruleset.CreateInstance(); var difficultyAdjustMod = ruleset.CreateMod(); - var originalDifficulty = advancedStats.BeatmapInfo.BaseDifficulty; + var originalDifficulty = advancedStats.BeatmapInfo.Difficulty; difficultyAdjustMod.ReadFromDifficulty(originalDifficulty); difficultyAdjustMod.DrainRate.Value = originalDifficulty.DrainRate - 0.5f; diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapListingSearchControl.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapListingSearchControl.cs index a9fe7ed7d8..f17de75f5c 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapListingSearchControl.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatmapListingSearchControl.cs @@ -110,25 +110,19 @@ namespace osu.Game.Tests.Visual.UserInterface base.Dispose(isDisposing); } - private static readonly BeatmapSetInfo beatmap_set = new BeatmapSetInfo + private static readonly APIBeatmapSet beatmap_set = new APIBeatmapSet { - OnlineInfo = new APIBeatmapSet + Covers = new BeatmapSetOnlineCovers { - Covers = new BeatmapSetOnlineCovers - { - Cover = "https://assets.ppy.sh/beatmaps/1094296/covers/cover@2x.jpg?1581416305" - } + Cover = "https://assets.ppy.sh/beatmaps/1094296/covers/cover@2x.jpg?1581416305" } }; - private static readonly BeatmapSetInfo no_cover_beatmap_set = new BeatmapSetInfo + private static readonly APIBeatmapSet no_cover_beatmap_set = new APIBeatmapSet { - OnlineInfo = new APIBeatmapSet + Covers = new BeatmapSetOnlineCovers { - Covers = new BeatmapSetOnlineCovers - { - Cover = string.Empty - } + Cover = string.Empty } }; } diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapBackgroundSprite.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapBackgroundSprite.cs index 74cd675a05..d30f1e8889 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapBackgroundSprite.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneUpdateableBeatmapBackgroundSprite.cs @@ -12,6 +12,7 @@ using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics.Containers; using osu.Game.Online.API; using osu.Game.Online.API.Requests; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Rulesets; using osu.Game.Tests.Beatmaps.IO; using osuTK; @@ -24,7 +25,6 @@ namespace osu.Game.Tests.Visual.UserInterface private BeatmapSetInfo testBeatmap; private IAPIProvider api; - private RulesetStore rulesets; [Resolved] private BeatmapManager beatmaps { get; set; } @@ -33,7 +33,6 @@ namespace osu.Game.Tests.Visual.UserInterface private void load(OsuGameBase osu, IAPIProvider api, RulesetStore rulesets) { this.api = api; - this.rulesets = rulesets; testBeatmap = ImportBeatmapTest.LoadOszIntoOsu(osu).Result; } @@ -81,7 +80,7 @@ namespace osu.Game.Tests.Visual.UserInterface Child = background = new TestUpdateableBeatmapBackgroundSprite { RelativeSizeAxes = Axes.Both, - Beatmap = { Value = new BeatmapInfo { BeatmapSet = req.Response?.ToBeatmapSet(rulesets) } } + Beatmap = { Value = new APIBeatmap { BeatmapSet = req.Response } } }; }); diff --git a/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs b/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs index 061e034648..653b011f73 100644 --- a/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs +++ b/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs @@ -100,10 +100,10 @@ namespace osu.Game.Online.API.Requests.Responses MaxCombo = MaxCombo, BaseDifficulty = new BeatmapDifficulty { - DrainRate = drainRate, - CircleSize = circleSize, - ApproachRate = approachRate, - OverallDifficulty = overallDifficulty, + DrainRate = DrainRate, + CircleSize = CircleSize, + ApproachRate = ApproachRate, + OverallDifficulty = OverallDifficulty, }, OnlineInfo = this, }; diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs b/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs index 03d36ff5df..fa57191ef3 100644 --- a/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs +++ b/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs @@ -12,9 +12,9 @@ using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Framework.Localisation; using osu.Framework.Threading; -using osu.Game.Beatmaps; using osu.Game.Online.API; using osu.Game.Online.API.Requests; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Rulesets; using osu.Game.Resources.Localisation.Web; using osuTK; @@ -206,7 +206,7 @@ namespace osu.Game.Overlays.BeatmapListing getSetsRequest.Success += response => { - var sets = response.BeatmapSets.Select(responseJson => responseJson.ToBeatmapSet(rulesets)).ToList(); + var sets = response.BeatmapSets.ToList(); // If the previous request returned a null cursor, the API is indicating we can't paginate further (maybe there are no more beatmaps left). if (sets.Count == 0 || response.Cursor == null) @@ -289,7 +289,7 @@ namespace osu.Game.Overlays.BeatmapListing /// Contains the beatmap sets returned from API. /// Valid for read if and only if is . /// - public List Results { get; private set; } + public List Results { get; private set; } /// /// Contains the names of supporter-only filters requested by the user. @@ -297,7 +297,7 @@ namespace osu.Game.Overlays.BeatmapListing /// public List SupporterOnlyFiltersUsed { get; private set; } - public static SearchResult ResultsReturned(List results) => new SearchResult + public static SearchResult ResultsReturned(List results) => new SearchResult { Type = SearchResultType.ResultsReturned, Results = results diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchControl.cs b/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchControl.cs index 776a8e73b0..2474515802 100644 --- a/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchControl.cs +++ b/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchControl.cs @@ -8,12 +8,12 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; -using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; using osu.Game.Configuration; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Input.Bindings; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Resources.Localisation.Web; using osu.Game.Rulesets; using osu.Game.Scoring; @@ -49,17 +49,17 @@ namespace osu.Game.Overlays.BeatmapListing public Bindable ExplicitContent => explicitContentFilter.Current; - public BeatmapSetInfo BeatmapSet + public APIBeatmapSet BeatmapSet { set { - if (value == null || string.IsNullOrEmpty(value.OnlineInfo.Covers.Cover)) + if (value == null || string.IsNullOrEmpty(value.Covers.Cover)) { beatmapCover.FadeOut(600, Easing.OutQuint); return; } - beatmapCover.OnlineInfo = value.OnlineInfo; + beatmapCover.OnlineInfo = value; beatmapCover.FadeTo(0.1f, 200, Easing.OutQuint); } } diff --git a/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanel.cs b/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanel.cs index c7fa98f159..2fd05f6742 100644 --- a/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanel.cs +++ b/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanel.cs @@ -23,6 +23,7 @@ using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; +using osu.Game.Online.API.Requests.Responses; using osuTK; using osuTK.Graphics; @@ -30,7 +31,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels { public abstract class BeatmapPanel : OsuClickableContainer, IHasContextMenu { - public readonly BeatmapSetInfo SetInfo; + public readonly APIBeatmapSet SetInfo; private const double hover_transition_time = 400; private const int maximum_difficulty_icons = 10; @@ -49,10 +50,10 @@ namespace osu.Game.Overlays.BeatmapListing.Panels protected Action ViewBeatmap; - protected BeatmapPanel(BeatmapSetInfo setInfo) + protected BeatmapPanel(APIBeatmapSet setInfo) : base(HoverSampleSet.Submit) { - Debug.Assert(setInfo.OnlineBeatmapSetID != null); + Debug.Assert(setInfo.OnlineID > 0); SetInfo = setInfo; } @@ -95,8 +96,8 @@ namespace osu.Game.Overlays.BeatmapListing.Panels Action = ViewBeatmap = () => { - Debug.Assert(SetInfo.OnlineBeatmapSetID != null); - beatmapSetOverlay?.FetchAndShowBeatmapSet(SetInfo.OnlineBeatmapSetID.Value); + Debug.Assert(SetInfo.OnlineID > 0); + beatmapSetOverlay?.FetchAndShowBeatmapSet(SetInfo.OnlineID); }; } @@ -146,14 +147,14 @@ namespace osu.Game.Overlays.BeatmapListing.Panels { var icons = new List(); - if (SetInfo.Beatmaps.Count > maximum_difficulty_icons) + if (SetInfo.Beatmaps.Count() > maximum_difficulty_icons) { foreach (var ruleset in SetInfo.Beatmaps.Select(b => b.Ruleset).Distinct()) - icons.Add(new GroupedDifficultyIcon(SetInfo.Beatmaps.FindAll(b => b.Ruleset.Equals(ruleset)), ruleset, this is ListBeatmapPanel ? Color4.White : colours.Gray5)); + icons.Add(new GroupedDifficultyIcon(SetInfo.Beatmaps.Where(b => b.RulesetID == ruleset.OnlineID).ToList(), ruleset, this is ListBeatmapPanel ? Color4.White : colours.Gray5)); } else { - foreach (var b in SetInfo.Beatmaps.OrderBy(beatmap => beatmap.Ruleset.ID).ThenBy(beatmap => beatmap.StarDifficulty)) + foreach (var b in SetInfo.Beatmaps.OrderBy(beatmap => beatmap.RulesetID).ThenBy(beatmap => beatmap.StarRating)) icons.Add(new DifficultyIcon(b)); } @@ -163,7 +164,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels protected Drawable CreateBackground() => new UpdateableOnlineBeatmapSetCover { RelativeSizeAxes = Axes.Both, - OnlineInfo = SetInfo.OnlineInfo, + OnlineInfo = SetInfo, }; public class Statistic : FillFlowContainer diff --git a/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelDownloadButton.cs b/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelDownloadButton.cs index dd12e8e467..d7c2837f4d 100644 --- a/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelDownloadButton.cs +++ b/osu.Game/Overlays/BeatmapListing/Panels/BeatmapPanelDownloadButton.cs @@ -11,6 +11,7 @@ using osu.Game.Configuration; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Online; +using osu.Game.Online.API.Requests.Responses; namespace osu.Game.Overlays.BeatmapListing.Panels { @@ -21,7 +22,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels /// /// Currently selected beatmap. Used to present the correct difficulty after completing a download. /// - public readonly IBindable SelectedBeatmap = new Bindable(); + public readonly IBindable SelectedBeatmap = new Bindable(); private readonly ShakeContainer shakeContainer; private readonly DownloadButton button; @@ -31,9 +32,9 @@ namespace osu.Game.Overlays.BeatmapListing.Panels protected readonly Bindable State = new Bindable(); - private readonly BeatmapSetInfo beatmapSet; + private readonly IBeatmapSetInfo beatmapSet; - public BeatmapPanelDownloadButton(BeatmapSetInfo beatmapSet) + public BeatmapPanelDownloadButton(IBeatmapSetInfo beatmapSet) { this.beatmapSet = beatmapSet; @@ -79,13 +80,13 @@ namespace osu.Game.Overlays.BeatmapListing.Panels case DownloadState.LocallyAvailable: Predicate findPredicate = null; if (SelectedBeatmap.Value != null) - findPredicate = b => b.OnlineBeatmapID == SelectedBeatmap.Value.OnlineBeatmapID; + findPredicate = b => b.OnlineBeatmapID == SelectedBeatmap.Value.OnlineID; game?.PresentBeatmap(beatmapSet, findPredicate); break; default: - beatmaps.Download(beatmapSet, noVideoSetting.Value); + beatmaps.Download(new BeatmapSetInfo { OnlineBeatmapSetID = beatmapSet.OnlineID }, noVideoSetting.Value); break; } }; @@ -100,7 +101,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels break; default: - if (beatmapSet.OnlineInfo?.Availability.DownloadDisabled ?? false) + if ((beatmapSet as IBeatmapSetOnlineInfo)?.Availability.DownloadDisabled == true) { button.Enabled.Value = false; button.TooltipText = "this beatmap is currently not available for download."; diff --git a/osu.Game/Overlays/BeatmapListing/Panels/DownloadProgressBar.cs b/osu.Game/Overlays/BeatmapListing/Panels/DownloadProgressBar.cs index 24f929f55e..93eaf775e0 100644 --- a/osu.Game/Overlays/BeatmapListing/Panels/DownloadProgressBar.cs +++ b/osu.Game/Overlays/BeatmapListing/Panels/DownloadProgressBar.cs @@ -18,7 +18,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels private readonly ProgressBar progressBar; private readonly BeatmapDownloadTracker downloadTracker; - public DownloadProgressBar(BeatmapSetInfo beatmapSet) + public DownloadProgressBar(IBeatmapSetInfo beatmapSet) { InternalChildren = new Drawable[] { diff --git a/osu.Game/Overlays/BeatmapListing/Panels/GridBeatmapPanel.cs b/osu.Game/Overlays/BeatmapListing/Panels/GridBeatmapPanel.cs index 4a0fa59c31..770e5af7bd 100644 --- a/osu.Game/Overlays/BeatmapListing/Panels/GridBeatmapPanel.cs +++ b/osu.Game/Overlays/BeatmapListing/Panels/GridBeatmapPanel.cs @@ -9,11 +9,11 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; using osu.Framework.Localisation; -using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays.BeatmapSet; using osuTK; using osuTK.Graphics; @@ -32,7 +32,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels protected override PlayButton PlayButton => playButton; protected override Box PreviewBar => progressBar; - public GridBeatmapPanel(BeatmapSetInfo beatmap) + public GridBeatmapPanel(APIBeatmapSet beatmap) : base(beatmap) { Width = 380; @@ -84,7 +84,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels { new OsuSpriteText { - Text = new RomanisableString(SetInfo.Metadata.TitleUnicode, SetInfo.Metadata.Title), + Text = new RomanisableString(SetInfo.TitleUnicode, SetInfo.Title), Font = OsuFont.GetFont(size: 18, weight: FontWeight.Bold, italics: true) }, } @@ -97,7 +97,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels { new OsuSpriteText { - Text = new RomanisableString(SetInfo.Metadata.ArtistUnicode, SetInfo.Metadata.Artist), + Text = new RomanisableString(SetInfo.ArtistUnicode, SetInfo.Artist), Font = OsuFont.GetFont(weight: FontWeight.Bold, italics: true) } } @@ -145,7 +145,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels { d.AutoSizeAxes = Axes.Both; d.AddText("mapped by ", t => t.Colour = colours.Gray5); - d.AddUserLink(SetInfo.Metadata.Author); + d.AddUserLink(SetInfo.Author); }), new Container { @@ -155,11 +155,11 @@ namespace osu.Game.Overlays.BeatmapListing.Panels { new OsuSpriteText { - Text = SetInfo.Metadata.Source, + Text = SetInfo.Source, Font = OsuFont.GetFont(size: 14), Shadow = false, Colour = colours.Gray5, - Alpha = string.IsNullOrEmpty(SetInfo.Metadata.Source) ? 0f : 1f, + Alpha = string.IsNullOrEmpty(SetInfo.Source) ? 0f : 1f, }, }, }, @@ -193,8 +193,8 @@ namespace osu.Game.Overlays.BeatmapListing.Panels Margin = new MarginPadding { Top = vertical_padding, Right = vertical_padding }, Children = new[] { - new Statistic(FontAwesome.Solid.PlayCircle, SetInfo.OnlineInfo?.PlayCount ?? 0), - new Statistic(FontAwesome.Solid.Heart, SetInfo.OnlineInfo?.FavouriteCount ?? 0), + new Statistic(FontAwesome.Solid.PlayCircle, SetInfo.PlayCount), + new Statistic(FontAwesome.Solid.Heart, SetInfo.FavouriteCount), }, }, statusContainer = new FillFlowContainer @@ -211,7 +211,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels }, }); - if (SetInfo.OnlineInfo?.HasExplicitContent ?? false) + if (SetInfo.HasExplicitContent) { titleContainer.Add(new ExplicitContentBeatmapPill { @@ -221,7 +221,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels }); } - if (SetInfo.OnlineInfo?.TrackId != null) + if (SetInfo.TrackId != null) { artistContainer.Add(new FeaturedArtistBeatmapPill { @@ -231,12 +231,12 @@ namespace osu.Game.Overlays.BeatmapListing.Panels }); } - if (SetInfo.OnlineInfo?.HasVideo ?? false) + if (SetInfo.HasVideo) { statusContainer.Add(new IconPill(FontAwesome.Solid.Film)); } - if (SetInfo.OnlineInfo?.HasStoryboard ?? false) + if (SetInfo.HasStoryboard) { statusContainer.Add(new IconPill(FontAwesome.Solid.Image)); } @@ -246,7 +246,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels AutoSizeAxes = Axes.Both, TextSize = 12, TextPadding = new MarginPadding { Horizontal = 10, Vertical = 5 }, - Status = SetInfo.OnlineInfo?.Status ?? BeatmapSetOnlineStatus.None, + Status = SetInfo.Status, }); PreviewPlaying.ValueChanged += _ => updateStatusContainer(); diff --git a/osu.Game/Overlays/BeatmapListing/Panels/ListBeatmapPanel.cs b/osu.Game/Overlays/BeatmapListing/Panels/ListBeatmapPanel.cs index 63d651f9de..dcd676724a 100644 --- a/osu.Game/Overlays/BeatmapListing/Panels/ListBeatmapPanel.cs +++ b/osu.Game/Overlays/BeatmapListing/Panels/ListBeatmapPanel.cs @@ -9,11 +9,11 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; -using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays.BeatmapSet; using osuTK; using osuTK.Graphics; @@ -37,7 +37,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels protected override PlayButton PlayButton => playButton; protected override Box PreviewBar => progressBar; - public ListBeatmapPanel(BeatmapSetInfo beatmap) + public ListBeatmapPanel(APIBeatmapSet beatmap) : base(beatmap) { RelativeSizeAxes = Axes.X; @@ -107,7 +107,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels { new OsuSpriteText { - Text = new RomanisableString(SetInfo.Metadata.TitleUnicode, SetInfo.Metadata.Title), + Text = new RomanisableString(SetInfo.TitleUnicode, SetInfo.Title), Font = OsuFont.GetFont(size: 18, weight: FontWeight.Bold, italics: true) }, } @@ -120,7 +120,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels { new OsuSpriteText { - Text = new RomanisableString(SetInfo.Metadata.ArtistUnicode, SetInfo.Metadata.Artist), + Text = new RomanisableString(SetInfo.ArtistUnicode, SetInfo.Artist), Font = OsuFont.GetFont(weight: FontWeight.Bold, italics: true) }, }, @@ -182,8 +182,8 @@ namespace osu.Game.Overlays.BeatmapListing.Panels Direction = FillDirection.Vertical, Children = new Drawable[] { - new Statistic(FontAwesome.Solid.PlayCircle, SetInfo.OnlineInfo?.PlayCount ?? 0), - new Statistic(FontAwesome.Solid.Heart, SetInfo.OnlineInfo?.FavouriteCount ?? 0), + new Statistic(FontAwesome.Solid.PlayCircle, SetInfo.PlayCount), + new Statistic(FontAwesome.Solid.Heart, SetInfo.FavouriteCount), new LinkFlowContainer(s => { s.Shadow = false; @@ -197,15 +197,15 @@ namespace osu.Game.Overlays.BeatmapListing.Panels { d.AutoSizeAxes = Axes.Both; d.AddText("mapped by "); - d.AddUserLink(SetInfo.Metadata.Author); + d.AddUserLink(SetInfo.Author); }), new OsuSpriteText { - Text = SetInfo.Metadata.Source, + Text = SetInfo.Source, Anchor = Anchor.TopRight, Origin = Anchor.TopRight, Font = OsuFont.GetFont(size: 14), - Alpha = string.IsNullOrEmpty(SetInfo.Metadata.Source) ? 0f : 1f, + Alpha = string.IsNullOrEmpty(SetInfo.Source) ? 0f : 1f, }, }, }, @@ -225,7 +225,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels }, }); - if (SetInfo.OnlineInfo?.HasExplicitContent ?? false) + if (SetInfo.HasExplicitContent) { titleContainer.Add(new ExplicitContentBeatmapPill { @@ -235,7 +235,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels }); } - if (SetInfo.OnlineInfo?.TrackId != null) + if (SetInfo.TrackId != null) { artistContainer.Add(new FeaturedArtistBeatmapPill { @@ -245,12 +245,12 @@ namespace osu.Game.Overlays.BeatmapListing.Panels }); } - if (SetInfo.OnlineInfo?.HasVideo ?? false) + if (SetInfo.HasVideo) { statusContainer.Add(new IconPill(FontAwesome.Solid.Film) { IconSize = new Vector2(20) }); } - if (SetInfo.OnlineInfo?.HasStoryboard ?? false) + if (SetInfo.HasStoryboard) { statusContainer.Add(new IconPill(FontAwesome.Solid.Image) { IconSize = new Vector2(20) }); } @@ -260,7 +260,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels AutoSizeAxes = Axes.Both, TextSize = 12, TextPadding = new MarginPadding { Horizontal = 10, Vertical = 4 }, - Status = SetInfo.OnlineInfo?.Status ?? BeatmapSetOnlineStatus.None, + Status = SetInfo.Status, }); } } diff --git a/osu.Game/Overlays/BeatmapListing/Panels/PlayButton.cs b/osu.Game/Overlays/BeatmapListing/Panels/PlayButton.cs index 3aa9aa5ca5..c352fe0223 100644 --- a/osu.Game/Overlays/BeatmapListing/Panels/PlayButton.cs +++ b/osu.Game/Overlays/BeatmapListing/Panels/PlayButton.cs @@ -8,9 +8,9 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; using osu.Game.Audio; -using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; +using osu.Game.Online.API.Requests.Responses; using osuTK; using osuTK.Graphics; @@ -24,9 +24,9 @@ namespace osu.Game.Overlays.BeatmapListing.Panels public PreviewTrack Preview { get; private set; } - private BeatmapSetInfo beatmapSet; + private APIBeatmapSet beatmapSet; - public BeatmapSetInfo BeatmapSet + public APIBeatmapSet BeatmapSet { get => beatmapSet; set @@ -66,7 +66,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels } } - public PlayButton(BeatmapSetInfo setInfo = null) + public PlayButton(APIBeatmapSet setInfo = null) { BeatmapSet = setInfo; AddRange(new Drawable[] diff --git a/osu.Game/Overlays/BeatmapListingOverlay.cs b/osu.Game/Overlays/BeatmapListingOverlay.cs index 935a89b99b..e08af52a72 100644 --- a/osu.Game/Overlays/BeatmapListingOverlay.cs +++ b/osu.Game/Overlays/BeatmapListingOverlay.cs @@ -15,10 +15,10 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Framework.Input.Events; using osu.Game.Audio; -using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Containers; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays.BeatmapListing; using osu.Game.Overlays.BeatmapListing.Panels; using osu.Game.Resources.Localisation.Web; @@ -136,7 +136,7 @@ namespace osu.Game.Overlays return; } - var newPanels = searchResult.Results.Select(b => new GridBeatmapPanel(b) + var newPanels = searchResult.Results.Select(b => new GridBeatmapPanel(b) { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, diff --git a/osu.Game/Overlays/BeatmapSet/AuthorInfo.cs b/osu.Game/Overlays/BeatmapSet/AuthorInfo.cs index 265d9bf125..c90e2e2085 100644 --- a/osu.Game/Overlays/BeatmapSet/AuthorInfo.cs +++ b/osu.Game/Overlays/BeatmapSet/AuthorInfo.cs @@ -6,7 +6,6 @@ using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Game.Beatmaps; using osu.Game.Graphics.Sprites; using osu.Game.Users.Drawables; using osuTK; @@ -16,6 +15,7 @@ using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; using osu.Game.Users; using osu.Game.Graphics.Containers; +using osu.Game.Online.API.Requests.Responses; namespace osu.Game.Overlays.BeatmapSet { @@ -26,9 +26,9 @@ namespace osu.Game.Overlays.BeatmapSet private UpdateableAvatar avatar; private FillFlowContainer fields; - private BeatmapSetInfo beatmapSet; + private APIBeatmapSet beatmapSet; - public BeatmapSetInfo BeatmapSet + public APIBeatmapSet BeatmapSet { get => beatmapSet; set @@ -78,30 +78,28 @@ namespace osu.Game.Overlays.BeatmapSet private void updateDisplay() { - avatar.User = BeatmapSet?.Metadata.Author; + avatar.User = BeatmapSet?.Author; fields.Clear(); if (BeatmapSet == null) return; - var online = BeatmapSet.OnlineInfo; - fields.Children = new Drawable[] { - new Field("mapped by", BeatmapSet.Metadata.Author, OsuFont.GetFont(weight: FontWeight.Regular, italics: true)), - new Field("submitted", online.Submitted, OsuFont.GetFont(weight: FontWeight.Bold)) + new Field("mapped by", BeatmapSet.Author, OsuFont.GetFont(weight: FontWeight.Regular, italics: true)), + new Field("submitted", BeatmapSet.Submitted, OsuFont.GetFont(weight: FontWeight.Bold)) { Margin = new MarginPadding { Top = 5 }, }, }; - if (online.Ranked.HasValue) + if (BeatmapSet.Ranked.HasValue) { - fields.Add(new Field(online.Status.ToString().ToLowerInvariant(), online.Ranked.Value, OsuFont.GetFont(weight: FontWeight.Bold))); + fields.Add(new Field(BeatmapSet.Status.ToString().ToLowerInvariant(), BeatmapSet.Ranked.Value, OsuFont.GetFont(weight: FontWeight.Bold))); } - else if (online.LastUpdated.HasValue) + else if (BeatmapSet.LastUpdated.HasValue) { - fields.Add(new Field("last updated", online.LastUpdated.Value, OsuFont.GetFont(weight: FontWeight.Bold))); + fields.Add(new Field("last updated", BeatmapSet.LastUpdated.Value, OsuFont.GetFont(weight: FontWeight.Bold))); } } diff --git a/osu.Game/Overlays/BeatmapSet/BasicStats.cs b/osu.Game/Overlays/BeatmapSet/BasicStats.cs index 683f4f0c49..8f848edf24 100644 --- a/osu.Game/Overlays/BeatmapSet/BasicStats.cs +++ b/osu.Game/Overlays/BeatmapSet/BasicStats.cs @@ -14,6 +14,7 @@ using osu.Game.Beatmaps; using osu.Game.Extensions; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Resources.Localisation.Web; using osuTK; @@ -23,9 +24,9 @@ namespace osu.Game.Overlays.BeatmapSet { private readonly Statistic length, bpm, circleCount, sliderCount; - private BeatmapSetInfo beatmapSet; + private APIBeatmapSet beatmapSet; - public BeatmapSetInfo BeatmapSet + public APIBeatmapSet BeatmapSet { get => beatmapSet; set @@ -38,9 +39,9 @@ namespace osu.Game.Overlays.BeatmapSet } } - private BeatmapInfo beatmapInfo; + private IBeatmapInfo beatmapInfo; - public BeatmapInfo BeatmapInfo + public IBeatmapInfo BeatmapInfo { get => beatmapInfo; set @@ -55,7 +56,7 @@ namespace osu.Game.Overlays.BeatmapSet private void updateDisplay() { - bpm.Value = BeatmapSet?.OnlineInfo?.BPM.ToLocalisableString(@"0.##") ?? (LocalisableString)"-"; + bpm.Value = BeatmapSet?.BPM.ToLocalisableString(@"0.##") ?? (LocalisableString)"-"; if (beatmapInfo == null) { @@ -68,8 +69,10 @@ namespace osu.Game.Overlays.BeatmapSet length.TooltipText = BeatmapsetsStrings.ShowStatsTotalLength(TimeSpan.FromMilliseconds(beatmapInfo.Length).ToFormattedDuration()); length.Value = TimeSpan.FromMilliseconds(beatmapInfo.Length).ToFormattedDuration(); - circleCount.Value = beatmapInfo.OnlineInfo.CircleCount.ToLocalisableString(@"N0"); - sliderCount.Value = beatmapInfo.OnlineInfo.SliderCount.ToLocalisableString(@"N0"); + var onlineInfo = beatmapInfo as IBeatmapOnlineInfo; + + circleCount.Value = (onlineInfo?.CircleCount ?? 0).ToLocalisableString(@"N0"); + sliderCount.Value = (onlineInfo?.SliderCount ?? 0).ToLocalisableString(@"N0"); } } diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapAvailability.cs b/osu.Game/Overlays/BeatmapSet/BeatmapAvailability.cs index f005a37eaa..dc46452dcb 100644 --- a/osu.Game/Overlays/BeatmapSet/BeatmapAvailability.cs +++ b/osu.Game/Overlays/BeatmapSet/BeatmapAvailability.cs @@ -5,19 +5,19 @@ using osu.Framework.Extensions.Color4Extensions; 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.Online.API.Requests.Responses; using osuTK.Graphics; namespace osu.Game.Overlays.BeatmapSet { public class BeatmapAvailability : Container { - private BeatmapSetInfo beatmapSet; + private APIBeatmapSet beatmapSet; - private bool downloadDisabled => BeatmapSet?.OnlineInfo.Availability.DownloadDisabled ?? false; - private bool hasExternalLink => !string.IsNullOrEmpty(BeatmapSet?.OnlineInfo.Availability.ExternalLink); + private bool downloadDisabled => BeatmapSet?.Availability.DownloadDisabled ?? false; + private bool hasExternalLink => !string.IsNullOrEmpty(BeatmapSet?.Availability.ExternalLink); private readonly LinkFlowContainer textContainer; @@ -44,7 +44,7 @@ namespace osu.Game.Overlays.BeatmapSet }; } - public BeatmapSetInfo BeatmapSet + public APIBeatmapSet BeatmapSet { get => beatmapSet; @@ -76,7 +76,7 @@ namespace osu.Game.Overlays.BeatmapSet { textContainer.NewParagraph(); textContainer.NewParagraph(); - textContainer.AddLink("Check here for more information.", BeatmapSet.OnlineInfo.Availability.ExternalLink, creationParameters: t => t.Font = OsuFont.GetFont(size: 10)); + textContainer.AddLink("Check here for more information.", BeatmapSet.Availability.ExternalLink, creationParameters: t => t.Font = OsuFont.GetFont(size: 10)); } } } diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs b/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs index 3df275c6d3..c43ac74714 100644 --- a/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs +++ b/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs @@ -17,6 +17,7 @@ using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Resources.Localisation.Web; using osu.Game.Rulesets; using osuTK; @@ -34,10 +35,10 @@ namespace osu.Game.Overlays.BeatmapSet public readonly DifficultiesContainer Difficulties; - public readonly Bindable Beatmap = new Bindable(); - private BeatmapSetInfo beatmapSet; + public readonly Bindable Beatmap = new Bindable(); + private APIBeatmapSet beatmapSet; - public BeatmapSetInfo BeatmapSet + public APIBeatmapSet BeatmapSet { get => beatmapSet; set @@ -164,35 +165,38 @@ namespace osu.Game.Overlays.BeatmapSet if (BeatmapSet != null) { - Difficulties.ChildrenEnumerable = BeatmapSet.Beatmaps.Where(b => b.Ruleset.Equals(ruleset.Value)).OrderBy(b => b.StarDifficulty).Select(b => new DifficultySelectorButton(b) - { - State = DifficultySelectorState.NotSelected, - OnHovered = beatmap => - { - showBeatmap(beatmap); - starRating.Text = beatmap.StarDifficulty.ToLocalisableString(@"0.##"); - starRatingContainer.FadeIn(100); - }, - OnClicked = beatmap => { Beatmap.Value = beatmap; }, - }); + Difficulties.ChildrenEnumerable = BeatmapSet.Beatmaps + .Where(b => b.Ruleset?.OnlineID == ruleset.Value?.OnlineID) + .OrderBy(b => b.StarRating) + .Select(b => new DifficultySelectorButton(b) + { + State = DifficultySelectorState.NotSelected, + OnHovered = beatmap => + { + showBeatmap(beatmap); + starRating.Text = beatmap.StarRating.ToLocalisableString(@"0.##"); + starRatingContainer.FadeIn(100); + }, + OnClicked = beatmap => { Beatmap.Value = beatmap; }, + }); } starRatingContainer.FadeOut(100); - Beatmap.Value = Difficulties.FirstOrDefault()?.BeatmapInfo; - plays.Value = BeatmapSet?.OnlineInfo.PlayCount ?? 0; - favourites.Value = BeatmapSet?.OnlineInfo.FavouriteCount ?? 0; + Beatmap.Value = Difficulties.FirstOrDefault()?.Beatmap; + plays.Value = BeatmapSet?.PlayCount ?? 0; + favourites.Value = BeatmapSet?.FavouriteCount ?? 0; updateDifficultyButtons(); } - private void showBeatmap(BeatmapInfo beatmapInfo) + private void showBeatmap(IBeatmapInfo beatmapInfo) { - version.Text = beatmapInfo?.Version; + version.Text = beatmapInfo?.DifficultyName; } private void updateDifficultyButtons() { - Difficulties.Children.ToList().ForEach(diff => diff.State = diff.BeatmapInfo == Beatmap.Value ? DifficultySelectorState.Selected : DifficultySelectorState.NotSelected); + Difficulties.Children.ToList().ForEach(diff => diff.State = diff.Beatmap == Beatmap.Value ? DifficultySelectorState.Selected : DifficultySelectorState.NotSelected); } public class DifficultiesContainer : FillFlowContainer @@ -216,10 +220,10 @@ namespace osu.Game.Overlays.BeatmapSet private readonly Box backgroundBox; private readonly DifficultyIcon icon; - public readonly BeatmapInfo BeatmapInfo; + public readonly APIBeatmap Beatmap; - public Action OnHovered; - public Action OnClicked; + public Action OnHovered; + public Action OnClicked; public event Action StateChanged; private DifficultySelectorState state; @@ -241,9 +245,9 @@ namespace osu.Game.Overlays.BeatmapSet } } - public DifficultySelectorButton(BeatmapInfo beatmapInfo) + public DifficultySelectorButton(APIBeatmap beatmapInfo) { - BeatmapInfo = beatmapInfo; + Beatmap = beatmapInfo; Size = new Vector2(size); Margin = new MarginPadding { Horizontal = tile_spacing / 2 }; @@ -273,7 +277,7 @@ namespace osu.Game.Overlays.BeatmapSet protected override bool OnHover(HoverEvent e) { fadeIn(); - OnHovered?.Invoke(BeatmapInfo); + OnHovered?.Invoke(Beatmap); return base.OnHover(e); } @@ -286,7 +290,7 @@ namespace osu.Game.Overlays.BeatmapSet protected override bool OnClick(ClickEvent e) { - OnClicked?.Invoke(BeatmapInfo); + OnClicked?.Invoke(Beatmap); return base.OnClick(e); } diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapRulesetSelector.cs b/osu.Game/Overlays/BeatmapSet/BeatmapRulesetSelector.cs index 005d21726b..6564ca3d41 100644 --- a/osu.Game/Overlays/BeatmapSet/BeatmapRulesetSelector.cs +++ b/osu.Game/Overlays/BeatmapSet/BeatmapRulesetSelector.cs @@ -3,17 +3,17 @@ using osu.Framework.Bindables; using osu.Framework.Graphics.UserInterface; -using osu.Game.Beatmaps; using osu.Game.Rulesets; using System.Linq; +using osu.Game.Online.API.Requests.Responses; namespace osu.Game.Overlays.BeatmapSet { public class BeatmapRulesetSelector : OverlayRulesetSelector { - private readonly Bindable beatmapSet = new Bindable(); + private readonly Bindable beatmapSet = new Bindable(); - public BeatmapSetInfo BeatmapSet + public APIBeatmapSet BeatmapSet { get => beatmapSet.Value; set diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapRulesetTabItem.cs b/osu.Game/Overlays/BeatmapSet/BeatmapRulesetTabItem.cs index 79c95d6646..b3b3d1980b 100644 --- a/osu.Game/Overlays/BeatmapSet/BeatmapRulesetTabItem.cs +++ b/osu.Game/Overlays/BeatmapSet/BeatmapRulesetTabItem.cs @@ -1,22 +1,22 @@ // 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.Sprites; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Rulesets; -using System.Linq; namespace osu.Game.Overlays.BeatmapSet { public class BeatmapRulesetTabItem : OverlayRulesetTabItem { - public readonly Bindable BeatmapSet = new Bindable(); + public readonly Bindable BeatmapSet = new Bindable(); [Resolved] private OverlayColourProvider colourProvider { get; set; } @@ -64,7 +64,7 @@ namespace osu.Game.Overlays.BeatmapSet BeatmapSet.BindValueChanged(setInfo => { - int beatmapsCount = setInfo.NewValue?.Beatmaps.Count(b => b.Ruleset.Equals(Value)) ?? 0; + int beatmapsCount = setInfo.NewValue?.Beatmaps.Count(b => b.Ruleset.OnlineID == Value.OnlineID) ?? 0; count.Text = beatmapsCount.ToString(); countContainer.FadeTo(beatmapsCount > 0 ? 1 : 0); diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapSetHeader.cs b/osu.Game/Overlays/BeatmapSet/BeatmapSetHeader.cs index 4a0c0e9f75..102cddfa92 100644 --- a/osu.Game/Overlays/BeatmapSet/BeatmapSetHeader.cs +++ b/osu.Game/Overlays/BeatmapSet/BeatmapSetHeader.cs @@ -6,7 +6,7 @@ using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Effects; -using osu.Game.Beatmaps; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Resources.Localisation.Web; using osu.Game.Rulesets; using osuTK; @@ -16,7 +16,7 @@ namespace osu.Game.Overlays.BeatmapSet { public class BeatmapSetHeader : OverlayHeader { - public readonly Bindable BeatmapSet = new Bindable(); + public readonly Bindable BeatmapSet = new Bindable(); public BeatmapSetHeaderContent HeaderContent { get; private set; } diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs b/osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs index 97cd17313e..f94fee7a8b 100644 --- a/osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs +++ b/osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs @@ -10,13 +10,13 @@ using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Localisation; -using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Online; using osu.Game.Online.API; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays.BeatmapListing.Panels; using osu.Game.Overlays.BeatmapSet.Buttons; using osuTK; @@ -25,7 +25,7 @@ namespace osu.Game.Overlays.BeatmapSet { public class BeatmapSetHeaderContent : CompositeDrawable { - public readonly Bindable BeatmapSet = new Bindable(); + public readonly Bindable BeatmapSet = new Bindable(); private const float transition_duration = 200; private const float buttons_height = 45; @@ -219,7 +219,7 @@ namespace osu.Game.Overlays.BeatmapSet Picker.Beatmap.ValueChanged += b => { Details.BeatmapInfo = b.NewValue; - externalLink.Link = $@"{api.WebsiteRootUrl}/beatmapsets/{BeatmapSet.Value?.OnlineBeatmapSetID}#{b.NewValue?.Ruleset.ShortName}/{b.NewValue?.OnlineBeatmapID}"; + externalLink.Link = $@"{api.WebsiteRootUrl}/beatmapsets/{BeatmapSet.Value?.OnlineID}#{b.NewValue?.Ruleset.ShortName}/{b.NewValue?.OnlineID}"; }; } @@ -231,7 +231,7 @@ namespace osu.Game.Overlays.BeatmapSet BeatmapSet.BindValueChanged(setInfo => { Picker.BeatmapSet = rulesetSelector.BeatmapSet = author.BeatmapSet = beatmapAvailability.BeatmapSet = Details.BeatmapSet = setInfo.NewValue; - cover.OnlineInfo = setInfo.NewValue?.OnlineInfo; + cover.OnlineInfo = setInfo.NewValue; downloadTracker?.RemoveAndDisposeImmediately(); @@ -254,14 +254,14 @@ namespace osu.Game.Overlays.BeatmapSet loading.Hide(); - title.Text = new RomanisableString(setInfo.NewValue.Metadata.TitleUnicode, setInfo.NewValue.Metadata.Title); - artist.Text = new RomanisableString(setInfo.NewValue.Metadata.ArtistUnicode, setInfo.NewValue.Metadata.Artist); + title.Text = new RomanisableString(setInfo.NewValue.TitleUnicode, setInfo.NewValue.Title); + artist.Text = new RomanisableString(setInfo.NewValue.ArtistUnicode, setInfo.NewValue.Artist); - explicitContentPill.Alpha = setInfo.NewValue.OnlineInfo.HasExplicitContent ? 1 : 0; - featuredArtistPill.Alpha = setInfo.NewValue.OnlineInfo.TrackId != null ? 1 : 0; + explicitContentPill.Alpha = setInfo.NewValue.HasExplicitContent ? 1 : 0; + featuredArtistPill.Alpha = setInfo.NewValue.TrackId != null ? 1 : 0; onlineStatusPill.FadeIn(500, Easing.OutQuint); - onlineStatusPill.Status = setInfo.NewValue.OnlineInfo.Status; + onlineStatusPill.Status = setInfo.NewValue.Status; downloadButtonsContainer.FadeIn(transition_duration); favouriteButton.FadeIn(transition_duration); @@ -275,7 +275,7 @@ namespace osu.Game.Overlays.BeatmapSet { if (BeatmapSet.Value == null) return; - if (BeatmapSet.Value.OnlineInfo.Availability.DownloadDisabled && downloadTracker.State.Value != DownloadState.LocallyAvailable) + if (BeatmapSet.Value.Availability.DownloadDisabled && downloadTracker.State.Value != DownloadState.LocallyAvailable) { downloadButtonsContainer.Clear(); return; @@ -301,7 +301,7 @@ namespace osu.Game.Overlays.BeatmapSet default: downloadButtonsContainer.Child = new HeaderDownloadButton(BeatmapSet.Value); - if (BeatmapSet.Value.OnlineInfo.HasVideo) + if (BeatmapSet.Value.HasVideo) downloadButtonsContainer.Add(new HeaderDownloadButton(BeatmapSet.Value, true)); break; } diff --git a/osu.Game/Overlays/BeatmapSet/Buttons/FavouriteButton.cs b/osu.Game/Overlays/BeatmapSet/Buttons/FavouriteButton.cs index 43dd1438f1..d4873f241c 100644 --- a/osu.Game/Overlays/BeatmapSet/Buttons/FavouriteButton.cs +++ b/osu.Game/Overlays/BeatmapSet/Buttons/FavouriteButton.cs @@ -8,10 +8,10 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; -using osu.Game.Beatmaps; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API; using osu.Game.Online.API.Requests; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays.Notifications; using osu.Game.Resources.Localisation.Web; using osu.Game.Users; @@ -21,7 +21,7 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons { public class FavouriteButton : HeaderButton, IHasTooltip { - public readonly Bindable BeatmapSet = new Bindable(); + public readonly Bindable BeatmapSet = new Bindable(); private readonly BindableBool favourited = new BindableBool(); @@ -61,13 +61,13 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons Action = () => { // guaranteed by disabled state above. - Debug.Assert(BeatmapSet.Value.OnlineBeatmapSetID != null); + Debug.Assert(BeatmapSet.Value.OnlineID > 0); loading.Show(); request?.Cancel(); - request = new PostBeatmapFavouriteRequest(BeatmapSet.Value.OnlineBeatmapSetID.Value, favourited.Value ? BeatmapFavouriteAction.UnFavourite : BeatmapFavouriteAction.Favourite); + request = new PostBeatmapFavouriteRequest(BeatmapSet.Value.OnlineID, favourited.Value ? BeatmapFavouriteAction.UnFavourite : BeatmapFavouriteAction.Favourite); request.Success += () => { @@ -98,11 +98,11 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons BeatmapSet.BindValueChanged(setInfo => { updateEnabled(); - favourited.Value = setInfo.NewValue?.OnlineInfo?.HasFavourited ?? false; + favourited.Value = setInfo.NewValue?.HasFavourited ?? false; }, true); } - private void updateEnabled() => Enabled.Value = !(localUser.Value is GuestUser) && BeatmapSet.Value?.OnlineBeatmapSetID > 0; + private void updateEnabled() => Enabled.Value = !(localUser.Value is GuestUser) && BeatmapSet.Value?.OnlineID > 0; protected override void UpdateAfterChildren() { diff --git a/osu.Game/Overlays/BeatmapSet/Buttons/HeaderDownloadButton.cs b/osu.Game/Overlays/BeatmapSet/Buttons/HeaderDownloadButton.cs index 88d0778ae4..6862864c55 100644 --- a/osu.Game/Overlays/BeatmapSet/Buttons/HeaderDownloadButton.cs +++ b/osu.Game/Overlays/BeatmapSet/Buttons/HeaderDownloadButton.cs @@ -14,11 +14,13 @@ using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Online; using osu.Game.Online.API; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays.BeatmapListing.Panels; using osu.Game.Resources.Localisation.Web; using osu.Game.Users; using osuTK; using osuTK.Graphics; +using CommonStrings = osu.Game.Localisation.CommonStrings; namespace osu.Game.Overlays.BeatmapSet.Buttons { @@ -36,9 +38,10 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons private HeaderButton button; private BeatmapDownloadTracker downloadTracker; - private readonly BeatmapSetInfo beatmapSet; - public HeaderDownloadButton(BeatmapSetInfo beatmapSet, bool noVideo = false) + private readonly APIBeatmapSet beatmapSet; + + public HeaderDownloadButton(APIBeatmapSet beatmapSet, bool noVideo = false) { this.beatmapSet = beatmapSet; this.noVideo = noVideo; @@ -105,7 +108,7 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons return; } - beatmaps.Download(beatmapSet, noVideo); + beatmaps.Download(new BeatmapSetInfo { OnlineBeatmapSetID = beatmapSet.OnlineID }, noVideo); }; localUser.BindTo(api.LocalUser); @@ -121,7 +124,7 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons { new OsuSpriteText { - Text = Localisation.CommonStrings.Downloading, + Text = CommonStrings.Downloading, Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Bold) }, }; @@ -132,7 +135,7 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons { new OsuSpriteText { - Text = Localisation.CommonStrings.Importing, + Text = CommonStrings.Importing, Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Bold) }, }; @@ -168,7 +171,7 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons private LocalisableString getVideoSuffixText() { - if (!beatmapSet.OnlineInfo.HasVideo) + if (!beatmapSet.HasVideo) return string.Empty; return noVideo ? BeatmapsetsStrings.ShowDetailsDownloadNoVideo : BeatmapsetsStrings.ShowDetailsDownloadVideo; diff --git a/osu.Game/Overlays/BeatmapSet/Buttons/PreviewButton.cs b/osu.Game/Overlays/BeatmapSet/Buttons/PreviewButton.cs index 5b3c142a66..6bcdb7bdc5 100644 --- a/osu.Game/Overlays/BeatmapSet/Buttons/PreviewButton.cs +++ b/osu.Game/Overlays/BeatmapSet/Buttons/PreviewButton.cs @@ -8,9 +8,9 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; using osu.Game.Audio; -using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Containers; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays.BeatmapListing.Panels; using osuTK; @@ -25,7 +25,7 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons public IBindable Playing => playButton.Playing; - public BeatmapSetInfo BeatmapSet + public APIBeatmapSet BeatmapSet { get => playButton.BeatmapSet; set => playButton.BeatmapSet = value; diff --git a/osu.Game/Overlays/BeatmapSet/Details.cs b/osu.Game/Overlays/BeatmapSet/Details.cs index d6720e5f35..dfc8d5e680 100644 --- a/osu.Game/Overlays/BeatmapSet/Details.cs +++ b/osu.Game/Overlays/BeatmapSet/Details.cs @@ -6,6 +6,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Game.Beatmaps; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays.BeatmapSet.Buttons; using osu.Game.Screens.Select.Details; using osuTK; @@ -21,9 +22,9 @@ namespace osu.Game.Overlays.BeatmapSet private readonly AdvancedStats advanced; private readonly DetailBox ratingBox; - private BeatmapSetInfo beatmapSet; + private APIBeatmapSet beatmapSet; - public BeatmapSetInfo BeatmapSet + public APIBeatmapSet BeatmapSet { get => beatmapSet; set @@ -37,9 +38,9 @@ namespace osu.Game.Overlays.BeatmapSet } } - private BeatmapInfo beatmapInfo; + private IBeatmapInfo beatmapInfo; - public BeatmapInfo BeatmapInfo + public IBeatmapInfo BeatmapInfo { get => beatmapInfo; set @@ -53,7 +54,7 @@ namespace osu.Game.Overlays.BeatmapSet private void updateDisplay() { Ratings.Ratings = BeatmapSet?.Ratings; - ratingBox.Alpha = BeatmapSet?.OnlineInfo?.Status > 0 ? 1 : 0; + ratingBox.Alpha = BeatmapSet?.Status > 0 ? 1 : 0; } public Details() diff --git a/osu.Game/Overlays/BeatmapSet/Info.cs b/osu.Game/Overlays/BeatmapSet/Info.cs index 970e9bbf42..3ef52d718d 100644 --- a/osu.Game/Overlays/BeatmapSet/Info.cs +++ b/osu.Game/Overlays/BeatmapSet/Info.cs @@ -6,9 +6,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.Sprites; +using osu.Game.Online.API.Requests.Responses; namespace osu.Game.Overlays.BeatmapSet { @@ -22,12 +22,12 @@ namespace osu.Game.Overlays.BeatmapSet private readonly Box background; private readonly SuccessRate successRate; - public readonly Bindable BeatmapSet = new Bindable(); + public readonly Bindable BeatmapSet = new Bindable(); - public BeatmapInfo BeatmapInfo + public APIBeatmap BeatmapInfo { - get => successRate.BeatmapInfo; - set => successRate.BeatmapInfo = value; + get => successRate.Beatmap; + set => successRate.Beatmap = value; } public Info() @@ -115,11 +115,11 @@ namespace osu.Game.Overlays.BeatmapSet BeatmapSet.ValueChanged += b => { - source.Text = b.NewValue?.Metadata.Source ?? string.Empty; - tags.Text = b.NewValue?.Metadata.Tags ?? string.Empty; - genre.Text = b.NewValue?.OnlineInfo?.Genre.Name ?? string.Empty; - language.Text = b.NewValue?.OnlineInfo?.Language.Name ?? string.Empty; - bool setHasLeaderboard = b.NewValue?.OnlineInfo?.Status > 0; + source.Text = b.NewValue?.Source ?? string.Empty; + tags.Text = b.NewValue?.Tags ?? string.Empty; + genre.Text = b.NewValue?.Genre.Name ?? string.Empty; + language.Text = b.NewValue?.Language.Name ?? string.Empty; + bool setHasLeaderboard = b.NewValue?.Status > 0; successRate.Alpha = setHasLeaderboard ? 1 : 0; notRankedPlaceholder.Alpha = setHasLeaderboard ? 0 : 1; Height = setHasLeaderboard ? 270 : base_height; diff --git a/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs b/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs index 6349f115cb..a9723c9c62 100644 --- a/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs +++ b/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs @@ -13,6 +13,7 @@ using osu.Game.Graphics.UserInterface; using osuTK.Graphics; using System; using System.Linq; +using osu.Framework.Allocation; using osu.Framework.Extensions.IEnumerableExtensions; namespace osu.Game.Overlays.BeatmapSet @@ -20,7 +21,7 @@ namespace osu.Game.Overlays.BeatmapSet public class LeaderboardModSelector : CompositeDrawable { public readonly BindableList SelectedMods = new BindableList(); - public readonly Bindable Ruleset = new Bindable(); + public readonly Bindable Ruleset = new Bindable(); private readonly FillFlowContainer modsContainer; @@ -45,7 +46,10 @@ namespace osu.Game.Overlays.BeatmapSet Ruleset.BindValueChanged(onRulesetChanged, true); } - private void onRulesetChanged(ValueChangedEvent ruleset) + [Resolved] + private RulesetStore rulesets { get; set; } + + private void onRulesetChanged(ValueChangedEvent ruleset) { SelectedMods.Clear(); modsContainer.Clear(); @@ -54,7 +58,7 @@ namespace osu.Game.Overlays.BeatmapSet return; modsContainer.Add(new ModButton(new ModNoMod())); - modsContainer.AddRange(ruleset.NewValue.CreateInstance().AllMods.Where(m => m.UserPlayable).Select(m => new ModButton(m))); + modsContainer.AddRange(rulesets.GetRuleset(ruleset.NewValue.OnlineID).CreateInstance().AllMods.Where(m => m.UserPlayable).Select(m => new ModButton(m))); modsContainer.ForEach(button => { diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs index c6eb516c7c..4a00c8b4a0 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs @@ -1,24 +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 osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osuTK; using System.Linq; using System.Threading; using System.Threading.Tasks; -using osu.Game.Online.API.Requests.Responses; +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.UserInterface; using osu.Game.Online.API; using osu.Game.Online.API.Requests; -using osu.Framework.Bindables; -using osu.Game.Graphics.UserInterface; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Rulesets; using osu.Game.Scoring; using osu.Game.Screens.Select.Leaderboards; using osu.Game.Users; +using osuTK; namespace osu.Game.Overlays.BeatmapSet.Scores { @@ -26,8 +26,8 @@ namespace osu.Game.Overlays.BeatmapSet.Scores { private const int spacing = 15; - public readonly Bindable Beatmap = new Bindable(); - private readonly Bindable ruleset = new Bindable(); + public readonly Bindable Beatmap = new Bindable(); + private readonly Bindable ruleset = new Bindable(); private readonly Bindable scope = new Bindable(BeatmapLeaderboardScope.Global); private readonly IBindable user = new Bindable(); @@ -66,7 +66,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores if (value?.Scores.Any() != true) return; - scoreManager.OrderByTotalScoreAsync(value.Scores.Select(s => s.CreateScoreInfo(rulesets, Beatmap.Value)).ToArray(), loadCancellationSource.Token) + scoreManager.OrderByTotalScoreAsync(value.Scores.Select(s => s.CreateScoreInfo(rulesets, Beatmap.Value.ToBeatmapInfo(rulesets))).ToArray(), loadCancellationSource.Token) .ContinueWith(ordered => Schedule(() => { if (loadCancellationSource.IsCancellationRequested) @@ -78,7 +78,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores scoreTable.Show(); var userScore = value.UserScore; - var userScoreInfo = userScore?.Score.CreateScoreInfo(rulesets, Beatmap.Value); + var userScoreInfo = userScore?.Score.CreateScoreInfo(rulesets, Beatmap.Value.ToBeatmapInfo(rulesets)); topScoresContainer.Add(new DrawableTopScore(topScore)); @@ -200,11 +200,11 @@ namespace osu.Game.Overlays.BeatmapSet.Scores user.BindValueChanged(onUserChanged, true); } - private void onBeatmapChanged(ValueChangedEvent beatmap) + private void onBeatmapChanged(ValueChangedEvent beatmap) { var beatmapRuleset = beatmap.NewValue?.Ruleset; - if (ruleset.Value?.Equals(beatmapRuleset) ?? false) + if (ruleset.Value?.OnlineID == beatmapRuleset?.OnlineID) { modSelector.DeselectAll(); ruleset.TriggerChange(); @@ -232,7 +232,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores noScoresPlaceholder.Hide(); - if (Beatmap.Value?.OnlineBeatmapID.HasValue != true || Beatmap.Value.Status <= BeatmapSetOnlineStatus.Pending) + if (Beatmap.Value == null || Beatmap.Value.OnlineID <= 0 || (Beatmap.Value?.BeatmapSet as IBeatmapSetOnlineInfo)?.Status <= BeatmapSetOnlineStatus.Pending) { Scores = null; Hide(); diff --git a/osu.Game/Overlays/BeatmapSet/SuccessRate.cs b/osu.Game/Overlays/BeatmapSet/SuccessRate.cs index 40a3c9fe8b..e08f099226 100644 --- a/osu.Game/Overlays/BeatmapSet/SuccessRate.cs +++ b/osu.Game/Overlays/BeatmapSet/SuccessRate.cs @@ -5,10 +5,10 @@ using osu.Framework.Allocation; using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Resources.Localisation.Web; using osu.Game.Screens.Select.Details; @@ -23,16 +23,16 @@ namespace osu.Game.Overlays.BeatmapSet private readonly Bar successRate; private readonly Container percentContainer; - private BeatmapInfo beatmapInfo; + private APIBeatmap beatmap; - public BeatmapInfo BeatmapInfo + public APIBeatmap Beatmap { - get => beatmapInfo; + get => beatmap; set { - if (value == beatmapInfo) return; + if (value == beatmap) return; - beatmapInfo = value; + beatmap = value; updateDisplay(); } @@ -40,15 +40,15 @@ namespace osu.Game.Overlays.BeatmapSet private void updateDisplay() { - int passCount = beatmapInfo?.OnlineInfo?.PassCount ?? 0; - int playCount = beatmapInfo?.OnlineInfo?.PlayCount ?? 0; + int passCount = beatmap?.PassCount ?? 0; + int playCount = beatmap?.PlayCount ?? 0; float rate = playCount != 0 ? (float)passCount / playCount : 0; successPercent.Text = rate.ToLocalisableString(@"0.#%"); successRate.Length = rate; percentContainer.ResizeWidthTo(successRate.Length, 250, Easing.InOutCubic); - Graph.FailTimes = beatmapInfo?.FailTimes; + Graph.FailTimes = beatmap?.FailTimes; } public SuccessRate() diff --git a/osu.Game/Overlays/BeatmapSetOverlay.cs b/osu.Game/Overlays/BeatmapSetOverlay.cs index f987b57d6e..fa5a7c66d0 100644 --- a/osu.Game/Overlays/BeatmapSetOverlay.cs +++ b/osu.Game/Overlays/BeatmapSetOverlay.cs @@ -7,8 +7,8 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input.Events; -using osu.Game.Beatmaps; using osu.Game.Online.API.Requests; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays.BeatmapSet; using osu.Game.Overlays.BeatmapSet.Scores; using osu.Game.Overlays.Comments; @@ -27,7 +27,7 @@ namespace osu.Game.Overlays [Resolved] private RulesetStore rulesets { get; set; } - private readonly Bindable beatmapSet = new Bindable(); + private readonly Bindable beatmapSet = new Bindable(); // receive input outside our bounds so we can trigger a close event on ourselves. public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; @@ -89,8 +89,8 @@ namespace osu.Game.Overlays var req = new GetBeatmapSetRequest(beatmapId, BeatmapSetLookupType.BeatmapId); req.Success += res => { - beatmapSet.Value = res.ToBeatmapSet(rulesets); - Header.HeaderContent.Picker.Beatmap.Value = Header.BeatmapSet.Value.Beatmaps.First(b => b.OnlineBeatmapID == beatmapId); + beatmapSet.Value = res; + Header.HeaderContent.Picker.Beatmap.Value = Header.BeatmapSet.Value.Beatmaps.First(b => b.OnlineID == beatmapId); }; API.Queue(req); @@ -102,7 +102,7 @@ namespace osu.Game.Overlays beatmapSet.Value = null; var req = new GetBeatmapSetRequest(beatmapSetId); - req.Success += res => beatmapSet.Value = res.ToBeatmapSet(rulesets); + req.Success += res => beatmapSet.Value = res; API.Queue(req); Show(); @@ -112,7 +112,7 @@ namespace osu.Game.Overlays /// Show an already fully-populated beatmap set. /// /// The set to show. - public void ShowBeatmapSet(BeatmapSetInfo set) + public void ShowBeatmapSet(APIBeatmapSet set) { beatmapSet.Value = set; Show(); @@ -120,7 +120,7 @@ namespace osu.Game.Overlays private class CommentsSection : BeatmapSetLayoutSection { - public readonly Bindable BeatmapSet = new Bindable(); + public readonly Bindable BeatmapSet = new Bindable(); public CommentsSection() { @@ -130,10 +130,10 @@ namespace osu.Game.Overlays BeatmapSet.BindValueChanged(beatmapSet => { - if (beatmapSet.NewValue?.OnlineBeatmapSetID is int onlineBeatmapSetID) + if (beatmapSet.NewValue?.OnlineID > 0) { Show(); - comments.ShowComments(CommentableType.Beatmapset, onlineBeatmapSetID); + comments.ShowComments(CommentableType.Beatmapset, beatmapSet.NewValue.OnlineID); } else { diff --git a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs index c1e56facd9..fffa20dc11 100644 --- a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs @@ -61,7 +61,7 @@ namespace osu.Game.Overlays.Profile.Sections.Beatmaps new GetUserBeatmapsRequest(User.Value.Id, type, VisiblePages++, ItemsPerPage); protected override Drawable CreateDrawableItem(APIBeatmapSet model) => model.OnlineID > 0 - ? new GridBeatmapPanel(model.ToBeatmapSet(Rulesets)) + ? new GridBeatmapPanel(model) { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, diff --git a/osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedBeatmap.cs b/osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedBeatmap.cs index ac4299ae49..3ed4bd9e50 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedBeatmap.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/DrawableMostPlayedBeatmap.cs @@ -5,15 +5,15 @@ using System.Diagnostics; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Sprites; +using osu.Framework.Localisation; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osuTK; -using osu.Framework.Graphics.Cursor; -using osu.Framework.Localisation; using osu.Game.Online.API.Requests.Responses; using osu.Game.Resources.Localisation.Web; diff --git a/osu.Game/Overlays/Rankings/SpotlightsLayout.cs b/osu.Game/Overlays/Rankings/SpotlightsLayout.cs index b16e0a4908..b95b0a1afc 100644 --- a/osu.Game/Overlays/Rankings/SpotlightsLayout.cs +++ b/osu.Game/Overlays/Rankings/SpotlightsLayout.cs @@ -143,7 +143,7 @@ namespace osu.Game.Overlays.Rankings AutoSizeAxes = Axes.Y, RelativeSizeAxes = Axes.X, Spacing = new Vector2(10), - Children = response.BeatmapSets.Select(b => new GridBeatmapPanel(b.ToBeatmapSet(rulesets)) + Children = response.BeatmapSets.Select(b => new GridBeatmapPanel(b) { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, diff --git a/osu.Game/Screens/Play/SoloSpectator.cs b/osu.Game/Screens/Play/SoloSpectator.cs index 9d4dad8bdc..83ea660092 100644 --- a/osu.Game/Screens/Play/SoloSpectator.cs +++ b/osu.Game/Screens/Play/SoloSpectator.cs @@ -223,7 +223,7 @@ namespace osu.Game.Screens.Play onlineBeatmapRequest.Success += res => Schedule(() => { onlineBeatmap = res.ToBeatmapSet(rulesets); - beatmapPanelContainer.Child = new GridBeatmapPanel(onlineBeatmap); + beatmapPanelContainer.Child = new GridBeatmapPanel(res); checkForAutomaticDownload(); }); diff --git a/osu.Game/Screens/Select/BeatmapDetails.cs b/osu.Game/Screens/Select/BeatmapDetails.cs index dfbaa9c6a5..ece16a8e57 100644 --- a/osu.Game/Screens/Select/BeatmapDetails.cs +++ b/osu.Game/Screens/Select/BeatmapDetails.cs @@ -57,7 +57,7 @@ namespace osu.Game.Screens.Select beatmapInfo = value; failTimes = beatmapInfo?.OnlineInfo?.FailTimes; - ratings = beatmapInfo?.BeatmapSet?.Ratings; + ratings = beatmapInfo?.BeatmapSet?.OnlineInfo?.Ratings; Scheduler.AddOnce(updateStatistics); } @@ -182,7 +182,7 @@ namespace osu.Game.Screens.Select source.Text = BeatmapInfo?.Metadata?.Source; tags.Text = BeatmapInfo?.Metadata?.Tags; - // metrics may have been previously fetched + // failTimes may have been previously fetched if (ratings != null && failTimes != null) { updateMetrics(); diff --git a/osu.Game/Screens/Select/Details/AdvancedStats.cs b/osu.Game/Screens/Select/Details/AdvancedStats.cs index 562ebad9fe..e129e75ec2 100644 --- a/osu.Game/Screens/Select/Details/AdvancedStats.cs +++ b/osu.Game/Screens/Select/Details/AdvancedStats.cs @@ -38,9 +38,9 @@ namespace osu.Game.Screens.Select.Details protected readonly StatisticRow FirstValue, HpDrain, Accuracy, ApproachRate; private readonly StatisticRow starDifficulty; - private BeatmapInfo beatmapInfo; + private IBeatmapInfo beatmapInfo; - public BeatmapInfo BeatmapInfo + public IBeatmapInfo BeatmapInfo { get => beatmapInfo; set @@ -106,7 +106,7 @@ namespace osu.Game.Screens.Select.Details private void updateStatistics() { - IBeatmapDifficultyInfo baseDifficulty = BeatmapInfo?.BaseDifficulty; + IBeatmapDifficultyInfo baseDifficulty = BeatmapInfo?.Difficulty; BeatmapDifficulty adjustedDifficulty = null; if (baseDifficulty != null && mods.Value.Any(m => m is IApplicableToDifficulty)) @@ -117,7 +117,7 @@ namespace osu.Game.Screens.Select.Details mod.ApplyToDifficulty(adjustedDifficulty); } - switch (BeatmapInfo?.Ruleset?.ID ?? 0) + switch (BeatmapInfo?.Ruleset.OnlineID) { case 3: // Account for mania differences locally for now From 1fd05ab78aa7b09c63c8040e19fd92420ad02fc6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Oct 2021 18:56:13 +0900 Subject: [PATCH 205/260] 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 552675d706..dec994bcb2 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,7 +52,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 8ba6e41d53..8052ab5254 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index e55dbb3bfe..d152cb7066 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -93,7 +93,7 @@ - + From 22c2f48e4dd7468f2abf91702110a612c0cabe94 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Oct 2021 19:05:30 +0900 Subject: [PATCH 206/260] Fix more null rulesets --- osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetails.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetails.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetails.cs index 1125e16d91..88acba756c 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetails.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetails.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics; using osu.Game.Beatmaps; using osu.Game.Online.API; using osu.Game.Online.API.Requests.Responses; +using osu.Game.Rulesets.Osu; using osu.Game.Screens.Select; namespace osu.Game.Tests.Visual.SongSelect @@ -54,6 +55,7 @@ namespace osu.Game.Tests.Visual.SongSelect ApproachRate = 3.5f, }, StarDifficulty = 5.3f, + Ruleset = new OsuRuleset().RulesetInfo, OnlineInfo = new APIBeatmap { FailTimes = new APIFailTimes @@ -90,6 +92,7 @@ namespace osu.Game.Tests.Visual.SongSelect ApproachRate = 3.5f, }, StarDifficulty = 5.3f, + Ruleset = new OsuRuleset().RulesetInfo, OnlineInfo = new APIBeatmap { FailTimes = new APIFailTimes @@ -119,6 +122,7 @@ namespace osu.Game.Tests.Visual.SongSelect Source = "osu!", Tags = "this beatmap has ratings metrics but not retries or fails", }, + Ruleset = new OsuRuleset().RulesetInfo, BaseDifficulty = new BeatmapDifficulty { CircleSize = 6, @@ -148,6 +152,7 @@ namespace osu.Game.Tests.Visual.SongSelect OverallDifficulty = 6, ApproachRate = 7, }, + Ruleset = new OsuRuleset().RulesetInfo, StarDifficulty = 2.91f, OnlineInfo = new APIBeatmap { @@ -171,6 +176,7 @@ namespace osu.Game.Tests.Visual.SongSelect Source = "osu!", Tags = "this beatmap has no metrics", }, + Ruleset = new OsuRuleset().RulesetInfo, BaseDifficulty = new BeatmapDifficulty { CircleSize = 5, From bb9769aa178f52b75551cc0276e12dbd918c33bb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Oct 2021 20:12:12 +0900 Subject: [PATCH 207/260] Fix null ruleset in another place --- osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetails.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetails.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetails.cs index 88acba756c..9856b28b00 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetails.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapDetails.cs @@ -200,6 +200,7 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep("online ratings/retries/fails", () => details.BeatmapInfo = new BeatmapInfo { OnlineBeatmapID = 162, + Ruleset = new OsuRuleset().RulesetInfo }); AddStep("set online", () => api.SetState(APIState.Online)); AddStep("set offline", () => api.SetState(APIState.Offline)); From 4d988340eb02121e4f4645b480d657bbafbbfe4b Mon Sep 17 00:00:00 2001 From: Opelkuh <25430283+Opelkuh@users.noreply.github.com> Date: Sat, 30 Oct 2021 01:02:53 +0200 Subject: [PATCH 208/260] Add hardware acceleration toggle --- .../Localisation/GraphicsSettingsStrings.cs | 10 +++++ .../Sections/Graphics/VideoSettings.cs | 43 +++++++++++++++++++ .../Settings/Sections/GraphicsSection.cs | 1 + 3 files changed, 54 insertions(+) create mode 100644 osu.Game/Overlays/Settings/Sections/Graphics/VideoSettings.cs diff --git a/osu.Game/Localisation/GraphicsSettingsStrings.cs b/osu.Game/Localisation/GraphicsSettingsStrings.cs index f85cc0f2ae..996a1350eb 100644 --- a/osu.Game/Localisation/GraphicsSettingsStrings.cs +++ b/osu.Game/Localisation/GraphicsSettingsStrings.cs @@ -119,6 +119,16 @@ namespace osu.Game.Localisation /// public static LocalisableString ShowCursorInScreenshots => new TranslatableString(getKey(@"show_cursor_in_screenshots"), @"Show menu cursor in screenshots"); + /// + /// "Video" + /// + public static LocalisableString VideoHeader => new TranslatableString(getKey(@"video_header"), @"Video"); + + /// + /// "Use hardware acceleration" + /// + public static LocalisableString UseHardwareAcceleration => new TranslatableString(getKey(@"use_hardware_acceleration"), @"Use hardware acceleration"); + private static string getKey(string key) => $"{prefix}:{key}"; } } diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/VideoSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/VideoSettings.cs new file mode 100644 index 0000000000..921eab63ed --- /dev/null +++ b/osu.Game/Overlays/Settings/Sections/Graphics/VideoSettings.cs @@ -0,0 +1,43 @@ +// 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.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Video; +using osu.Framework.Localisation; +using osu.Game.Localisation; + +namespace osu.Game.Overlays.Settings.Sections.Graphics +{ + public class VideoSettings : SettingsSubsection + { + protected override LocalisableString Header => GraphicsSettingsStrings.VideoHeader; + + private Bindable hardwareVideoDecoder; + private SettingsCheckbox hwAccelCheckbox; + + [BackgroundDependencyLoader] + private void load(FrameworkConfigManager config) + { + hardwareVideoDecoder = config.GetBindable(FrameworkSetting.HardwareVideoDecoder); + + Children = new Drawable[] + { + hwAccelCheckbox = new SettingsCheckbox + { + LabelText = GraphicsSettingsStrings.UseHardwareAcceleration, + }, + }; + + hwAccelCheckbox.Current.Default = hardwareVideoDecoder.Default != HardwareVideoDecoder.None; + hwAccelCheckbox.Current.Value = hardwareVideoDecoder.Value != HardwareVideoDecoder.None; + + hwAccelCheckbox.Current.BindValueChanged(val => + { + hardwareVideoDecoder.Value = val.NewValue ? HardwareVideoDecoder.Any : HardwareVideoDecoder.None; + }); + } + } +} diff --git a/osu.Game/Overlays/Settings/Sections/GraphicsSection.cs b/osu.Game/Overlays/Settings/Sections/GraphicsSection.cs index 591848506a..c792098c6d 100644 --- a/osu.Game/Overlays/Settings/Sections/GraphicsSection.cs +++ b/osu.Game/Overlays/Settings/Sections/GraphicsSection.cs @@ -25,6 +25,7 @@ namespace osu.Game.Overlays.Settings.Sections new LayoutSettings(), new RendererSettings(), new ScreenshotSettings(), + new VideoSettings(), }; } } From e9473db77c61ce3e7b15f7f770634c5665b50d2d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 30 Oct 2021 20:37:55 +0900 Subject: [PATCH 209/260] Reorder to have video settings next to renderer Co-authored-by: Salman Ahmed --- osu.Game/Overlays/Settings/Sections/GraphicsSection.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/GraphicsSection.cs b/osu.Game/Overlays/Settings/Sections/GraphicsSection.cs index c792098c6d..8cd3b841c2 100644 --- a/osu.Game/Overlays/Settings/Sections/GraphicsSection.cs +++ b/osu.Game/Overlays/Settings/Sections/GraphicsSection.cs @@ -24,8 +24,8 @@ namespace osu.Game.Overlays.Settings.Sections { new LayoutSettings(), new RendererSettings(), - new ScreenshotSettings(), new VideoSettings(), + new ScreenshotSettings(), }; } } From 6e4f7af8d36dbf59d396fec4d1ccdcbed9a0ea45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 30 Oct 2021 14:15:20 +0200 Subject: [PATCH 210/260] Mark `IHasOnlineID` implementation with region --- osu.Game/Scoring/ScoreInfo.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index 8eaec0a477..ed3b27dd22 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -272,6 +272,10 @@ namespace osu.Game.Scoring return ReferenceEquals(this, other); } + #region Implementation of IHasOnlineID + public long OnlineID => OnlineScoreID ?? -1; + + #endregion } } From e9ba1ea1989b3dd03a4703a069df2ff9939b0714 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 30 Oct 2021 15:08:45 +0200 Subject: [PATCH 211/260] Mark `IScoreInfo` implementation with region --- osu.Game/Scoring/ScoreInfo.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index 0efce4686c..36608e2a74 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -250,8 +250,12 @@ namespace osu.Game.Scoring #endregion + #region Implementation of IScoreInfo + IBeatmapInfo IScoreInfo.Beatmap => BeatmapInfo; IRulesetInfo IScoreInfo.Ruleset => Ruleset; bool IScoreInfo.HasReplay => Files.Any(); + + #endregion } } From b63a90966ba260c5122203c4b06299d2af40aa52 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 31 Oct 2021 23:49:26 +0900 Subject: [PATCH 212/260] Remove misplaced access modifier in interface specification MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- osu.Game/Scoring/IScoreInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Scoring/IScoreInfo.cs b/osu.Game/Scoring/IScoreInfo.cs index 171964206d..77579f23d9 100644 --- a/osu.Game/Scoring/IScoreInfo.cs +++ b/osu.Game/Scoring/IScoreInfo.cs @@ -29,7 +29,7 @@ namespace osu.Game.Scoring IRulesetInfo Ruleset { get; } - public ScoreRank Rank { get; } + ScoreRank Rank { get; } // Mods is currently missing from this interface as the `IMod` class has properties which can't be fulfilled by `APIMod`, // but also doesn't expose `Settings`. We can consider how to implement this in the future if required. From 914e5c0f9d22485ace4e69a42d44e3d020f6d26d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 31 Oct 2021 18:58:17 +0100 Subject: [PATCH 213/260] Fix star rating not updating for some mods on details tab in song select `AdvancedStats` was locally assuming that the only changes in mods that are relevant to it are if the mods are `IApplicableToDifficulty`. This is not true, as other mods (such as `IApplicableToRate` mods, or more recently, Flashlight) can also affect star difficulty, which is shown on one of the bars in `AdvancedStats`. --- osu.Game/Screens/Select/Details/AdvancedStats.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game/Screens/Select/Details/AdvancedStats.cs b/osu.Game/Screens/Select/Details/AdvancedStats.cs index 562ebad9fe..25ca6ee264 100644 --- a/osu.Game/Screens/Select/Details/AdvancedStats.cs +++ b/osu.Game/Screens/Select/Details/AdvancedStats.cs @@ -94,9 +94,6 @@ namespace osu.Game.Screens.Select.Details modSettingChangeTracker = new ModSettingChangeTracker(mods.NewValue); modSettingChangeTracker.SettingChanged += m => { - if (!(m is IApplicableToDifficulty)) - return; - debouncedStatisticsUpdate?.Cancel(); debouncedStatisticsUpdate = Scheduler.AddDelayed(updateStatistics, 100); }; From 02e69626e3eb8ffe01c0037421710dbbd2a6e37a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 1 Nov 2021 13:06:26 +0900 Subject: [PATCH 214/260] Reduce fail animation tint slightly --- osu.Game/Screens/Play/FailAnimation.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/FailAnimation.cs b/osu.Game/Screens/Play/FailAnimation.cs index edfb8186bb..f3676baf80 100644 --- a/osu.Game/Screens/Play/FailAnimation.cs +++ b/osu.Game/Screens/Play/FailAnimation.cs @@ -10,6 +10,7 @@ using ManagedBass.Fx; using osu.Framework.Allocation; using osu.Framework.Audio.Sample; using osu.Framework.Audio.Track; +using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -83,7 +84,7 @@ namespace osu.Game.Screens.Play Content, redFlashLayer = new Box { - Colour = Color4.Red, + Colour = Color4.Red.Opacity(0.6f), RelativeSizeAxes = Axes.Both, Blending = BlendingParameters.Additive, Depth = float.MinValue, From 269a8df0ecff144c4052c3640767aa98632e2e07 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 1 Nov 2021 13:20:37 +0900 Subject: [PATCH 215/260] Fix `HasReplay` not being corrrectly implemented by `APIScoreInfo` --- .../Visual/Gameplay/TestSceneReplayDownloadButton.cs | 2 +- osu.Game/Online/API/Requests/Responses/APIScoreInfo.cs | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs index b84ac6c2f1..288da71ba3 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs @@ -136,7 +136,7 @@ namespace osu.Game.Tests.Visual.Gameplay { OnlineID = 2553163309, OnlineRulesetID = 0, - Replay = replayAvailable, + HasReplay = replayAvailable, User = new User { Id = 39828, diff --git a/osu.Game/Online/API/Requests/Responses/APIScoreInfo.cs b/osu.Game/Online/API/Requests/Responses/APIScoreInfo.cs index 4abb227414..1057819c6d 100644 --- a/osu.Game/Online/API/Requests/Responses/APIScoreInfo.cs +++ b/osu.Game/Online/API/Requests/Responses/APIScoreInfo.cs @@ -26,13 +26,11 @@ namespace osu.Game.Online.API.Requests.Responses [JsonProperty(@"user")] public User User { get; set; } - public bool HasReplay { get; set; } - [JsonProperty(@"id")] public long OnlineID { get; set; } [JsonProperty(@"replay")] - public bool Replay { get; set; } + public bool HasReplay { get; set; } [JsonProperty(@"created_at")] public DateTimeOffset Date { get; set; } @@ -97,7 +95,7 @@ namespace osu.Game.Online.API.Requests.Responses Date = Date, PP = PP, RulesetID = OnlineRulesetID, - Hash = Replay ? "online" : string.Empty, // todo: temporary? + Hash = HasReplay ? "online" : string.Empty, // todo: temporary? Rank = Rank, Ruleset = ruleset, Mods = mods, From 708b57348dfb2c1ef369c8a260875a10c7e11cb8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 1 Nov 2021 13:22:16 +0900 Subject: [PATCH 216/260] Change loose api ordering requirement to throw instead --- osu.Game/Online/API/Requests/Responses/APIScoreInfo.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/API/Requests/Responses/APIScoreInfo.cs b/osu.Game/Online/API/Requests/Responses/APIScoreInfo.cs index 1057819c6d..3d82f91fb5 100644 --- a/osu.Game/Online/API/Requests/Responses/APIScoreInfo.cs +++ b/osu.Game/Online/API/Requests/Responses/APIScoreInfo.cs @@ -50,8 +50,11 @@ namespace osu.Game.Online.API.Requests.Responses set { // in the deserialisation case we need to ferry this data across. - if (Beatmap is APIBeatmap apiBeatmap) - apiBeatmap.BeatmapSet = value; + // the order of properties returned by the API guarantees that the beatmap is populated by this point. + if (!(Beatmap is APIBeatmap apiBeatmap)) + throw new InvalidOperationException("Beatmap set metadata arrived before beatmap metadata in response"); + + apiBeatmap.BeatmapSet = value; } } From 5bfb16efe25986088f7686b681391ae9faec09ef Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 1 Nov 2021 13:55:34 +0900 Subject: [PATCH 217/260] Ignore case when searching for existing `skin.ini` --- osu.Game/Skinning/Skin.cs | 2 +- osu.Game/Skinning/SkinManager.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Skinning/Skin.cs b/osu.Game/Skinning/Skin.cs index a5639c3301..f8f9c1172d 100644 --- a/osu.Game/Skinning/Skin.cs +++ b/osu.Game/Skinning/Skin.cs @@ -93,7 +93,7 @@ namespace osu.Game.Skinning private Stream getConfigurationStream() { - string path = SkinInfo.Files.SingleOrDefault(f => f.Filename == "skin.ini")?.FileInfo.StoragePath; + string path = SkinInfo.Files.SingleOrDefault(f => f.Filename.Equals(@"skin.ini", StringComparison.OrdinalIgnoreCase))?.FileInfo.StoragePath; if (string.IsNullOrEmpty(path)) return null; diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index 2187d2d875..a537e8d5c3 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -194,7 +194,7 @@ namespace osu.Game.Skinning string nameLine = $"Name: {item.Name}"; string authorLine = $"Author: {item.Creator}"; - var existingFile = item.Files.SingleOrDefault(f => f.Filename == "skin.ini"); + var existingFile = item.Files.SingleOrDefault(f => f.Filename.Equals(@"skin.ini", StringComparison.OrdinalIgnoreCase)); if (existingFile != null) { From 32817a4c66dda765de5774222e730af71686cf2f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 1 Nov 2021 13:55:45 +0900 Subject: [PATCH 218/260] Add test coverage of failure from incorrect case in `skin.ini` filename --- osu.Game.Tests/Skins/IO/ImportSkinTest.cs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Skins/IO/ImportSkinTest.cs b/osu.Game.Tests/Skins/IO/ImportSkinTest.cs index b62fb8bd87..a5f588be45 100644 --- a/osu.Game.Tests/Skins/IO/ImportSkinTest.cs +++ b/osu.Game.Tests/Skins/IO/ImportSkinTest.cs @@ -29,6 +29,15 @@ namespace osu.Game.Tests.Skins.IO assertCorrectMetadata(import1, "test skin [skin]", "skinner", osu); }); + [Test] + public Task TestSingleImportWeirdIniFileCase() => runSkinTest(async osu => + { + var import1 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("test skin", "skinner", iniFilename: "Skin.InI"), "skin.osk")); + + // When the import filename doesn't match, it should be appended (and update the skin.ini). + assertCorrectMetadata(import1, "test skin [skin]", "skinner", osu); + }); + [Test] public Task TestSingleImportMatchingFilename() => runSkinTest(async osu => { @@ -190,11 +199,11 @@ namespace osu.Game.Tests.Skins.IO return zipStream; } - private MemoryStream createOskWithIni(string name, string author, bool makeUnique = false) + private MemoryStream createOskWithIni(string name, string author, bool makeUnique = false, string iniFilename = "skin.ini") { var zipStream = new MemoryStream(); using var zip = ZipArchive.Create(); - zip.AddEntry("skin.ini", generateSkinIni(name, author, makeUnique)); + zip.AddEntry(iniFilename, generateSkinIni(name, author, makeUnique)); zip.SaveTo(zipStream); return zipStream; } From 6b250955d7ad884d66403b8e956b3327f640fb59 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 1 Nov 2021 14:09:17 +0900 Subject: [PATCH 219/260] Add missing verbatim string markers --- osu.Game.Tests/Skins/IO/ImportSkinTest.cs | 2 +- osu.Game/Skinning/SkinManager.cs | 30 +++++++++++------------ 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/osu.Game.Tests/Skins/IO/ImportSkinTest.cs b/osu.Game.Tests/Skins/IO/ImportSkinTest.cs index a5f588be45..c75714032e 100644 --- a/osu.Game.Tests/Skins/IO/ImportSkinTest.cs +++ b/osu.Game.Tests/Skins/IO/ImportSkinTest.cs @@ -199,7 +199,7 @@ namespace osu.Game.Tests.Skins.IO return zipStream; } - private MemoryStream createOskWithIni(string name, string author, bool makeUnique = false, string iniFilename = "skin.ini") + private MemoryStream createOskWithIni(string name, string author, bool makeUnique = false, string iniFilename = @"skin.ini") { var zipStream = new MemoryStream(); using var zip = ZipArchive.Create(); diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index a537e8d5c3..76d36ae7d9 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -108,7 +108,7 @@ namespace osu.Game.Skinning } } - protected override bool ShouldDeleteArchive(string path) => Path.GetExtension(path)?.ToLowerInvariant() == ".osk"; + protected override bool ShouldDeleteArchive(string path) => Path.GetExtension(path)?.ToLowerInvariant() == @".osk"; /// /// Returns a list of all usable s. Includes the special default skin plus all skins from . @@ -149,9 +149,9 @@ namespace osu.Game.Skinning CurrentSkinInfo.Value = ModelStore.ConsumableItems.Single(i => i.ID == chosen.ID); } - protected override SkinInfo CreateModel(ArchiveReader archive) => new SkinInfo { Name = archive.Name ?? "No name" }; + protected override SkinInfo CreateModel(ArchiveReader archive) => new SkinInfo { Name = archive.Name ?? @"No name" }; - private const string unknown_creator_string = "Unknown"; + private const string unknown_creator_string = @"Unknown"; protected override bool HasCustomHashFunction => true; @@ -164,7 +164,7 @@ namespace osu.Game.Skinning // `Skin` will parse the skin.ini and populate `Skin.Configuration` during construction above. string skinIniSourcedName = instance.Configuration.SkinInfo.Name; string skinIniSourcedCreator = instance.Configuration.SkinInfo.Creator; - string archiveName = item.Name.Replace(".osk", "", StringComparison.OrdinalIgnoreCase); + string archiveName = item.Name.Replace(@".osk", string.Empty, StringComparison.OrdinalIgnoreCase); bool isImport = item.ID == 0; @@ -177,7 +177,7 @@ namespace osu.Game.Skinning // In an ideal world, skin.ini would be the only source of metadata, but a lot of skin creators and users don't update it when making modifications. // In both of these cases, the expectation from the user is that the filename or folder name is displayed somewhere to identify the skin. if (archiveName != item.Name) - item.Name = $"{item.Name} [{archiveName}]"; + item.Name = @$"{item.Name} [{archiveName}]"; } // By this point, the metadata in SkinInfo will be correct. @@ -191,8 +191,8 @@ namespace osu.Game.Skinning private void updateSkinIniMetadata(SkinInfo item) { - string nameLine = $"Name: {item.Name}"; - string authorLine = $"Author: {item.Creator}"; + string nameLine = @$"Name: {item.Name}"; + string authorLine = @$"Author: {item.Creator}"; var existingFile = item.Files.SingleOrDefault(f => f.Filename.Equals(@"skin.ini", StringComparison.OrdinalIgnoreCase)); @@ -210,12 +210,12 @@ namespace osu.Game.Skinning while ((line = sr.ReadLine()) != null) { - if (line.StartsWith("Name:", StringComparison.Ordinal)) + if (line.StartsWith(@"Name:", StringComparison.Ordinal)) { outputLines.Add(nameLine); addedName = true; } - else if (line.StartsWith("Author:", StringComparison.Ordinal)) + else if (line.StartsWith(@"Author:", StringComparison.Ordinal)) { outputLines.Add(authorLine); addedAuthor = true; @@ -229,7 +229,7 @@ namespace osu.Game.Skinning { outputLines.AddRange(new[] { - "[General]", + @"[General]", nameLine, authorLine, }); @@ -252,13 +252,13 @@ namespace osu.Game.Skinning { using (var sw = new StreamWriter(stream, Encoding.UTF8, 1024, true)) { - sw.WriteLine("[General]"); + sw.WriteLine(@"[General]"); sw.WriteLine(nameLine); sw.WriteLine(authorLine); - sw.WriteLine("Version: latest"); + sw.WriteLine(@"Version: latest"); } - AddFile(item, stream, "skin.ini"); + AddFile(item, stream, @"skin.ini"); } } } @@ -295,7 +295,7 @@ namespace osu.Game.Skinning // if the user is attempting to save one of the default skin implementations, create a copy first. CurrentSkinInfo.Value = Import(new SkinInfo { - Name = skin.SkinInfo.Name + " (modified)", + Name = skin.SkinInfo.Name + @" (modified)", Creator = skin.SkinInfo.Creator, InstantiationInfo = skin.SkinInfo.InstantiationInfo, }).Result.Value; @@ -312,7 +312,7 @@ namespace osu.Game.Skinning using (var streamContent = new MemoryStream(Encoding.UTF8.GetBytes(json))) { - string filename = $"{drawableInfo.Key}.json"; + string filename = @$"{drawableInfo.Key}.json"; var oldFile = skin.SkinInfo.Files.FirstOrDefault(f => f.Filename == filename); From 10a9266956104120a6b17e36d26f142671a6fc09 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 1 Nov 2021 14:33:24 +0900 Subject: [PATCH 220/260] Fix beatmap creator showing twice in tournament map pool panels --- osu.Game.Tournament/Components/TournamentBeatmapPanel.cs | 2 +- osu.Game/Beatmaps/BeatmapInfoExtensions.cs | 4 ++-- osu.Game/Beatmaps/BeatmapMetadataInfoExtensions.cs | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs index ed107c3a83..f3550f7465 100644 --- a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs +++ b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs @@ -76,7 +76,7 @@ namespace osu.Game.Tournament.Components { new TournamentSpriteText { - Text = Beatmap.GetDisplayTitleRomanisable(false), + Text = Beatmap.GetDisplayTitleRomanisable(false, false), Font = OsuFont.Torus.With(weight: FontWeight.Bold), }, new FillFlowContainer diff --git a/osu.Game/Beatmaps/BeatmapInfoExtensions.cs b/osu.Game/Beatmaps/BeatmapInfoExtensions.cs index c35370d572..2d69015933 100644 --- a/osu.Game/Beatmaps/BeatmapInfoExtensions.cs +++ b/osu.Game/Beatmaps/BeatmapInfoExtensions.cs @@ -16,9 +16,9 @@ namespace osu.Game.Beatmaps /// /// A user-presentable display title representing this beatmap, with localisation handling for potentially romanisable fields. /// - public static RomanisableString GetDisplayTitleRomanisable(this IBeatmapInfo beatmapInfo, bool includeDifficultyName = true) + public static RomanisableString GetDisplayTitleRomanisable(this IBeatmapInfo beatmapInfo, bool includeDifficultyName = true, bool includeCreator = true) { - var metadata = getClosestMetadata(beatmapInfo).GetDisplayTitleRomanisable(); + var metadata = getClosestMetadata(beatmapInfo).GetDisplayTitleRomanisable(includeCreator); if (includeDifficultyName) { diff --git a/osu.Game/Beatmaps/BeatmapMetadataInfoExtensions.cs b/osu.Game/Beatmaps/BeatmapMetadataInfoExtensions.cs index 732b76e967..fcaad17059 100644 --- a/osu.Game/Beatmaps/BeatmapMetadataInfoExtensions.cs +++ b/osu.Game/Beatmaps/BeatmapMetadataInfoExtensions.cs @@ -34,9 +34,9 @@ namespace osu.Game.Beatmaps /// /// A user-presentable display title representing this beatmap, with localisation handling for potentially romanisable fields. /// - public static RomanisableString GetDisplayTitleRomanisable(this IBeatmapMetadataInfo metadataInfo) + public static RomanisableString GetDisplayTitleRomanisable(this IBeatmapMetadataInfo metadataInfo, bool includeCreator = true) { - string author = string.IsNullOrEmpty(metadataInfo.Author) ? string.Empty : $"({metadataInfo.Author})"; + string author = !includeCreator || string.IsNullOrEmpty(metadataInfo.Author) ? string.Empty : $"({metadataInfo.Author})"; string artistUnicode = string.IsNullOrEmpty(metadataInfo.ArtistUnicode) ? metadataInfo.Artist : metadataInfo.ArtistUnicode; string titleUnicode = string.IsNullOrEmpty(metadataInfo.TitleUnicode) ? metadataInfo.Title : metadataInfo.TitleUnicode; From 21e0074d3bf2f23476012ff1a83568cf1148a9fd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 1 Nov 2021 14:43:32 +0900 Subject: [PATCH 221/260] Add missing `AddInternal` call for `BeatmapSetHeaderContent`'s download tracker --- osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs b/osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs index 97cd17313e..1bfa7d1c47 100644 --- a/osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs +++ b/osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs @@ -249,6 +249,7 @@ namespace osu.Game.Overlays.BeatmapSet { downloadTracker = new BeatmapDownloadTracker(setInfo.NewValue); downloadTracker.State.BindValueChanged(_ => updateDownloadButtons()); + AddInternal(downloadTracker); fadeContent.FadeIn(500, Easing.OutQuint); From 123b10f647d379195d4a702c56209fa7eae9a3e2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 1 Nov 2021 15:10:32 +0900 Subject: [PATCH 222/260] Fix playlist item download button being clickable when it shouldn't --- .../OnlinePlay/DrawableRoomPlaylistItem.cs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs index c946bf12fd..69ab7225ac 100644 --- a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs +++ b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs @@ -248,10 +248,7 @@ namespace osu.Game.Screens.OnlinePlay protected virtual IEnumerable CreateButtons() => new Drawable[] { - new PlaylistDownloadButton(Item) - { - Size = new Vector2(50, 30) - }, + new PlaylistDownloadButton(Item), new PlaylistRemoveButton { Size = new Vector2(30, 30), @@ -292,10 +289,14 @@ namespace osu.Game.Screens.OnlinePlay // required for download tracking, as this button hides itself. can probably be removed with a bit of consideration. public override bool IsPresent => true; + private const float width = 50; + public PlaylistDownloadButton(PlaylistItem playlistItem) : base(playlistItem.Beatmap.Value.BeatmapSet) { this.playlistItem = playlistItem; + + Size = new Vector2(width, 30); Alpha = 0; } @@ -316,12 +317,16 @@ namespace osu.Game.Screens.OnlinePlay if (beatmapManager.QueryBeatmap(b => b.MD5Hash == playlistItem.Beatmap.Value.MD5Hash) == null) State.Value = DownloadState.NotDownloaded; else - this.FadeTo(0, 500); + { + this.FadeTo(0, 500) + .ResizeWidthTo(0, 500, Easing.OutQuint); + } break; default: - this.FadeTo(1, 500); + this.ResizeWidthTo(width, 500, Easing.OutQuint) + .FadeTo(1, 500); break; } } From 227963047a681aea23ccadb0307065ab6be0e6b7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 1 Nov 2021 15:15:41 +0900 Subject: [PATCH 223/260] Fix hash validity check function returning incorrect results after soft deleting a beatmap The soft-deleted one would potentially match before the newly imported copy, resulting in a false-positive hash mismatch. --- osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs b/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs index 88793c7cfc..6cd735af23 100644 --- a/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs +++ b/osu.Game/Online/Rooms/OnlinePlayBeatmapAvailabilityTracker.cs @@ -109,8 +109,7 @@ namespace osu.Game.Online.Rooms int onlineId = SelectedItem.Value.Beatmap.Value.OnlineID; string checksum = SelectedItem.Value.Beatmap.Value.MD5Hash; - var beatmap = beatmapManager.QueryBeatmap(b => b.OnlineBeatmapID == onlineId && b.MD5Hash == checksum); - return beatmap?.BeatmapSet.DeletePending == false; + return beatmapManager.QueryBeatmap(b => b.OnlineBeatmapID == onlineId && b.MD5Hash == checksum && !b.BeatmapSet.DeletePending) != null; } } } From 94dce3f92acf5ff156afd7cefe0b3ad609e8f2ed Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 1 Nov 2021 15:43:19 +0900 Subject: [PATCH 224/260] Remove whitespace --- osu.Game/Online/API/Requests/Responses/APIScoreWithPosition.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Online/API/Requests/Responses/APIScoreWithPosition.cs b/osu.Game/Online/API/Requests/Responses/APIScoreWithPosition.cs index a0fc549d98..48b7134901 100644 --- a/osu.Game/Online/API/Requests/Responses/APIScoreWithPosition.cs +++ b/osu.Game/Online/API/Requests/Responses/APIScoreWithPosition.cs @@ -17,7 +17,6 @@ namespace osu.Game.Online.API.Requests.Responses public APIScoreInfo Score; public ScoreInfo CreateScoreInfo(RulesetStore rulesets, BeatmapInfo beatmap = null) - { var score = Score.CreateScoreInfo(rulesets, beatmap); score.Position = Position; From 722e0d50bb9c2602c82c81b182c9dca4e51373ee Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 1 Nov 2021 15:54:39 +0900 Subject: [PATCH 225/260] Fix `BeatmapInfo` not being correctly populated in `CreateScoreInfo` call --- osu.Game/Online/API/Requests/Responses/APIScoreInfo.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Online/API/Requests/Responses/APIScoreInfo.cs b/osu.Game/Online/API/Requests/Responses/APIScoreInfo.cs index 3d82f91fb5..1b66a1dcc3 100644 --- a/osu.Game/Online/API/Requests/Responses/APIScoreInfo.cs +++ b/osu.Game/Online/API/Requests/Responses/APIScoreInfo.cs @@ -92,6 +92,7 @@ namespace osu.Game.Online.API.Requests.Responses { TotalScore = TotalScore, MaxCombo = MaxCombo, + BeatmapInfo = Beatmap.ToBeatmapInfo(rulesets), User = User, Accuracy = Accuracy, OnlineScoreID = OnlineID, From 63c9512ab11a722ffe0849d4de279797ae011c94 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 1 Nov 2021 16:02:00 +0900 Subject: [PATCH 226/260] Update dashboard components to use `APIBeatmapSet` --- .../TestSceneDashboardBeatmapListing.cs | 108 +++++++----------- .../Dashboard/Home/DashboardBeatmapListing.cs | 8 +- .../Dashboard/Home/DashboardBeatmapPanel.cs | 20 ++-- .../Home/DashboardNewBeatmapPanel.cs | 8 +- .../Home/DashboardPopularBeatmapPanel.cs | 8 +- .../Dashboard/Home/DrawableBeatmapList.cs | 12 +- .../Dashboard/Home/DrawableNewBeatmapList.cs | 8 +- .../Home/DrawablePopularBeatmapList.cs | 8 +- 8 files changed, 78 insertions(+), 102 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneDashboardBeatmapListing.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneDashboardBeatmapListing.cs index 6727c7560b..06c64a566e 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneDashboardBeatmapListing.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneDashboardBeatmapListing.cs @@ -56,95 +56,71 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("Set width to 300", () => content.ResizeWidthTo(300, 500)); } - private static readonly List new_beatmaps = new List + private static readonly List new_beatmaps = new List { - new BeatmapSetInfo + new APIBeatmapSet { - Metadata = new BeatmapMetadata + Title = "Very Long Title (TV size) [TATOE]", + Artist = "This artist has a really long name how is this possible", + Author = new User { - Title = "Very Long Title (TV size) [TATOE]", - Artist = "This artist has a really long name how is this possible", - Author = new User - { - Username = "author", - Id = 100 - } + Username = "author", + Id = 100 }, - OnlineInfo = new APIBeatmapSet + Covers = new BeatmapSetOnlineCovers { - Covers = new BeatmapSetOnlineCovers - { - Cover = "https://assets.ppy.sh/beatmaps/1189904/covers/cover.jpg?1595456608", - }, - Ranked = DateTimeOffset.Now - } + Cover = "https://assets.ppy.sh/beatmaps/1189904/covers/cover.jpg?1595456608", + }, + Ranked = DateTimeOffset.Now }, - new BeatmapSetInfo + new APIBeatmapSet { - Metadata = new BeatmapMetadata + Title = "Very Long Title (TV size) [TATOE]", + Artist = "This artist has a really long name how is this possible", + Author = new User { - Title = "Very Long Title (TV size) [TATOE]", - Artist = "This artist has a really long name how is this possible", - Author = new User - { - Username = "author", - Id = 100 - } + Username = "author", + Id = 100 }, - OnlineInfo = new APIBeatmapSet + Covers = new BeatmapSetOnlineCovers { - Covers = new BeatmapSetOnlineCovers - { - Cover = "https://assets.ppy.sh/beatmaps/1189904/covers/cover.jpg?1595456608", - }, - Ranked = DateTimeOffset.MinValue - } + Cover = "https://assets.ppy.sh/beatmaps/1189904/covers/cover.jpg?1595456608", + }, + Ranked = DateTimeOffset.Now } }; - private static readonly List popular_beatmaps = new List + private static readonly List popular_beatmaps = new List { - new BeatmapSetInfo + new APIBeatmapSet { - Metadata = new BeatmapMetadata + Title = "Very Long Title (TV size) [TATOE]", + Artist = "This artist has a really long name how is this possible", + Author = new User { - Title = "Title", - Artist = "Artist", - Author = new User - { - Username = "author", - Id = 100 - } + Username = "author", + Id = 100 }, - OnlineInfo = new APIBeatmapSet + Covers = new BeatmapSetOnlineCovers { - Covers = new BeatmapSetOnlineCovers - { - Cover = "https://assets.ppy.sh/beatmaps/1079428/covers/cover.jpg?1595295586", - }, - FavouriteCount = 100 - } + Cover = "https://assets.ppy.sh/beatmaps/1189904/covers/cover.jpg?1595456608", + }, + Ranked = DateTimeOffset.Now }, - new BeatmapSetInfo + new APIBeatmapSet { - Metadata = new BeatmapMetadata + Title = "Very Long Title (TV size) [TATOE]", + Artist = "This artist has a really long name how is this possible", + Author = new User { - Title = "Title 2", - Artist = "Artist 2", - Author = new User - { - Username = "someone", - Id = 100 - } + Username = "author", + Id = 100 }, - OnlineInfo = new APIBeatmapSet + Covers = new BeatmapSetOnlineCovers { - Covers = new BeatmapSetOnlineCovers - { - Cover = "https://assets.ppy.sh/beatmaps/1079428/covers/cover.jpg?1595295586", - }, - FavouriteCount = 10 - } + Cover = "https://assets.ppy.sh/beatmaps/1189904/covers/cover.jpg?1595456608", + }, + Ranked = DateTimeOffset.Now } }; } diff --git a/osu.Game/Overlays/Dashboard/Home/DashboardBeatmapListing.cs b/osu.Game/Overlays/Dashboard/Home/DashboardBeatmapListing.cs index 4d96825353..c781aa0cfb 100644 --- a/osu.Game/Overlays/Dashboard/Home/DashboardBeatmapListing.cs +++ b/osu.Game/Overlays/Dashboard/Home/DashboardBeatmapListing.cs @@ -5,17 +5,17 @@ using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Game.Beatmaps; +using osu.Game.Online.API.Requests.Responses; using osuTK; namespace osu.Game.Overlays.Dashboard.Home { public class DashboardBeatmapListing : CompositeDrawable { - private readonly List newBeatmaps; - private readonly List popularBeatmaps; + private readonly List newBeatmaps; + private readonly List popularBeatmaps; - public DashboardBeatmapListing(List newBeatmaps, List popularBeatmaps) + public DashboardBeatmapListing(List newBeatmaps, List popularBeatmaps) { this.newBeatmaps = newBeatmaps; this.popularBeatmaps = popularBeatmaps; diff --git a/osu.Game/Overlays/Dashboard/Home/DashboardBeatmapPanel.cs b/osu.Game/Overlays/Dashboard/Home/DashboardBeatmapPanel.cs index 50186def37..9276e6ce80 100644 --- a/osu.Game/Overlays/Dashboard/Home/DashboardBeatmapPanel.cs +++ b/osu.Game/Overlays/Dashboard/Home/DashboardBeatmapPanel.cs @@ -7,11 +7,11 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; -using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; +using osu.Game.Online.API.Requests.Responses; using osuTK; namespace osu.Game.Overlays.Dashboard.Home @@ -24,14 +24,14 @@ namespace osu.Game.Overlays.Dashboard.Home [Resolved(canBeNull: true)] private BeatmapSetOverlay beatmapOverlay { get; set; } - protected readonly BeatmapSetInfo SetInfo; + protected readonly APIBeatmapSet BeatmapSet; private Box hoverBackground; private SpriteIcon chevron; - protected DashboardBeatmapPanel(BeatmapSetInfo setInfo) + protected DashboardBeatmapPanel(APIBeatmapSet beatmapSet) { - SetInfo = setInfo; + BeatmapSet = beatmapSet; } [BackgroundDependencyLoader] @@ -82,7 +82,7 @@ namespace osu.Game.Overlays.Dashboard.Home RelativeSizeAxes = Axes.Both, Anchor = Anchor.Centre, Origin = Anchor.Centre, - OnlineInfo = SetInfo.OnlineInfo + OnlineInfo = BeatmapSet } }, new Container @@ -103,14 +103,14 @@ namespace osu.Game.Overlays.Dashboard.Home RelativeSizeAxes = Axes.X, Truncate = true, Font = OsuFont.GetFont(weight: FontWeight.Regular), - Text = SetInfo.Metadata.Title + Text = BeatmapSet.Title }, new OsuSpriteText { RelativeSizeAxes = Axes.X, Truncate = true, Font = OsuFont.GetFont(size: 12, weight: FontWeight.Regular), - Text = SetInfo.Metadata.Artist + Text = BeatmapSet.Artist }, new LinkFlowContainer(f => f.Font = OsuFont.GetFont(size: 10, weight: FontWeight.Regular)) { @@ -121,7 +121,7 @@ namespace osu.Game.Overlays.Dashboard.Home }.With(c => { c.AddText("by"); - c.AddUserLink(SetInfo.Metadata.Author); + c.AddUserLink(BeatmapSet.Author); c.AddArbitraryDrawable(CreateInfo()); }) } @@ -143,8 +143,8 @@ namespace osu.Game.Overlays.Dashboard.Home Action = () => { - if (SetInfo.OnlineBeatmapSetID.HasValue) - beatmapOverlay?.FetchAndShowBeatmapSet(SetInfo.OnlineBeatmapSetID.Value); + if (BeatmapSet.OnlineID > 0) + beatmapOverlay?.FetchAndShowBeatmapSet(BeatmapSet.OnlineID); }; } diff --git a/osu.Game/Overlays/Dashboard/Home/DashboardNewBeatmapPanel.cs b/osu.Game/Overlays/Dashboard/Home/DashboardNewBeatmapPanel.cs index b212eaf20a..249b355be3 100644 --- a/osu.Game/Overlays/Dashboard/Home/DashboardNewBeatmapPanel.cs +++ b/osu.Game/Overlays/Dashboard/Home/DashboardNewBeatmapPanel.cs @@ -3,19 +3,19 @@ using System; using osu.Framework.Graphics; -using osu.Game.Beatmaps; using osu.Game.Graphics; +using osu.Game.Online.API.Requests.Responses; namespace osu.Game.Overlays.Dashboard.Home { public class DashboardNewBeatmapPanel : DashboardBeatmapPanel { - public DashboardNewBeatmapPanel(BeatmapSetInfo setInfo) - : base(setInfo) + public DashboardNewBeatmapPanel(APIBeatmapSet beatmapSet) + : base(beatmapSet) { } - protected override Drawable CreateInfo() => new DrawableDate(SetInfo.OnlineInfo.Ranked ?? DateTimeOffset.Now, 10, false) + protected override Drawable CreateInfo() => new DrawableDate(BeatmapSet.Ranked ?? DateTimeOffset.Now, 10, false) { Colour = ColourProvider.Foreground1 }; diff --git a/osu.Game/Overlays/Dashboard/Home/DashboardPopularBeatmapPanel.cs b/osu.Game/Overlays/Dashboard/Home/DashboardPopularBeatmapPanel.cs index e9066c0657..4e50cce890 100644 --- a/osu.Game/Overlays/Dashboard/Home/DashboardPopularBeatmapPanel.cs +++ b/osu.Game/Overlays/Dashboard/Home/DashboardPopularBeatmapPanel.cs @@ -4,17 +4,17 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; -using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; +using osu.Game.Online.API.Requests.Responses; using osuTK; namespace osu.Game.Overlays.Dashboard.Home { public class DashboardPopularBeatmapPanel : DashboardBeatmapPanel { - public DashboardPopularBeatmapPanel(BeatmapSetInfo setInfo) - : base(setInfo) + public DashboardPopularBeatmapPanel(APIBeatmapSet beatmapSet) + : base(beatmapSet) { } @@ -34,7 +34,7 @@ namespace osu.Game.Overlays.Dashboard.Home new OsuSpriteText { Font = OsuFont.GetFont(size: 10, weight: FontWeight.Regular), - Text = SetInfo.OnlineInfo.FavouriteCount.ToString() + Text = BeatmapSet.FavouriteCount.ToString() } } }; diff --git a/osu.Game/Overlays/Dashboard/Home/DrawableBeatmapList.cs b/osu.Game/Overlays/Dashboard/Home/DrawableBeatmapList.cs index f6535b7db3..c73cc828e2 100644 --- a/osu.Game/Overlays/Dashboard/Home/DrawableBeatmapList.cs +++ b/osu.Game/Overlays/Dashboard/Home/DrawableBeatmapList.cs @@ -6,20 +6,20 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; +using osu.Game.Online.API.Requests.Responses; using osuTK; namespace osu.Game.Overlays.Dashboard.Home { public abstract class DrawableBeatmapList : CompositeDrawable { - private readonly List beatmaps; + private readonly List beatmapSets; - protected DrawableBeatmapList(List beatmaps) + protected DrawableBeatmapList(List beatmapSets) { - this.beatmaps = beatmaps; + this.beatmapSets = beatmapSets; } [BackgroundDependencyLoader] @@ -46,11 +46,11 @@ namespace osu.Game.Overlays.Dashboard.Home } }; - flow.AddRange(beatmaps.Select(CreateBeatmapPanel)); + flow.AddRange(beatmapSets.Select(CreateBeatmapPanel)); } protected abstract string Title { get; } - protected abstract DashboardBeatmapPanel CreateBeatmapPanel(BeatmapSetInfo setInfo); + protected abstract DashboardBeatmapPanel CreateBeatmapPanel(APIBeatmapSet beatmapSet); } } diff --git a/osu.Game/Overlays/Dashboard/Home/DrawableNewBeatmapList.cs b/osu.Game/Overlays/Dashboard/Home/DrawableNewBeatmapList.cs index 75e8ca336d..714e07a7ed 100644 --- a/osu.Game/Overlays/Dashboard/Home/DrawableNewBeatmapList.cs +++ b/osu.Game/Overlays/Dashboard/Home/DrawableNewBeatmapList.cs @@ -2,18 +2,18 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; -using osu.Game.Beatmaps; +using osu.Game.Online.API.Requests.Responses; namespace osu.Game.Overlays.Dashboard.Home { public class DrawableNewBeatmapList : DrawableBeatmapList { - public DrawableNewBeatmapList(List beatmaps) - : base(beatmaps) + public DrawableNewBeatmapList(List beatmapSets) + : base(beatmapSets) { } - protected override DashboardBeatmapPanel CreateBeatmapPanel(BeatmapSetInfo setInfo) => new DashboardNewBeatmapPanel(setInfo); + protected override DashboardBeatmapPanel CreateBeatmapPanel(APIBeatmapSet beatmapSet) => new DashboardNewBeatmapPanel(beatmapSet); protected override string Title => "New Ranked Beatmaps"; } diff --git a/osu.Game/Overlays/Dashboard/Home/DrawablePopularBeatmapList.cs b/osu.Game/Overlays/Dashboard/Home/DrawablePopularBeatmapList.cs index 90bd00008c..48b100b04e 100644 --- a/osu.Game/Overlays/Dashboard/Home/DrawablePopularBeatmapList.cs +++ b/osu.Game/Overlays/Dashboard/Home/DrawablePopularBeatmapList.cs @@ -2,18 +2,18 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; -using osu.Game.Beatmaps; +using osu.Game.Online.API.Requests.Responses; namespace osu.Game.Overlays.Dashboard.Home { public class DrawablePopularBeatmapList : DrawableBeatmapList { - public DrawablePopularBeatmapList(List beatmaps) - : base(beatmaps) + public DrawablePopularBeatmapList(List beatmapSets) + : base(beatmapSets) { } - protected override DashboardBeatmapPanel CreateBeatmapPanel(BeatmapSetInfo setInfo) => new DashboardPopularBeatmapPanel(setInfo); + protected override DashboardBeatmapPanel CreateBeatmapPanel(APIBeatmapSet beatmapSet) => new DashboardPopularBeatmapPanel(beatmapSet); protected override string Title => "Popular Beatmaps"; } From 90a0a5e0b7383488796634e64b7099cd60689f9a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 1 Nov 2021 17:12:15 +0900 Subject: [PATCH 227/260] Fix attempting to set properties of null `BeatmapSet`s in tests --- .../Visual/Online/TestSceneUserProfileScores.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserProfileScores.cs b/osu.Game.Tests/Visual/Online/TestSceneUserProfileScores.cs index fac350ea57..9c2cc13416 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneUserProfileScores.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneUserProfileScores.cs @@ -25,7 +25,7 @@ namespace osu.Game.Tests.Visual.Online Rank = ScoreRank.SH, Beatmap = new APIBeatmap { - BeatmapSet = + BeatmapSet = new APIBeatmapSet { Title = "JUSTadICE (TV Size)", Artist = "Oomori Seiko", @@ -48,7 +48,7 @@ namespace osu.Game.Tests.Visual.Online Rank = ScoreRank.A, Beatmap = new APIBeatmap { - BeatmapSet = + BeatmapSet = new APIBeatmapSet { Title = "Triumph & Regret", Artist = "typeMARS", @@ -70,7 +70,7 @@ namespace osu.Game.Tests.Visual.Online Rank = ScoreRank.S, Beatmap = new APIBeatmap { - BeatmapSet = + BeatmapSet = new APIBeatmapSet { Title = "Idolize", Artist = "Creo", @@ -86,7 +86,7 @@ namespace osu.Game.Tests.Visual.Online Rank = ScoreRank.B, Beatmap = new APIBeatmap { - BeatmapSet = + BeatmapSet = new APIBeatmapSet { Title = "C18H27NO3(extend)", Artist = "Team Grimoire", From cd4681496a9ac088d037a9f4a9932d8a20e205a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 1 Nov 2021 09:35:02 +0100 Subject: [PATCH 228/260] Fix incorrect state transition in score download tracker --- osu.Game/Online/ScoreDownloadTracker.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/osu.Game/Online/ScoreDownloadTracker.cs b/osu.Game/Online/ScoreDownloadTracker.cs index 8222a5382c..675dbf608c 100644 --- a/osu.Game/Online/ScoreDownloadTracker.cs +++ b/osu.Game/Online/ScoreDownloadTracker.cs @@ -122,10 +122,8 @@ namespace osu.Game.Online { Schedule(() => { - if (!checkEquality(item, TrackedItem)) - return; - - UpdateState(DownloadState.NotDownloaded); + if (checkEquality(item, TrackedItem)) + UpdateState(DownloadState.LocallyAvailable); }); } } From 6a44cf3ff1181311d6acffd769793838505a3f6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 1 Nov 2021 09:14:02 +0100 Subject: [PATCH 229/260] Add missing beatmap spec in test scene --- osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs index 288da71ba3..311c3ddc03 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs @@ -13,6 +13,7 @@ using osu.Framework.Bindables; using osu.Framework.Testing; using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets; +using osu.Game.Rulesets.Osu; using osu.Game.Screens.Ranking; using osuTK.Input; @@ -136,6 +137,7 @@ namespace osu.Game.Tests.Visual.Gameplay { OnlineID = 2553163309, OnlineRulesetID = 0, + Beatmap = CreateAPIBeatmapSet(new OsuRuleset().RulesetInfo).Beatmaps.First(), HasReplay = replayAvailable, User = new User { From 793c384ecd29dd07f4c334329fdaf7860cd76879 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 1 Nov 2021 12:02:37 +0100 Subject: [PATCH 230/260] Remove unnecessary null-conditional access --- osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs b/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs index c43ac74714..b152375062 100644 --- a/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs +++ b/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs @@ -166,7 +166,7 @@ namespace osu.Game.Overlays.BeatmapSet if (BeatmapSet != null) { Difficulties.ChildrenEnumerable = BeatmapSet.Beatmaps - .Where(b => b.Ruleset?.OnlineID == ruleset.Value?.OnlineID) + .Where(b => b.Ruleset.OnlineID == ruleset.Value?.OnlineID) .OrderBy(b => b.StarRating) .Select(b => new DifficultySelectorButton(b) { From 0d65493b5689c72480623957754492101680fa41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 1 Nov 2021 12:12:21 +0100 Subject: [PATCH 231/260] Mark test usages of `Ruleset.CreateInstance()` as returning non-null --- .../Visual/SongSelect/TestSceneAdvancedStats.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneAdvancedStats.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneAdvancedStats.cs index 6a2b577fd3..07e68ef509 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneAdvancedStats.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneAdvancedStats.cs @@ -5,6 +5,7 @@ using System; using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics.Sprites; using osu.Framework.Testing; using osu.Game.Beatmaps; @@ -88,7 +89,7 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep("select EZ mod", () => { - var ruleset = advancedStats.BeatmapInfo.Ruleset.CreateInstance(); + var ruleset = advancedStats.BeatmapInfo.Ruleset.CreateInstance().AsNonNull(); SelectedMods.Value = new[] { ruleset.CreateMod() }; }); @@ -105,7 +106,7 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep("select HR mod", () => { - var ruleset = advancedStats.BeatmapInfo.Ruleset.CreateInstance(); + var ruleset = advancedStats.BeatmapInfo.Ruleset.CreateInstance().AsNonNull(); SelectedMods.Value = new[] { ruleset.CreateMod() }; }); @@ -122,7 +123,7 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep("select unchanged Difficulty Adjust mod", () => { - var ruleset = advancedStats.BeatmapInfo.Ruleset.CreateInstance(); + var ruleset = advancedStats.BeatmapInfo.Ruleset.CreateInstance().AsNonNull(); var difficultyAdjustMod = ruleset.CreateMod(); difficultyAdjustMod.ReadFromDifficulty(advancedStats.BeatmapInfo.Difficulty); SelectedMods.Value = new[] { difficultyAdjustMod }; @@ -141,7 +142,7 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep("select changed Difficulty Adjust mod", () => { - var ruleset = advancedStats.BeatmapInfo.Ruleset.CreateInstance(); + var ruleset = advancedStats.BeatmapInfo.Ruleset.CreateInstance().AsNonNull(); var difficultyAdjustMod = ruleset.CreateMod(); var originalDifficulty = advancedStats.BeatmapInfo.Difficulty; From 3a5aed118224ccd57a559026e798774bcbd78dec Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 1 Nov 2021 22:58:55 +0900 Subject: [PATCH 232/260] Ensure `APIScoreInfo.Mods` is non-null --- osu.Game/Online/API/Requests/Responses/APIScoreInfo.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/API/Requests/Responses/APIScoreInfo.cs b/osu.Game/Online/API/Requests/Responses/APIScoreInfo.cs index 2b7183512d..2e41723f34 100644 --- a/osu.Game/Online/API/Requests/Responses/APIScoreInfo.cs +++ b/osu.Game/Online/API/Requests/Responses/APIScoreInfo.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using JetBrains.Annotations; using Newtonsoft.Json; using Newtonsoft.Json.Converters; using osu.Game.Beatmaps; @@ -67,7 +68,8 @@ namespace osu.Game.Online.API.Requests.Responses [JsonProperty(@"mods")] private string[] mods { set => Mods = value.Select(acronym => new APIMod { Acronym = acronym }); } - public IEnumerable Mods { get; set; } + [NotNull] + public IEnumerable Mods { get; set; } = Array.Empty(); [JsonProperty("rank")] [JsonConverter(typeof(StringEnumConverter))] @@ -85,7 +87,7 @@ namespace osu.Game.Online.API.Requests.Responses var rulesetInstance = ruleset.CreateInstance(); - var modInstances = Mods != null ? Mods.Select(apiMod => rulesetInstance.CreateModFromAcronym(apiMod.Acronym)).Where(m => m != null).ToArray() : Array.Empty(); + var modInstances = Mods.Select(apiMod => rulesetInstance.CreateModFromAcronym(apiMod.Acronym)).Where(m => m != null).ToArray(); // all API scores provided by this class are considered to be legacy. modInstances = modInstances.Append(rulesetInstance.CreateMod()).ToArray(); From 7747fd5810dfcd53fc0ab02913bd277bea5355e0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Nov 2021 17:01:54 +0000 Subject: [PATCH 233/260] Bump Microsoft.NET.Test.Sdk from 16.11.0 to 17.0.0 Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 16.11.0 to 17.0.0. - [Release notes](https://github.com/microsoft/vstest/releases) - [Commits](https://github.com/microsoft/vstest/compare/v16.11.0...v17.0.0) --- updated-dependencies: - dependency-name: Microsoft.NET.Test.Sdk dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .../osu.Game.Rulesets.EmptyFreeform.Tests.csproj | 2 +- .../osu.Game.Rulesets.Pippidon.Tests.csproj | 2 +- .../osu.Game.Rulesets.EmptyScrolling.Tests.csproj | 2 +- .../osu.Game.Rulesets.Pippidon.Tests.csproj | 2 +- .../osu.Game.Rulesets.Catch.Tests.csproj | 2 +- .../osu.Game.Rulesets.Mania.Tests.csproj | 2 +- osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj | 2 +- .../osu.Game.Rulesets.Taiko.Tests.csproj | 2 +- osu.Game.Tests/osu.Game.Tests.csproj | 2 +- osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform.Tests/osu.Game.Rulesets.EmptyFreeform.Tests.csproj b/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform.Tests/osu.Game.Rulesets.EmptyFreeform.Tests.csproj index e28053d0ca..e9b92be0c3 100644 --- a/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform.Tests/osu.Game.Rulesets.EmptyFreeform.Tests.csproj +++ b/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform.Tests/osu.Game.Rulesets.EmptyFreeform.Tests.csproj @@ -10,7 +10,7 @@ - + diff --git a/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj b/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj index 027bd0b7e2..e145dd7b69 100644 --- a/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj +++ b/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj @@ -10,7 +10,7 @@ - + diff --git a/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling.Tests/osu.Game.Rulesets.EmptyScrolling.Tests.csproj b/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling.Tests/osu.Game.Rulesets.EmptyScrolling.Tests.csproj index e2c715d385..a301432a6c 100644 --- a/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling.Tests/osu.Game.Rulesets.EmptyScrolling.Tests.csproj +++ b/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling.Tests/osu.Game.Rulesets.EmptyScrolling.Tests.csproj @@ -10,7 +10,7 @@ - + diff --git a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj index 027bd0b7e2..e145dd7b69 100644 --- a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj +++ b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj @@ -10,7 +10,7 @@ - + diff --git a/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj b/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj index 6457ec92da..4c8b9b2b08 100644 --- a/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj +++ b/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj @@ -2,7 +2,7 @@ - + diff --git a/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj b/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj index 674a22df98..fad39ef9d6 100644 --- a/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj +++ b/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj @@ -2,7 +2,7 @@ - + diff --git a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj index f5f1159542..66f4ad3d3f 100644 --- a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj +++ b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj @@ -2,7 +2,7 @@ - + diff --git a/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj b/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj index b9b295767e..568e35c221 100644 --- a/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj +++ b/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj @@ -2,7 +2,7 @@ - + diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index cd56cb51ae..57815d9273 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -3,7 +3,7 @@ - + diff --git a/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj b/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj index 2673c9ec9f..c0f94d49c7 100644 --- a/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj +++ b/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj @@ -5,7 +5,7 @@ - + From f893556b80025d5dfbf24ca5eff94886af549cb6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Nov 2021 17:02:00 +0000 Subject: [PATCH 234/260] Bump Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson Bumps [Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson](https://github.com/dotnet/aspnetcore) from 5.0.10 to 5.0.11. - [Release notes](https://github.com/dotnet/aspnetcore/releases) - [Commits](https://github.com/dotnet/aspnetcore/compare/v5.0.10...v5.0.11) --- updated-dependencies: - dependency-name: Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- osu.Game/osu.Game.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 8052ab5254..ea85ca5faa 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -25,7 +25,7 @@ - + From f8f4b9d3d328bd197709627471f889222754b2bc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Nov 2021 17:02:04 +0000 Subject: [PATCH 235/260] Bump SharpCompress from 0.29.0 to 0.30.0 Bumps [SharpCompress](https://github.com/adamhathcock/sharpcompress) from 0.29.0 to 0.30.0. - [Release notes](https://github.com/adamhathcock/sharpcompress/releases) - [Commits](https://github.com/adamhathcock/sharpcompress/compare/0.29...0.30) --- updated-dependencies: - dependency-name: SharpCompress dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 8052ab5254..49c00d3679 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -39,7 +39,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index d152cb7066..0baf067a63 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -94,7 +94,7 @@ - + From f215439b4f13899ec2938d9f43e489208e6ab4c5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Nov 2021 17:02:10 +0000 Subject: [PATCH 236/260] Bump Microsoft.AspNetCore.SignalR.Protocols.MessagePack Bumps [Microsoft.AspNetCore.SignalR.Protocols.MessagePack](https://github.com/dotnet/aspnetcore) from 5.0.10 to 5.0.11. - [Release notes](https://github.com/dotnet/aspnetcore/releases) - [Commits](https://github.com/dotnet/aspnetcore/compare/v5.0.10...v5.0.11) --- updated-dependencies: - dependency-name: Microsoft.AspNetCore.SignalR.Protocols.MessagePack dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- osu.Game/osu.Game.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 8052ab5254..4bfcc9ed09 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -24,7 +24,7 @@ - + From e0a940be1de453997f2ff8a60c40ea79a3049791 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Nov 2021 17:02:14 +0000 Subject: [PATCH 237/260] Bump Sentry from 3.9.4 to 3.10.0 Bumps [Sentry](https://github.com/getsentry/sentry-dotnet) from 3.9.4 to 3.10.0. - [Release notes](https://github.com/getsentry/sentry-dotnet/releases) - [Changelog](https://github.com/getsentry/sentry-dotnet/blob/main/CHANGELOG.md) - [Commits](https://github.com/getsentry/sentry-dotnet/compare/3.9.4...3.10.0) --- updated-dependencies: - dependency-name: Sentry dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- osu.Game/osu.Game.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 8052ab5254..b6100e4c73 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -38,7 +38,7 @@ - + From 3b7379b777345bad676d4d477d1ffa111f607edb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Nov 2021 17:02:20 +0000 Subject: [PATCH 238/260] Bump Microsoft.AspNetCore.SignalR.Client from 5.0.10 to 5.0.11 Bumps [Microsoft.AspNetCore.SignalR.Client](https://github.com/dotnet/aspnetcore) from 5.0.10 to 5.0.11. - [Release notes](https://github.com/dotnet/aspnetcore/releases) - [Commits](https://github.com/dotnet/aspnetcore/compare/v5.0.10...v5.0.11) --- updated-dependencies: - dependency-name: Microsoft.AspNetCore.SignalR.Client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- osu.Game/osu.Game.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 8052ab5254..650498bc5f 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -23,7 +23,7 @@ - + From 6e17f92f5583a4713683b0d3308bb6066d40aba1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Nov 2021 17:02:23 +0000 Subject: [PATCH 239/260] Bump Microsoft.CodeAnalysis.BannedApiAnalyzers from 3.3.2 to 3.3.3 Bumps [Microsoft.CodeAnalysis.BannedApiAnalyzers](https://github.com/dotnet/roslyn-analyzers) from 3.3.2 to 3.3.3. - [Release notes](https://github.com/dotnet/roslyn-analyzers/releases) - [Changelog](https://github.com/dotnet/roslyn-analyzers/blob/main/PostReleaseActivities.md) - [Commits](https://github.com/dotnet/roslyn-analyzers/commits) --- updated-dependencies: - dependency-name: Microsoft.CodeAnalysis.BannedApiAnalyzers dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 53ad973e47..894ea25c8b 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -16,7 +16,7 @@ - + From badbbace101ae4fe19bbd6d96e85345e2756a434 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 2 Nov 2021 04:51:54 +0300 Subject: [PATCH 240/260] Add android intent filter for osu! replays --- osu.Android/OsuGameActivity.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Android/OsuGameActivity.cs b/osu.Android/OsuGameActivity.cs index 0bcbfc4baf..fec96c9165 100644 --- a/osu.Android/OsuGameActivity.cs +++ b/osu.Android/OsuGameActivity.cs @@ -20,6 +20,7 @@ namespace osu.Android [Activity(Theme = "@android:style/Theme.NoTitleBar", MainLauncher = true, ScreenOrientation = ScreenOrientation.FullUser, SupportsPictureInPicture = false, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize, HardwareAccelerated = false, LaunchMode = LaunchMode.SingleInstance, Exported = true)] [IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataPathPattern = ".*\\\\.osz", DataHost = "*", DataMimeType = "*/*")] [IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataPathPattern = ".*\\\\.osk", DataHost = "*", DataMimeType = "*/*")] + [IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataPathPattern = ".*\\\\.osr", DataHost = "*", DataMimeType = "*/*")] [IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataMimeType = "application/x-osu-beatmap-archive")] [IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataMimeType = "application/x-osu-skin-archive")] [IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataMimeType = "application/x-osu-replay")] From ab28948744f0114e5053d3b0321f0e37990db7c1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Nov 2021 13:17:21 +0900 Subject: [PATCH 241/260] Default legacy decoder to `General` section, rather than none This is mainly to reach parity with how stable handles such cases for skins specifically. It was brought to our attention after finding a skin with a malformed `\[General]` tag which did not break further parsing of the file on stable, but did on lazer. Note that stable defaults to an "unknown" section for beatmaps, but functionally I can't think of a regression case from changing this to a "general" default. --- osu.Game/Beatmaps/Formats/LegacyDecoder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs index 56525ddb14..f17366a1b3 100644 --- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs @@ -27,7 +27,7 @@ namespace osu.Game.Beatmaps.Formats protected override void ParseStreamInto(LineBufferedReader stream, T output) { - Section section = Section.None; + Section section = Section.General; string line; From 15c770e155c8396c4a4b2413be3acc1362471193 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Nov 2021 14:01:41 +0900 Subject: [PATCH 242/260] Remove no longer used `Section.None` --- osu.Game/Beatmaps/Formats/LegacyDecoder.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs index f17366a1b3..0276abc3ff 100644 --- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs @@ -47,10 +47,7 @@ namespace osu.Game.Beatmaps.Formats if (line.StartsWith('[') && line.EndsWith(']')) { if (!Enum.TryParse(line[1..^1], out section)) - { Logger.Log($"Unknown section \"{line}\" in \"{output}\""); - section = Section.None; - } OnBeginNewSection(section); continue; @@ -148,7 +145,6 @@ namespace osu.Game.Beatmaps.Formats protected enum Section { - None, General, Editor, Metadata, From d4be6fc79761a984f812221a754e29c2afb97651 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Nov 2021 14:04:25 +0900 Subject: [PATCH 243/260] Add test coverage of skin with missing section header --- osu.Game.Tests/Skins/IO/ImportSkinTest.cs | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Skins/IO/ImportSkinTest.cs b/osu.Game.Tests/Skins/IO/ImportSkinTest.cs index c75714032e..ecc9c92025 100644 --- a/osu.Game.Tests/Skins/IO/ImportSkinTest.cs +++ b/osu.Game.Tests/Skins/IO/ImportSkinTest.cs @@ -38,6 +38,15 @@ namespace osu.Game.Tests.Skins.IO assertCorrectMetadata(import1, "test skin [skin]", "skinner", osu); }); + [Test] + public Task TestSingleImportMissingSectionHeader() => runSkinTest(async osu => + { + var import1 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOskWithIni("test skin", "skinner", includeSectionHeader: false), "skin.osk")); + + // When the import filename doesn't match, it should be appended (and update the skin.ini). + assertCorrectMetadata(import1, "test skin [skin]", "skinner", osu); + }); + [Test] public Task TestSingleImportMatchingFilename() => runSkinTest(async osu => { @@ -199,21 +208,23 @@ namespace osu.Game.Tests.Skins.IO return zipStream; } - private MemoryStream createOskWithIni(string name, string author, bool makeUnique = false, string iniFilename = @"skin.ini") + private MemoryStream createOskWithIni(string name, string author, bool makeUnique = false, string iniFilename = @"skin.ini", bool includeSectionHeader = true) { var zipStream = new MemoryStream(); using var zip = ZipArchive.Create(); - zip.AddEntry(iniFilename, generateSkinIni(name, author, makeUnique)); + zip.AddEntry(iniFilename, generateSkinIni(name, author, makeUnique, includeSectionHeader)); zip.SaveTo(zipStream); return zipStream; } - private MemoryStream generateSkinIni(string name, string author, bool makeUnique = true) + private MemoryStream generateSkinIni(string name, string author, bool makeUnique = true, bool includeSectionHeader = true) { var stream = new MemoryStream(); var writer = new StreamWriter(stream); - writer.WriteLine("[General]"); + if (includeSectionHeader) + writer.WriteLine("[General]"); + writer.WriteLine($"Name: {name}"); writer.WriteLine($"Author: {author}"); From 2e66ab453de7e8de39b721f62033badc5ba3a1a4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Nov 2021 13:11:29 +0900 Subject: [PATCH 244/260] Always write automated changes to skin metadata at the end of the file Apart from being cleaner (and allowing removal at the point of export, if that is ever requires), this also gives us a better guarantee that the metadata is going to take effect. By writing the `[General]` section ourselves, we avoid any issues that may be present from a user constructing the file (incorrectly) themselves, such as multiple `Title:` entries or a malformed `[General]` marker. The main goal is to ensure that the name is updated to what we expect it to be. --- osu.Game/Skinning/SkinManager.cs | 52 +++++++++++--------------------- 1 file changed, 17 insertions(+), 35 deletions(-) diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index 76d36ae7d9..8891fe3a06 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -198,48 +198,30 @@ namespace osu.Game.Skinning if (existingFile != null) { - List outputLines = new List(); + List additionalLines = new List(); - bool addedName = false; - bool addedAuthor = false; - - using (var stream = Files.Storage.GetStream(existingFile.FileInfo.StoragePath)) - using (var sr = new StreamReader(stream)) + additionalLines.AddRange(new[] { - string line; - - while ((line = sr.ReadLine()) != null) - { - if (line.StartsWith(@"Name:", StringComparison.Ordinal)) - { - outputLines.Add(nameLine); - addedName = true; - } - else if (line.StartsWith(@"Author:", StringComparison.Ordinal)) - { - outputLines.Add(authorLine); - addedAuthor = true; - } - else - outputLines.Add(line); - } - } - - if (!addedName || !addedAuthor) - { - outputLines.AddRange(new[] - { - @"[General]", - nameLine, - authorLine, - }); - } + @"", + @"// The following content was automatically added by osu! during import, based on filename / folder metadata.", + @"[General]", + nameLine, + authorLine, + }); using (Stream stream = new MemoryStream()) { using (var sw = new StreamWriter(stream, Encoding.UTF8, 1024, true)) { - foreach (string line in outputLines) + using (var existingStream = Files.Storage.GetStream(existingFile.FileInfo.StoragePath)) + using (var sr = new StreamReader(existingStream)) + { + string line; + while ((line = sr.ReadLine()) != null) + sw.WriteLine(line); + } + + foreach (string line in additionalLines) sw.WriteLine(line); } From 5d784b2ef84cbb0aed379b0255ae6246d8dc13fe Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Nov 2021 13:57:07 +0900 Subject: [PATCH 245/260] Perform a consistency check by decoding the newly written `skin.ini` during `ComputeHash` As this has regressed twice now, let's play it safe and bail rather than stack overflowing. Note that as all the underlying issues that could trigger this have been fixed, no additional tests have been added. To test, comment out `SkinManager.cs` line 228-229 to cause a failure. The new logic will kick in and show a log output message, but all tests will still (correctly) pass. --- osu.Game/Skinning/SkinManager.cs | 78 ++++++++++++++++++++------------ 1 file changed, 50 insertions(+), 28 deletions(-) diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index 8891fe3a06..35f4cbd3d0 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -194,50 +194,61 @@ namespace osu.Game.Skinning string nameLine = @$"Name: {item.Name}"; string authorLine = @$"Author: {item.Creator}"; + var newLines = new[] + { + @"// The following content was automatically added by osu! during import, based on filename / folder metadata.", + @"[General]", + nameLine, + authorLine, + }; + var existingFile = item.Files.SingleOrDefault(f => f.Filename.Equals(@"skin.ini", StringComparison.OrdinalIgnoreCase)); - if (existingFile != null) + if (existingFile == null) { - List additionalLines = new List(); + // In the case a skin doesn't have a skin.ini yet, let's create one. + writeNewSkinIni(); + return; + } - additionalLines.AddRange(new[] + using (Stream stream = new MemoryStream()) + { + using (var sw = new StreamWriter(stream, Encoding.UTF8, 1024, true)) { - @"", - @"// The following content was automatically added by osu! during import, based on filename / folder metadata.", - @"[General]", - nameLine, - authorLine, - }); - - using (Stream stream = new MemoryStream()) - { - using (var sw = new StreamWriter(stream, Encoding.UTF8, 1024, true)) + using (var existingStream = Files.Storage.GetStream(existingFile.FileInfo.StoragePath)) + using (var sr = new StreamReader(existingStream)) { - using (var existingStream = Files.Storage.GetStream(existingFile.FileInfo.StoragePath)) - using (var sr = new StreamReader(existingStream)) - { - string line; - while ((line = sr.ReadLine()) != null) - sw.WriteLine(line); - } - - foreach (string line in additionalLines) + string line; + while ((line = sr.ReadLine()) != null) sw.WriteLine(line); } - ReplaceFile(item, existingFile, stream); + sw.WriteLine(); + + foreach (string line in newLines) + sw.WriteLine(line); + } + + ReplaceFile(item, existingFile, stream); + + // can be removed 20220502. + if (!ensureIniWasUpdated(item)) + { + Logger.Log($"Skin {item}'s skin.ini had issues and has been removed. Please report this and provide the problematic skin.", LoggingTarget.Database, LogLevel.Important); + + DeleteFile(item, item.Files.SingleOrDefault(f => f.Filename.Equals(@"skin.ini", StringComparison.OrdinalIgnoreCase))); + writeNewSkinIni(); } } - else + + void writeNewSkinIni() { using (Stream stream = new MemoryStream()) { using (var sw = new StreamWriter(stream, Encoding.UTF8, 1024, true)) { - sw.WriteLine(@"[General]"); - sw.WriteLine(nameLine); - sw.WriteLine(authorLine); - sw.WriteLine(@"Version: latest"); + foreach (string line in newLines) + sw.WriteLine(line); } AddFile(item, stream, @"skin.ini"); @@ -245,6 +256,17 @@ namespace osu.Game.Skinning } } + private bool ensureIniWasUpdated(SkinInfo item) + { + // This is a final consistency check to ensure that hash computation doesn't enter an infinite loop. + // With other changes to the surrounding code this should never be hit, but until we are 101% sure that there + // are no other cases let's avoid a hard startup crash by bailing and alerting. + + var instance = GetSkin(item); + + return instance.Configuration.SkinInfo.Name == item.Name; + } + protected override Task Populate(SkinInfo model, ArchiveReader archive, CancellationToken cancellationToken = default) { var instance = GetSkin(model); From 9b705460dbf60e41749837fb87371893344ef919 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Nov 2021 14:53:54 +0900 Subject: [PATCH 246/260] Remove trailing exclamation marks from autoplay character names As mentioned in https://github.com/ppy/osu/discussions/15401#discussioncomment-1573839. --- osu.Game.Rulesets.Catch/Mods/CatchModAutoplay.cs | 2 +- osu.Game.Rulesets.Catch/Mods/CatchModCinema.cs | 2 +- osu.Game.Rulesets.Mania/Mods/ManiaModAutoplay.cs | 2 +- osu.Game.Rulesets.Mania/Mods/ManiaModCinema.cs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModAutoplay.cs b/osu.Game.Rulesets.Catch/Mods/CatchModAutoplay.cs index f1b51e51d0..6f3e6763bd 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModAutoplay.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModAutoplay.cs @@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Catch.Mods { public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList mods) => new Score { - ScoreInfo = new ScoreInfo { User = new User { Username = "osu!salad!" } }, + ScoreInfo = new ScoreInfo { User = new User { Username = "osu!salad" } }, Replay = new CatchAutoGenerator(beatmap).Generate(), }; } diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModCinema.cs b/osu.Game.Rulesets.Catch/Mods/CatchModCinema.cs index d53d019e90..1b7d254321 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModCinema.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModCinema.cs @@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Catch.Mods { public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList mods) => new Score { - ScoreInfo = new ScoreInfo { User = new User { Username = "osu!salad!" } }, + ScoreInfo = new ScoreInfo { User = new User { Username = "osu!salad" } }, Replay = new CatchAutoGenerator(beatmap).Generate(), }; } diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModAutoplay.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModAutoplay.cs index 6ae854e7f3..86f667466f 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModAutoplay.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModAutoplay.cs @@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Mania.Mods { public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList mods) => new Score { - ScoreInfo = new ScoreInfo { User = new User { Username = "osu!topus!" } }, + ScoreInfo = new ScoreInfo { User = new User { Username = "osu!topus" } }, Replay = new ManiaAutoGenerator((ManiaBeatmap)beatmap).Generate(), }; } diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModCinema.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModCinema.cs index 064c55ed8d..1c06bb389b 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModCinema.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModCinema.cs @@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Mania.Mods { public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList mods) => new Score { - ScoreInfo = new ScoreInfo { User = new User { Username = "osu!topus!" } }, + ScoreInfo = new ScoreInfo { User = new User { Username = "osu!topus" } }, Replay = new ManiaAutoGenerator((ManiaBeatmap)beatmap).Generate(), }; } From 1e9211c00ba15f1c1fb9a4e70f5c7143c09ed6fc Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 2 Nov 2021 15:41:25 +0900 Subject: [PATCH 247/260] Use explicit type --- osu.Game/Skinning/SkinManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index 35f4cbd3d0..0739026544 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -194,7 +194,7 @@ namespace osu.Game.Skinning string nameLine = @$"Name: {item.Name}"; string authorLine = @$"Author: {item.Creator}"; - var newLines = new[] + string[] newLines = { @"// The following content was automatically added by osu! during import, based on filename / folder metadata.", @"[General]", From e472656b3750705abcf7fdb2415cfbd726d245ef Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Nov 2021 17:11:34 +0900 Subject: [PATCH 248/260] Add test coverage of room participants updating on room join --- osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs index 11caf9f498..38cf9d662f 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs @@ -231,6 +231,9 @@ namespace osu.Game.Tests.Visual.Multiplayer } } }); + + AddAssert("Check participant count correct", () => client.APIRoom?.ParticipantCount.Value == 1); + AddAssert("Check participant list contains user", () => client.APIRoom?.RecentParticipants.Count(u => u.Id == API.LocalUser.Value.Id) == 1); } [Test] @@ -290,6 +293,9 @@ namespace osu.Game.Tests.Visual.Multiplayer AddUntilStep("wait for room open", () => this.ChildrenOfType().FirstOrDefault()?.IsLoaded == true); AddUntilStep("wait for join", () => client.Room != null); + + AddAssert("Check participant count correct", () => client.APIRoom?.ParticipantCount.Value == 1); + AddAssert("Check participant list contains user", () => client.APIRoom?.RecentParticipants.Count(u => u.Id == API.LocalUser.Value.Id) == 1); } [Test] From b0bd39fc4972b46b3577d90feab9aa2a1521e201 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Nov 2021 16:36:28 +0900 Subject: [PATCH 249/260] Fix multiplayer room participants display not updating as new users join/leave Can add tests on request, but not sure the code around this area is here to stay permanently. Should be pretty low risk. - Closes https://github.com/ppy/osu/issues/14788. --- .../Online/Multiplayer/MultiplayerClient.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index 28505f6b0e..bf4bb13655 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -148,6 +148,10 @@ namespace osu.Game.Online.Multiplayer { Room = joinedRoom; APIRoom = room; + + Debug.Assert(LocalUser != null); + addUserToAPIRoom(LocalUser); + foreach (var user in joinedRoom.Users) updateUserPlayingState(user.UserID, user.State); @@ -372,6 +376,8 @@ namespace osu.Game.Online.Multiplayer Room.Users.Add(user); + addUserToAPIRoom(user); + UserJoined?.Invoke(user); RoomUpdated?.Invoke(); }); @@ -391,6 +397,14 @@ namespace osu.Game.Online.Multiplayer return handleUserLeft(user, UserKicked); } + private void addUserToAPIRoom(MultiplayerRoomUser user) + { + Debug.Assert(APIRoom != null); + + APIRoom.RecentParticipants.Add(user.User); + APIRoom.ParticipantCount.Value++; + } + private Task handleUserLeft(MultiplayerRoomUser user, Action? callback) { if (Room == null) @@ -404,6 +418,10 @@ namespace osu.Game.Online.Multiplayer Room.Users.Remove(user); PlayingUserIds.Remove(user.UserID); + Debug.Assert(APIRoom != null); + APIRoom.RecentParticipants.RemoveAll(u => u.Id == user.UserID); + APIRoom.ParticipantCount.Value--; + callback?.Invoke(user); RoomUpdated?.Invoke(); }, false); From 1bfa1365a00c977483b523685b2478d587687c22 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Nov 2021 16:51:27 +0900 Subject: [PATCH 250/260] Rename "null user" tests to better explain purpose --- .../Multiplayer/TestSceneMultiplayerParticipantsList.cs | 4 ++-- osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs | 2 +- osu.Game/Tests/Visual/TestUserLookupCache.cs | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerParticipantsList.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerParticipantsList.cs index e50b150f94..2549681519 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerParticipantsList.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerParticipantsList.cs @@ -45,11 +45,11 @@ namespace osu.Game.Tests.Visual.Multiplayer } [Test] - public void TestAddNullUser() + public void TestAddUnresolvedUser() { AddAssert("one unique panel", () => this.ChildrenOfType().Select(p => p.User).Distinct().Count() == 1); - AddStep("add non-resolvable user", () => Client.AddNullUser()); + AddStep("add non-resolvable user", () => Client.TestAddUnresolvedUser()); AddAssert("null user added", () => Client.Room.AsNonNull().Users.Count(u => u.User == null) == 1); AddUntilStep("two unique panels", () => this.ChildrenOfType().Select(p => p.User).Distinct().Count() == 2); diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs index cd0f070d73..c6634abe82 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs @@ -62,7 +62,7 @@ namespace osu.Game.Tests.Visual.Multiplayer return roomUser; } - public void AddNullUser() => addUser(new MultiplayerRoomUser(TestUserLookupCache.NULL_USER_ID)); + public void TestAddUnresolvedUser() => addUser(new MultiplayerRoomUser(TestUserLookupCache.UNRESOLVED_USER_ID)); private void addUser(MultiplayerRoomUser user) { diff --git a/osu.Game/Tests/Visual/TestUserLookupCache.cs b/osu.Game/Tests/Visual/TestUserLookupCache.cs index b73e81d0dd..fcb9c070ff 100644 --- a/osu.Game/Tests/Visual/TestUserLookupCache.cs +++ b/osu.Game/Tests/Visual/TestUserLookupCache.cs @@ -14,11 +14,11 @@ namespace osu.Game.Tests.Visual /// A special user ID which would return a for. /// As a simulation to what a regular would return in the case of failing to fetch the user. /// - public const int NULL_USER_ID = -1; + public const int UNRESOLVED_USER_ID = -1; protected override Task ComputeValueAsync(int lookup, CancellationToken token = default) { - if (lookup == NULL_USER_ID) + if (lookup == UNRESOLVED_USER_ID) return Task.FromResult((User)null); return Task.FromResult(new User From 135df0e190cba4a8ef459d456911993a46d578d9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Nov 2021 16:51:42 +0900 Subject: [PATCH 251/260] Avoid adding null users to `RecentParticipants` --- osu.Game/Online/Multiplayer/MultiplayerClient.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index bf4bb13655..0586e0ae60 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -401,7 +401,11 @@ namespace osu.Game.Online.Multiplayer { Debug.Assert(APIRoom != null); - APIRoom.RecentParticipants.Add(user.User); + APIRoom.RecentParticipants.Add(user.User ?? new User + { + Id = user.UserID, + Username = "[Unresolved]" + }); APIRoom.ParticipantCount.Value++; } From 5486ca084e66e6a8bb751a153837b72e534769ae Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 2 Nov 2021 23:32:58 +0900 Subject: [PATCH 252/260] Resolve tests --- .../OsuDifficultyCalculatorTest.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs index 15675e74d1..7cd06c5225 100644 --- a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs @@ -15,13 +15,13 @@ namespace osu.Game.Rulesets.Osu.Tests { protected override string ResourceAssembly => "osu.Game.Rulesets.Osu"; - [TestCase(6.5867229481955389d, "diffcalc-test")] - [TestCase(1.0416315570967911d, "zero-length-sliders")] + [TestCase(6.5295339534769958d, "diffcalc-test")] + [TestCase(1.1514260533755143d, "zero-length-sliders")] public void Test(double expected, string name) => base.Test(expected, name); - [TestCase(8.2730989071947896d, "diffcalc-test")] - [TestCase(1.2726413186221039d, "zero-length-sliders")] + [TestCase(9.047752485219954d, "diffcalc-test")] + [TestCase(1.3985711787077566d, "zero-length-sliders")] public void TestClockRateAdjusted(double expected, string name) => Test(expected, name, new OsuModDoubleTime()); From be8a1f60c03d89e66a94fec9e8d3801e2c2a0031 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 2 Nov 2021 23:33:51 +0900 Subject: [PATCH 253/260] Apply styling changes --- osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs index f706646cdf..f1eb464a29 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs @@ -6,7 +6,6 @@ using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Difficulty.Preprocessing; using osu.Game.Rulesets.Osu.Objects; -using osu.Framework.Utils; namespace osu.Game.Rulesets.Osu.Difficulty.Skills { @@ -80,10 +79,12 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills if (osuCurrObj.StrainTime > 100) // Only buff deltaTime exceeding 300 bpm 1/2. acuteAngleBonus = 0; else + { acuteAngleBonus *= calcAcuteAngleBonus(prevAngle) // Multiply by previous angle, we don't want to buff unless this is a wiggle type pattern. - * Math.Min(angleBonus, 125 / osuCurrObj.StrainTime) // The maximum velocity we buff is equal to 125 / strainTime - * Math.Pow(Math.Sin(Math.PI / 2 * Math.Min(1, (100 - osuCurrObj.StrainTime) / 25)), 2) // scale buff from 150 bpm 1/4 to 200 bpm 1/4 - * Math.Pow(Math.Sin(Math.PI / 2 * (Math.Clamp(osuCurrObj.JumpDistance, 50, 100) - 50) / 50), 2); // Buff distance exceeding 50 (radius) up to 100 (diameter). + * Math.Min(angleBonus, 125 / osuCurrObj.StrainTime) // The maximum velocity we buff is equal to 125 / strainTime + * Math.Pow(Math.Sin(Math.PI / 2 * Math.Min(1, (100 - osuCurrObj.StrainTime) / 25)), 2) // scale buff from 150 bpm 1/4 to 200 bpm 1/4 + * Math.Pow(Math.Sin(Math.PI / 2 * (Math.Clamp(osuCurrObj.JumpDistance, 50, 100) - 50) / 50), 2); // Buff distance exceeding 50 (radius) up to 100 (diameter). + } wideAngleBonus *= angleBonus * (1 - Math.Min(wideAngleBonus, Math.Pow(calcWideAngleBonus(prevAngle), 3))); // Penalize wide angles if they're repeated, reducing the penalty as the prevAngle gets more acute. acuteAngleBonus *= 0.5 + 0.5 * (1 - Math.Min(acuteAngleBonus, Math.Pow(calcAcuteAngleBonus(lastAngle), 3))); // Penalize acute angles if they're repeated, reducing the penalty as the lastAngle gets more obtuse. From 5454de7ae876fee195d2e03feab8cab88516ebdd Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 2 Nov 2021 23:47:20 +0900 Subject: [PATCH 254/260] Refactor xmldocs --- .../Difficulty/Preprocessing/OsuDifficultyHitObject.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs index e5bb80d9a5..c274d08a1b 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs @@ -39,12 +39,12 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing public double? Angle { get; private set; } /// - /// Milliseconds elapsed since the end time of the Previous , with a minimum of 25ms. + /// Milliseconds elapsed since the end time of the previous , with a minimum of 25ms. /// public double MovementTime { get; private set; } /// - /// Milliseconds elapsed since from the start time of the Previous to the end time of the same Previous , with a minimum of 25ms. + /// Milliseconds elapsed since the start time of the previous to the end time of the same previous , with a minimum of 25ms. /// public double TravelTime { get; private set; } @@ -62,7 +62,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing this.lastLastObject = (OsuHitObject)lastLastObject; this.lastObject = (OsuHitObject)lastObject; - // Capped to 25ms to prevent difficulty calculation breaking from simulatenous objects. + // Capped to 25ms to prevent difficulty calculation breaking from simultaneous objects. StrainTime = Math.Max(DeltaTime, min_delta_time); setDistances(clockRate); From 7e0629774b1421c076d60da42a10ff3ff57c6ff0 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 2 Nov 2021 23:51:09 +0900 Subject: [PATCH 255/260] Rename variables for readability In particular, "last" vs "prev" can get confusing because they mean the same thing. --- .../Difficulty/Skills/Aim.cs | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs index f1eb464a29..d18d177515 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs @@ -35,12 +35,12 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills return 0; var osuCurrObj = (OsuDifficultyHitObject)current; - var osuPrevObj = (OsuDifficultyHitObject)Previous[0]; - var osuLastObj = (OsuDifficultyHitObject)Previous[1]; + var osuLastObj = (OsuDifficultyHitObject)Previous[0]; + var osuLastLastObj = (OsuDifficultyHitObject)Previous[1]; double currVelocity = osuCurrObj.JumpDistance / osuCurrObj.StrainTime; // Start with the base distance / time - if (osuPrevObj.BaseObject is Slider) // If object is a slider + if (osuLastObj.BaseObject is Slider) // If object is a slider { double movementVelocity = osuCurrObj.MovementDistance / osuCurrObj.MovementTime; // calculate the movement velocity from slider end to next note double travelVelocity = osuCurrObj.TravelDistance / osuCurrObj.TravelTime; // calculate the slider velocity from slider head to lazy end. @@ -48,12 +48,12 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills currVelocity = Math.Max(currVelocity, movementVelocity + travelVelocity); // take the larger total combined velocity. } - double prevVelocity = osuPrevObj.JumpDistance / osuPrevObj.StrainTime; // do the same for the previous velocity. + double prevVelocity = osuLastObj.JumpDistance / osuLastObj.StrainTime; // do the same for the previous velocity. - if (osuLastObj.BaseObject is Slider) + if (osuLastLastObj.BaseObject is Slider) { - double movementVelocity = osuPrevObj.MovementDistance / osuPrevObj.MovementTime; - double travelVelocity = osuPrevObj.TravelDistance / osuPrevObj.TravelTime; + double movementVelocity = osuLastObj.MovementDistance / osuLastObj.MovementTime; + double travelVelocity = osuLastObj.TravelDistance / osuLastObj.TravelTime; prevVelocity = Math.Max(prevVelocity, movementVelocity + travelVelocity); } @@ -62,13 +62,13 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills double aimStrain = currVelocity; // Start strain with regular velocity. - if (Math.Max(osuCurrObj.StrainTime, osuPrevObj.StrainTime) < 1.25 * Math.Min(osuCurrObj.StrainTime, osuPrevObj.StrainTime)) // If rhythms are the same. + if (Math.Max(osuCurrObj.StrainTime, osuLastObj.StrainTime) < 1.25 * Math.Min(osuCurrObj.StrainTime, osuLastObj.StrainTime)) // If rhythms are the same. { - if (osuCurrObj.Angle != null && osuPrevObj.Angle != null && osuLastObj.Angle != null) + if (osuCurrObj.Angle != null && osuLastObj.Angle != null && osuLastLastObj.Angle != null) { double currAngle = osuCurrObj.Angle.Value; - double prevAngle = osuPrevObj.Angle.Value; - double lastAngle = osuLastObj.Angle.Value; + double prevAngle = osuLastObj.Angle.Value; + double lastAngle = osuLastLastObj.Angle.Value; // Rewarding angles, take the smaller velocity as base. angleBonus = Math.Min(currVelocity, prevVelocity); From 93c03b9d37a25b8c187a124c6a90152a155187ee Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 3 Nov 2021 00:04:19 +0900 Subject: [PATCH 256/260] Refactor documentation around velocity calculations --- osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs index d18d177515..ccf69e30a2 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs @@ -38,17 +38,20 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills var osuLastObj = (OsuDifficultyHitObject)Previous[0]; var osuLastLastObj = (OsuDifficultyHitObject)Previous[1]; - double currVelocity = osuCurrObj.JumpDistance / osuCurrObj.StrainTime; // Start with the base distance / time + // Calculate the velocity to the current hitobject, which starts with a base distance / time assuming the last object is a hitcircle. + double currVelocity = osuCurrObj.JumpDistance / osuCurrObj.StrainTime; - if (osuLastObj.BaseObject is Slider) // If object is a slider + // But if the last object is a slider, then we extend the travel velocity through the slider into the current object. + if (osuLastObj.BaseObject is Slider) { - double movementVelocity = osuCurrObj.MovementDistance / osuCurrObj.MovementTime; // calculate the movement velocity from slider end to next note - double travelVelocity = osuCurrObj.TravelDistance / osuCurrObj.TravelTime; // calculate the slider velocity from slider head to lazy end. + double movementVelocity = osuCurrObj.MovementDistance / osuCurrObj.MovementTime; // calculate the movement velocity from slider end to current object + double travelVelocity = osuCurrObj.TravelDistance / osuCurrObj.TravelTime; // calculate the slider velocity from slider head to slider end. currVelocity = Math.Max(currVelocity, movementVelocity + travelVelocity); // take the larger total combined velocity. } - double prevVelocity = osuLastObj.JumpDistance / osuLastObj.StrainTime; // do the same for the previous velocity. + // As above, do the same for the previous hitobject. + double prevVelocity = osuLastObj.JumpDistance / osuLastObj.StrainTime; if (osuLastLastObj.BaseObject is Slider) { @@ -59,7 +62,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills } double angleBonus = 0; - double aimStrain = currVelocity; // Start strain with regular velocity. if (Math.Max(osuCurrObj.StrainTime, osuLastObj.StrainTime) < 1.25 * Math.Min(osuCurrObj.StrainTime, osuLastObj.StrainTime)) // If rhythms are the same. From b0d9c0eca3709d22b2bae21cc89b36d1b42a2cb3 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 3 Nov 2021 00:16:33 +0900 Subject: [PATCH 257/260] Refactor variables for readability in angle calculations --- osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs index ccf69e30a2..a054b46366 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs @@ -69,8 +69,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills if (osuCurrObj.Angle != null && osuLastObj.Angle != null && osuLastLastObj.Angle != null) { double currAngle = osuCurrObj.Angle.Value; - double prevAngle = osuLastObj.Angle.Value; - double lastAngle = osuLastLastObj.Angle.Value; + double lastAngle = osuLastObj.Angle.Value; + double lastLastAngle = osuLastLastObj.Angle.Value; // Rewarding angles, take the smaller velocity as base. angleBonus = Math.Min(currVelocity, prevVelocity); @@ -82,16 +82,16 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills acuteAngleBonus = 0; else { - acuteAngleBonus *= calcAcuteAngleBonus(prevAngle) // Multiply by previous angle, we don't want to buff unless this is a wiggle type pattern. + acuteAngleBonus *= calcAcuteAngleBonus(lastAngle) // Multiply by previous angle, we don't want to buff unless this is a wiggle type pattern. * Math.Min(angleBonus, 125 / osuCurrObj.StrainTime) // The maximum velocity we buff is equal to 125 / strainTime * Math.Pow(Math.Sin(Math.PI / 2 * Math.Min(1, (100 - osuCurrObj.StrainTime) / 25)), 2) // scale buff from 150 bpm 1/4 to 200 bpm 1/4 * Math.Pow(Math.Sin(Math.PI / 2 * (Math.Clamp(osuCurrObj.JumpDistance, 50, 100) - 50) / 50), 2); // Buff distance exceeding 50 (radius) up to 100 (diameter). } - wideAngleBonus *= angleBonus * (1 - Math.Min(wideAngleBonus, Math.Pow(calcWideAngleBonus(prevAngle), 3))); // Penalize wide angles if they're repeated, reducing the penalty as the prevAngle gets more acute. - acuteAngleBonus *= 0.5 + 0.5 * (1 - Math.Min(acuteAngleBonus, Math.Pow(calcAcuteAngleBonus(lastAngle), 3))); // Penalize acute angles if they're repeated, reducing the penalty as the lastAngle gets more obtuse. + wideAngleBonus *= angleBonus * (1 - Math.Min(wideAngleBonus, Math.Pow(calcWideAngleBonus(lastAngle), 3))); // Penalize wide angles if they're repeated, reducing the penalty as the lastAngle gets more acute. + acuteAngleBonus *= 0.5 + 0.5 * (1 - Math.Min(acuteAngleBonus, Math.Pow(calcAcuteAngleBonus(lastLastAngle), 3))); // Penalize acute angles if they're repeated, reducing the penalty as the lastLastAngle gets more obtuse. - angleBonus = acuteAngleBonus * acute_angle_multiplier + wideAngleBonus * wide_angle_multiplier; // add the anglebuffs together. + angleBonus = acuteAngleBonus * acute_angle_multiplier + wideAngleBonus * wide_angle_multiplier; // add the angle buffs together. } } From 133218ecbb41f87f8d6975380ccff5805da1a010 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 3 Nov 2021 01:04:07 +0900 Subject: [PATCH 258/260] Document special MovementDistance for sliders, ensure always has a value --- .../Preprocessing/OsuDifficultyHitObject.cs | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs index c274d08a1b..4b90285fd4 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs @@ -83,19 +83,29 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing scalingFactor *= 1 + smallCircleBonus; } + Vector2 lastCursorPosition = getEndCursorPosition(lastObject); + JumpDistance = (BaseObject.StackedPosition * scalingFactor - lastCursorPosition * scalingFactor).Length; + if (lastObject is Slider lastSlider) { computeSliderCursorPosition(lastSlider); TravelDistance = lastSlider.LazyTravelDistance * scalingFactor; TravelTime = Math.Max(lastSlider.LazyTravelTime / clockRate, min_delta_time); MovementTime = Math.Max(StrainTime - TravelTime, min_delta_time); - MovementDistance = Vector2.Subtract(lastSlider.TailCircle.StackedPosition, BaseObject.StackedPosition).Length * scalingFactor; + + // Jump distance from the slider tail to the next object, as opposed to the lazy position of JumpDistance. + float tailJumpDistance = Vector2.Subtract(lastSlider.TailCircle.StackedPosition, BaseObject.StackedPosition).Length * scalingFactor; + + // For hitobjects which continue in the direction of the slider, the player will normally follow through the slider, + // such that they're not jumping from the lazy position but rather from very close to (or the end of) the slider. + // In such cases, a leniency is applied by also considering the jump distance from the tail of the slider, and taking the minimum jump distance. + MovementDistance = Math.Min(JumpDistance, tailJumpDistance); + } + else + { + MovementTime = StrainTime; + MovementDistance = JumpDistance; } - - Vector2 lastCursorPosition = getEndCursorPosition(lastObject); - - JumpDistance = (BaseObject.StackedPosition * scalingFactor - lastCursorPosition * scalingFactor).Length; - MovementDistance = Math.Min(JumpDistance, MovementDistance); if (lastLastObject != null && !(lastLastObject is Spinner)) { From 9afca5c5b62173d8deee5811ef58de10f6abb318 Mon Sep 17 00:00:00 2001 From: Xexxar Date: Wed, 3 Nov 2021 15:38:23 +0000 Subject: [PATCH 259/260] rewrote slider algorithm to be based on leniency and stored current cursor positions --- .../Preprocessing/OsuDifficultyHitObject.cs | 63 +++++++++++++------ 1 file changed, 43 insertions(+), 20 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs index 8722b85839..fd14f664d8 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs @@ -93,37 +93,60 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing int repeatCount = 0; + Vector2 currSliderPosition = ((OsuHitObject)lastSlider.NestedHitObjects[0]).StackedPosition; + for (int i = 1; i < lastSlider.NestedHitObjects.Count; i++) { - Vector2 currSlider = Vector2.Subtract(((OsuHitObject)lastSlider.NestedHitObjects[i]).StackedPosition, ((OsuHitObject)lastSlider.NestedHitObjects[i - 1]).StackedPosition); + Vector2 currSlider = Vector2.Subtract(((OsuHitObject)lastSlider.NestedHitObjects[i]).StackedPosition, currSliderPosition); + double currSliderLength = currSlider.Length * scalingFactor; - if ((OsuHitObject)lastSlider.NestedHitObjects[i] is SliderRepeat && (OsuHitObject)lastSlider.NestedHitObjects[i - 1] is SliderRepeat) + if ((OsuHitObject)lastSlider.NestedHitObjects[i] is SliderEndCircle && !((OsuHitObject)lastSlider.NestedHitObjects[i] is SliderRepeat)) { - repeatCount++; - TravelDistance += Math.Max(0, currSlider.Length * scalingFactor - 240); // remove 240 distance to avoid buffing overlapping followcircles. + Vector2 possSlider = Vector2.Subtract((Vector2)lastSlider.LazyEndPosition, currSliderPosition); + if (possSlider.Length < currSlider.Length) + currSlider = possSlider; // Take the least distance from slider end vs lazy end. + + currSliderLength = currSlider.Length * scalingFactor; + } + + if ((OsuHitObject)lastSlider.NestedHitObjects[i] is SliderTick) + { + if (currSliderLength > 120) + { + currSliderPosition = Vector2.Add(currSliderPosition, Vector2.Multiply(currSlider, (float)((currSliderLength - 120) / currSliderLength))); + currSliderLength *= (currSliderLength - 120) / currSliderLength; + } + else + currSliderLength = 0; } else if ((OsuHitObject)lastSlider.NestedHitObjects[i] is SliderRepeat) { - repeatCount++; - TravelDistance += Math.Max(0, currSlider.Length * scalingFactor - 120); // remove 120 distance to avoid buffing overlapping followcircles. - } - else if ((OsuHitObject)lastSlider.NestedHitObjects[i] is SliderEndCircle) - { - Vector2 possSlider = Vector2.Subtract((Vector2)lastSlider.LazyEndPosition, ((OsuHitObject)lastSlider.NestedHitObjects[i - 1]).StackedPosition); - TravelDistance += Math.Min(possSlider.Length, currSlider.Length) * scalingFactor; // Take the least distance from slider end vs lazy end. + if (currSliderLength > 50) + { + currSliderPosition = Vector2.Add(currSliderPosition, Vector2.Multiply(currSlider, (float)((currSliderLength - 50) / currSliderLength))); + currSliderLength *= (currSliderLength - 50) / currSliderLength; + } + else + currSliderLength = 0; } else - TravelDistance += Math.Max(0, currSlider.Length * scalingFactor - 100); // remove 100 distance to avoid buffing overlapping ticks, mostly needed to prevent buffing slow sliders with high tick rate. + { + if (currSliderLength > 0) + { + currSliderPosition = Vector2.Add(currSliderPosition, Vector2.Multiply(currSlider, (float)((currSliderLength - 0) / currSliderLength))); + currSliderLength *= (currSliderLength - 0) / currSliderLength; + } + else + currSliderLength = 0; + } + + if ((OsuHitObject)lastSlider.NestedHitObjects[i] is SliderRepeat) + repeatCount++; + + TravelDistance += currSliderLength; } - if (repeatCount == 0) - { - TravelDistance = Math.Max(TravelDistance, scalingFactor * // idea here is to prevent ticks from dropping difficulty of slider by removing distance in calculation. - Math.Min(Vector2.Subtract(((OsuHitObject)lastSlider.NestedHitObjects[lastSlider.NestedHitObjects.Count - 1]).StackedPosition, ((OsuHitObject)lastSlider.NestedHitObjects[0]).StackedPosition).Length, - Vector2.Subtract((Vector2)lastSlider.LazyEndPosition, ((OsuHitObject)lastSlider.NestedHitObjects[0]).StackedPosition).Length)); - } - else - TravelDistance *= Math.Pow(1 + repeatCount / 2.0, 1.0 / 2.0); // Bonus for repeat sliders until a better per nested object strain system can be achieved. + TravelDistance *= Math.Pow(1 + repeatCount / 2.5, 1.0 / 2.5); // Bonus for repeat sliders until a better per nested object strain system can be achieved. } Vector2 lastCursorPosition = getEndCursorPosition(lastObject); From d74e18153108cb893c027c44ea3b8373844aaf32 Mon Sep 17 00:00:00 2001 From: Xexxar Date: Wed, 3 Nov 2021 15:54:49 +0000 Subject: [PATCH 260/260] renamed prev to last --- osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs index 8f6a92a3a9..3bdb397909 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs @@ -102,16 +102,16 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills if (Math.Max(prevVelocity, currVelocity) != 0) { - prevVelocity = (osuPrevObj.JumpDistance + osuPrevObj.TravelDistance) / osuPrevObj.StrainTime; // We want to use the average velocity when awarding differences, not necessarily combined. + prevVelocity = (osuLastObj.JumpDistance + osuLastObj.TravelDistance) / osuLastObj.StrainTime; // We want to use the average velocity when awarding differences, not necessarily combined. currVelocity = (osuCurrObj.JumpDistance + osuCurrObj.TravelDistance) / osuCurrObj.StrainTime; velChangeBonus = Math.Max(Math.Abs(prevVelocity - currVelocity) // reward for % distance slowed down compared to previous, paying attention to not award overlap * Math.Pow(Math.Sin(Math.PI / 2 * Math.Min(1, osuCurrObj.JumpDistance / 100)), 2) // do not award overlap * Math.Pow(Math.Sin(Math.PI / 2 * Math.Abs(prevVelocity - currVelocity) / Math.Max(prevVelocity, currVelocity)), 2), // scale with ratio of difference compared to max - Math.Min(125 / Math.Min(osuCurrObj.StrainTime, osuPrevObj.StrainTime), Math.Abs(prevVelocity - currVelocity)) // reward for % distance up to 125 / strainTime for overlaps where velocity is still changing. + Math.Min(125 / Math.Min(osuCurrObj.StrainTime, osuLastObj.StrainTime), Math.Abs(prevVelocity - currVelocity)) // reward for % distance up to 125 / strainTime for overlaps where velocity is still changing. * Math.Pow(Math.Sin(Math.PI / 2 * Math.Abs(prevVelocity - currVelocity) / Math.Max(prevVelocity, currVelocity)), 2)); // scale with ratio of difference compared to max - velChangeBonus *= Math.Pow(Math.Min(osuCurrObj.StrainTime, osuPrevObj.StrainTime) / Math.Max(osuCurrObj.StrainTime, osuPrevObj.StrainTime), 2); // penalize for rhythm changes. + velChangeBonus *= Math.Pow(Math.Min(osuCurrObj.StrainTime, osuLastObj.StrainTime) / Math.Max(osuCurrObj.StrainTime, osuLastObj.StrainTime), 2); // penalize for rhythm changes. } if (osuCurrObj.TravelTime != 0)